There are commands perldo, rubydo, luado and tcldo, but no Python equivalence. I managed to add this Python 3 version one, which behaves exactly like luado, using arguments and return value. This is the first time I add a feature to Vim. Please review and test it.
PS: After the digit ("py3"), Tab won't complete it any more. -- Best regards, lilydjwg Linux Vim Python 我的博客: http://lilydjwg.is-programmer.com/ -- A: Because it obfuscates the reading. Q: Why is top posting so bad? -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php
diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index e542a04..a6215d1 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -343,6 +343,22 @@ The |:py3| and |:python3| commands work similar to |:python|. *:py3file* The |:py3file| command works similar to |:pyfile|. + *:py3do* +:[range]py3do {body} Execute Python function "def _vim_pydo(line, linenr): + {body}" for each line in the [range], with the + function arguments being set to the text of each line + in turn, without a trailing <EOL>, and the current + line number. The function should return a string or + None. If a string is returned, it becomes the text of + the line in the current turn. The default for [range] + is the whole file: "1,$". + {not in Vi} + +Examples: +> + :py3do return "%s\t%d" % (line[::-1], len(line)) + :py3do if line: return "%4d: %s" % (linenr, line) + Vim can be built in four ways (:version output): 1. No Python support (-python, -python3) 2. Python 2 support only (+python or +python/dyn, -python3) diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 16540bd..3433f93 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -743,6 +743,8 @@ EX(CMD_pyfile, "pyfile", ex_pyfile, RANGE|FILE1|NEEDARG|CMDWIN), EX(CMD_py3, "py3", ex_py3, RANGE|EXTRA|NEEDARG|CMDWIN), +EX(CMD_py3do, "py3do", ex_py3do, + RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN), EX(CMD_python3, "python3", ex_py3, RANGE|EXTRA|NEEDARG|CMDWIN), EX(CMD_py3file, "py3file", ex_py3file, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 133fa7d..17f914a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -272,6 +272,7 @@ static void ex_popup __ARGS((exarg_T *eap)); #endif #ifndef FEAT_PYTHON3 # define ex_py3 ex_script_ni +# define ex_py3do ex_ni # define ex_py3file ex_ni #endif #ifndef FEAT_TCL diff --git a/src/if_python3.c b/src/if_python3.c index ac6f166..8b27795 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -74,6 +74,7 @@ static void init_structs(void); #else # define CODEC_ERROR_HANDLER NULL #endif +#define DOPY_FUNC "_vim_pydo" #define PyInt Py_ssize_t #define PyString_Check(obj) PyUnicode_Check(obj) @@ -114,6 +115,7 @@ static void init_structs(void); # define PyErr_Clear py3_PyErr_Clear # define PyErr_NoMemory py3_PyErr_NoMemory # define PyErr_Occurred py3_PyErr_Occurred +# define PyErr_PrintEx py3_PyErr_PrintEx # define PyErr_SetNone py3_PyErr_SetNone # define PyErr_SetString py3_PyErr_SetString # define PyEval_InitThreads py3_PyEval_InitThreads @@ -136,6 +138,11 @@ static void init_structs(void); # define PyModule_GetDict py3_PyModule_GetDict #undef PyRun_SimpleString # define PyRun_SimpleString py3_PyRun_SimpleString +# define PyObject_GetAttrString py3_PyObject_GetAttrString +# define PyObject_SetAttrString py3_PyObject_SetAttrString +# define PyObject_CallFunctionObjArgs py3_PyObject_CallFunctionObjArgs +# define PyEval_GetLocals py3_PyEval_GetLocals +# define PyEval_GetGlobals py3_PyEval_GetGlobals # define PySys_SetObject py3_PySys_SetObject # define PySys_SetArgv py3_PySys_SetArgv # define PyType_Type (*py3_PyType_Type) @@ -149,6 +156,7 @@ static void init_structs(void); # define _Py_NoneStruct (*py3__Py_NoneStruct) # define PyModule_AddObject py3_PyModule_AddObject # define PyImport_AppendInittab py3_PyImport_AppendInittab +# define PyImport_AddModule py3_PyImport_AddModule # define _PyUnicode_AsString py3__PyUnicode_AsString # undef PyUnicode_AsEncodedString # define PyUnicode_AsEncodedString py3_PyUnicode_AsEncodedString @@ -200,11 +208,17 @@ static PyObject* (*py3_PyErr_NoMemory)(void); static void (*py3_Py_Finalize)(void); static void (*py3_PyErr_SetString)(PyObject *, const char *); static int (*py3_PyRun_SimpleString)(char *); +static PyObject* (*py3_PyObject_GetAttrString)(PyObject *, const char *); +static PyObject* (*py3_PyObject_SetAttrString)(PyObject *, const char *, PyObject *); +static PyObject* (*py3_PyObject_CallFunctionObjArgs)(PyObject *, ...); +static PyObject* (*py3_PyEval_GetGlobals)(); +static PyObject* (*py3_PyEval_GetLocals)(); static PyObject* (*py3_PyList_GetItem)(PyObject *, Py_ssize_t); static PyObject* (*py3_PyImport_ImportModule)(const char *); static int (*py3_PyErr_BadArgument)(void); static PyTypeObject* py3_PyType_Type; static PyObject* (*py3_PyErr_Occurred)(void); +static void (*py3_PyErr_PrintEx)(int); static PyObject* (*py3_PyModule_GetDict)(PyObject *); static int (*py3_PyList_SetItem)(PyObject *, Py_ssize_t, PyObject *); static PyObject* (*py3_PyDict_GetItemString)(PyObject *, const char *); @@ -230,6 +244,7 @@ static PyObject*(*py3__PyObject_Init)(PyObject *, PyTypeObject *); static PyObject* py3__Py_NoneStruct; static int (*py3_PyModule_AddObject)(PyObject *m, const char *name, PyObject *o); static int (*py3_PyImport_AppendInittab)(const char *name, PyObject* (*initfunc)(void)); +static PyObject* (*py3_PyImport_AddModule)(const char *); static char* (*py3__PyUnicode_AsString)(PyObject *unicode); static PyObject* (*py3_PyUnicode_AsEncodedString)(PyObject *unicode, const char* encoding, const char* errors); static char* (*py3_PyBytes_AsString)(PyObject *bytes); @@ -291,11 +306,17 @@ static struct {"Py_Finalize", (PYTHON_PROC*)&py3_Py_Finalize}, {"PyErr_SetString", (PYTHON_PROC*)&py3_PyErr_SetString}, {"PyRun_SimpleString", (PYTHON_PROC*)&py3_PyRun_SimpleString}, + {"PyObject_GetAttrString", (PYTHON_PROC*)&py3_PyObject_GetAttrString}, + {"PyObject_SetAttrString", (PYTHON_PROC*)&py3_PyObject_SetAttrString}, + {"PyObject_CallFunctionObjArgs", (PYTHON_PROC*)&py3_PyObject_CallFunctionObjArgs}, + {"PyEval_GetGlobals", (PYTHON_PROC*)&py3_PyEval_GetGlobals}, + {"PyEval_GetLocals", (PYTHON_PROC*)&py3_PyEval_GetLocals}, {"PyList_GetItem", (PYTHON_PROC*)&py3_PyList_GetItem}, {"PyImport_ImportModule", (PYTHON_PROC*)&py3_PyImport_ImportModule}, {"PyErr_BadArgument", (PYTHON_PROC*)&py3_PyErr_BadArgument}, {"PyType_Type", (PYTHON_PROC*)&py3_PyType_Type}, {"PyErr_Occurred", (PYTHON_PROC*)&py3_PyErr_Occurred}, + {"PyErr_PrintEx", (PYTHON_PROC*)&py3_PyErr_PrintEx}, {"PyModule_GetDict", (PYTHON_PROC*)&py3_PyModule_GetDict}, {"PyList_SetItem", (PYTHON_PROC*)&py3_PyList_SetItem}, {"PyDict_GetItemString", (PYTHON_PROC*)&py3_PyDict_GetItemString}, @@ -316,6 +337,7 @@ static struct {"PyObject_Init", (PYTHON_PROC*)&py3__PyObject_Init}, {"PyModule_AddObject", (PYTHON_PROC*)&py3_PyModule_AddObject}, {"PyImport_AppendInittab", (PYTHON_PROC*)&py3_PyImport_AppendInittab}, + {"PyImport_AddModule", (PYTHON_PROC*)&py3_PyImport_AddModule}, {"_PyUnicode_AsString", (PYTHON_PROC*)&py3__PyUnicode_AsString}, {"PyBytes_AsString", (PYTHON_PROC*)&py3_PyBytes_AsString}, {"PyObject_GenericGetAttr", (PYTHON_PROC*)&py3_PyObject_GenericGetAttr}, @@ -775,6 +797,96 @@ ex_py3file(exarg_T *eap) DoPy3Command(eap, buffer); } +void ex_py3do(exarg_T *eap) +{ + linenr_T i; + const char *code_hdr = "def " DOPY_FUNC "(line, linenr):\n "; + const char *s = (const char *) eap->arg; + size_t len; + char *code; + int status; + linenr_T save; + PyObject *pyfunc, *pymain; + + if (Python3_Init()) + goto theend; + + if (u_save(eap->line1 - 1, eap->line2 + 1) != OK) + { + EMSG(_("cannot save undo information")); + return; + } + len = strlen(code_hdr) + strlen(s); + code = malloc(len+1); + STRCPY(code, code_hdr); + STRNCAT(code, s, len+1); + pygilstate = PyGILState_Ensure(); + status = PyRun_SimpleString(code); + vim_free(code); + if(status) + { + EMSG(_("failed to run the code")); + return; + } + status = 0; /* good */ + pymain = PyImport_AddModule("__main__"); + pyfunc = PyObject_GetAttrString(pymain, DOPY_FUNC); + PyGILState_Release(pygilstate); + + for (i = eap->line1; i <= eap->line2; i++) { + const char *line; + PyObject *pyline, *pylinenr, *pyret, *pybytes; + + line = (char *)ml_get(i); + pygilstate = PyGILState_Ensure(); + pyline = PyUnicode_Decode(line, strlen(line), + (char *)ENC_OPT, CODEC_ERROR_HANDLER); + pylinenr = PyLong_FromLong(i); + pyret = PyObject_CallFunctionObjArgs(pyfunc, pyline, pylinenr, NULL); + Py_DECREF(pyline); + Py_DECREF(pylinenr); + if (!pyret){ + PyErr_PrintEx(0); + PythonIO_Flush(); + status = 1; + goto out; + } + + if (pyret && pyret != Py_None) { + if (!PyUnicode_Check(pyret)) { + /* TODO: a proper error number */ + EMSG(_("E000: return value must be an instance of str")); + Py_XDECREF(pyret); + status = 1; + goto out; + } + pybytes = PyUnicode_AsEncodedString(pyret, + (char *)ENC_OPT, CODEC_ERROR_HANDLER); + ml_replace(i, (char_u *) PyBytes_AsString(pybytes), 1); + Py_DECREF(pybytes); + changed(); +#ifdef SYNTAX_HL + syn_changed(i); /* recompute syntax hl. for this line */ +#endif + } + Py_XDECREF(pyret); + PythonIO_Flush(); + PyGILState_Release(pygilstate); + } + pygilstate = PyGILState_Ensure(); +out: + Py_DECREF(pyfunc); + PyObject_SetAttrString(pymain, DOPY_FUNC, NULL); + PyGILState_Release(pygilstate); + if(status) + return; + check_cursor(); + update_curbuf(NOT_VALID); + +theend: + return; +} + /****************************************************** * 2. Python output stream: writes output via [e]msg(). */ diff --git a/src/proto/if_python3.pro b/src/proto/if_python3.pro index 9c132c3..3996a66 100644 --- a/src/proto/if_python3.pro +++ b/src/proto/if_python3.pro @@ -3,6 +3,7 @@ int python3_enabled __ARGS((int verbose)); void python3_end __ARGS((void)); int python3_loaded __ARGS((void)); void ex_py3 __ARGS((exarg_T *eap)); +void ex_py3do __ARGS((exarg_T *eap)); void ex_py3file __ARGS((exarg_T *eap)); void python3_buffer_free __ARGS((buf_T *buf)); void python3_window_free __ARGS((win_T *win));