This is if_python.c in view mode; [Download] [Up]
/* vi:set ts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. */ /* * Python extensions by Paul Moore. * * This consists of four parts: * 1. Python interpreter main program * 2. Python output stream: writes output via [e]msg(). * 3. Implementation of the Vim module for Python * 4. Utility functions for handling the interface between Vim and Python. */ #include <stdio.h> #include <stdarg.h> #include <limits.h> #include "python.h" #undef main /* Defined in python.h - aargh */ #undef HAVE_FCNTL_H /* Clash with os_win32.h */ /* Parser flags */ #define single_input 256 #define file_input 257 #define eval_input 258 #include "vim.h" #include "globals.h" #include "proto.h" /************************************************************************* * Internal function prototypes. *************************************************************************/ static int DoPythonCmd (EXARG *, const char *); static int RangeStart; static int RangeEnd; static void PythonIO_Flush (void); static int PythonIO_Init (void); static int PythonMod_Init (void); /* Utility functions for the vim/python interface * ---------------------------------------------- */ static PyObject *GetBufferLine (BUF *, int); static PyObject *GetBufferLineList (BUF *, int, int); static int SetBufferLine (BUF *, int, PyObject *, int *); static int SetBufferLineList (BUF *, int, int, PyObject *, int *); static int InsertBufferLines (BUF *, int, PyObject *, int *); static char *VimArrayToString (char **, int, int *); static int MemToVimArray (char *, int); static void PyErr_SetVim (const char *, ...); static char *VimStringAlloc (PyObject *); /************************************************************************* * 1. Python interpreter main program. *************************************************************************/ static int initialised = 0; static int Python_Init (void) { if (!initialised) { Py_Initialize(); if (PythonIO_Init()) goto fail; if (PythonMod_Init()) goto fail; initialised = 1; } return 0; fail: /* We call PythonIO_Flush() here to print any Python errors. * This is OK, as it is possible to call this function even * if PythonIO_Init() has not completed successfully (it will * not do anything in this case). */ PythonIO_Flush(); return -1; } /* External interface */ static int DoPythonCommand (EXARG *eap, const char *cmd) { if (Python_Init()) return FAIL; RangeStart = eap->line1; RangeEnd = eap->line2; PyRun_SimpleString ((char *)(cmd)); PythonIO_Flush (); return OK; } int do_python (EXARG *eap) { return DoPythonCommand (eap, eap->arg); } #define BUFFER_SIZE 1024 int do_pyfile (EXARG *eap) { static char buffer[BUFFER_SIZE]; const char *file = eap->arg; char *p; /* Have to do it like this. PyRun_SimpleFile requires you to pass a * stdio file pointer, but Vim and the Python DLL are compiled with * different options under Windows, meaning that stdio pointers aren't * compatible between the two. Yuk. * * Put the string "execfile('file')" into buffer. But, we need to * escape any backslashes or single quotes in the filename, so that * Python won't mangle the filename. */ strcpy(buffer, "execfile('"); p = buffer + 10; /* size of "execfile('" */ while (*file && p < buffer + (BUFFER_SIZE - 3)) { if (*file == '\\' || *file == '\'') *p++ = '\\'; *p++ = *file++; } /* If we didn't finish the filename, we hit a buffer overflow */ if (*file != '\0') return FAIL; /* Put in the terminating "')" and a null */ *p++ = '\''; *p++ = ')'; *p++ = '\0'; /* Execute the file */ return DoPythonCommand (eap, buffer); } /************************************************************************* * 2. Python output stream: writes output via [e]msg(). *************************************************************************/ /* Implementation functions */ static PyObject *OutputGetattr (PyObject *, char *); static int OutputSetattr (PyObject *, char *, PyObject *); static PyObject *OutputWrite (PyObject *, PyObject *); static PyObject *OutputWritelines (PyObject *, PyObject *); typedef void (*writefn) (char_u *); static void writer (writefn fn, char_u *str, int n); /* Output object definition */ typedef struct { PyObject_HEAD long softspace; long error; } OutputObject; static struct PyMethodDef OutputMethods[] = { /* name, function, calling, documentation */ {"write", OutputWrite, 1, "" }, {"writelines", OutputWritelines, 1, "" }, { NULL, NULL, 0, NULL } }; static PyTypeObject OutputType = { PyObject_HEAD_INIT(0) 0, "OutputObject", sizeof(OutputObject), 0, (destructor) 0, (printfunc) 0, (getattrfunc) OutputGetattr, (setattrfunc) OutputSetattr, (cmpfunc) 0, (reprfunc) 0, 0, /* as number */ 0, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, (binaryfunc) 0, (reprfunc) 0 }; /**********************************************************************/ static PyObject *OutputGetattr (PyObject *self, char *name) { if (strcmp(name, "softspace") == 0) return PyInt_FromLong(((OutputObject *)(self))->softspace); return Py_FindMethod (OutputMethods, self, name); } static int OutputSetattr (PyObject *self, char *name, PyObject *val) { if (val == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete OutputObject attributes"); return -1; } if (strcmp(name, "softspace") == 0) { if (!PyInt_Check(val)) { PyErr_SetString(PyExc_TypeError, "softspace must be an integer"); return -1; } ((OutputObject *)(self))->softspace = PyInt_AsLong (val); return 0; } PyErr_SetString(PyExc_AttributeError, "invalid attribute"); return -1; } /**********************************************************************/ static PyObject *OutputWrite (PyObject *self, PyObject *args) { int len; char *str; int error = ((OutputObject *)(self))->error; if (!PyArg_ParseTuple (args, "s#", &str, &len)) return NULL; writer ((error ? emsg : msg), str, len); Py_INCREF(Py_None); return Py_None; } static PyObject *OutputWritelines (PyObject *self, PyObject *args) { int n; int i; PyObject *list; int error = ((OutputObject *)(self))->error; if (!PyArg_ParseTuple (args, "O", &list)) return NULL; Py_INCREF(list); if (!PyList_Check(list)) { PyErr_SetString(PyExc_TypeError, "writelines() requires list of strings"); Py_DECREF(list); return NULL; } n = PyList_Size(list); for (i = 0; i < n; ++i) { PyObject *line = PyList_GetItem (list, i); char *str; int len; if (!PyArg_Parse (line, "s#", &str, &len)) { PyErr_SetString(PyExc_TypeError, "writelines() requires list of strings"); Py_DECREF(list); return NULL; } writer ((error ? emsg : msg), str, len); } Py_DECREF(list); Py_INCREF(Py_None); return Py_None; } /* Output buffer management */ static char_u *buffer = NULL; static int buffer_len = 0; static int buffer_size = 0; static writefn old_fn = NULL; static void buffer_ensure (int n) { int new_size; char_u *new_buffer; if (n < buffer_size) return; new_size = buffer_size; while (new_size < n) new_size += 80; if (new_size != buffer_size) { new_buffer = malloc (new_size); if (new_buffer == NULL) { emsg("Out of memory!"); return; } if (buffer) { memcpy (new_buffer, buffer, buffer_len); free (buffer); } buffer = new_buffer; buffer_size = new_size; } } static void PythonIO_Flush (void) { if (old_fn && buffer_len) { buffer[buffer_len] = 0; old_fn(buffer); } buffer_len = 0; } static void writer (writefn fn, char_u *str, int n) { char_u *ptr; if (fn != old_fn && old_fn != NULL) PythonIO_Flush(); old_fn = fn; while (n > 0 && (ptr = memchr(str, '\n', n)) != NULL) { int len = ptr - str; buffer_ensure (buffer_len + len + 1); memcpy (buffer + buffer_len, str, len); buffer_len += len; buffer[buffer_len] = 0; fn(buffer); str = ptr + 1; n -= len + 1; buffer_len = 0; } /* Put the remaining text into the buffer for later printing */ buffer_ensure (buffer_len + n + 1); memcpy (buffer + buffer_len, str, n); buffer_len += n; } /**********************************************************************/ static OutputObject Output = { PyObject_HEAD_INIT(&OutputType) 0, 0 }; static OutputObject Error = { PyObject_HEAD_INIT(&OutputType) 0, 1 }; static int PythonIO_Init (void) { // Fixups... OutputType.ob_type = &PyType_Type; PySys_SetObject ("stdout", (PyObject *)(&Output)); PySys_SetObject ("stderr", (PyObject *)(&Error)); if (PyErr_Occurred()) { emsg("Python: Error initialising I/O objects"); return -1; } return 0; } /************************************************************************* * 3. Implementation of the Vim module for Python *************************************************************************/ /* Vim module - Implementation functions * ------------------------------------- */ static PyObject *VimError; static int VimInit (void); static PyObject *VimCommand (PyObject *, PyObject *); static PyObject *VimRegister (PyObject *, PyObject *); static PyObject *VimSetRegister (PyObject *, PyObject *); /* Window type - Implementation functions * -------------------------------------- */ typedef struct { PyObject_HEAD WIN *win; } WindowObject; #define INVALID_WINDOW_VALUE ((WIN*)(-1)) static PyTypeObject WindowType; #define WindowType_Check(obj) ((obj)->ob_type == &WindowType) static PyObject *WindowNew (WIN *); static void WindowDestructor (PyObject *); static PyObject *WindowGetattr (PyObject *, char *); static int WindowSetattr (PyObject *, char *, PyObject *); /* Buffer type - Implementation functions * -------------------------------------- */ typedef struct { PyObject_HEAD BUF *buf; } BufferObject; #define INVALID_BUFFER_VALUE ((BUF*)(-1)) static PyTypeObject BufferType; #define BufferType_Check(obj) ((obj)->ob_type == &BufferType) static PyObject *BufferNew (BUF *); static void BufferDestructor (PyObject *); static PyObject *BufferGetattr (PyObject *, char *); static int BufferLength (PyObject *); static PyObject *BufferItem (PyObject *, int); static PyObject *BufferSlice (PyObject *, int, int); static int BufferAssItem (PyObject *, int, PyObject *); static int BufferAssSlice (PyObject *, int, int, PyObject *); static PyObject *BufferAppend (PyObject *, PyObject *); static PyObject *BufferMark (PyObject *, PyObject *); static PyObject *BufferRange (PyObject *, PyObject *); /* Line range type - Implementation functions * -------------------------------------- */ typedef struct { PyObject_HEAD BufferObject *buf; int start; int end; } RangeObject; static PyTypeObject RangeType; #define RangeType_Check(obj) ((obj)->ob_type == &RangeType) static PyObject *RangeNew (BUF *, int, int); static void RangeDestructor (PyObject *); static PyObject *RangeGetattr (PyObject *, char *); static int RangeLength (PyObject *); static PyObject *RangeItem (PyObject *, int); static PyObject *RangeSlice (PyObject *, int, int); static int RangeAssItem (PyObject *, int, PyObject *); static int RangeAssSlice (PyObject *, int, int, PyObject *); static PyObject *RangeAppend (PyObject *, PyObject *); /* Window list type - Implementation functions * ------------------------------------------- */ static int WinListLength (PyObject *); static PyObject *WinListItem (PyObject *, int); /* Buffer list type - Implementation functions * ------------------------------------------- */ static int BufListLength (PyObject *); static PyObject *BufListItem (PyObject *, int); /* Current objects type - Implementation functions * ----------------------------------------------- */ static PyObject *CurrentGetattr (PyObject *, char *); static int CurrentSetattr (PyObject *, char *, PyObject *); /* Vim module - Definitions */ static struct PyMethodDef VimMethods[] = { /* name, function, calling, documentation */ {"command", VimCommand, 1, "" }, {"register", VimRegister, 1, "" }, {"set_register", VimSetRegister, 1, "" }, { NULL, NULL, 0, NULL } }; /* Vim module - Implementation */ static PyObject *VimCommand (PyObject *self, PyObject *args) { char *cmd; if (!PyArg_ParseTuple(args, "s", &cmd)) return NULL; do_cmdline (cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); update_screen(NOT_VALID); if (got_int) { PyErr_SetNone (PyExc_KeyboardInterrupt); return NULL; } else if (did_emsg) { PyErr_SetVim ("command failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject *VimRegister (PyObject *self, PyObject *args) { int n; int size; char reg; char *str; int linewise; char **array; PyObject *result; if (!PyArg_ParseTuple(args, "c", ®)) return NULL; n = get_register (reg, &array, &linewise); if (n == -1) { PyErr_SetVim ("invalid register '%c'", reg); return NULL; } /* Empty register - return None */ if (n == 0) { Py_INCREF(Py_None); return Py_None; } str = VimArrayToString (array, n, &size); if (str == NULL) return PyErr_NoMemory(); /* Omit the terminating newline if the register is not linewise */ if (!linewise) --size; result = PyString_FromStringAndSize (str, size); free (str); return result; } static PyObject *VimSetRegister (PyObject *self, PyObject *args) { char reg; char *str = NULL; int len = 0; int linewise = -1; int i; int n; char *p; int result; char *copy; char **array; if (!PyArg_ParseTuple(args, "cs#|i", ®, &str, &len, &linewise)) return NULL; /* If linewise was not specified, make the register linewise only if * the string had a final newline. */ if (linewise == -1) { linewise = (str[len-1] == '\n' ? 1 : 0); } /* Copy the string, as we are going to mess with it. * Add a terminating newline, to normalise what goes on below. */ copy = alloc(len+1); if (copy == NULL) return PyErr_NoMemory(); memcpy (copy, str, len); copy[len] = '\n'; ++len; /* Convert the string to a Vim array (lines separated by nulls, and * embedded nulls replaced with \n characters */ n = MemToVimArray(copy, len); /* Allocate an array of pointers, and fill it */ array = (char_u **) alloc (n * sizeof(char_u *)); if (array == NULL) { vim_free (copy); return PyErr_NoMemory(); } p = copy; for (i = 0; i < n; ++i) { array[i] = p; p = p + strlen(p) + 1; } result = set_register (reg, array, n, linewise); vim_free (array); vim_free (copy); if (result == FAIL) { PyErr_SetVim ("cannot set register '%c'", reg); return NULL; } Py_INCREF(Py_None); return Py_None; } /* Common routines for buffers and line ranges * ------------------------------------------- */ static int CheckBuffer (BufferObject *this) { if (this->buf == INVALID_BUFFER_VALUE) { PyErr_SetVim ("attempt to refer to deleted buffer"); return -1; } return 0; } static PyObject *RBItem (BufferObject *self, int n, int start, int end) { if (CheckBuffer(self)) return NULL; if (n < 0 || n > end - start) { PyErr_SetString(PyExc_IndexError, "line number out of range"); return NULL; } return GetBufferLine (self->buf, n+start); } static PyObject *RBSlice (BufferObject *self, int lo, int hi, int start, int end) { int size; if (CheckBuffer(self)) return NULL; size = end - start + 1; if (lo < 0) lo = 0; else if (lo > size) lo = size; if (hi < 0) hi = 0; if (hi < lo) hi = lo; else if (hi > size) hi = size; return GetBufferLineList (self->buf, lo+start, hi+start); } static int RBAssItem (BufferObject *self, int n, PyObject *val, int start, int end, int *new_end) { int len_change; if (CheckBuffer(self)) return -1; if (n < 0 || n > end - start) { PyErr_SetString(PyExc_IndexError, "line number out of range"); return -1; } if (SetBufferLine (self->buf, n+start, val, &len_change) == FAIL) return -1; if (new_end) *new_end = end + len_change; return 0; } static int RBAssSlice (BufferObject *self, int lo, int hi, PyObject *val, int start, int end, int *new_end) { int size; int ret = 0; int len_change; /* Self must be a valid buffer */ if (CheckBuffer(self)) return -1; /* Sort out the slice range */ size = end - start + 1; if (lo < 0) lo = 0; else if (lo > size) lo = size; if (hi < 0) hi = 0; if (hi < lo) hi = lo; else if (hi > size) hi = size; if (SetBufferLineList (self->buf, lo+start, hi+start, val, &len_change) == FAIL) return -1; if (new_end) *new_end = end + len_change; return 0; } static PyObject *RBAppend (BufferObject *self, PyObject *args, int start, int end, int *new_end) { PyObject *lines; int len_change; int max; int n; if (CheckBuffer(self)) return NULL; max = n = end - start + 1; if (!PyArg_ParseTuple (args, "O|i", &lines, &n)) return NULL; if (n < 0 || n > max) { PyErr_SetString (PyExc_ValueError, "line number out of range"); return NULL; } if (InsertBufferLines(self->buf, n + start - 1, lines, &len_change) == FAIL) return NULL; if (new_end) *new_end = end + len_change; Py_INCREF(Py_None); return Py_None; } /* Buffer object - Definitions */ static struct PyMethodDef BufferMethods[] = { /* name, function, calling, documentation */ {"append", BufferAppend, 1, "" }, {"mark", BufferMark, 1, "" }, {"range", BufferRange, 1, "" }, { NULL, NULL, 0, NULL } }; static PySequenceMethods BufferAsSeq = { (inquiry) BufferLength, /* sq_length, len(x) */ (binaryfunc) 0, /* BufferConcat, */ /* sq_concat, x+y */ (intargfunc) 0, /* BufferRepeat, */ /* sq_repeat, x*n */ (intargfunc) BufferItem, /* sq_item, x[i] */ (intintargfunc) BufferSlice, /* sq_slice, x[i:j] */ (intobjargproc) BufferAssItem, /* sq_ass_item, x[i]=v */ (intintobjargproc) BufferAssSlice, /* sq_ass_slice, x[i:j]=v */ }; static PyTypeObject BufferType = { PyObject_HEAD_INIT(0) 0, "BufferObject", sizeof(BufferObject), 0, (destructor) BufferDestructor, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) BufferGetattr, /* tp_getattr, x.attr */ (setattrfunc) 0, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ &BufferAsSeq, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Buffer object - Implementation */ static PyObject *BufferNew (BUF *buf) { /* We need to handle deletion of buffers underneath us. * If we add a "python_ref" field to the BUF structure, * then we can get at it in buf_freeall() in vim. We then * need to create only ONE Python object per buffer - if * we try to create a second, just INCREF the existing one * and return it. The (single) Python object referring to * the buffer is stored in "python_ref". * Question: what to do on a buf_freeall(). We'll probably * have to either delete the Python object (DECREF it to * zero - a bad idea, as it leaves dangling refs!) or * set the BUF* value to an invalid value (-1?), which * means we need checks in all access functions... Bah. */ BufferObject *self; if (buf->python_ref) self = buf->python_ref; else { self = PyObject_NEW (BufferObject, &BufferType); if (self == NULL) return NULL; self->buf = buf; buf->python_ref = self; } return (PyObject *)(self); } static void BufferDestructor (PyObject *self) { BufferObject *this = (BufferObject *)(self); if (this->buf && this->buf != INVALID_BUFFER_VALUE) this->buf->python_ref = NULL; PyMem_DEL (self); } static PyObject *BufferGetattr (PyObject *self, char *name) { BufferObject *this = (BufferObject *)(self); if (CheckBuffer(this)) return NULL; if (strcmp(name, "name") == 0) return Py_BuildValue("s",this->buf->b_ffname); else if (strcmp(name,"__members__") == 0) return Py_BuildValue("[s]", "name"); else return Py_FindMethod (BufferMethods, self, name); } /**********************************************************************/ static int BufferLength (PyObject *self) { /* HOW DO WE SIGNAL AN ERROR FROM THIS FUNCTION? */ if (CheckBuffer((BufferObject *)(self))) return -1; /* ??? */ return (((BufferObject *)(self))->buf->b_ml.ml_line_count); } static PyObject *BufferItem (PyObject *self, int n) { return RBItem((BufferObject *)(self), n, 1, ((BufferObject *)(self))->buf->b_ml.ml_line_count); } static PyObject *BufferSlice (PyObject *self, int lo, int hi) { return RBSlice((BufferObject *)(self), lo, hi, 1, ((BufferObject *)(self))->buf->b_ml.ml_line_count); } static int BufferAssItem (PyObject *self, int n, PyObject *val) { return RBAssItem((BufferObject *)(self), n, val, 1, ((BufferObject *)(self))->buf->b_ml.ml_line_count, NULL); } static int BufferAssSlice (PyObject *self, int lo, int hi, PyObject *val) { return RBAssSlice((BufferObject *)(self), lo, hi, val, 1, ((BufferObject *)(self))->buf->b_ml.ml_line_count, NULL); } static PyObject *BufferAppend (PyObject *self, PyObject *args) { return RBAppend((BufferObject *)(self), args, 1, ((BufferObject *)(self))->buf->b_ml.ml_line_count, NULL); } static PyObject *BufferMark (PyObject *self, PyObject *args) { FPOS pos; char mark; if (CheckBuffer((BufferObject *)(self))) return NULL; if (!PyArg_ParseTuple(args, "c", &mark)) return NULL; if (islower(mark)) pos = ((BufferObject *)(self))->buf->b_namedm[mark-'a']; else if (mark == '[') pos = ((BufferObject *)(self))->buf->b_op_start; else if (mark == ']') pos = ((BufferObject *)(self))->buf->b_op_end; else if (mark == '"') pos = ((BufferObject *)(self))->buf->b_last_cursor; else if (mark == '<' || mark == '>') { FPOS *start, *end; if (VIsual_active) start = &VIsual_save; else start = &VIsual; end = &VIsual_end; pos = ((mark == '<') == lt(*start, *end) ? *start : *end); } else { PyErr_SetVim ("invalid mark name '%c'", mark); return NULL; } if (pos.lnum == 0) { /* Or raise an error? */ Py_INCREF(Py_None); return Py_None; } return Py_BuildValue("(ll)", (long)(pos.lnum), (long)(pos.col)); } static PyObject *BufferRange (PyObject *self, PyObject *args) { int start; int end; if (CheckBuffer((BufferObject *)(self))) return NULL; if (!PyArg_ParseTuple(args, "ii", &start, &end)) return NULL; return RangeNew(((BufferObject *)(self))->buf, start, end); } /* Line range object - Definitions */ static struct PyMethodDef RangeMethods[] = { /* name, function, calling, documentation */ {"append", RangeAppend, 1, "" }, { NULL, NULL, 0, NULL } }; static PySequenceMethods RangeAsSeq = { (inquiry) RangeLength, /* sq_length, len(x) */ (binaryfunc) 0, /* BufferConcat, */ /* sq_concat, x+y */ (intargfunc) 0, /* BufferRepeat, */ /* sq_repeat, x*n */ (intargfunc) RangeItem, /* sq_item, x[i] */ (intintargfunc) RangeSlice, /* sq_slice, x[i:j] */ (intobjargproc) RangeAssItem, /* sq_ass_item, x[i]=v */ (intintobjargproc) RangeAssSlice, /* sq_ass_slice, x[i:j]=v */ }; static PyTypeObject RangeType = { PyObject_HEAD_INIT(0) 0, "RangeObject", sizeof(RangeObject), 0, (destructor) RangeDestructor, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) RangeGetattr, /* tp_getattr, x.attr */ (setattrfunc) 0, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ &RangeAsSeq, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Line range object - Implementation */ static PyObject *RangeNew (BUF *buf, int start, int end) { BufferObject *buffer; RangeObject *self; self = PyObject_NEW (RangeObject, &RangeType); if (self == NULL) return NULL; buffer = (BufferObject *)BufferNew(buf); if (buffer == NULL) { PyMem_DEL(self); return NULL; } Py_INCREF(buffer); self->buf = buffer; self->start = start; self->end = end; return (PyObject *)(self); } static void RangeDestructor (PyObject *self) { Py_DECREF(((RangeObject *)(self))->buf); PyMem_DEL (self); } static PyObject *RangeGetattr (PyObject *self, char *name) { return Py_FindMethod (RangeMethods, self, name); } /**********************************************************************/ static int RangeLength (PyObject *self) { /* HOW DO WE SIGNAL AN ERROR FROM THIS FUNCTION? */ if (CheckBuffer(((RangeObject *)(self))->buf)) return -1; /* ??? */ return (((RangeObject *)(self))->end - ((RangeObject *)(self))->start + 1); } static PyObject *RangeItem (PyObject *self, int n) { return RBItem(((RangeObject *)(self))->buf, n, ((RangeObject *)(self))->start, ((RangeObject *)(self))->end); } static PyObject *RangeSlice (PyObject *self, int lo, int hi) { return RBSlice(((RangeObject *)(self))->buf, lo, hi, ((RangeObject *)(self))->start, ((RangeObject *)(self))->end); } static int RangeAssItem (PyObject *self, int n, PyObject *val) { return RBAssItem(((RangeObject *)(self))->buf, n, val, ((RangeObject *)(self))->start, ((RangeObject *)(self))->end, &((RangeObject *)(self))->end); } static int RangeAssSlice (PyObject *self, int lo, int hi, PyObject *val) { return RBAssSlice(((RangeObject *)(self))->buf, lo, hi, val, ((RangeObject *)(self))->start, ((RangeObject *)(self))->end, &((RangeObject *)(self))->end); } static PyObject *RangeAppend (PyObject *self, PyObject *args) { return RBAppend(((RangeObject *)(self))->buf, args, ((RangeObject *)(self))->start, ((RangeObject *)(self))->end, &((RangeObject *)(self))->end); } /* Buffer list object - Definitions */ typedef struct { PyObject_HEAD } BufListObject; static PySequenceMethods BufListAsSeq = { (inquiry) BufListLength, /* sq_length, len(x) */ (binaryfunc) 0, /* sq_concat, x+y */ (intargfunc) 0, /* sq_repeat, x*n */ (intargfunc) BufListItem, /* sq_item, x[i] */ (intintargfunc) 0, /* sq_slice, x[i:j] */ (intobjargproc) 0, /* sq_ass_item, x[i]=v */ (intintobjargproc) 0, /* sq_ass_slice, x[i:j]=v */ }; static PyTypeObject BufListType = { PyObject_HEAD_INIT(0) 0, "BufListObject", sizeof(BufListObject), 0, (destructor) 0, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) 0, /* tp_getattr, x.attr */ (setattrfunc) 0, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ &BufListAsSeq, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Buffer list object - Implementation */ static int BufListLength (PyObject *self) { BUF *b = firstbuf; int n = 0; while (b) { ++n; b = b->b_next; } return n; } static PyObject *BufListItem (PyObject *self, int n) { BUF *b; for (b = firstbuf; b; b = b->b_next, --n) { if (n == 0) return BufferNew(b); } PyErr_SetString(PyExc_IndexError, "no such buffer"); return NULL; } /* Window object - Definitions */ static struct PyMethodDef WindowMethods[] = { /* name, function, calling, documentation */ { NULL, NULL, 0, NULL } }; static PyTypeObject WindowType = { PyObject_HEAD_INIT(0) 0, "WindowObject", sizeof(WindowObject), 0, (destructor) WindowDestructor, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) WindowGetattr, /* tp_getattr, x.attr */ (setattrfunc) WindowSetattr, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ 0, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Window object - Implementation */ static PyObject *WindowNew (WIN *win) { /* We need to handle deletion of windows underneath us. * If we add a "python_ref" field to the WIN structure, * then we can get at it in win_free() in vim. We then * need to create only ONE Python object per window - if * we try to create a second, just INCREF the existing one * and return it. The (single) Python object referring to * the window is stored in "python_ref". * On a win_free() we set the Python object's WIN* field * to an invalid value. We trap all uses of a window * object, and reject them if the WIN* field is invalid. */ WindowObject *self; if (win->python_ref) self = win->python_ref; else { self = PyObject_NEW (WindowObject, &WindowType); if (self == NULL) return NULL; self->win = win; win->python_ref = self; } return (PyObject *)(self); } static void WindowDestructor (PyObject *self) { WindowObject *this = (WindowObject *)(self); if (this->win && this->win != INVALID_WINDOW_VALUE) this->win->python_ref = NULL; PyMem_DEL (self); } static int CheckWindow (WindowObject *this) { if (this->win == INVALID_WINDOW_VALUE) { PyErr_SetVim ("attempt to refer to deleted window"); return -1; } return 0; } static PyObject *WindowGetattr (PyObject *self, char *name) { WindowObject *this = (WindowObject *)(self); if (CheckWindow(this)) return NULL; if (strcmp(name, "buffer") == 0) return (PyObject *)BufferNew(this->win->w_buffer); else if (strcmp (name, "cursor") == 0) { FPOS *pos = &this->win->w_cursor; return Py_BuildValue("(ll)", (long)(pos->lnum), (long)(pos->col)); } else if (strcmp (name, "height") == 0) return Py_BuildValue("l", (long)(this->win->w_height)); else if (strcmp(name,"__members__") == 0) return Py_BuildValue("[sss]", "buffer", "cursor", "height"); else return Py_FindMethod (WindowMethods, self, name); } static int WindowSetattr (PyObject *self, char *name, PyObject *val) { WindowObject *this = (WindowObject *)(self); if (CheckWindow(this)) return -1; if (strcmp(name, "buffer") == 0) { PyErr_SetString(PyExc_TypeError, "readonly attribute"); return -1; } else if (strcmp (name, "cursor") == 0) { long lnum; long col; if (!PyArg_Parse(val, "(ll)", &lnum, &col)) return -1; if (lnum <= 0 || lnum > this->win->w_buffer->b_ml.ml_line_count) { PyErr_SetVim ("cursor position outside buffer"); return -1; } /* NO CHECK ON COLUMN - SEEMS NOT TO MATTER */ this->win->w_cursor.lnum = lnum; this->win->w_cursor.col = col; update_screen(NOT_VALID); return 0; } else if (strcmp (name, "height") == 0) { int height; WIN *savewin; if (!PyArg_Parse(val, "i", &height)) return -1; savewin = curwin; curwin = this->win; win_setheight (height); curwin = savewin; return 0; } else { PyErr_SetString(PyExc_AttributeError, name); return -1; } } /* Window list object - Definitions */ typedef struct { PyObject_HEAD } WinListObject; static PySequenceMethods WinListAsSeq = { (inquiry) WinListLength, /* sq_length, len(x) */ (binaryfunc) 0, /* sq_concat, x+y */ (intargfunc) 0, /* sq_repeat, x*n */ (intargfunc) WinListItem, /* sq_item, x[i] */ (intintargfunc) 0, /* sq_slice, x[i:j] */ (intobjargproc) 0, /* sq_ass_item, x[i]=v */ (intintobjargproc) 0, /* sq_ass_slice, x[i:j]=v */ }; static PyTypeObject WinListType = { PyObject_HEAD_INIT(0) 0, "WinListObject", sizeof(WinListObject), 0, (destructor) 0, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) 0, /* tp_getattr, x.attr */ (setattrfunc) 0, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ &WinListAsSeq, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Window list object - Implementation */ static int WinListLength (PyObject *self) { WIN *w = firstwin; int n = 0; while (w) { ++n; w = w->w_next; } return n; } static PyObject *WinListItem (PyObject *self, int n) { WIN *w; for (w = firstwin; w; w = w->w_next, --n) { if (n == 0) return WindowNew(w); } PyErr_SetString(PyExc_IndexError, "no such window"); return NULL; } /* Current items object - Definitions */ typedef struct { PyObject_HEAD } CurrentObject; static PyTypeObject CurrentType = { PyObject_HEAD_INIT(0) 0, "CurrentObject", sizeof(CurrentObject), 0, (destructor) 0, /* tp_dealloc, refcount==0 */ (printfunc) 0, /* tp_print, print x */ (getattrfunc) CurrentGetattr, /* tp_getattr, x.attr */ (setattrfunc) CurrentSetattr, /* tp_setattr, x.attr=v */ (cmpfunc) 0, /* tp_compare, x>y */ (reprfunc) 0, /* tp_repr, `x`, print x */ 0, /* as number */ 0, /* as sequence */ 0, /* as mapping */ (hashfunc) 0, /* tp_hash, dict(x) */ (binaryfunc) 0, /* tp_call, x() */ (reprfunc) 0, /* tp_str, str(x) */ }; /* Current items object - Implementation */ static PyObject *CurrentGetattr (PyObject *self, char *name) { if (strcmp(name, "buffer") == 0) return (PyObject *)BufferNew(curbuf); else if (strcmp (name, "window") == 0) return (PyObject *)WindowNew(curwin); else if (strcmp (name, "line") == 0) return GetBufferLine (curbuf, curwin->w_cursor.lnum); else if (strcmp (name, "range") == 0) return RangeNew (curbuf, RangeStart, RangeEnd); else if (strcmp(name,"__members__") == 0) return Py_BuildValue("[ssss]", "buffer", "window", "line", "range"); else { PyErr_SetString(PyExc_AttributeError, name); return NULL; } } static int CurrentSetattr (PyObject *self, char *name, PyObject *value) { if (strcmp(name, "buffer") == 0) { PyErr_SetString(PyExc_TypeError, "readonly attribute"); return -1; } else if (strcmp (name, "window") == 0) { PyErr_SetString(PyExc_TypeError, "readonly attribute"); return -1; } else if (strcmp (name, "line") == 0) { if (SetBufferLine(curbuf, curwin->w_cursor.lnum, value, NULL) == FAIL) return -1; return 0; } else if (strcmp (name, "range") == 0) { PyErr_SetString (PyExc_AttributeError, "not yet implemented"); return -1; } else { PyErr_SetString(PyExc_AttributeError, name); return -1; } } /* External interface */ void python_buffer_free (BUF *buf) { if (buf->python_ref) { BufferObject *bp = buf->python_ref; bp->buf = INVALID_BUFFER_VALUE; buf->python_ref = NULL; } } void python_window_free (WIN *win) { if (win->python_ref) { WindowObject *wp = win->python_ref; wp->win = INVALID_WINDOW_VALUE; win->python_ref = NULL; } } static BufListObject TheBufferList = { PyObject_HEAD_INIT(&BufListType) }; static WinListObject TheWindowList = { PyObject_HEAD_INIT(&WinListType) }; static CurrentObject TheCurrent = { PyObject_HEAD_INIT(&CurrentType) }; static int VimInit (void) { PyObject *mod; PyObject *dict; mod = Py_InitModule ("vim", VimMethods); dict = PyModule_GetDict (mod); VimError = Py_BuildValue ("s", "vim.error"); PyDict_SetItemString (dict, "error", VimError); PyDict_SetItemString (dict, "buffers", (PyObject *)(&TheBufferList)); PyDict_SetItemString (dict, "current", (PyObject *)(&TheCurrent)); PyDict_SetItemString (dict, "windows", (PyObject *)(&TheWindowList)); if (PyErr_Occurred()) return -1; return 0; } static int PythonMod_Init (void) { // Fixups... BufferType.ob_type = &PyType_Type; RangeType.ob_type = &PyType_Type; WindowType.ob_type = &PyType_Type; BufListType.ob_type = &PyType_Type; WinListType.ob_type = &PyType_Type; CurrentType.ob_type = &PyType_Type; return VimInit(); } /************************************************************************* * 4. Utility functions for handling the interface between Vim and Python. *************************************************************************/ /* Get a line from the specified buffer. The line number is * in Vim format (1-based). The line is returned as a Python * string object. */ static PyObject *GetBufferLine (BUF *buf, int n) { char_u *line = vim_strsave(ml_get_buf (buf, n, FALSE)); int len = strlen(line); PyObject *obj; char_u *p; /* Convert embedded '\n' characters to nulls */ for (p = memchr(line, '\n', len); p; p = memchr(p, '\n', len-(p-line))) *p++ = '\0'; /* Create a Python string object */ obj = PyString_FromStringAndSize (line, len); /* Free the temporary memory */ vim_free (line); /* Return the object. If a Python error occurred, this will be a * NULL pointer. It is the caller's responsibility to check for * this and react appropriately. * * The ownership of the Python object is passed to the caller (ie, * the caller should Py_DECREF() the object when it is finished * with it). */ return obj; } /* Get a list of lines from the specified buffer. The line numbers * are in Vim format (1-based). The range is from lo up to, but not * including, hi. The list is returned as a Python list of string objects. */ static PyObject *GetBufferLineList (BUF *buf, int lo, int hi) { int i; int n = hi - lo; PyObject *list = PyList_New (n); if (list == NULL) return NULL; for (i = 0; i < n; ++i) { char_u *line = vim_strsave(ml_get_buf (buf, lo + i, FALSE)); int len = strlen(line); PyObject *str; char_u *p; /* Convert embedded '\n' characters to nulls */ for (p = memchr(line, '\n', len); p; p = memchr(p, '\n', len-(p-line))) *p++ = '\0'; /* Create a Python string object */ str = PyString_FromStringAndSize (line, len); /* Free the temporary memory */ vim_free (line); /* Error check - was the Python string creation OK? */ if (str == NULL) { Py_DECREF(list); return NULL; } /* Set the list item */ if (PyList_SetItem (list, i, str)) { Py_DECREF(str); Py_DECREF(list); return NULL; } } /* The ownership of the Python list is passed to the caller (ie, * the caller should Py_DECREF() the object when it is finished * with it). */ return list; } /* Replace a line in the specified buffer. The line number is * in Vim format (1-based). The replacement line is given as * a Python string object. The object is checked for validity * and correct format. Errors are returned as a value of FAIL. * The return value is OK on success. * If OK is returned and len_change is not NULL, *len_change * is set to the change in the buffer length. */ static int SetBufferLine (BUF *buf, int n, PyObject *line, int *len_change) { /* First of all, we check the thpe of the supplied Python object. * There are three cases: * 1. NULL, or None - this is a deletion. * 2. A string - this is a replacement. * 3. Anything else - this is an error. */ if (line == Py_None || line == NULL) { /* TODO: Add error checking */ BUF *savebuf = curbuf; curbuf = buf; u_savedel (n, 1); ml_delete (n, FALSE); mark_adjust (n, n, MAXLNUM, -1); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = -1; return OK; } else if (PyString_Check(line)) { const char *str = PyString_AsString (line); char *save = VimStringAlloc(line); BUF *savebuf; if (save == NULL) return FAIL; /* We do not need to free save, as we pass responsibility for * it to vim, via the final parameter of ml_replace(). */ /* TODO: Add error checking */ savebuf = curbuf; curbuf = buf; u_savesub(n); ml_replace (n, save, TRUE); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = 0; return OK; } else { PyErr_BadArgument(); return FAIL; } } /* Replace a range of lines in the specified buffer. The line numbers are in * Vim format (1-based). The range is from lo up to, but not including, hi. * The replacement lines are given as a Python list of string objects. The * list is checked for validity and correct format. Errors are returned as a * value of FAIL. The return value is OK on success. * If OK is returned and len_change is not NULL, *len_change * is set to the change in the buffer length. */ static int SetBufferLineList (BUF *buf, int lo, int hi, PyObject *list, int *len_change) { /* First of all, we check the thpe of the supplied Python object. * There are three cases: * 1. NULL, or None - this is a deletion. * 2. A list - this is a replacement. * 3. Anything else - this is an error. */ if (list == Py_None || list == NULL) { /* TODO: Add error checking */ int i; int n = hi - lo; BUF *savebuf = curbuf; curbuf = buf; u_savedel (lo, n); for (i = 0; i < n; ++i) ml_delete (lo, FALSE); mark_adjust (lo, hi-1, MAXLNUM, -n); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = -n; return OK; } else if (PyList_Check(list)) { int i; int n = PyList_Size(list); int lines = hi - lo; char **array; BUF *savebuf; array = (char **) alloc (n * sizeof(char *)); if (array == NULL) { PyErr_NoMemory(); return FAIL; } for (i = 0; i < n; ++i) { PyObject *line = PyList_GetItem (list, i); array[i] = VimStringAlloc(line); if (array[i] == NULL) { while (i) vim_free (array[--i]); vim_free (array); return FAIL; } } /* TODO: Add error checking */ savebuf = curbuf; curbuf = buf; u_save(lo-1, hi); /* If the size of the range is reducing (ie, n < lines) we * need to delete some lines. We do this at the start, by * repeatedly deleting line "lo". */ for (i = 0; i < lines - n; ++i) ml_delete(lo, FALSE); /* For as long as possible, replace the existing lines with the * new lines. This is a more efficient operation, as it requires * less memory allocation and freeing. */ for (i = 0; i < lines && i < n; ++i) { ml_replace (lo+i, array[i], TRUE); } /* Now we may need to insert the remaining new lines. If we do, we * must free the strings as we finish with them (we can't pass the * responsibility to vim in this case). */ while (i < n) { ml_append (lo+i-1, array[i], 0, FALSE); vim_free (array[i]); ++i; } /* Adjust marks. Invalidate any which lie in the * changed range, and move any in the remainder of the buffer. */ mark_adjust (lo, hi-1, MAXLNUM, n - lines); /* Free the array of lines. All of its contents have now * been dealt with (either freed, or the responsibility passed * to vim. */ vim_free (array); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = n - lines; return OK; } else { PyErr_BadArgument(); return FAIL; } } /* Insert a number of lines into the specified buffer after the specifed line. * The line number is in Vim format (1-based). The lines to be inserted are * given as a Python list of string objects or as a single string. The lines * to be added are checked for validity and correct format. Errors are * returned as a value of FAIL. The return value is OK on success. * If OK is returned and len_change is not NULL, *len_change * is set to the change in the buffer length. */ static int InsertBufferLines (BUF *buf, int n, PyObject *lines, int *len_change) { /* First of all, we check the type of the supplied Python object. * It must be a string or a list, or the call is in error. */ if (PyString_Check(lines)) { char *str = VimStringAlloc(lines); BUF *savebuf; if (str == NULL) return FAIL; /* TODO: Add error checking */ savebuf = curbuf; curbuf = buf; u_save(n, n+1); ml_append (n, str, 0, FALSE); mark_adjust (n+1, MAXLNUM, 1, 0); vim_free(str); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = 1; return OK; } else if (PyList_Check(lines)) { int i; int size = PyList_Size(lines); char **array; BUF *savebuf; array = (char **) alloc (size * sizeof(char *)); if (array == NULL) { PyErr_NoMemory(); return FAIL; } for (i = 0; i < size; ++i) { PyObject *line = PyList_GetItem (lines, i); array[i] = VimStringAlloc(line); if (array[i] == NULL) { while (i) vim_free (array[--i]); vim_free (array); return FAIL; } } /* TODO: Add error checking */ savebuf = curbuf; curbuf = buf; u_save(n, n+1); /* TODO: Check this! How to save for an insert? */ for (i = 0; i < size; ++i) { ml_append (n+i, array[i], 0, FALSE); vim_free (array[i]); } mark_adjust (n+1, MAXLNUM, size, 0); /* Free the array of lines. All of its contents have now * been freed. */ vim_free (array); curbuf = savebuf; update_screen(NOT_VALID); if (len_change) *len_change = size; return OK; } else { PyErr_BadArgument(); return FAIL; } } /* Convert an array of lines in Vim format (with null bytes replaced * by \n) into a single string, with embedded nulls and with the * individual lines separated by \n characters. * A newline is added at the end. * The size of the resulting string is returned in *size. * The returned string has been allocated on the heap. * NULL is returned on errors (no data, memory exhaustion, etc). */ static char *VimArrayToString (char **array, int n, int *size) { int i, j; char *p; int len = 0; char *result; if (size == NULL) return NULL; for (i = 0; i < n; ++i) { len += strlen(array[i]) + 1; } if (len == 0) return NULL; result = malloc(len); if (result == NULL) return NULL; p = result; for (i = 0; i < n; ++i) { char *str = array[i]; int max = strlen(str); for (j = 0; j < max; ++j) { char ch = str[j]; *p++ = (ch == '\n' ? '\0' : ch); } *p++ = '\n'; } *size = len; return result; } /* Swap newline and null characters in a block of memory. * Converts a normal string into a Vim array of lines. * Returns the number of lines in the array. */ static int MemToVimArray (char *mem, int len) { int lines = 0; while (len) { if (*mem == '\n') { ++lines; *mem = '\0'; } else if (*mem == '\0') *mem = '\n'; ++mem; --len; } return lines; } /* Set the Python error information to indicate a Vim error. * There is a fixed length buffer of 256 bytes. This should be enough. */ void PyErr_SetVim (const char *fmt, ...) { static char buf[256]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); PyErr_SetString(VimError, buf); } static char *VimStringAlloc (PyObject *obj) { const char *str; char *save; int len; int i; if (obj == NULL || !PyString_Check(obj)) { PyErr_BadArgument(); return NULL; } str = PyString_AsString (obj); len = PyString_Size (obj); /* Error checking: String must not contain newlines, as we * are replacing a single line, and we must replace it with * a single line. */ if (memchr(str, '\n', len)) { PyErr_SetVim ("string cannot contain newlines"); return NULL; } /* Create a copy of the string, with internal nulls replaced by * newline characters, as is the vim convention. */ save = alloc (len+1); if (save == NULL) { PyErr_NoMemory(); return NULL; } for (i = 0; i < len; ++i) { if (str[i] == '\0') save[i] = '\n'; else save[i] = str[i]; } save[i] = '\0'; return save; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.