diff options
Diffstat (limited to 'src/if_py_both.h')
-rw-r--r-- | src/if_py_both.h | 241 |
1 files changed, 234 insertions, 7 deletions
diff --git a/src/if_py_both.h b/src/if_py_both.h index f78eccbd..6cd1b23d 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -65,10 +65,10 @@ static struct PyMethodDef OutputMethods[] = { OutputWrite(PyObject *self, PyObject *args) { int len; - char *str; + char *str = NULL; int error = ((OutputObject *)(self))->error; - if (!PyArg_ParseTuple(args, "s#", &str, &len)) + if (!PyArg_ParseTuple(args, "es#", p_enc, &str, &len)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -76,6 +76,7 @@ OutputWrite(PyObject *self, PyObject *args) writer((writefn)(error ? emsg : msg), (char_u *)str, len); Python_Release_Vim(); Py_END_ALLOW_THREADS + PyMem_Free(str); Py_INCREF(Py_None); return Py_None; @@ -104,10 +105,10 @@ OutputWritelines(PyObject *self, PyObject *args) for (i = 0; i < n; ++i) { PyObject *line = PyList_GetItem(list, i); - char *str; + char *str = NULL; PyInt len; - if (!PyArg_Parse(line, "s#", &str, &len)) { + if (!PyArg_Parse(line, "es#", p_enc, &str, &len)) { PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings")); Py_DECREF(list); return NULL; @@ -118,6 +119,7 @@ OutputWritelines(PyObject *self, PyObject *args) writer((writefn)(error ? emsg : msg), (char_u *)str, len); Python_Release_Vim(); Py_END_ALLOW_THREADS + PyMem_Free(str); } Py_DECREF(list); @@ -681,6 +683,7 @@ StringToLine(PyObject *obj) { const char *str; char *save; + PyObject *bytes; PyInt len; PyInt i; char *p; @@ -691,8 +694,9 @@ StringToLine(PyObject *obj) return NULL; } - str = PyString_AsString(obj); - len = PyString_Size(obj); + bytes = PyString_AsBytes(obj); /* for Python 2 this does nothing */ + str = PyString_AsString(bytes); + len = PyString_Size(bytes); /* * Error checking: String must not contain newlines, as we @@ -731,6 +735,7 @@ StringToLine(PyObject *obj) } save[i] = '\0'; + PyString_FreeBytes(bytes); /* Python 2 does nothing here */ return save; } @@ -817,7 +822,8 @@ py_fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) invalidate_botline(); } -/* Replace a line in the specified buffer. The line number is +/* + * 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. @@ -908,6 +914,193 @@ SetBufferLine(buf_T *buf, PyInt n, PyObject *line, PyInt *len_change) } } +/* 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_T *buf, PyInt lo, PyInt hi, PyObject *list, PyInt *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) + { + PyInt i; + PyInt n = (int)(hi - lo); + buf_T *savebuf = curbuf; + + PyErr_Clear(); + curbuf = buf; + + if (u_savedel((linenr_T)lo, (long)n) == FAIL) + PyErr_SetVim(_("cannot save undo information")); + else + { + for (i = 0; i < n; ++i) + { + if (ml_delete((linenr_T)lo, FALSE) == FAIL) + { + PyErr_SetVim(_("cannot delete line")); + break; + } + } + if (buf == curwin->w_buffer) + py_fix_cursor((linenr_T)lo, (linenr_T)hi, (linenr_T)-n); + deleted_lines_mark((linenr_T)lo, (long)i); + } + + curbuf = savebuf; + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = -n; + + return OK; + } + else if (PyList_Check(list)) + { + PyInt i; + PyInt new_len = PyList_Size(list); + PyInt old_len = hi - lo; + PyInt extra = 0; /* lines added to text, can be negative */ + char **array; + buf_T *savebuf; + + if (new_len == 0) /* avoid allocating zero bytes */ + array = NULL; + else + { + array = (char **)alloc((unsigned)(new_len * sizeof(char *))); + if (array == NULL) + { + PyErr_NoMemory(); + return FAIL; + } + } + + for (i = 0; i < new_len; ++i) + { + PyObject *line = PyList_GetItem(list, i); + + array[i] = StringToLine(line); + if (array[i] == NULL) + { + while (i) + vim_free(array[--i]); + vim_free(array); + return FAIL; + } + } + + savebuf = curbuf; + + PyErr_Clear(); + curbuf = buf; + + if (u_save((linenr_T)(lo-1), (linenr_T)hi) == FAIL) + PyErr_SetVim(_("cannot save undo information")); + + /* If the size of the range is reducing (ie, new_len < old_len) we + * need to delete some old_len. We do this at the start, by + * repeatedly deleting line "lo". + */ + if (!PyErr_Occurred()) + { + for (i = 0; i < old_len - new_len; ++i) + if (ml_delete((linenr_T)lo, FALSE) == FAIL) + { + PyErr_SetVim(_("cannot delete line")); + break; + } + extra -= i; + } + + /* For as long as possible, replace the existing old_len with the + * new old_len. This is a more efficient operation, as it requires + * less memory allocation and freeing. + */ + if (!PyErr_Occurred()) + { + for (i = 0; i < old_len && i < new_len; ++i) + if (ml_replace((linenr_T)(lo+i), (char_u *)array[i], FALSE) + == FAIL) + { + PyErr_SetVim(_("cannot replace line")); + break; + } + } + else + i = 0; + + /* Now we may need to insert the remaining new old_len. If we do, we + * must free the strings as we finish with them (we can't pass the + * responsibility to vim in this case). + */ + if (!PyErr_Occurred()) + { + while (i < new_len) + { + if (ml_append((linenr_T)(lo + i - 1), + (char_u *)array[i], 0, FALSE) == FAIL) + { + PyErr_SetVim(_("cannot insert line")); + break; + } + vim_free(array[i]); + ++i; + ++extra; + } + } + + /* Free any left-over old_len, as a result of an error */ + while (i < new_len) + { + vim_free(array[i]); + ++i; + } + + /* Free the array of old_len. All of its contents have now + * been dealt with (either freed, or the responsibility passed + * to vim. + */ + vim_free(array); + + /* Adjust marks. Invalidate any which lie in the + * changed range, and move any in the remainder of the buffer. + */ + mark_adjust((linenr_T)lo, (linenr_T)(hi - 1), + (long)MAXLNUM, (long)extra); + changed_lines((linenr_T)lo, 0, (linenr_T)hi, (long)extra); + + if (buf == curwin->w_buffer) + py_fix_cursor((linenr_T)lo, (linenr_T)hi, (linenr_T)extra); + + curbuf = savebuf; + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = new_len - old_len; + + 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 @@ -1113,6 +1306,40 @@ RBAsItem(BufferObject *self, PyInt n, PyObject *val, PyInt start, PyInt end, PyI return 0; } + static PyInt +RBAsSlice(BufferObject *self, PyInt lo, PyInt hi, PyObject *val, PyInt start, PyInt end, PyInt *new_end) +{ + PyInt size; + PyInt 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, PyInt start, PyInt end, PyInt *new_end) |