/*
- * Copyright Neil Brown ©2015-2020 <neil@brown.name>
+ * Copyright Neil Brown ©2015-2023 <neil@brown.name>
* May be distributed under terms of GPLv2 - see file:COPYING
*
* Python3 bindings for edlib.
- * An 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.
+ * An edlib command "global-load-modules:python" will read and execute
+ * a python module.
+ * It must "import edlib" and it can use "edlib.editor" to get the editor
+ * instance.
*
* Types available are:
* edlib.pane - a generic pane. These form a tree of which edlib.editor
#define __linux__
#define __x86_64__
#define __LP64__
+
+//#define PyType_HasFeature __PyType_HasFeature
#include <Python.h>
+#undef PyType_HasFeature __PyType_HasFeature
+int PyType_HasFeature(PyTypeObject *type, unsigned long feature);
+
#undef Py_INCREF
#define Py_INCREF(op) (0)
#undef Py_DECREF
#define Py_DECREF(op) (0)
#undef Py_XDECREF
#define Py_XDECREF(op) (0)
-#else
+#undef Py_IS_TYPE
+#define Py_IS_TYPE(ob, type) (ob == (void*)type)
+#else /* CHECKER */
#include <Python.h>
#endif
-#define MARK_DATA_PTR PyObject
+struct Mark;
+#define MARK_DATA_PTR struct Mark
#define PRIVATE_DOC_REF
struct doc_ref {
PyObject *c;
int o;
};
+
+#include <fcntl.h>
+#include <signal.h>
+
+#define DOC_DATA_TYPE struct python_doc
+#define PANE_DATA_PTR_TYPE struct Pane *
+struct Pane;
#include "core.h"
#include "misc.h"
+#include "rexel.h"
+
+struct python_doc {
+ struct doc doc;
+ struct pydoc *pdoc;
+};
+
+#include "core-pane.h"
#define SAFE_CI {.key=safe_cast NULL,\
.home=safe_cast NULL,\
DEF_CMD(python_doc_call);
static void python_free_command(struct command *c safe);
-static struct python_command *export_callable(PyObject *callable safe)
+static struct python_command *export_callable(PyObject *callable safe,
+ PyObject *nm)
{
struct python_command *c;
+ const char *name = NULL;
+ int len;
+
+ if (nm && PyUnicode_Check(nm))
+ name = PyUnicode_AsUTF8(nm);
+ if (!name)
+ name = "";
+ len = strlen(name);
list_for_each_entry(c, &exported_commands, lst)
if (c->callable == callable) {
c->c = python_call;
c->c.free = python_free_command;
c->c.refcnt = 0;
+ if (strcmp(name, "handle_close") == 0 ||
+ (len > 10 &&
+ strcmp(name+len-10, "_closed_ok") == 0))
+ c->c.closed_ok = 1;
+ else
+ c->c.closed_ok = 0;
command_get(&c->c);
Py_INCREF(callable);
c->callable = callable;
return c;
}
-typedef struct {
+typedef struct Pane {
PyObject_HEAD;
struct pane *pane;
struct command cmd;
} PaneIter;
static PyTypeObject PaneIterType;
-typedef struct {
+typedef struct pydoc {
PyObject_HEAD;
struct pane *pane;
struct command cmd;
struct map *map;
int map_init;
- struct doc doc;
} Doc;
static PyTypeObject DocType;
-typedef struct {
+typedef struct Mark {
PyObject_HEAD;
struct mark *mark;
} Mark;
} Comm;
static PyTypeObject CommType;
-static inline int pane_valid(Pane *p safe)
+static inline bool pane_valid(Pane *p safe)
{
if (p->pane && p->pane->handle)
- return 1;
+ return True;
PyErr_SetString(PyExc_TypeError, "Pane has been freed");
- return 0;
+ return False;
}
-static inline int doc_valid(Doc *p safe)
+static inline bool doc_valid(Doc *p safe)
{
if (p->pane && p->pane->handle)
- return 1;
+ return True;
PyErr_SetString(PyExc_TypeError, "Doc pane has been freed");
- return 0;
+ return False;
}
-static int get_cmd_info(struct cmd_info *ci safe, PyObject *args safe, PyObject *kwds,
- PyObject **s1 safe, PyObject **s2 safe);
+static bool get_cmd_info(struct cmd_info *ci safe, PyObject *args safe, PyObject *kwds,
+ PyObject **s1 safe, PyObject **s2 safe);
static int in_pane_frompane = 0;
static inline PyObject *safe Pane_Frompane(struct pane *p)
pane = p->data;
Py_INCREF(pane);
} else if (p && p->handle && p->handle->func == python_doc_call.func) {
- struct doc *doc = p->data;
- Doc *pdoc = container_of(doc, Doc, doc);
+ struct python_doc *pd = p->doc_data;
+ Doc *pdoc = pd->pdoc;
pane = (Pane*)pdoc;
Py_INCREF(pane);
} else {
in_pane_frompane = 1;
- pane = (Pane * safe)PyObject_CallObject((PyObject*)&PaneType, NULL);
+ pane = (Pane *)PyObject_CallObject((PyObject*)&PaneType, NULL);
in_pane_frompane = 0;
- if (pane)
+ if (!pane)
+ ;
+ else if (p)
+ pane->pane = pane_get(p);
+ else
pane->pane = p;
}
return (PyObject*)pane;
{
Mark *mark;
- if (m->mtype == &MarkType && m->mdata) {
+ if (mark_valid(m) && m->mtype == &MarkType && m->mdata) {
/* This is a vmark, re-use the PyObject */
Py_INCREF(m->mdata);
- return m->mdata;
+ return (PyObject*)m->mdata;
}
- mark = (Mark * safe)PyObject_CallObject((PyObject*)&MarkType, NULL);
- mark->mark = m;
+ mark = (Mark *)PyObject_CallObject((PyObject*)&MarkType, NULL);
+ if (mark && mark_valid(m))
+ mark->mark = m;
return (PyObject*)mark;
}
static inline PyObject *safe Comm_Fromcomm(struct command *c safe)
{
- Comm *comm = (Comm*safe)PyObject_CallObject((PyObject*)&CommType, NULL);
- comm->comm = command_get(c);
+ Comm *comm = (Comm *)PyObject_CallObject((PyObject*)&CommType, NULL);
+ if (comm)
+ comm->comm = command_get(c);
return (PyObject*)comm;
}
PyErr_Fetch(&exc_typ, &exc_val, &exc_tb);
PyErr_NormalizeException(&exc_typ, &exc_val, &exc_tb);
+ if (exc_tb)
+ PyException_SetTraceback(exc_val, exc_tb);
/* Import the modules we need - StringIO and traceback */
errorMsg = "Can't import io";
Py_XDECREF(exc_tb);
}
-DEF_CMD(python_load)
-{
- const char *fname = ci->str;
- FILE *fp;
- PyObject *globals, *main_mod;
- PyObject *Ed;
-
- if (!fname)
- return Enoarg;
- fp = fopen(fname, "r");
- if (!fp)
- return Efail;
-
- main_mod = PyImport_AddModule("__main__");
- if (main_mod == NULL)
- return Einval;
- globals = PyModule_GetDict(main_mod);
-
- Ed = Pane_Frompane(ci->home);
- PyDict_SetItemString(globals, "editor", Ed);
- PyDict_SetItemString(globals, "edlib", EdlibModule);
- PyRun_FileExFlags(fp, fname, Py_file_input, globals, globals, 0, NULL);
- PyErr_LOG();
- Py_DECREF(Ed);
- fclose(fp);
- return 1;
-}
-
DEF_CMD(python_load_module)
{
const char *name = ci->str;
- FILE *fp;
- PyObject *globals, *main_mod;
- PyObject *Ed;
- char buf [PATH_MAX];
+ int fd;
+ long long size;
+ char *code;
+ char buf[PATH_MAX];
+ char buf2[PATH_MAX];
+ PyObject *builtins, *compile, *args, *bytecode;
if (!name)
return Enoarg;
snprintf(buf, sizeof(buf), "%s/python/%s.py", module_dir, name);
- fp = fopen(buf, "r");
- if (!fp)
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
return Efail;
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ code = malloc(size+1);
+ if (!code) {
+ close(fd);
+ return Efail;
+ }
+ size = read(fd, code, size);
+ close(fd);
+ if (size <= 0) {
+ free(code);
+ return Efail;
+ }
+ code[size] = 0;
LOG("Loading python module %s from %s", name, buf);
- main_mod = PyImport_AddModule("__main__");
- if (main_mod == NULL)
- return Einval;
- globals = PyModule_GetDict(main_mod);
-
- Ed = Pane_Frompane(ci->home);
- PyDict_SetItemString(globals, "editor", Ed);
- PyDict_SetItemString(globals, "pane", Pane_Frompane(ci->focus));
- PyDict_SetItemString(globals, "edlib", EdlibModule);
- PyRun_FileExFlags(fp, buf, Py_file_input, globals, globals, 0, NULL);
- PyErr_LOG();
- Py_DECREF(Ed);
- fclose(fp);
+
+ builtins = PyEval_GetBuiltins();
+ compile = PyDict_GetItemString(builtins, "compile");
+ args = safe_cast Py_BuildValue("(sss)", code, buf, "exec");
+ bytecode = PyObject_Call(compile, args, NULL);
+ Py_DECREF(args);
+ free(code);
+ if (bytecode == NULL) {
+ PyErr_LOG();
+ return Efail;
+ }
+
+ snprintf(buf2, sizeof(buf2), "edlib.%s", name);
+
+ if (PyImport_ExecCodeModule(buf2, bytecode) == NULL)
+ PyErr_LOG();
+ Py_DECREF(bytecode);
return 1;
}
-static PyObject *safe python_string(const char *s safe)
+static PyObject *safe python_string(const char *s safe, int len)
{
const char *c = s;
- while (*c && !(*c & 0x80))
+ const char *e = NULL;
+ wint_t wch;
+
+ if (len >= 0)
+ e = s + len;
+ while ((!e || c < e) && *c && !(*c & 0x80))
c++;
- if (*c)
- /* must be Unicode */
- return safe_cast PyUnicode_DecodeUTF8(s, strlen(s), NULL);
- else
- return safe_cast Py_BuildValue("s", s);
+ while ((wch = get_utf8(&c, e)) != WEOF)
+ if (wch == WERR || wch > 0x10FFFF)
+ break;
+
+ return safe_cast PyUnicode_DecodeUTF8(s, c - s, NULL);
}
static char *python_as_string(PyObject *s, PyObject **tofree safe)
return 1;
}
+static bool python_running;
+DEF_CB(handle_alarm)
+{
+ if (python_running)
+ PyErr_SetInterrupt();
+ return 1;
+}
+
REDEF_CB(python_call)
{
struct python_command *pc = container_of(ci->comm, struct python_command, c);
- PyObject *ret = NULL, *args, *kwds;
+ PyObject *ret = NULL, *args, *kwds, *str;
int rv = 1;
+ bool unterminated = False;
+ int klen = strlen(ci->key);
+
+ if (klen > 13 &&
+ strcmp(ci->key + klen - 13, " unterminated") == 0)
+ unterminated = True;
args = safe_cast Py_BuildValue("(s)", ci->key);
kwds = PyDict_New();
rv = rv && dict_add(kwds, "mark2",
ci->mark2 ? Mark_Frommark(ci->mark2):
(Py_INCREF(Py_None), Py_None));
- rv = rv && dict_add(kwds, "str",
- ci->str ? python_string(ci->str):
- (Py_INCREF(Py_None), safe_cast Py_None));
+
+ if (ci->str)
+ str = python_string(ci->str, unterminated ? ci->num2 : -1);
+ else {
+ str = Py_None;
+ Py_INCREF(Py_None);
+ }
+ if (str) {
+ dict_add(kwds, "str", str);
+ dict_add(kwds, "str1", str);
+ Py_INCREF(str);
+ } else {
+ rv = 0;
+ }
+
rv = rv && dict_add(kwds, "str2",
- ci->str2 ? python_string(ci->str2):
+ ci->str2 ? python_string(ci->str2, -1):
(Py_INCREF(Py_None), safe_cast Py_None));
rv = rv && dict_add(kwds, "comm", Comm_Fromcomm(ci->comm));
rv = rv && dict_add(kwds, "comm2",
rv = rv && dict_add(kwds, "xy",
Py_BuildValue("ii", ci->x, ci->y));
- if (rv && pc->callable)
+ if (rv && pc->callable) {
+ python_running = True;
ret = PyObject_Call(pc->callable, args, kwds);
+ python_running = False;
+ }
Py_DECREF(args);
Py_DECREF(kwds);
return Efail;
}
if (ret == Py_None)
- rv = 0;
+ rv = Efallthrough;
else if (PyLong_Check(ret))
rv = PyLong_AsLong(ret);
else if (PyBool_Check(ret))
- rv = (ret == Py_True);
- else if (PyUnicode_Check(ret) && PyUnicode_GET_SIZE(ret) >= 1)
+ rv = (ret == Py_True) ? 1 : Efalse;
+ else if (PyUnicode_Check(ret) && PyUnicode_GET_LENGTH(ret) >= 1)
rv = CHAR_RET(PyUnicode_READ_CHAR(ret, 0));
else
rv = 1;
if (!self->map || !self->pane || !l)
return;
n = PyList_Size(l);
+ /* First add the ranges, so individuals can over-ride them */
for (i = 0; i < n ; i++) {
PyObject *e = PyList_GetItem(l, i);
PyObject *m = PyObject_GetAttr((PyObject*)self, e);
if (doc && doc != Py_None) {
PyObject *tofree = NULL;
char *docs = python_as_string(doc, &tofree);
+
if (docs &&
- strncmp(docs, "handle:", 7) == 0) {
- struct python_command *comm =
- export_callable(m);
- key_add(self->map, docs+7, &comm->c);
- command_put(&comm->c);
- }
- if (docs &&
- strncmp(docs, "handle-range", 12) == 0 &&
+ strstarts(docs, "handle-range") &&
docs[12]) {
char sep = docs[12];
char *s1 = strchr(docs+13, sep);
char *b = strndup(s1+1, s2-(s1+1));
struct python_command *comm =
- export_callable(m);
+ export_callable(m, e);
key_add_range(self->map, a, b,
&comm->c);
free(a); free(b);
}
}
if (docs &&
- strncmp(docs, "handle-prefix:", 14) == 0) {
+ strstarts(docs, "handle-prefix:")) {
char *a = strconcat(self->pane, docs+14);
char *b = strconcat(self->pane,
a, "\xFF\xFF\xFF\xFF");
struct python_command *comm =
- export_callable(m);
+ export_callable(m, e);
key_add_range(self->map, a, b,
&comm->c);
command_put(&comm->c);
}
- if (docs &&
- strncmp(docs, "handle-list", 11) == 0 &&
+ Py_XDECREF(tofree);
+ }
+ Py_XDECREF(doc);
+ }
+ Py_XDECREF(m);
+ }
+ /* Now add the non-ranges */
+ for (i = 0; i < n ; i++) {
+ PyObject *e = PyList_GetItem(l, i);
+ PyObject *m = PyObject_GetAttr((PyObject*)self, e);
+
+ if (m && PyMethod_Check(m)) {
+ PyObject *doc = PyObject_GetAttrString(m, "__doc__");
+ char *docs;
+ PyObject *tofree = NULL;
+ if (doc && doc != Py_None &&
+ (docs = python_as_string(doc, &tofree)) != NULL) {
+
+ if (strstarts(docs, "handle:")) {
+ struct python_command *comm =
+ export_callable(m, e);
+ key_add(self->map, docs+7, &comm->c);
+ command_put(&comm->c);
+ }
+ if (strstarts(docs, "handle-list") &&
docs[11]) {
char sep = docs[11];
char *s1 = docs + 12;
while (s1 && *s1 && *s1 != sep) {
struct python_command *comm =
- export_callable(m);
+ export_callable(m, e);
char *a;
char *s2 = strchr(s1, sep);
if (s2) {
command_put(&comm->c);
}
}
- Py_XDECREF(tofree);
}
+ Py_XDECREF(tofree);
Py_XDECREF(doc);
}
Py_XDECREF(m);
static void python_pane_free(struct command *c safe)
{
Pane *p = container_of(c, Pane, cmd);
+ struct pane *pn = p->pane;
/* pane has been closed */
p->pane = NULL;
if (p->map)
key_free(p->map);
p->map = NULL;
- if (PyObject_TypeCheck(p, &DocType)) {
- Doc *d = (Doc*)p;
- doc_free(&d->doc);
- }
+ if (pn && PyObject_TypeCheck(p, &DocType))
+ doc_free(&pn->doc_data->doc, safe_cast pn);
+ if (pn)
+ pane_put(pn);
Py_DECREF(p);
}
-static int __Pane_init(Pane *self safe, PyObject *args, PyObject *kwds,
+DEF_CMD_CLOSED(python_close_mark)
+{
+ struct mark *m = ci->mark;
+
+ if (m && m->viewnum >= 0 && m->mtype == &MarkType && m->mdata) {
+ Mark *M = m->mdata;
+ m->mdata = NULL;
+ m->mtype = NULL;
+ M->mark = NULL;
+ Py_DECREF(M);
+ }
+ return 1;
+}
+
+static int do_Pane_init(Pane *self safe, PyObject *args, PyObject *kwds,
Pane **parentp safe,
int *zp safe)
{
- Pane *parent = NULL;
int ret;
- static char *keywords[] = {"parent", "z", NULL};
+ static const char *keywords[] = {"parent", "z", NULL};
if (self->pane) {
PyErr_SetString(PyExc_TypeError, "Pane already initialised");
return -1;
}
- /* Pane(parent, handler, data, z=0 */
if (in_pane_frompane)
/* An internal Pane_Frompane call - it will set .pane,
* and we don't want a .handler.
*/
return 0;
- ret = PyArg_ParseTupleAndKeywords(args, kwds, "|Oi", keywords,
- &parent, zp);
+ /* Pane(parent=None, z=0) */
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "O!|i", (char**)keywords,
+ &PaneType, parentp, zp);
if (ret <= 0)
return -1;
- if ((PyObject*)parent == Py_None)
- parent = NULL;
-
- if (parent && !PyObject_TypeCheck(parent, &PaneType)) {
- PyErr_SetString(PyExc_TypeError, "First arg must be edlib.Pane or None");
- return -1;
- }
-
- *parentp = parent;
-
self->map = key_alloc();
+ key_add(self->map, "Close:mark", &python_close_mark);
self->cmd = python_pane_call;
+ self->cmd.closed_ok = 1;
self->cmd.free = python_pane_free;
+ if (self->ob_base.ob_type)
+ self->cmd.name = self->ob_base.ob_type->tp_name;
return 1;
}
{
Pane *parent = NULL;
int z = 0;
- int ret = __Pane_init(self, args, kwds, &parent, &z);
+ int ret = do_Pane_init(self, args, kwds, &parent, &z);
if (ret <= 0)
return ret;
+ if (!parent || !parent->pane)
+ return -1;
/* The pane holds a reference to the Pane through the ->handle
* function
*/
Py_INCREF(self);
- self->pane = pane_register(parent ? parent->pane : NULL,
- z, &self->cmd, self);
+ self->pane = pane_register(parent->pane, z, &self->cmd, self);
+ if (!self->pane)
+ return -1;
+
+ pane_get(self->pane);
return 0;
}
static int Doc_init(Doc *self, PyObject *args, PyObject *kwds)
{
Pane *parent = NULL;
+ struct python_doc *pd;
int z = 0;
- int ret = __Pane_init((Pane*safe)self, args, kwds, &parent, &z);
+ int ret = do_Pane_init((Pane*safe)self, args, kwds, &parent, &z);
- if (ret <= 0 || !self)
+ if (ret <= 0)
return ret;
+ if (!self || !parent || !parent->pane)
+ return -1;
self->cmd.func = python_doc_call_func;
- self->pane = doc_register(parent ? parent->pane : NULL,
- &self->cmd, self);
- self->doc.refcnt = mark_refcnt;
+ self->pane = doc_register(parent->pane, &self->cmd);
+ if (self->pane) {
+ pane_get(self->pane);
+ pd = self->pane->doc_data;
+ pd->pdoc = self;
+ pd->doc.refcnt = mark_refcnt;
+ }
return 0;
}
ob->ob_type->tp_free(ob);
}
+DEF_CMD(python_null_call)
+{
+ return 1;
+}
+
+static void python_pane_free_final(struct command *c safe)
+{
+ Pane *p = container_of(c, Pane, cmd);
+
+ if (p->pane)
+ pane_put(p->pane);
+ python_pane_free(c);
+ do_free((PyObject*safe)p);
+}
+
static void pane_dealloc(Pane *self safe)
{
- do_free((PyObject*safe)self);
+ struct pane *p = self->pane;
+
+ /* if initialization failed, then dealloc happens before the
+ * pane gets closed. In that case we need to modify the
+ * free sequence so do_free() gets called after the close.
+ */
+
+ if (p && p->handle && p->handle->func == python_pane_call.func) {
+ p->handle = &python_null_call;
+ p->handle->free = python_pane_free_final;
+ pane_close(p);
+ } else if (p && p->handle && p->handle->func == python_doc_call.func) {
+ p->handle = &python_null_call;
+ p->handle->free = python_pane_free_final;
+ pane_close(p);
+ } else {
+ if (p)
+ pane_put(p);
+ do_free((PyObject*safe)self);
+ }
}
static PyObject *pane_children(Pane *self safe, PyObject *args)
return Py_None;
}
-static PyObject *Pane_focus(Pane *self safe, PyObject *args)
+static PyObject *Pane_take_focus(Pane *self safe, PyObject *args)
{
if (!pane_valid(self))
return NULL;
- pane_focus(self->pane);
+ pane_take_focus(self->pane);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *Pane_has_focus(Pane *self safe, PyObject *args)
{
+ Pane *other = NULL;
+ int ret;
+
if (!pane_valid(self))
return NULL;
- if (pane_has_focus(self->pane)) {
+ ret = PyArg_ParseTuple(args, "|O!", &PaneType, &other);
+ if (ret <= 0)
+ return NULL;
+
+ if (pane_has_focus(self->pane, other ? other->pane : NULL)) {
Py_INCREF(Py_True);
return Py_True;
} else {
}
}
-static PyObject *Pane_refresh(Pane *self safe, PyObject *args)
-{
- if (!pane_valid(self))
- return NULL;
-
- pane_refresh(self->pane);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
static PaneIter *pane_this_iter(PaneIter *self safe)
{
Py_INCREF(self);
if (pr->ret)
return Einval;
- if (!ci->mark)
+ if (!mark_valid(ci->mark))
return Efallthrough;
- pr->ret = Mark_Frommark(ci->mark);
+ if (ci->mark->viewnum == MARK_UNGROUPED) {
+ /* Cannot rely on this mark persisting, take a copy */
+ struct mark *m = mark_dup(ci->mark);
+ pr->ret = Mark_Frommark(m);
+ m->mtype = (void*)pr->ret;
+ } else
+ pr->ret = Mark_Frommark(ci->mark);
return 1;
}
if (pr->ret)
return Einval;
- if (!ci->mark2)
+ if (!mark_valid(ci->mark2))
return Efallthrough;
pr->ret = Mark_Frommark(ci->mark2);
return 1;
}
+DEF_CB(take_2_marks)
+{
+ struct pyret *pr = container_of(ci->comm, struct pyret, comm);
+ PyObject *m1, *m2;
+
+ if (pr->ret)
+ return Einval;
+ if (mark_valid(ci->mark))
+ m1 = Mark_Frommark(ci->mark);
+ else
+ m1 = Py_None;
+ if (mark_valid(ci->mark2))
+ m2 = Mark_Frommark(ci->mark2);
+ else
+ m2 = Py_None;
+
+ pr->ret = Py_BuildValue("OO", m1, m2);
+ if (mark_valid(ci->mark))
+ Py_DECREF(m1);
+ if (mark_valid(ci->mark2))
+ Py_DECREF(m2);
+ return 1;
+}
+
DEF_CB(take_str)
{
struct pyret *pr = container_of(ci->comm, struct pyret, comm);
return Einval;
if (!ci->str)
return Efallthrough;
- pr->ret = python_string(ci->str);
+ pr->ret = python_string(ci->str, -1);
+ return 1;
+}
+
+DEF_CB(take_bytes)
+{
+ struct pyret *pr = container_of(ci->comm, struct pyret, comm);
+
+ if (pr->ret)
+ return Einval;
+ if (!ci->str)
+ return Efallthrough;
+ pr->ret = safe_cast PyBytes_FromStringAndSize(ci->str, ci->num);
return 1;
}
static struct command *map_ret(char *ret safe)
{
- if (strcmp(ret, "focus") == 0)
+ if (strcmp(ret, "pane") == 0)
return &take_focus;
if (strcmp(ret, "mark") == 0)
return &take_mark;
if (strcmp(ret, "mark2") == 0)
return &take_mark2;
+ if (strcmp(ret, "marks") == 0)
+ return &take_2_marks;
if (strcmp(ret, "str") == 0)
return &take_str;
+ if (strcmp(ret, "bytes") == 0)
+ return &take_bytes;
if (strcmp(ret, "comm") == 0)
return &take_comm;
return NULL;
return True;
}
+static void set_err(int rv)
+{
+ switch(rv) {
+ case Enoarg:
+ PyErr_SetObject(Edlib_CommandFailed,
+ PyUnicode_FromFormat("Enoarg"));
+ break;
+ case Einval:
+ PyErr_SetObject(Edlib_CommandFailed,
+ PyUnicode_FromFormat("Einval"));
+ break;
+ case Enosup:
+ PyErr_SetObject(Edlib_CommandFailed,
+ PyUnicode_FromFormat("Enosup"));
+ break;
+ case Efail:
+ PyErr_SetObject(Edlib_CommandFailed,
+ PyUnicode_FromFormat("Efail"));
+ break;
+ default:
+ PyErr_SetObject(Edlib_CommandFailed,
+ PyUnicode_FromFormat("%d", rv));
+ }
+}
+
static PyObject *choose_ret(int rv, struct pyret *pr safe)
{
- if (pr->comm.func && rv >= 0) {
+ if (pr->comm.func && rv >= Efalse) {
if (pr->ret)
return pr->ret;
Py_INCREF(Py_None);
}
Py_XDECREF(pr->ret);
if (rv < Efalse) {
- PyErr_SetObject(Edlib_CommandFailed, PyLong_FromLong(rv));
+ set_err(rv);
return NULL;
}
if (pr->return_char) {
Py_INCREF(Py_None);
return Py_None;
}
- return PyUnicode_FromFormat("%c", rv & 0xFFFFF);
+ return PyUnicode_FromFormat("%c", rv & 0x1FFFFF);
}
return PyLong_FromLong(rv);
}
ci.home = self->pane;
- rv = get_cmd_info(&ci, args, kwds, &s1, &s2);
-
- if (rv <= 0 || !handle_ret(kwds, &ci, &pr)) {
+ if (!get_cmd_info(&ci, args, kwds, &s1, &s2) ||
+ !handle_ret(kwds, &ci, &pr)) {
Py_XDECREF(s1); Py_XDECREF(s2);
command_put(ci.comm2);
return NULL;
}
+ python_running = False;
rv = key_handle(&ci);
+ python_running = True;
/* Just in case ... */
PyErr_Clear();
ci.home = self->pane;
- rv = get_cmd_info(&ci, args, kwds, &s1, &s2);
-
- if (rv <= 0 || !handle_ret(kwds, &ci, &pr)) {
+ if (!get_cmd_info(&ci, args, kwds, &s1, &s2) ||
+ !handle_ret(kwds, &ci, &pr)) {
Py_XDECREF(s1); Py_XDECREF(s2);
command_put(ci.comm2);
return NULL;
ci.home = self->pane;
- rv = get_cmd_info(&ci, args, kwds, &s1, &s2);
-
- if (rv <= 0) {
+ if (!get_cmd_info(&ci, args, kwds, &s1, &s2)) {
Py_XDECREF(s1); Py_XDECREF(s2);
command_put(ci.comm2);
return NULL;
Py_XDECREF(s1); Py_XDECREF(s2);
command_put(ci.comm2);
if (rv < Efalse) {
- PyErr_SetObject(Edlib_CommandFailed, PyLong_FromLong(rv));
+ set_err(rv);
return NULL;
}
return PyLong_FromLong(rv);
return Py_BuildValue("ii", xy.x, xy.y);
}
+static PyObject *Pane_set_time(Pane *self safe, PyObject *args)
+{
+ struct pane *p = self->pane;
+ if (p)
+ pane_set_time(p);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *Pane_too_long(Pane *self safe, PyObject *args)
+{
+ struct pane *p = self->pane;
+
+ if (!p || pane_too_long(p, 0)) {
+ Py_INCREF(Py_True);
+ return Py_True;
+ } else {
+ Py_INCREF(Py_False);
+ return Py_False;
+ }
+}
+
static PyObject *Pane_mychild(Pane *self safe, PyObject *args)
{
Pane *child = NULL;
{
Mark *start = NULL, *end = NULL;
int view = -1;
- int ret = PyArg_ParseTuple(args, "i|O!O!", &view, &MarkType, &start,
- &MarkType, &end);
+ int tostart = 0;
+ int ret = PyArg_ParseTuple(args, "iO!O!|i", &view, &MarkType, &start,
+ &MarkType, &end, &tostart);
if (ret > 0 && start && end && self->pane &&
start->mark && end->mark && view >= 0)
- marks_clip(self->pane, start->mark, end->mark, view, self->pane);
+ marks_clip(self->pane, start->mark, end->mark, view, self->pane,
+ !!tostart);
Py_INCREF(Py_None);
return Py_None;
}
Pane *newparent = NULL;
int ret = PyArg_ParseTuple(args, "O!", &PaneType, &newparent);
- if (ret > 0 && newparent && self->pane && newparent->pane)
+ if (ret > 0 && newparent && self->pane && newparent->pane) {
pane_reparent(self->pane, newparent->pane);
+ if (self->pane->parent != newparent->pane) {
+ PyErr_SetString(PyExc_TypeError, "reparent failed");
+ return NULL;
+ }
+ }
Py_INCREF(Py_None);
return Py_None;
}
if (!pane_valid(self))
return NULL;
- if (ret <= 0 || !m) {
+ if (ret <= 0 || !m || !mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Arg must be a mark");
return NULL;
}
- wch = doc_step(self->pane, m->mark, dir, move);
+ if (move)
+ wch = doc_move(self->pane, m->mark, dir);
+ else
+ wch = doc_pending(self->pane, m->mark, dir);
if (wch == WEOF) {
Py_INCREF(Py_None);
return Py_None;
static PyObject *Pane_step_prev(Pane *self safe, PyObject *args)
{
- return Pane_step(self, args, 0, 1);
+ return Pane_step(self, args, -1, 1);
}
static PyObject *Pane_step_following(Pane *self safe, PyObject *args)
static PyObject *Pane_step_prior(Pane *self safe, PyObject *args)
{
- return Pane_step(self, args, 0, 0);
+ return Pane_step(self, args, -1, 0);
+}
+
+static PyObject *Pane_get_vmarks(Pane *self safe, PyObject *args)
+{
+ struct pyret pr;
+ Pane *owner = NULL;
+ int view = -1;
+ int ret;
+
+ if (!pane_valid(self))
+ return NULL;
+ ret = PyArg_ParseTuple(args, "i|O!", &view, &PaneType, &owner);
+ if (ret <= 0 || view < 0 || (owner && !pane_valid(owner))) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ memset(&pr, 0, sizeof(pr));
+ pr.comm = take_2_marks;
+ home_call_comm(self->pane, "doc:vmark-get",
+ owner ? owner->pane : self->pane,
+ &pr.comm, view);
+ if (pr.ret)
+ return pr.ret;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *Pane_vmark_at_or_before(Pane *self safe, PyObject *args)
+{
+ Mark *m = NULL;
+ Pane *owner = NULL;
+ struct pyret pr;
+ int view = -1;
+ int ret;
+
+ if (!pane_valid(self))
+ return NULL;
+ ret = PyArg_ParseTuple(args, "iO!|O!", &view, &MarkType, &m,
+ &PaneType, &owner);
+ if (ret <= 0 || view < 0 || !m || !mark_valid(m->mark) ||
+ (owner && !pane_valid(owner))) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ memset(&pr, 0, sizeof(pr));
+ pr.comm = take_mark;
+ home_call_comm(self->pane, "doc:vmark-prev",
+ owner ? owner->pane : self->pane,
+ &pr.comm, view, m->mark);
+ if (pr.ret)
+ return pr.ret;
+ Py_INCREF(Py_None);
+ return Py_None;
}
-static PyMethodDef pane_methods[] = {
+static const PyMethodDef pane_methods[] = {
{"close", (PyCFunction)Pane_close, METH_NOARGS,
"close the pane"},
{"children", (PyCFunction)pane_children, METH_NOARGS,
"provides an iterator which will iterate over all children"},
{"clone_children", (PyCFunction)Pane_clone_children, METH_VARARGS,
"Clone all children onto the target"},
- {"take_focus", (PyCFunction)Pane_focus, METH_NOARGS,
+ {"take_focus", (PyCFunction)Pane_take_focus, METH_NOARGS,
"Claim the focus for this pane"},
- {"has_focus", (PyCFunction)Pane_has_focus, METH_NOARGS,
+ {"has_focus", (PyCFunction)Pane_has_focus, METH_VARARGS,
"Check if pane is focus of display"},
- {"refresh", (PyCFunction)Pane_refresh, METH_NOARGS,
- "Trigger refresh on this pane"},
{"call", (void*)(PyCFunctionWithKeywords)Pane_call, METH_VARARGS|METH_KEYWORDS,
"Call a command from a pane"},
{"notify", (void*)(PyCFunctionWithKeywords)Pane_notify, METH_VARARGS|METH_KEYWORDS,
"returning the character after mark"},
{"prior", (PyCFunction)Pane_step_prior, METH_VARARGS,
"returning the character before mark"},
+ {"vmarks", (PyCFunction)Pane_get_vmarks, METH_VARARGS,
+ "return first and last vmark given view number"},
+ {"vmark_at_or_before", (PyCFunction)Pane_vmark_at_or_before, METH_VARARGS,
+ "return vmark at-or-before given mark"},
+ {"set_time", (PyCFunction)Pane_set_time, METH_NOARGS,
+ "Set start time for long running operation"},
+ {"too_long", (PyCFunction)Pane_too_long, METH_NOARGS,
+ "Check if command in pane has been running for too long"},
{NULL}
};
new = p->pane->focus;
if (*which == 'r')
new = pane_root(p->pane);
+ if (*which == 'F')
+ new = pane_focus(p->pane);
if (*which == 'L')
new = pane_leaf(p->pane);
if (new == NULL) {
if (!pane_valid(self))
asprintf(&s, "<edlib.Pane FREED!!! %p>", self);
else
- asprintf(&s, "<edlib.Pane %p>", self->pane);
+ asprintf(&s, "<edlib.Pane %p-%s>", self->pane, self->pane->name);
ret = Py_BuildValue("s", s);
free(s);
return ret;
if (!doc_valid(self))
asprintf(&s, "<edlib.Doc FREED!!! %p>", self);
else
- asprintf(&s, "<edlib.Doc %p>", self->pane);
+ asprintf(&s, "<edlib.Doc %p-%s>", self->pane, self->pane->name);
ret = Py_BuildValue("s", s);
free(s);
return ret;
Py_RETURN_RICHCOMPARE(p1->pane, p2->pane, op);
}
-static PyGetSetDef pane_getseters[] = {
+static const PyGetSetDef pane_getseters[] = {
{"x",
(getter)pane_getnum, (setter)pane_setnum,
"X offset in parent", "x" },
{"root",
(getter)pane_getpane, (setter)pane_nosetpane,
"Root pane", "r"},
+ {"final_focus",
+ (getter)pane_getpane, (setter)pane_nosetpane,
+ "Final focus pane", "F"},
{"leaf",
(getter)pane_getpane, (setter)pane_nosetpane,
"Leaf pane", "L"},
return 0;
}
-static PyMappingMethods pane_mapping = {
+static const PyMappingMethods pane_mapping = {
.mp_length = NULL,
.mp_subscript = (binaryfunc)Pane_get_item,
.mp_ass_subscript = (objobjargproc)Pane_set_item,
.tp_dealloc = (destructor)pane_dealloc,
.tp_richcompare = (richcmpfunc)pane_cmp,
.tp_repr = (reprfunc)pane_repr,
- .tp_as_mapping = &pane_mapping,
+ .tp_as_mapping = (PyMappingMethods*)&pane_mapping,
.tp_hash = (hashfunc)pane_hash,
.tp_call = (ternaryfunc)pane_direct_call,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "edlib panes",
- .tp_methods = pane_methods,
- .tp_getset = pane_getseters,
+ .tp_methods = (PyMethodDef*)pane_methods,
+ .tp_getset = (PyGetSetDef*)pane_getseters,
.tp_init = (initproc)Pane_init,
.tp_new = (newfunc)pane_new,
};
if (!doc_valid(self))
return NULL;
- m = mark_first(&self->doc);
+ m = mark_first(&self->pane->doc_data->doc);
if (!m) {
Py_INCREF(Py_None);
return Py_None;
if (!doc_valid(self))
return NULL;
- ret = PyArg_ParseTuple(args, "O!i", &MarkType, &mark, &end);
- if (ret <= 0 || !mark || !mark->mark) {
+ ret = PyArg_ParseTuple(args, "O!p", &MarkType, &mark, &end);
+ if (ret <= 0 || !mark || !mark_valid(mark->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark undefined or uninitialized");
return NULL;
}
- mark_to_end(&self->doc, mark->mark, end);
+ mark_to_end(self->pane, mark->mark, end);
Py_INCREF(Py_None);
return Py_None;
}
-static PyMethodDef doc_methods[] = {
+static const PyMethodDef doc_methods[] = {
{"first_mark", (PyCFunction)first_mark, METH_NOARGS,
"first mark of document"},
{"to_end", (PyCFunction)to_end, METH_VARARGS,
.tp_repr = (reprfunc)doc_repr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "edlib document",
- .tp_methods = doc_methods,
+ .tp_methods = (PyMethodDef*)doc_methods,
.tp_base = &PaneType,
.tp_init = (initproc)Doc_init,
.tp_new = (newfunc)Doc_new,
static PyObject *mark_getoffset(Mark *m safe, void *x)
{
- if (m->mark == NULL) {
+ struct doc *d;
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
- if (m->mark->owner->refcnt == mark_refcnt)
+ d = &m->mark->owner->doc;
+ if (d->refcnt == mark_refcnt)
return PyLong_FromLong(m->mark->ref.o);
return PyLong_FromLong(0);
}
static int mark_setoffset(Mark *m safe, PyObject *v safe, void *x)
{
+ struct doc *d;
long val;
- if (m->mark == NULL) {
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return -1;
}
val = PyLong_AsLong(v);
if (val == -1 && PyErr_Occurred())
return -1;
- if (m->mark->owner->refcnt == mark_refcnt)
+ d = &m->mark->owner->doc;
+ if (d->refcnt == mark_refcnt)
m->mark->ref.o = val;
else {
PyErr_SetString(PyExc_TypeError, "Setting offset on non-local mark");
static PyObject *mark_getseq(Mark *m safe, void *x)
{
- if (m->mark == NULL) {
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
static PyObject *mark_getpos(Mark *m safe, void *x)
{
- if (m->mark == NULL) {
+ struct doc *d;
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
- if (m->mark->owner->refcnt == mark_refcnt && m->mark->ref.c) {
+ d = &m->mark->owner->doc;
+ if (d->refcnt == mark_refcnt && m->mark->ref.c) {
Py_INCREF(m->mark->ref.c);
return m->mark->ref.c;
} else {
static int mark_setpos(Mark *m safe, PyObject *v, void *x)
{
struct mark *m2;
- if (m->mark == NULL) {
+ struct doc *d;
+
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return -1;
}
- if (m->mark->owner->refcnt != mark_refcnt) {
- PyErr_SetString(PyExc_TypeError, "Not set ref for non-local mark");
+ d = &m->mark->owner->doc;
+ if (d->refcnt != mark_refcnt) {
+ PyErr_SetString(PyExc_TypeError, "Cannot set ref for non-local mark");
return -1;
}
- m->mark->owner->refcnt(m->mark, -1);
+ d->refcnt(m->mark, -1);
+ if (v == Py_None)
+ v = NULL;
/* If an adjacent mark has a ref.c with a matching value
* use that instead, so that mark_same() works.
*/
- m2 = mark_next(m->mark);
- if (m2 && m2->owner->refcnt == mark_refcnt && m2->ref.c != NULL &&
+ if ((m2 = mark_next(m->mark)) != NULL &&
+ m2->owner->doc.refcnt == mark_refcnt &&
+ m2->ref.c != NULL && v != NULL &&
PyObject_RichCompareBool(v, m2->ref.c, Py_EQ) == 1)
m->mark->ref.c = m2->ref.c;
else if ((m2 = mark_prev(m->mark)) != NULL &&
- m2->owner->refcnt == mark_refcnt &&
- m2->ref.c != NULL &&
+ m2->owner->doc.refcnt == mark_refcnt &&
+ m2->ref.c != NULL && v != NULL &&
PyObject_RichCompareBool(v, m2->ref.c, Py_EQ) == 1)
m->mark->ref.c = m2->ref.c;
else
m->mark->ref.c = v;
- m->mark->owner->refcnt(m->mark, 1);
+ d->refcnt(m->mark, 1);
return 0;
}
static PyObject *mark_getview(Mark *m safe, void *x)
{
- if (m->mark == NULL) {
+ if (!mark_valid(m->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
PyObject_TypeCheck(b, &MarkType) == 0) {
PyErr_SetString(PyExc_TypeError, "Mark compared with non-Mark");
return NULL;
- } else if (!a->mark || !b->mark)
+ } else if (!mark_valid(a->mark) || !mark_valid(b->mark))
return NULL;
else {
int cmp = a->mark->seq - b->mark->seq;
}
}
-static PyGetSetDef mark_getseters[] = {
+static const PyGetSetDef mark_getseters[] = {
{"pos",
(getter)mark_getpos, (setter)mark_setpos,
"Position ref", NULL},
{NULL} /* Sentinel */
};
-static Mark *mark_new(PyTypeObject *type safe, PyObject *args, PyObject *kwds)
+static Mark *Mark_new(PyTypeObject *type safe, PyObject *args, PyObject *kwds)
{
Mark *self;
Pane *owner = NULL;
int view = MARK_UNGROUPED;
Mark *orig = NULL;
- static char *keywords[] = {"pane","view","orig", "owner", NULL};
+ static const char *keywords[] = {"pane","view","orig", "owner", NULL};
int ret;
if (!PyTuple_Check(args) ||
/* Internal Mark_Frommark call */
return 1;
- ret = PyArg_ParseTupleAndKeywords(args, kwds, "|O!iO!O!", keywords,
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "|O!iO!O!", (char**)keywords,
&PaneType, &doc,
&view,
&MarkType, &orig,
if (!op)
op = p;
self->mark = vmark_new(p, view, op);
- } else if (orig && orig->mark) {
+ } else if (orig && mark_valid(orig->mark)) {
self->mark = mark_dup_view(orig->mark);
}
if (!self->mark) {
* explicitly released.
*/
self->mark->mtype = &MarkType;
- self->mark->mdata = (PyObject*)self;
+ self->mark->mdata = self;
Py_INCREF(self);
} else {
/* Other marks cannot use mdata and get freed when
static void mark_dealloc(Mark *self safe)
{
- if (self->mark && self->mark->mtype == (void*)self) {
+ if (mark_valid(self->mark) && self->mark->mtype == (void*)self) {
/* Python allocated this mark, so can free it. */
struct mark *m = self->mark;
self->mark = NULL;
{
Mark *other = NULL;
int ret = PyArg_ParseTuple(args, "O!", &MarkType, &other);
- if (ret <= 0 || !other || !self->mark || !other->mark)
+ if (ret <= 0 || !other ||
+ !mark_valid(self->mark) || !mark_valid(other->mark))
return NULL;
mark_to_mark(self->mark, other->mark);
{
Mark *other = NULL;
int ret = PyArg_ParseTuple(args, "O!", &MarkType, &other);
- if (ret <= 0 || !other || !self->mark || !other->mark)
+ if (ret <= 0 || !other ||
+ !mark_valid(self->mark) || !mark_valid(other->mark))
return NULL;
mark_to_mark_noref(self->mark, other->mark);
static PyObject *Mark_clip(Mark *self safe, PyObject *args)
{
Mark *start = NULL, *end = NULL;
- int ret = PyArg_ParseTuple(args, "O!O!", &MarkType, &start, &MarkType, &end);
+ int tostart = 0;
+ int ret = PyArg_ParseTuple(args, "O!O!|p", &MarkType, &start,
+ &MarkType, &end, &tostart);
- if (ret > 0 && start && end && self->mark &&
- start->mark && end->mark)
- mark_clip(self->mark, start->mark, end->mark);
+ if (ret > 0 && start && end && mark_valid(self->mark) &&
+ mark_valid(start->mark) && mark_valid(end->mark))
+ mark_clip(self->mark, start->mark, end->mark, tostart);
Py_INCREF(Py_None);
return Py_None;
static PyObject *Mark_step(Mark *self safe, PyObject *args)
{
- /* Convenience function to help implement doc:step */
+ /* Convenience function to help implement doc:char */
int forward = 1;
int ret = PyArg_ParseTuple(args, "i", &forward);
return Py_None;
}
+static PyObject *Mark_step_sharesref(Mark *self safe, PyObject *args)
+{
+ /* Convenience function to help implement doc:char */
+ int forward = 1;
+ int ret = PyArg_ParseTuple(args, "i", &forward);
+
+ if (ret > 0 && self->mark)
+ mark_step_sharesref(self->mark, forward);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
static PyObject *Mark_next(Mark *self safe, PyObject *args)
{
struct mark *next;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
static PyObject *Mark_prev(Mark *self safe, PyObject *args)
{
struct mark *prev;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
static PyObject *Mark_next_any(Mark *self safe, PyObject *args)
{
struct mark *next;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
static PyObject *Mark_prev_any(Mark *self safe, PyObject *args)
{
struct mark *prev;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
static PyObject *Mark_dup(Mark *self safe, PyObject *args)
{
struct mark *new;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
{
struct mark *m = self->mark;
- if (!m) {
+ if (!mark_valid(m)) {
PyErr_SetString(PyExc_TypeError, "Mark has been freed");
return NULL;
}
"Cannot release ungrouped marks or points");
return NULL;
}
- if (m->mtype == &MarkType) {
- /* We are dropping this mark - there cannot be any other ref */
- ASSERT(m->mdata == (PyObject*)self);
- Py_DECREF(self);
- m->mdata = NULL;
- m->mtype = NULL;
- self->mark = NULL;
- mark_free(m);
+ if (m->mtype != &MarkType) {
+ PyErr_SetString(PyExc_TypeError,
+ "Mark is not managed by python, and cannot be released");
+ return NULL;
}
+ /* We are dropping this mark - there cannot be any other ref */
+ ASSERT(m->mdata == self);
+ self->mark = NULL;
+ Py_DECREF(self);
+ m->mdata = NULL;
+ m->mtype = NULL;
+ mark_free(m);
+
Py_INCREF(Py_None);
return Py_None;
}
-static PyObject *Mark_ack(Mark *self safe, PyObject *args)
+static PyObject *Mark_watch(Mark *self safe, PyObject *args)
{
struct mark *m = self->mark;
- if (!m) {
+ if (!mark_valid(m)) {
PyErr_SetString(PyExc_TypeError, "Mark has been freed");
return NULL;
}
- mark_ack(m);
+ mark_watch(m);
Py_INCREF(Py_None);
return Py_None;
}
-static PyMethodDef mark_methods[] = {
+static const PyMethodDef mark_methods[] = {
{"to_mark", (PyCFunction)Mark_to_mark, METH_VARARGS,
"Move one mark to another"},
{"to_mark_noref", (PyCFunction)Mark_to_mark_noref, METH_VARARGS,
{"dup", (PyCFunction)Mark_dup, METH_NOARGS,
"duplicate a mark, as ungrouped"},
{"clip", (PyCFunction)Mark_clip, METH_VARARGS,
- "If this mark is in range, move to start"},
+ "If this mark is in range, move to end"},
{"release", (PyCFunction)Mark_release, METH_NOARGS,
"release a vmark so it can disappear"},
- {"ack", (PyCFunction)Mark_ack, METH_NOARGS,
+ {"watch", (PyCFunction)Mark_watch, METH_NOARGS,
"acknowledge movement of a point - allow further notifications"},
{"step", (PyCFunction)Mark_step, METH_VARARGS,
"Move mark over any adjacent marks with same reference"},
+ {"step_sharesref", (PyCFunction)Mark_step_sharesref, METH_VARARGS,
+ "Move mark over any adjacent marks with same reference"},
{NULL}
};
char *k, *v;
PyObject *t1 = NULL;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return NULL;
}
{
char *k, *v;
PyObject *t1 = NULL, *t2 = NULL;
- if (!self->mark) {
+ if (!mark_valid(self->mark)) {
PyErr_SetString(PyExc_TypeError, "Mark is NULL");
return -1;
}
static PyObject *mark_repr(Mark *self safe)
{
- char *s = NULL;
+ char *s = NULL, *dm;
PyObject *ret;
- if (self->mark)
- asprintf(&s, "<edlib.Mark seq=%d i=%d %p>",
- self->mark->seq, self->mark->ref.o, self->mark);
- else
+ if (!self->mark)
asprintf(&s, "<edlib.Mark NULL %p>", self);
+ else if (!mark_valid(self->mark))
+ asprintf(&s, "<edlib.Mark FREED %p>", self);
+ else if ((dm = call_ret(str, "doc:debug:mark", self->mark->owner,
+ 0, self->mark))
+ != NULL)
+ asprintf(&s, "<edlib.Mark seq=%d v=%d %s>",
+ self->mark->seq, self->mark->viewnum, dm);
+ else
+ asprintf(&s, "<edlib.Mark seq=%d v=%d i=%d %p>",
+ self->mark->seq, self->mark->viewnum,
+ self->mark->ref.o, self->mark);
+
ret = Py_BuildValue("s", s);
free(s);
return ret;
}
-static PyMappingMethods mark_mapping = {
+static const PyMappingMethods mark_mapping = {
.mp_length = NULL,
.mp_subscript = (binaryfunc)mark_get_item,
.mp_ass_subscript = (objobjargproc)mark_set_item,
};
+static int mark_bool(Mark *self safe)
+{
+ return mark_valid(self->mark);
+}
+
+static const PyNumberMethods mark_as_num = {
+ .nb_bool = (inquiry) mark_bool,
+};
+
static PyTypeObject MarkType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "edlib.Mark",
.tp_basicsize = sizeof(Mark),
.tp_dealloc = (destructor)mark_dealloc,
- .tp_as_mapping = &mark_mapping,
+ .tp_as_mapping = (PyMappingMethods*)&mark_mapping,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_doc = "edlib marks",
.tp_richcompare = (richcmpfunc)mark_compare,
- .tp_methods = mark_methods,
- .tp_getset = mark_getseters,
+ .tp_methods = (PyMethodDef*)mark_methods,
+ .tp_getset = (PyGetSetDef*)mark_getseters,
.tp_init = (initproc)Mark_init,
- .tp_new = (newfunc)mark_new,
- .tp_repr = (reprfunc)mark_repr
+ .tp_new = (newfunc)Mark_new,
+ .tp_repr = (reprfunc)mark_repr,
+ .tp_as_number = (PyNumberMethods*)&mark_as_num,
};
static void comm_dealloc(Comm *self safe)
if (!c->comm)
return NULL;
- rv = get_cmd_info(&ci, args, kwds, &s1, &s2);
- if (rv <= 0 || !handle_ret(kwds, &ci, &pr)) {
+ if (!get_cmd_info(&ci, args, kwds, &s1, &s2) ||
+ !handle_ret(kwds, &ci, &pr)) {
Py_XDECREF(s1); Py_XDECREF(s2);
command_put(ci.comm2);
return NULL;
* key, home, focus, xy, hxy, str, str2, mark, mark2, comm2.
* A 'None' arg is ignored - probably a mark or string or something. Just use NULL
*/
-static int get_cmd_info(struct cmd_info *ci safe, PyObject *args safe, PyObject *kwds,
- PyObject **s1 safe, PyObject **s2 safe)
+static bool get_cmd_info(struct cmd_info *ci safe, PyObject *args safe, PyObject *kwds,
+ PyObject **s1 safe, PyObject **s2 safe)
{
int argc;
PyObject *a;
*s1 = *s2 = NULL;
if (!PyTuple_Check(args))
- return 0;
+ return False;
argc = PyTuple_GET_SIZE(args);
if (argc >= 1) {
/* First positional arg must be the key */
a = PyTuple_GetItem(args, 0);
if (!PyUnicode_Check(a)) {
PyErr_SetString(PyExc_TypeError, "First arg must be key");
- return 0;
+ return False;
}
ci->key = safe_cast PyUnicode_AsUTF8(a);
}
for (i = 1; i < argc; i++) {
a = PyTuple_GetItem(args, i);
- if (a == Py_None)
+ if (!a || a == Py_None)
/* quietly ignore */;
else if (PyObject_TypeCheck(a, &PaneType)) {
if ((void*)ci->home == NULL)
ci->focus = safe_cast ((Pane*)a)->pane;
else {
PyErr_SetString(PyExc_TypeError, "Only 2 Pane args permitted");
- return 0;
+ return False;
}
} else if (PyObject_TypeCheck(a, &MarkType)) {
if (ci->mark == NULL)
ci->mark2 = ((Mark*)a)->mark;
else {
PyErr_SetString(PyExc_TypeError, "Only 2 Mark args permitted");
- return 0;
+ return False;
}
} else if (PyUnicode_Check(a)) {
char *str;
PyObject *s = NULL;
if (ci->str && ci->str2) {
PyErr_SetString(PyExc_TypeError, "Only 3 String args permitted");
- return 0;
+ return False;
}
str = python_as_string(a, &s);
if (!str)
- return 0;
+ return False;
if (s) {
*s2 = *s1;
*s1 = s;
num2_set = 1;
} else {
PyErr_SetString(PyExc_TypeError, "Only 2 Number args permitted");
- return 0;
+ return False;
}
} 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 0;
+ return False;
}
n1 = PyTuple_GetItem(a, 0);
n2 = PyTuple_GetItem(a, 1);
if (!PyLong_Check(n1) || !PyLong_Check(n2)) {
PyErr_SetString(PyExc_TypeError, "Only tuples of integers permitted");
- return 0;
+ return False;
}
if (!xy_set) {
ci->x = PyLong_AsLong(n1);
xy_set = 1;
} else {
PyErr_SetString(PyExc_TypeError, "Only one tuple permitted");
- return 0;
+ return False;
}
} else if (PyObject_TypeCheck(a, &CommType)) {
Comm *c = (Comm*)a;
ci->comm2 = command_get(c->comm);
} else {
PyErr_SetString(PyExc_TypeError, "Only one callable permitted");
- return 0;
+ return False;
}
} else if (PyCallable_Check(a)) {
- struct python_command *pc = export_callable(a);
+ struct python_command *pc = export_callable(a, NULL);
if (ci->comm2 == NULL)
ci->comm2 = &pc->c;
else {
command_put(&pc->c);
PyErr_SetString(PyExc_TypeError, "Only one callable permitted");
- return 0;
+ return False;
}
} else {
PyErr_Format(PyExc_TypeError, "Unsupported arg type %d", i);
- return 0;
+ return False;
}
}
if (kwds && PyDict_Check(kwds)) {
- a = PyDict_GetItemString(kwds, "str");
+ a = PyDict_GetItemString(kwds, "str1");
+ if (!a || a == Py_None)
+ a = PyDict_GetItemString(kwds, "str");
if (a && a != Py_None) {
if (*s1 || ci->str) {
PyErr_SetString(PyExc_TypeError,
"'str' given with other strings");
- return 0;
+ return False;
}
if (!PyUnicode_Check(a)) {
PyErr_SetString(PyExc_TypeError,
"'str' must be string or unicode");
- return 0;
+ return False;
}
ci->str = python_as_string(a, s1);
}
if (*s2 || ci->str2) {
PyErr_SetString(PyExc_TypeError,
"'str2' given with 2 strings");
- return 0;
+ return False;
}
if (!PyUnicode_Check(a)) {
PyErr_SetString(PyExc_TypeError,
"'str2' must be string or unicode");
- return 0;
+ return False;
}
ci->str2 = python_as_string(a, s2);
}
if (ci->mark) {
PyErr_SetString(PyExc_TypeError,
"'mark' given with other marks");
- return 0;
+ return False;
}
if (!PyObject_TypeCheck(a, &MarkType)) {
PyErr_SetString(PyExc_TypeError,
"'mark' must be an edlib.Mark");
- return 0;
+ return False;
}
ci->mark = ((Mark*)a)->mark;
}
if (ci->mark2) {
PyErr_SetString(PyExc_TypeError,
"'mark2' given with 2 other marks");
- return 0;
+ return False;
}
if (!PyObject_TypeCheck(a, &MarkType)) {
PyErr_SetString(PyExc_TypeError,
"'mark2' must be an edlib.Mark");
- return 0;
+ return False;
}
ci->mark2 = ((Mark*)a)->mark;
}
if (num_set) {
PyErr_SetString(PyExc_TypeError,
"'num' given with other numbers");
- return 0;
+ return False;
}
if (!PyLong_Check(a)) {
PyErr_SetString(PyExc_TypeError,
"'num' must be an integer");
- return 0;
+ return False;
}
ci->num = PyLong_AsLong(a);
num_set = 1;
if (num2_set) {
PyErr_SetString(PyExc_TypeError,
"'num2' given with 2 other numbers");
- return 0;
+ return False;
}
if (!PyLong_Check(a)) {
PyErr_SetString(PyExc_TypeError,
"'num2' must be an integer");
- return 0;
+ return False;
}
ci->num2 = PyLong_AsLong(a);
num2_set = 1;
}
+ a = PyDict_GetItemString(kwds, "focus");
+ if (a && a != Py_None) {
+ Pane *p;
+ if ((void*)ci->focus) {
+ PyErr_SetString(PyExc_TypeError,
+ "'focus' given with other pane");
+ return False;
+ }
+ if (!PyObject_TypeCheck(a, &PaneType)) {
+ PyErr_SetString(PyExc_TypeError,
+ "'focus' must be a pane");
+ return False;
+ }
+ p = (Pane*)a;
+ if (p->pane)
+ ci->focus = p->pane;
+ else {
+ PyErr_SetString(PyExc_TypeError, "focus value invalid");
+ return False;
+ }
+ }
+ a = PyDict_GetItemString(kwds, "xy");
+ if (a && a != Py_None) {
+ PyObject *n1, *n2;
+ if (xy_set) {
+ PyErr_SetString(PyExc_TypeError,
+ "'xy' given with other tuple");
+ return False;
+ }
+ if (!PyTuple_Check(a) || PyTuple_GET_SIZE(a) != 2) {
+ PyErr_SetString(PyExc_TypeError,
+ "'xy' must be a tuple of 2 integers");
+ return False;
+ }
+ n1 = PyTuple_GetItem(a, 0);
+ n2 = PyTuple_GetItem(a, 1);
+ if (!PyLong_Check(n1) || !PyLong_Check(n2)) {
+ PyErr_SetString(PyExc_TypeError, "Only tuples of integers permitted");
+ return False;
+ }
+ ci->x = PyLong_AsLong(n1);
+ ci->y = PyLong_AsLong(n2);
+ xy_set = 1;
+ }
+ a = PyDict_GetItemString(kwds, "comm2");
+ if (a && a != Py_None) {
+ if (ci->comm2) {
+ PyErr_SetString(PyExc_TypeError,
+ "'comm2' given with other command");
+ return False;
+ }
+ if (PyObject_TypeCheck(a, &CommType)) {
+ Comm *c = (Comm*)a;
+ if (c->comm)
+ ci->comm2 = command_get(c->comm);
+ else {
+ PyErr_SetString(PyExc_TypeError, "comm2 value invalid");
+ return False;
+ }
+ } else if (PyCallable_Check(a)) {
+ struct python_command *pc = export_callable(a, NULL);
+
+ ci->comm2 = &pc->c;
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "'comm2' must be a callable");
+ return False;
+ }
+ }
}
if (!(void*)ci->key) {
PyErr_SetString(PyExc_TypeError, "No key specified");
- return 0;
+ return False;
}
if (!(void*)ci->home) {
PyErr_SetString(PyExc_TypeError, "No pane specified");
- return 0;
+ return False;
}
if (!(void*)ci->focus)
ci->focus = ci->home;
- return xy_set ? 2 : 1;
+ return True;
}
static PyObject *py_time_start(PyObject *self, PyObject *args)
char buf[1024];
int l = 0;
- for (i = 0; i < argc; i++) {
+ for (i = 0; i < argc && l < (int)sizeof(buf) - 2; i++) {
PyObject *o = PySequence_GetItem(args, i);
PyObject *s, *tofree = NULL;
char *str;
continue;
str = python_as_string(s, &tofree);
slen = str ? strlen(str) : 0;
- if (str && slen < sizeof(buf) - l - 2) {
+ if (slen + l + 2 > sizeof(buf))
+ slen = sizeof(buf) - l - 2;
+ if (str) {
if (l)
buf[l++] = ' ';
- strcpy(buf+l, str);
+ strncpy(buf+l, str, slen);
l += slen;
}
Py_XDECREF(tofree);
}
buf[l] = 0;
if (l)
- LOG(buf);
+ LOG("%s", buf);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+static PyObject *py_LOG_BT(PyObject *self, PyObject *args)
+{
+ LOG_BT();
Py_INCREF(Py_None);
return Py_None;
}
-static PyMethodDef edlib_methods[] = {
+static const PyMethodDef edlib_methods[] = {
{"time_start", py_time_start, METH_VARARGS,
"Record start time"},
{"time_stop", py_time_stop, METH_VARARGS,
"Record stop time"},
{"LOG", py_LOG, METH_VARARGS,
"Generate log message"},
+ {"LOG_BT", py_LOG_BT, METH_NOARGS,
+ "Generate backtrace message"},
{NULL, NULL, 0, NULL}
};
/* This must be visible when the module is loaded so it
- * cannot be static. spares doesn't like variables that are
+ * cannot be static. sparse doesn't like variables that are
* neither extern nor static. So mark it extern
*/
extern char *edlib_module_path;
char *edlib_module_path;
-static struct PyModuleDef edlib_mod = {
- PyModuleDef_HEAD_INIT,
- .m_name = "edlib",
- .m_doc = "edlib - one more editor is never enough.",
- .m_methods = edlib_methods,
-};
-
void edlib_init(struct pane *ed safe)
{
PyObject *m;
- wchar_t *argv[1]= { NULL };
+ PyConfig config;
+ char *argv[2]= { "edlib", NULL };
+ sighandler_t sigint;
if (edlib_module_path)
module_dir = strdup(edlib_module_path);
else
module_dir = ".";
- /* This cast is for sparse, which doesn't seem to cope with L".."
- * FIXME
+ /* de-register SIGINT while we initialise python,
+ * so that Python thinks that it's registration is
+ * valid - so PyErr_SetInterrupt() don't think there
+ * is a race.
*/
- Py_SetProgramName((wchar_t*)L"edlib");
- Py_Initialize();
- PySys_SetArgv(0, argv);
+ sigint = signal(SIGINT, SIG_DFL);
+ PyConfig_InitPythonConfig(&config);
+ config.isolated = 1;
+ PyConfig_SetBytesArgv(&config, 0, argv);
+ Py_InitializeFromConfig(&config);
+ PyConfig_Clear(&config);
+ signal(SIGINT, sigint);
PaneType.tp_new = PyType_GenericNew;
PaneIterType.tp_new = PyType_GenericNew;
PyType_Ready(&CommType) < 0)
return;
- m = PyModule_Create(&edlib_mod);
-
- if (!m)
+ m = PyImport_AddModule("edlib");
+ if (!m) {
+ PyErr_LOG();
return;
+ }
+
+ PyModule_SetDocString(m , "edlib - one more editor is never enough");
+ PyModule_AddFunctions(m, (PyMethodDef*)edlib_methods);
+ PyModule_AddObject(m, "editor", Pane_Frompane(ed));
PyModule_AddObject(m, "Pane", (PyObject *)&PaneType);
PyModule_AddObject(m, "PaneIter", (PyObject *)&PaneIterType);
PyModule_AddObject(m, "Mark", (PyObject *)&MarkType);
PyModule_AddIntMacro(m, TIME_TIMER);
PyModule_AddIntMacro(m, TIME_IDLE);
PyModule_AddIntMacro(m, TIME_REFRESH);
+ PyModule_AddIntMacro(m, TIME_MISC);
- PyModule_AddIntConstant(m, "WEOF", 0x1FFFFF);
- call_comm("global-set-command", ed, &python_load, 0, NULL, "python-load");
- call_comm("global-set-command", ed, &python_load_module,
- 0, NULL, "global-load-modules:python");
+ PyModule_AddIntMacro(m, RXLF_ANCHORED);
+ PyModule_AddIntMacro(m, RXLF_BACKTRACK);
+
+ PyModule_AddIntMacro(m, MARK_UNGROUPED);
+ PyModule_AddIntMacro(m, MARK_POINT);
+
+ PyModule_AddIntConstant(m, "WEOF", 0x3FFFFF);
+
+ PyModule_AddIntConstant(m, "testing", edlib_testing(ed));
Edlib_CommandFailed = PyErr_NewException("edlib.commandfailed", NULL, NULL);
Py_INCREF(Edlib_CommandFailed);
PyModule_AddObject(m, "commandfailed", Edlib_CommandFailed);
+
EdlibModule = m;
ed_pane = ed;
+
+ call_comm("global-set-command", ed, &handle_alarm,
+ 0, NULL, "fast-alarm-python");
+
+ call_comm("global-set-command", ed, &python_load_module,
+ 0, NULL, "global-load-modules:python");
}