# HG changeset patch # User Bram Moolenaar # Date 1369857742 -7200 # Node ID fa39483a1363d533f9c7897d476bf0ebd1b8f44b # Parent 15cf120f2a6801b41ac75533fc5c7d79a08f82ea updated for version 7.3.1042 Problem: Python: can't assign to vim.Buffer.name. Solution: Python patch 3. (ZyX) diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -250,7 +250,7 @@ vim.windows *python-windows* object and always use windows from that tab page (or throw vim.error in case tab page was deleted). You can keep a reference to both without keeping a reference to vim module object or |python-tabpage|, - they will not loose their properties in this case. + they will not lose their properties in this case. vim.tabpages *python-tabpages* A sequence object providing access to the list of vim tab pages. The @@ -361,6 +361,11 @@ The buffer object attributes are: this object will raise KeyError. If option is |global-local| and local value is missing getting it will return None. + b.name String, RW. Contains buffer name (full path). + Note: when assigning to b.name |BufFilePre| and + |BufFilePost| autocommands are launched. + b.number Buffer number. Can be used as |python-buffers| key. + Read-only. The buffer object methods are: b.append(str) Append a line to the buffer diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -784,6 +784,7 @@ do_move(line1, line2, dest) */ last_line = curbuf->b_ml.ml_line_count; mark_adjust(line1, line2, last_line - line2, 0L); + changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines); if (dest >= line2) { mark_adjust(line2 + 1, dest, -num_lines, 0L); @@ -799,6 +800,7 @@ do_move(line1, line2, dest) curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L); + changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra); /* * Now we delete the original text -- webb @@ -2414,6 +2416,58 @@ print_line(lnum, use_number, list) info_message = FALSE; } + int +rename_buffer(new_fname) + char_u *new_fname; +{ + char_u *fname, *sfname, *xfname; +#ifdef FEAT_AUTOCMD + buf_T *buf = curbuf; + + apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, buf); + /* buffer changed, don't change name now */ + if (buf != curbuf) + return FAIL; +# ifdef FEAT_EVAL + if (aborting()) /* autocmds may abort script processing */ + return FAIL; +# endif +#endif + /* + * The name of the current buffer will be changed. + * A new (unlisted) buffer entry needs to be made to hold the old file + * name, which will become the alternate file name. + * But don't set the alternate file name if the buffer didn't have a + * name. + */ + fname = buf->b_ffname; + sfname = buf->b_sfname; + xfname = buf->b_fname; + buf->b_ffname = NULL; + buf->b_sfname = NULL; + if (setfname(buf, new_fname, NULL, TRUE) == FAIL) + { + buf->b_ffname = fname; + buf->b_sfname = sfname; + return FAIL; + } + buf->b_flags |= BF_NOTEDITED; + if (xfname != NULL && *xfname != NUL) + { + buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0); + if (buf != NULL && !cmdmod.keepalt) + curwin->w_alt_fnum = buf->b_fnum; + } + vim_free(fname); + vim_free(sfname); +#ifdef FEAT_AUTOCMD + apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, buf); +#endif + /* Change directories when the 'acd' option is set. */ + DO_AUTOCHDIR + return OK; +} + /* * ":file[!] [fname]". */ @@ -2421,9 +2475,6 @@ print_line(lnum, use_number, list) ex_file(eap) exarg_T *eap; { - char_u *fname, *sfname, *xfname; - buf_T *buf; - /* ":0file" removes the file name. Check for illegal uses ":3file", * "0file name", etc. */ if (eap->addr_count > 0 @@ -2437,49 +2488,8 @@ ex_file(eap) if (*eap->arg != NUL || eap->addr_count == 1) { -#ifdef FEAT_AUTOCMD - buf = curbuf; - apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf); - /* buffer changed, don't change name now */ - if (buf != curbuf) - return; -# ifdef FEAT_EVAL - if (aborting()) /* autocmds may abort script processing */ + if (rename_buffer(eap->arg) == FAIL) return; -# endif -#endif - /* - * The name of the current buffer will be changed. - * A new (unlisted) buffer entry needs to be made to hold the old file - * name, which will become the alternate file name. - * But don't set the alternate file name if the buffer didn't have a - * name. - */ - fname = curbuf->b_ffname; - sfname = curbuf->b_sfname; - xfname = curbuf->b_fname; - curbuf->b_ffname = NULL; - curbuf->b_sfname = NULL; - if (setfname(curbuf, eap->arg, NULL, TRUE) == FAIL) - { - curbuf->b_ffname = fname; - curbuf->b_sfname = sfname; - return; - } - curbuf->b_flags |= BF_NOTEDITED; - if (xfname != NULL && *xfname != NUL) - { - buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0); - if (buf != NULL && !cmdmod.keepalt) - curwin->w_alt_fnum = buf->b_fnum; - } - vim_free(fname); - vim_free(sfname); -#ifdef FEAT_AUTOCMD - apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf); -#endif - /* Change directories when the 'acd' option is set. */ - DO_AUTOCHDIR } /* print full file name if :cd used */ fileinfo(FALSE, FALSE, eap->forceit); diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -30,6 +30,14 @@ typedef int Py_ssize_t; /* Python 2.4 a #define INVALID_WINDOW_VALUE ((win_T *)(-1)) #define INVALID_TABPAGE_VALUE ((tabpage_T *)(-1)) +#define DICTKEY_DECL \ + PyObject *dictkey_todecref; +#define DICTKEY_GET(err) \ + if (!(key = StringToChars(keyObject, &dictkey_todecref))) \ + return err; +#define DICTKEY_UNREF \ + Py_XDECREF(dictkey_todecref); + typedef void (*rangeinitializer)(void *); typedef void (*runner)(const char *, void * #ifdef PY_CAN_RECURSE @@ -64,6 +72,51 @@ Python_Release_Vim(void) { } +/* + * The "todecref" argument holds a pointer to PyObject * that must be + * DECREF'ed after returned char_u * is no longer needed or NULL if all what + * was needed to generate returned value is object. + * + * Use Py_XDECREF to decrement reference count. + */ + static char_u * +StringToChars(PyObject *object, PyObject **todecref) +{ + char_u *p; + PyObject *bytes = NULL; + + if (PyBytes_Check(object)) + { + + if (PyString_AsStringAndSize(object, (char **) &p, NULL) == -1) + return NULL; + if (p == NULL) + return NULL; + + *todecref = NULL; + } + else if (PyUnicode_Check(object)) + { + bytes = PyUnicode_AsEncodedString(object, (char *)ENC_OPT, NULL); + if (bytes == NULL) + return NULL; + + if(PyString_AsStringAndSize(bytes, (char **) &p, NULL) == -1) + return NULL; + if (p == NULL) + return NULL; + + *todecref = bytes; + } + else + { + PyErr_SetString(PyExc_TypeError, _("object must be string")); + return NULL; + } + + return (char_u *) p; +} + /* Output buffer management */ @@ -1586,6 +1639,18 @@ set_option_value_for(key, numval, string return VimTryEnd(); } + static void * +py_memsave(void *p, size_t len) +{ + void *r; + if (!(r = PyMem_Malloc(len))) + return NULL; + mch_memmove(r, p, len); + return r; +} + +#define PY_STRSAVE(s) ((char_u *) py_memsave(s, STRLEN(s) + 1)) + static int OptionsAssItem(OptionsObject *self, PyObject *keyObject, PyObject *valObject) { @@ -1670,57 +1735,16 @@ OptionsAssItem(OptionsObject *self, PyOb else { char_u *val; - if (PyBytes_Check(valObject)) - { - - if (PyString_AsStringAndSize(valObject, (char **) &val, NULL) == -1) - { - DICTKEY_UNREF - return -1; - } - if (val == NULL) - { - DICTKEY_UNREF - return -1; - } - - val = vim_strsave(val); - } - else if (PyUnicode_Check(valObject)) + PyObject *todecref; + + if ((val = StringToChars(valObject, &todecref))) { - PyObject *bytes; - - bytes = PyUnicode_AsEncodedString(valObject, (char *)ENC_OPT, NULL); - if (bytes == NULL) - { - DICTKEY_UNREF - return -1; - } - - if(PyString_AsStringAndSize(bytes, (char **) &val, NULL) == -1) - { - DICTKEY_UNREF - return -1; - } - if (val == NULL) - { - DICTKEY_UNREF - return -1; - } - - val = vim_strsave(val); - Py_XDECREF(bytes); + r = set_option_value_for(key, 0, val, opt_flags, + self->opt_type, self->from); + Py_XDECREF(todecref); } else - { - PyErr_SetString(PyExc_TypeError, _("object must be string")); - DICTKEY_UNREF - return -1; - } - - r = set_option_value_for(key, 0, val, opt_flags, - self->opt_type, self->from); - vim_free(val); + r = -1; } DICTKEY_UNREF @@ -2541,7 +2565,7 @@ SetBufferLineList(buf_T *buf, PyInt lo, array = NULL; else { - array = (char **)alloc((unsigned)(new_len * sizeof(char *))); + array = PyMem_New(char *, new_len); if (array == NULL) { PyErr_NoMemory(); @@ -2558,7 +2582,7 @@ SetBufferLineList(buf_T *buf, PyInt lo, { while (i) vim_free(array[--i]); - vim_free(array); + PyMem_Free(array); return FAIL; } } @@ -2635,7 +2659,7 @@ SetBufferLineList(buf_T *buf, PyInt lo, * been dealt with (either freed, or the responsibility passed * to vim. */ - vim_free(array); + PyMem_Free(array); /* Adjust marks. Invalidate any which lie in the * changed range, and move any in the remainder of the buffer. @@ -2717,7 +2741,7 @@ InsertBufferLines(buf_T *buf, PyInt n, P char **array; buf_T *savebuf; - array = (char **)alloc((unsigned)(size * sizeof(char *))); + array = PyMem_New(char *, size); if (array == NULL) { PyErr_NoMemory(); @@ -2733,7 +2757,7 @@ InsertBufferLines(buf_T *buf, PyInt n, P { while (i) vim_free(array[--i]); - vim_free(array); + PyMem_Free(array); return FAIL; } } @@ -2768,7 +2792,7 @@ InsertBufferLines(buf_T *buf, PyInt n, P /* Free the array of lines. All of its contents have now * been freed. */ - vim_free(array); + PyMem_Free(array); restore_buffer(savebuf); update_screen(VALID); @@ -3179,6 +3203,45 @@ BufferAttr(BufferObject *self, char *nam return NULL; } + static int +BufferSetattr(BufferObject *self, char *name, PyObject *valObject) +{ + if (CheckBuffer(self)) + return -1; + + if (strcmp(name, "name") == 0) + { + char_u *val; + aco_save_T aco; + int r; + PyObject *todecref; + + if (!(val = StringToChars(valObject, &todecref))) + return -1; + + VimTryStart(); + /* Using aucmd_*: autocommands will be executed by rename_buffer */ + aucmd_prepbuf(&aco, self->buf); + r = rename_buffer(val); + aucmd_restbuf(&aco); + Py_XDECREF(todecref); + if (VimTryEnd()) + return -1; + + if (r == FAIL) + { + PyErr_SetVim(_("failed to rename buffer")); + return -1; + } + return 0; + } + else + { + PyErr_SetString(PyExc_AttributeError, name); + return -1; + } +} + static PyObject * BufferAppend(BufferObject *self, PyObject *args) { @@ -4040,7 +4103,7 @@ ConvertFromPyObject(PyObject *obj, typva if (result == NULL) return -1; - if (set_string_copy(result, tv) == -1) + if (set_string_copy(result, tv)) { Py_XDECREF(bytes); return -1; @@ -4169,11 +4232,13 @@ init_structs(void) BufferType.tp_methods = BufferMethods; #if PY_MAJOR_VERSION >= 3 BufferType.tp_getattro = (getattrofunc)BufferGetattro; + BufferType.tp_setattro = (setattrofunc)BufferSetattro; BufferType.tp_alloc = call_PyType_GenericAlloc; BufferType.tp_new = call_PyType_GenericNew; BufferType.tp_free = call_PyObject_Free; #else BufferType.tp_getattr = (getattrfunc)BufferGetattr; + BufferType.tp_setattr = (setattrfunc)BufferSetattr; #endif vim_memset(&WindowType, 0, sizeof(WindowType)); diff --git a/src/if_python.c b/src/if_python.c --- a/src/if_python.c +++ b/src/if_python.c @@ -676,18 +676,6 @@ get_exceptions(void) static int initialised = 0; #define PYINITIALISED initialised -#define DICTKEY_GET(err) \ - if (!PyString_Check(keyObject)) \ - { \ - PyErr_SetString(PyExc_TypeError, _("only string keys are allowed")); \ - return err; \ - } \ - if (PyString_AsStringAndSize(keyObject, (char **) &key, NULL) == -1) \ - return err; - -#define DICTKEY_UNREF -#define DICTKEY_DECL - #define DESTRUCTOR_FINISH(self) self->ob_type->tp_free((PyObject*)self); #define WIN_PYTHON_REF(win) win->w_python_ref @@ -926,7 +914,7 @@ DoPyCommand(const char *cmd, rangeinitia else { /* Need to make a copy, value may change when setting new locale. */ - saved_locale = (char *)vim_strsave((char_u *)saved_locale); + saved_locale = (char *) PY_STRSAVE(saved_locale); (void)setlocale(LC_NUMERIC, "C"); } #endif @@ -953,7 +941,7 @@ DoPyCommand(const char *cmd, rangeinitia if (saved_locale != NULL) { (void)setlocale(LC_NUMERIC, saved_locale); - vim_free(saved_locale); + PyMem_Free(saved_locale); } #endif diff --git a/src/if_python3.c b/src/if_python3.c --- a/src/if_python3.c +++ b/src/if_python3.c @@ -638,32 +638,6 @@ static int py3initialised = 0; #define PYINITIALISED py3initialised -#define DICTKEY_DECL PyObject *bytes = NULL; - -#define DICTKEY_GET(err) \ - if (PyBytes_Check(keyObject)) \ - { \ - if (PyString_AsStringAndSize(keyObject, (char **) &key, NULL) == -1) \ - return err; \ - } \ - else if (PyUnicode_Check(keyObject)) \ - { \ - bytes = PyString_AsBytes(keyObject); \ - if (bytes == NULL) \ - return err; \ - if (PyString_AsStringAndSize(bytes, (char **) &key, NULL) == -1) \ - return err; \ - } \ - else \ - { \ - PyErr_SetString(PyExc_TypeError, _("only string keys are allowed")); \ - return err; \ - } - -#define DICTKEY_UNREF \ - if (bytes != NULL) \ - Py_XDECREF(bytes); - #define DESTRUCTOR_FINISH(self) Py_TYPE(self)->tp_free((PyObject*)self) #define WIN_PYTHON_REF(win) win->w_python3_ref @@ -696,6 +670,7 @@ static PyObject *BufferDir(PyObject *); static PyObject *OutputGetattro(PyObject *, PyObject *); static int OutputSetattro(PyObject *, PyObject *, PyObject *); static PyObject *BufferGetattro(PyObject *, PyObject *); +static int BufferSetattro(PyObject *, PyObject *, PyObject *); static PyObject *TabPageGetattro(PyObject *, PyObject *); static PyObject *WindowGetattro(PyObject *, PyObject *); static int WindowSetattro(PyObject *, PyObject *, PyObject *); @@ -1108,6 +1083,14 @@ BufferGetattro(PyObject *self, PyObject* return PyObject_GenericGetAttr(self, nameobj); } + static int +BufferSetattro(PyObject *self, PyObject *nameobj, PyObject *val) +{ + GET_ATTR_STRING(name, nameobj); + + return BufferSetattr((BufferObject *)(self), name, val); +} + static PyObject * BufferDir(PyObject *self UNUSED) { diff --git a/src/proto/ex_cmds.pro b/src/proto/ex_cmds.pro --- a/src/proto/ex_cmds.pro +++ b/src/proto/ex_cmds.pro @@ -19,6 +19,7 @@ void viminfo_writestring __ARGS((FILE *f void do_fixdel __ARGS((exarg_T *eap)); void print_line_no_prefix __ARGS((linenr_T lnum, int use_number, int list)); void print_line __ARGS((linenr_T lnum, int use_number, int list)); +int rename_buffer __ARGS((char_u *new_fname)); void ex_file __ARGS((exarg_T *eap)); void ex_update __ARGS((exarg_T *eap)); void ex_write __ARGS((exarg_T *eap)); diff --git a/src/testdir/test86.in b/src/testdir/test86.in --- a/src/testdir/test86.in +++ b/src/testdir/test86.in @@ -476,6 +476,10 @@ EOF :py b=vim.current.buffer :wincmd w :mark a +:augroup BUFS +: autocmd BufFilePost * python cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) +: autocmd BufFilePre * python cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) +:augroup END py << EOF cb = vim.current.buffer # Tests BufferAppend and BufferItem @@ -496,9 +500,20 @@ b.append('foo') b[0]='bar' b[0:0]=['baz'] vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) +# Test assigning to name property +old_name = cb.name +cb.name = 'foo' +cb.append(cb.name[-11:]) +b.name = 'bar' +cb.append(b.name[-11:]) +cb.name = old_name +cb.append(cb.name[-17:]) # Test CheckBuffer -vim.command('bwipeout! ' + str(b.number)) -for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc")'): +for _b in vim.buffers: + if _b is not cb: + vim.command('bwipeout! ' + str(_b.number)) +del _b +for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc")', 'b.name = "!"'): try: exec(expr) except vim.error: @@ -507,7 +522,12 @@ for expr in ('b[1]','b[:] = ["A", "B"]', # Usually a SEGV here # Should not happen in any case cb.append('No exception for ' + expr) +vim.command('cd .') EOF +:augroup BUFS +: autocmd! +:augroup END +:augroup! BUFS :" :" Test vim.buffers object :set hidden @@ -586,7 +606,9 @@ def W(w): else: return repr(w) -def Cursor(w, start=len(cb)): +start = len(cb) + +def Cursor(w): if w.buffer is cb: return repr((start - w.cursor[0], w.cursor[1])) else: diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -319,14 +319,23 @@ bar Second line Third line foo +1:BufFilePre:1 +6:BufFilePost:1 +testdir/foo +5:BufFilePre:5 +5:BufFilePost:5 +testdir/bar +1:BufFilePre:1 +7:BufFilePost:1 +testdir/test86.in i: i2: i: i3: 1:= -6:= -7:= -8:= +8:= +9:= +10:= 4 i4: i4: @@ -335,7 +344,7 @@ Number of tabs: 4 Current tab pages: (1): 1 windows, current is Windows: - (1): displays buffer ; cursor is at (27, 0) + (1): displays buffer ; cursor is at (36, 0) (2): 1 windows, current is Windows: (1): displays buffer ; cursor is at (1, 0) diff --git a/src/testdir/test87.in b/src/testdir/test87.in --- a/src/testdir/test87.in +++ b/src/testdir/test87.in @@ -463,6 +463,10 @@ EOF :py3 b=vim.current.buffer :wincmd w :mark a +:augroup BUFS +: autocmd BufFilePost * python3 cb.append(vim.eval('expand("")') + ':BufFilePost:' + vim.eval('bufnr("%")')) +: autocmd BufFilePre * python3 cb.append(vim.eval('expand("")') + ':BufFilePre:' + vim.eval('bufnr("%")')) +:augroup END py3 << EOF cb = vim.current.buffer # Tests BufferAppend and BufferItem @@ -483,8 +487,19 @@ b.append('foo') b[0]='bar' b[0:0]=['baz'] vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) +# Test assigning to name property +old_name = cb.name +cb.name = 'foo' +cb.append(cb.name[-11:]) +b.name = 'bar' +cb.append(b.name[-11:]) +cb.name = old_name +cb.append(cb.name[-17:]) # Test CheckBuffer -vim.command('bwipeout! ' + str(b.number)) +for _b in vim.buffers: + if _b is not cb: + vim.command('bwipeout! ' + str(_b.number)) +del _b for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc")'): try: exec(expr) @@ -494,6 +509,7 @@ for expr in ('b[1]','b[:] = ["A", "B"]', # Usually a SEGV here # Should not happen in any case cb.append('No exception for ' + expr) +vim.command('cd .') EOF :" :" Test vim.buffers object diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -308,14 +308,23 @@ bar Second line Third line foo +1:BufFilePre:1 +6:BufFilePost:1 +testdir/foo +5:BufFilePre:5 +5:BufFilePost:5 +testdir/bar +1:BufFilePre:1 +7:BufFilePost:1 +testdir/test87.in i: i2: i: i3: 1:= -6:= -7:= -8:= +8:= +9:= +10:= 4 i4: i4: @@ -324,7 +333,7 @@ Number of tabs: 4 Current tab pages: (1): 1 windows, current is Windows: - (1): displays buffer ; cursor is at (27, 0) + (1): displays buffer ; cursor is at (36, 0) (2): 1 windows, current is Windows: (1): displays buffer ; cursor is at (1, 0) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1042, +/**/ 1041, /**/ 1040,