summaryrefslogtreecommitdiff
path: root/src/if_py_both.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/if_py_both.h')
-rw-r--r--src/if_py_both.h241
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)