]> git.neil.brown.name Git - edlib.git/commitdiff
Initial python support.
authorNeilBrown <neil@brown.name>
Sun, 29 Nov 2015 21:49:16 +0000 (08:49 +1100)
committerNeilBrown <neil@brown.name>
Tue, 1 Dec 2015 04:40:53 +0000 (15:40 +1100)
Add a new loadable module 'lang-python' which provides a
"python-load" command which will run a python script.

This has access to panes and marks and can send and receive commands...
At least it can in principle - only limited testing so far.

I think it is a good starting point for python support.

Signed-off-by: NeilBrown <neil@brown.name>
Makefile
edlib.c
lang-python.c [new file with mode: 0644]
python/test.py [new file with mode: 0644]

index 0caac2f9f55492c07aadca84f2c46a52b5b63d9b..73fb8e0e5659377683b37eb168bc0427ffe2f8ff 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,8 @@
 # May be distrubuted under terms of GPLv2 - see file:COPYING
 #
 
-LDLIBS= -lncursesw -levent -ldl
-CPPFLAGS= -I. -I/usr/include/ncursesw
+LDLIBS= -levent -ldl
+CPPFLAGS= -I.
 CFLAGS=-g -Wall -Werror -Wstrict-prototypes -Wextra -Wno-unused-parameter
 
 all: dirs edlib checksym lib shared
@@ -17,10 +17,17 @@ SHOBJ = O/doc-text.o O/doc-dir.o \
        O/render-format.o O/render-complete.o \
        O/lib-view.o O/lib-tile.o O/lib-popup.o O/lib-line-count.o O/lib-keymap.o \
        O/lib-search.o \
+       O/lang-python.o \
        O/mode-emacs.o \
        O/display-ncurses.o
 XOBJ = O/rexel.o O/emacs-search.o
 
+LIBS-lang-python = -lpython2.7
+INC-lang-python = -I/usr/include/python2.7
+
+LIBS-display-ncurses = -lncursesw
+INC-display-ncurses = -I/usr/include/ncursesw
+
 SO = $(patsubst O/%.o,lib/edlib-%.so,$(SHOBJ))
 H = list.h core.h misc.h
 edlib: $(OBJ) lib/libedlib.so
@@ -29,10 +36,10 @@ edlib: $(OBJ) lib/libedlib.so
 $(OBJ) $(SHOBJ) $(LIBOBJ) $(XOBJ) : $(H)
 
 $(OBJ) : O/%.o : %.c
-       gcc $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+       gcc $(CPPFLAGS) $(INC-$*) $(CFLAGS) -c -o $@ $<
 
 $(SHOBJ) $(LIBOBJ) $(XOBJ) : O/%.o : %.c
-       gcc -fPIC $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+       gcc -fPIC $(CPPFLAGS) $(INC-$*) $(CFLAGS) -c -o $@ $<
 
 dirs :
        @mkdir -p lib O
@@ -48,8 +55,7 @@ lib/edlib-mode-emacs.so : O/mode-emacs.o O/emacs-search.o
 
 $(SO) : lib/edlib-%.so : O/%.o
        @mkdir -p lib
-       gcc -shared -Wl,-soname,edlib-$*.so -o $@ $^
-
+       gcc -shared -Wl,-soname,edlib-$*.so -o $@ $^ $(LIBS-$*)
 
 CSRC= attr.c
 
diff --git a/edlib.c b/edlib.c
index 4fdf87c56641ac0be0bd8514da40d334c7964e7b..cc1f76bb729978317cfc79b4f12693b4e48d4d8b 100644 (file)
--- a/edlib.c
+++ b/edlib.c
@@ -78,6 +78,13 @@ int main(int argc, char *argv[])
        if (b)
                p = doc_from_text(b, "*Welcome*", WelcomeText);
        if (p) {
+               editor_load_module(ed, "lang-python");
+               memset(&ci, 0, sizeof(ci));
+               ci.home = ci.focus = p;
+               ci.key = "python-load";
+               ci.str = "python/test.py";
+               key_lookup(ed->commands, &ci);
+
                pane_refresh(root);
                event_base_dispatch(base);
        }
diff --git a/lang-python.c b/lang-python.c
new file mode 100644 (file)
index 0000000..ed01b9f
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * Copyright Neil Brown <neil@brown.name> 2015
+ * May be distributed under terms of GPLv2 - see file:COPYING
+ *
+ * Python bindings for edlib.
+ * And edlib command "python-load" will read and execute a python
+ * script.
+ * It can use "edlib.editor" to get the editor instance, and can
+ * use "edlib.call()" to issue edlib commands.
+ *
+ * Types available are:
+ *  edlib.pane  - a generic pane.  These form a tree of which edlib.editor
+ *                is the root.
+ *                get/set operations for x,y,z,w,h,cx,cy as numbers
+ *                     changes x,y,w,h call pane_resize()
+ *                     z cannot be changed (only pane_register() does that )
+ *                     cx,cy can be changed freely.
+ *                  'parent' and 'focus' can be read but not written
+ *                method for 'children' provides first child as an iterator.
+ *  edlib.mark  - these reference locations in a document.  The document is
+ *                not directly accessible, it can only be accessed through
+ *                a pane (which may translate events and results).
+ *                get/set operations for rpos.
+ *                get/set for viewnum (cannot be set)
+ *                iterator for both 'all' and 'view' lists.
+ *                no methods yet.
+ *
+ *
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "core.h"
+
+PyObject *Edlib_CommandFailed;
+PyObject *EdlibModule;
+
+typedef struct {
+       PyObject_HEAD
+       struct pane     *pane;
+} Pane;
+static PyTypeObject PaneType;
+
+typedef struct {
+       PyObject_HEAD
+       struct mark     *mark;
+       short           iter_type;
+} Mark;
+static PyTypeObject MarkType;
+#define        ITER_ALL        1
+#define        ITER_VIEW       2
+#define        ITER_VIEW_REVERSE 3
+
+static inline PyObject *Pane_Frompane(struct pane *p)
+{
+       Pane *pane = (Pane *)PyObject_CallObject((PyObject*)&PaneType, NULL);
+       pane->pane = p;
+       return (PyObject*)pane;
+}
+
+static inline PyObject *Mark_Frommark(struct mark *m)
+{
+       Mark *mark = (Mark *)PyObject_CallObject((PyObject*)&MarkType, NULL);
+       mark->mark = m;
+       return (PyObject*)mark;
+}
+
+DEF_CMD(python_load)
+{
+       char *fname = ci->str;
+       FILE *fp;
+       PyObject *globals;
+       struct editor *ed = pane2ed(ci->home);
+       PyObject *Ed;
+
+       if (!fname)
+               return -1;
+       fp = fopen(fname, "r");
+       if (!fp)
+               return -1;
+
+       Ed = Pane_Frompane(&ed->root);
+       globals = PyDict_New();
+       PyDict_SetItemString(globals, "editor", Ed);
+       PyDict_SetItemString(globals, "pane", Pane_Frompane(ci->home));
+       PyDict_SetItemString(globals, "edlib", EdlibModule);
+       PyRun_FileExFlags(fp, fname, Py_file_input, globals, globals, 0, NULL);
+       PyErr_Print();
+       Py_DECREF(Ed);
+       Py_DECREF(globals);
+       fclose(fp);
+       return 1;
+}
+
+/* When a python callable is passed to a edlib_call() we combine it
+ * with this "python_call" to edlib to call back that callable.
+ */
+struct python_command {
+       struct command  c;
+       PyObject        *callable;
+};
+
+DEF_CMD(python_call)
+{
+       struct python_command *pc = container_of(ci->comm, struct python_command, c);
+       PyObject *ret, *args, *kwds;
+       int rv = 1;
+
+       args = Py_BuildValue("(s)", ci->str);
+       kwds = PyDict_New();
+       if (ci->focus)
+               PyDict_SetItemString(kwds, "focus", Pane_Frompane(ci->focus));
+       if (ci->home)
+               PyDict_SetItemString(kwds, "home", Pane_Frompane(ci->home));
+       if (ci->mark)
+               PyDict_SetItemString(kwds, "mark", Mark_Frommark(ci->mark));
+       if (ci->mark2)
+               PyDict_SetItemString(kwds, "mark2", Mark_Frommark(ci->mark2));
+       if (ci->str)
+               PyDict_SetItemString(kwds, "str", Py_BuildValue("s", ci->str));
+       if (ci->str2)
+               PyDict_SetItemString(kwds, "str2", Py_BuildValue("s", ci->str2));
+       PyDict_SetItemString(kwds, "numeric", Py_BuildValue("i", ci->numeric));
+       PyDict_SetItemString(kwds, "extra", Py_BuildValue("i", ci->extra));
+       PyDict_SetItemString(kwds, "xy", Py_BuildValue("ii", ci->x, ci->y));
+       PyDict_SetItemString(kwds, "hxy", Py_BuildValue("ii", ci->hx, ci->hy));
+       /* FIXME what about ->comm and ->comm2?? */
+
+       ret = PyObject_Call(pc->callable, args, kwds);
+
+       Py_DECREF(args);
+       Py_DECREF(kwds);
+       if (!ret) {
+               /* FIXME cancel error?? */
+               return -1;
+       }
+       if (PyDict_Check(ret)) {
+               PyObject *r;
+               r = PyDict_GetItemString(ret, "focus");
+               if (r && Py_TYPE(r) == &PaneType)
+                       ci->focus = ((Pane*)r)->pane;
+               r = PyDict_GetItemString(ret, "home");
+               if (r && Py_TYPE(r) == &PaneType)
+                       ci->home = ((Pane*)r)->pane;
+
+               r = PyDict_GetItemString(ret, "mark");
+               if (r && Py_TYPE(r) == &MarkType)
+                       ci->mark = ((Mark*)r)->mark;
+               r = PyDict_GetItemString(ret, "mark2");
+               if (r && Py_TYPE(r) == &MarkType)
+                       ci->mark2 = ((Mark*)r)->mark;
+
+               r = PyDict_GetItemString(ret, "str");
+               if (r && PyString_Check(r))
+                       ci->str = PyString_AsString(r);
+               r = PyDict_GetItemString(ret, "str2");
+               if (r && PyString_Check(r))
+                       ci->str2 = PyString_AsString(r);
+
+               r = PyDict_GetItemString(ret, "numeric");
+               if (r && PyInt_Check(r))
+                       ci->numeric = PyInt_AsLong(r);
+               r = PyDict_GetItemString(ret, "extra");
+               if (r && PyInt_Check(r))
+                       ci->extra = PyInt_AsLong(r);
+
+               /* FIXME xy and hxy */
+       }
+       Py_DECREF(ret);
+       return rv;
+}
+
+static Pane *pane_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+       Pane *self;
+
+       self = (Pane *)type->tp_alloc(type, 0);
+       if (self) {
+               self->pane = NULL;
+       }
+       return self;
+}
+
+static void pane_dealloc(Pane *self)
+{
+       self->ob_type->tp_free((PyObject*)self);
+}
+
+static Pane *pane_children(Pane *self)
+{
+       if (!self->pane) {
+               PyErr_SetString(PyExc_TypeError, "Pane is NULL");
+               return NULL;
+       }
+       if (list_empty(&self->pane->children)) {
+               /* FIXME is this right for an empty iterator? */
+               Py_INCREF(Py_None);
+               return (Pane*)Py_None;
+       }
+
+       return (Pane*)Pane_Frompane(list_first_entry(&self->pane->children,
+                                                    struct pane, siblings));
+}
+
+static Pane *pane_this(Pane *self)
+{
+       return (Pane *)Pane_Frompane(self->pane);
+}
+
+static Pane *pane_next(Pane *self)
+{
+       if (!self->pane) {
+               PyErr_SetString(PyExc_TypeError, "Pane is NULL");
+               return NULL;
+       }
+       if (self->pane->siblings.next == &self->pane->parent->children) {
+               /* Reached the end of the list */
+               return NULL;
+       }
+
+       return (Pane*)Pane_Frompane(list_next_entry(self->pane, siblings));
+}
+
+static PyMethodDef pane_methods[] = {
+       {"children", (PyCFunction)pane_children, METH_NOARGS,
+        "provides and iterator which will iterate over all children"},
+       {NULL}
+};
+
+static PyObject *pane_getnum(Pane *p, char *which)
+{
+       long n = 0;
+       if (p->pane == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Pane is NULL");
+               return NULL;
+       }
+       switch(*which) {
+       case 'x': n = p->pane->x; break;
+       case 'y': n = p->pane->y; break;
+       case 'w': n = p->pane->w; break;
+       case 'h': n = p->pane->h; break;
+       case 'X': n = p->pane->cx; break;
+       case 'Y': n = p->pane->cy; break;
+       case 'z': n = p->pane->z; break;
+       }
+       return PyInt_FromLong(n);
+}
+
+static int pane_setnum(Pane *p, PyObject *v, char *which)
+{
+       int x,y,w,h;
+       long val;
+
+       if (p->pane == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Pane is NULL");
+               return -1;
+       }
+       if (*which == 'z') {
+               PyErr_SetString(PyExc_TypeError, "z cannot be set");
+               return -1;
+       }
+       val = PyInt_AsLong(v);
+       if (val == -1 && PyErr_Occurred())
+               return -1;
+
+       x = p->pane->x; y = p->pane->y;
+       w = p->pane->w; h = p->pane->h;
+       switch(*which) {
+       case 'x': x = val; break;
+       case 'y': y = val; break;
+       case 'w': w = val; break;
+       case 'h': h = val; break;
+       case 'X': p->pane->cx = val; return 0;
+       case 'Y': p->pane->cy = val; return 0;
+       }
+       pane_resize(p->pane, x, y, w, h);
+       return 0;
+}
+
+static Pane *pane_getpane(Pane *p, char *which)
+{
+       Pane *newpane = (Pane *)Pane_Frompane(NULL);
+
+       if (p->pane == NULL) {
+               Py_DECREF(newpane);
+               PyErr_SetString(PyExc_TypeError, "pane not initialized");
+               return NULL;
+       }
+       if (*which == 'p')
+               newpane->pane = p->pane->parent;
+       if (*which == 'f')
+               newpane->pane = p->pane->focus;
+       return newpane;
+}
+
+static int pane_nosetpane(Pane *p, PyObject *v, void *which)
+{
+       PyErr_SetString(PyExc_TypeError, "Cannot set panes");
+       return -1;
+}
+
+static PyObject *pane_repr(Pane *p)
+{
+       char buf[50];
+       sprintf(buf, "<pane-0x%p>", p->pane);
+       return Py_BuildValue("s", buf);
+}
+
+static PyGetSetDef pane_getseters[] = {
+    {"x",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "X offset in parent", "x" },
+    {"y",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "Y offset in parent", "y" },
+    {"z",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "Z offset in parent", "z" },
+    {"w",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "width of pane", "w" },
+    {"h",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "heigth of pane", "h" },
+    {"cx",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "Cursor X offset in pane", "X" },
+    {"cy",
+     (getter)pane_getnum, (setter)pane_setnum,
+     "Cursor Y offset in pane", "Y" },
+    {"parent",
+     (getter)pane_getpane, (setter)pane_nosetpane,
+     "Parent pane", "p"},
+    {"focus",
+     (getter)pane_getpane, (setter)pane_nosetpane,
+     "Focal child", "f"},
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject PaneType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "edlib.Pane",              /*tp_name*/
+    sizeof(Pane),              /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)pane_dealloc,  /*tp_dealloc*/
+    0,                         /*tp_print*/
+    0,                         /*tp_getattr*/
+    0,                         /*tp_setattr*/
+    0,                         /*tp_compare*/
+    (reprfunc)pane_repr,       /*tp_repr*/
+    0,                         /*tp_as_number*/
+    0,                         /*tp_as_sequence*/
+    0,                         /*tp_as_mapping*/
+    0,                         /*tp_hash */
+    0,                         /*tp_call*/
+    0,                         /*tp_str*/
+    0,                         /*tp_getattro*/
+    0,                         /*tp_setattro*/
+    0,                         /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+    "edlib panes",             /* tp_doc */
+    0,                         /* tp_traverse */
+    0,                         /* tp_clear */
+    0,                         /* tp_richcompare */
+    0,                         /* tp_weaklistoffset */
+    (getiterfunc)pane_this,    /* tp_iter */
+    (iternextfunc)pane_next,   /* tp_iternext */
+    pane_methods,              /* tp_methods */
+    0,                         /* tp_members */
+    pane_getseters,            /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    0,                         /* tp_init */
+    0,                         /* tp_alloc */
+    .tp_new = (newfunc)pane_new,/* tp_new */
+};
+
+static PyObject *mark_getrpos(Mark *m, void *x)
+{
+       if (m->mark == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Mark is NULL");
+               return NULL;
+       }
+       return PyInt_FromLong(m->mark->rpos);
+}
+
+static int mark_setrpos(Mark *m, PyObject *v, void *x)
+{
+       long val;
+
+       if (m->mark == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Mark is NULL");
+               return -1;
+       }
+       val = PyInt_AsLong(v);
+       if (val == -1 && PyErr_Occurred())
+               return -1;
+       m->mark->rpos = val;
+       return 0;
+}
+
+static PyObject *mark_getview(Mark *m, void *x)
+{
+       if (m->mark == NULL) {
+               PyErr_SetString(PyExc_TypeError, "Mark is NULL");
+               return NULL;
+       }
+       return PyInt_FromLong(m->mark->viewnum);
+}
+
+static int mark_nosetview(Mark *m, PyObject *v, void *which)
+{
+       PyErr_SetString(PyExc_TypeError, "Cannot set mark viewnum");
+       return -1;
+}
+
+PyObject *mark_compare(Mark *a, Mark *b, int op)
+{
+       int ret = 0;
+       switch(op) {
+       case Py_LT: ret = mark_ordered(a->mark, b->mark); break;
+       case Py_LE: ret = mark_ordered(a->mark, b->mark); break;
+       case Py_GT: ret = mark_ordered(a->mark, b->mark); break;
+       case Py_GE: ret = mark_ordered(a->mark, b->mark); break;
+       case Py_EQ: ret = mark_ordered(a->mark, b->mark); break;
+       case Py_NE: ret = mark_ordered(a->mark, b->mark); break;
+       }
+       return ret ? Py_True : Py_False;
+}
+
+static Mark *mark_this(Mark *self)
+{
+       Mark *ret = (Mark*)Mark_Frommark(self->mark);
+       ret->iter_type = ITER_ALL;
+       return ret;
+}
+
+static Mark *Mark_next(Mark *self)
+{
+       struct mark *next;
+
+       if (!self->mark) {
+               PyErr_SetString(PyExc_TypeError, "Mark is NULL");
+               return NULL;
+       }
+       switch (self->iter_type) {
+       default:
+       case ITER_ALL: next = doc_next_mark_all(self->mark); break;
+       case ITER_VIEW: next = vmark_next(self->mark); break;
+       case ITER_VIEW_REVERSE: next = vmark_prev(self->mark); break;
+       }
+       self->mark = next;
+       if (next == NULL) {
+               /* Reached the end of the list */
+               return NULL;
+       }
+
+       return (Mark*)Mark_Frommark(next);
+}
+
+static PyGetSetDef mark_getseters[] = {
+    {"rpos",
+     (getter)mark_getrpos, (setter)mark_setrpos,
+     "Rendering Position",  NULL},
+    {"viewnum",
+     (getter)mark_getview, (setter)mark_nosetview,
+     "Index for view list", NULL},
+    {NULL}  /* Sentinel */
+};
+
+static Mark *mark_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+       Mark *self;
+
+       self = (Mark *)type->tp_alloc(type, 0);
+       if (self) {
+               self->mark = NULL;
+       }
+       return self;
+}
+
+static void mark_dealloc(Mark *self)
+{
+       self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyTypeObject MarkType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "edlib.Mark",              /*tp_name*/
+    sizeof(Mark),              /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)mark_dealloc,  /*tp_dealloc*/
+    0,                         /*tp_print*/
+    0,                         /*tp_getattr*/
+    0,                         /*tp_setattr*/
+    0,                         /*tp_compare*/
+    0,                         /*tp_repr*/
+    0,                         /*tp_as_number*/
+    0,                         /*tp_as_sequence*/
+    0,                         /*tp_as_mapping*/
+    0,                         /*tp_hash */
+    0,                         /*tp_call*/
+    0,                         /*tp_str*/
+    0,                         /*tp_getattro*/
+    0,                         /*tp_setattro*/
+    0,                         /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+    "edlib marks",             /* tp_doc */
+    0,                         /* tp_traverse */
+    0,                         /* tp_clear */
+    (richcmpfunc)mark_compare, /* tp_richcompare */
+    0,                         /* tp_weaklistoffset */
+    (getiterfunc)mark_this,    /* tp_iter */
+    (iternextfunc)Mark_next,   /* tp_iternext */
+    0,                         /* tp_methods */
+    0,                         /* tp_members */
+    mark_getseters,            /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    0,                         /* tp_init */
+    0,                         /* tp_alloc */
+    .tp_new = (newfunc)mark_new,/* tp_new */
+};
+
+/* The 'call' function takes liberties with arg passing.
+ * Positional args must start with the key, and then are handled based on
+ * there type.  They can be panes, strigns, ints, pairs, marks, commands.
+ * Panes are assigned to focus, then home.
+ * Strings (after the key) are assigned to 'str' then 'str2'.
+ * Ints are assigned to numeric then extra
+ * Pairs (must be of ints) are assigned to x,y then hx,hy.
+ * Marks are assigned to mark then mark2
+ * commands are assigned to comm2, then comm (yes - backwards).
+ * kwd arguments can also be used, currently
+ * key, home, focus, xy, hxy, str, str2, mark, mark2, comm, comm2.
+ */
+static PyObject *edlib_call(PyObject *self, PyObject *args, PyObject *kwds)
+{
+       struct cmd_info ci = {0};
+       int argc;
+       PyObject *a;
+       int i;
+       int rv;
+       int numeric_set = 0, extra_set = 0;
+       int xy_set = 0, hxy_set = 0;
+
+       if (!PyTuple_Check(args))
+               return NULL;
+       argc = PyTuple_GET_SIZE(args);
+       if (argc >= 1) {
+               /* First positional arg must be the key */
+               a = PyTuple_GetItem(args, 0);
+               if (!PyString_Check(a)) {
+                       PyErr_SetString(PyExc_TypeError, "First are must be key");
+                       return NULL;
+               }
+               ci.key = PyString_AsString(a);
+       }
+       for (i = 1; i < argc; i++) {
+               a = PyTuple_GetItem(args, i);
+               if (Py_TYPE(a) == &PaneType) {
+                       if (ci.focus == NULL)
+                               ci.focus = ((Pane*)a)->pane;
+                       else if (ci.home == NULL)
+                               ci.home = ((Pane*)a)->pane;
+                       else {
+                               PyErr_SetString(PyExc_TypeError, "Only 2 Pane args permitted");
+                               return NULL;
+                       }
+               } else if (Py_TYPE(a) == &MarkType) {
+                       if (ci.mark == NULL)
+                               ci.mark = ((Mark*)a)->mark;
+                       else if (ci.mark2 == NULL)
+                               ci.mark2 = ((Mark*)a)->mark;
+                       else {
+                               PyErr_SetString(PyExc_TypeError, "Only 2 Mark args permitted");
+                               return NULL;
+                       }
+               } else if (PyString_Check(a)) {
+                       if (ci.str == NULL)
+                               ci.str = PyString_AsString(a);
+                       else if (ci.str2 == NULL)
+                               ci.str2 = PyString_AsString(a);
+                       else {
+                               PyErr_SetString(PyExc_TypeError, "Only 3 String args permitted");
+                               return NULL;
+                       }
+               } else if (PyInt_Check(a)) {
+                       if (!numeric_set) {
+                               ci.numeric = PyInt_AsLong(a);
+                               numeric_set = 1;
+                       } else if (!extra_set) {
+                               ci.extra = PyInt_AsLong(a);
+                               extra_set = 1;
+                       } else {
+                               PyErr_SetString(PyExc_TypeError, "Only 2 Number args permitted");
+                               return NULL;
+                       }
+               } else if (PyTuple_Check(a)) {
+                       int n = PyTuple_GET_SIZE(a);
+                       PyObject *n1, *n2;
+                       if (n != 2) {
+                               PyErr_SetString(PyExc_TypeError, "Only 2-element tuples permitted");
+                               return NULL;
+                       }
+                       n1 = PyTuple_GetItem(a, 0);
+                       n2 = PyTuple_GetItem(a, 1);
+                       if (!PyInt_Check(n1) || !PyInt_Check(n2)) {
+                               PyErr_SetString(PyExc_TypeError, "Only tuples of integers permitted");
+                               return NULL;
+                       }
+                       if (!xy_set) {
+                               ci.x = PyInt_AsLong(n1);
+                               ci.y = PyInt_AsLong(n2);
+                               xy_set = 1;
+                       } else if (!hxy_set) {
+                               ci.hx = PyInt_AsLong(n1);
+                               ci.hy = PyInt_AsLong(n2);
+                               hxy_set = 1;
+                       } else {
+                               PyErr_SetString(PyExc_TypeError, "Only two tuples permitted");
+                               return NULL;
+                       }
+               } else if (PyCallable_Check(a)) {
+                       /* FIXME this is never freed */
+                       struct python_command *pc = malloc(sizeof(*pc));
+                       pc->callable = a;
+                       pc->c = python_call;
+                       if (ci.comm2 == NULL)
+                               ci.comm2 = &pc->c;
+                       else if (ci.comm == NULL)
+                               ci.comm = &pc->c;
+                       else {
+                               free(pc);
+                               PyErr_SetString(PyExc_TypeError, "Only two callables permitted");
+                               return NULL;
+                       }
+               } else {
+                       PyErr_SetString(PyExc_TypeError, "Unsupported arg type");
+                       return NULL;
+               }
+       }
+       /* Handle keyword args - later */
+       if (!ci.key) {
+               PyErr_SetString(PyExc_TypeError, "No key specified");
+               return NULL;
+       }
+       if (!ci.focus) {
+               PyErr_SetString(PyExc_TypeError, "No focus specified");
+               return NULL;
+       }
+
+       if (xy_set)
+               rv = key_handle_xy(&ci);
+       else
+               rv = key_handle_focus(&ci);
+       if (!rv) {
+               Py_INCREF(Py_None);
+               return Py_None;
+       }
+       if (rv < 0) {
+               PyErr_SetString(Edlib_CommandFailed, ci.str ? ci.str : "Command Failed");
+               return NULL;
+       }
+       /* FIXME how do I return the various return values? */
+       Py_INCREF(Py_True);
+       return Py_True;
+}
+
+static PyMethodDef edlib_methods[] = {
+       {"call", (PyCFunction)edlib_call, METH_VARARGS | METH_KEYWORDS,
+        "Make an edlib call"},
+       {NULL, NULL, 0, NULL}
+};
+
+void edlib_init(struct editor *ed)
+{
+       PyObject *m;
+
+       Py_SetProgramName("edlib");
+       Py_Initialize();
+
+       PaneType.tp_new = PyType_GenericNew;
+       MarkType.tp_new = PyType_GenericNew;
+       if (PyType_Ready(&PaneType) < 0)
+               return;
+       if (PyType_Ready(&MarkType) < 0)
+               return;
+
+       m = Py_InitModule3("edlib", edlib_methods,
+                          "edlib - one more editor is never enough.");
+
+       if (!m)
+               return;
+
+       Py_INCREF(&PaneType);
+       Py_INCREF(&MarkType);
+       PyModule_AddObject(m, "pane", (PyObject *)&PaneType);
+       PyModule_AddObject(m, "mark", (PyObject *)&MarkType);
+       key_add(ed->commands, "python-load", &python_load);
+
+       Edlib_CommandFailed = PyErr_NewException("edlib.commandfailed", NULL, NULL);
+       Py_INCREF(Edlib_CommandFailed);
+       PyModule_AddObject(m, "commandfailed", Edlib_CommandFailed);
+       EdlibModule = m;
+}
diff --git a/python/test.py b/python/test.py
new file mode 100644 (file)
index 0000000..df039a8
--- /dev/null
@@ -0,0 +1,4 @@
+
+edlib.call("Replace", "This text was inserted from python\n", pane, pane);
+
+