/* vi:set ts=8 sts=4 sw=4 noet: * * VIM - Vi IMproved by Bram Moolenaar * * Lua interface by Luis Carvalho * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ #include "vim.h" #include #include #include /* Only do the following when the feature is enabled. Needed for "make * depend". */ #if defined(FEAT_LUA) || defined(PROTO) #define LUAVIM_CHUNKNAME "vim chunk" #define LUAVIM_NAME "vim" #define LUAVIM_EVALNAME "luaeval" #define LUAVIM_EVALHEADER "local _A=select(1,...) return " typedef buf_T *luaV_Buffer; typedef win_T *luaV_Window; typedef dict_T *luaV_Dict; typedef list_T *luaV_List; typedef void (*msgfunc_T)(char_u *); static const char LUAVIM_DICT[] = "dict"; static const char LUAVIM_LIST[] = "list"; static const char LUAVIM_BUFFER[] = "buffer"; static const char LUAVIM_WINDOW[] = "window"; static const char LUAVIM_FREE[] = "luaV_free"; static const char LUAVIM_LUAEVAL[] = "luaV_luaeval"; static const char LUAVIM_SETREF[] = "luaV_setref"; /* most functions are closures with a cache table as first upvalue; * get/setudata manage references to vim userdata in cache table through * object pointers (light userdata) */ #define luaV_getudata(L, v) \ lua_pushlightuserdata((L), (void *) (v)); \ lua_rawget((L), lua_upvalueindex(1)) #define luaV_setudata(L, v) \ lua_pushlightuserdata((L), (void *) (v)); \ lua_pushvalue((L), -2); \ lua_rawset((L), lua_upvalueindex(1)) #define luaV_getfield(L, s) \ lua_pushlightuserdata((L), (void *)(s)); \ lua_rawget((L), LUA_REGISTRYINDEX) #define luaV_checksandbox(L) \ if (sandbox) luaL_error((L), "not allowed in sandbox") #define luaV_msg(L) luaV_msgfunc((L), (msgfunc_T) msg) #define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg) static luaV_List *luaV_pushlist (lua_State *L, list_T *lis); static luaV_Dict *luaV_pushdict (lua_State *L, dict_T *dic); #if LUA_VERSION_NUM <= 501 #define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n) #define luaL_typeerror luaL_typerror #else #define luaV_openlib luaL_setfuncs #endif #ifdef DYNAMIC_LUA #ifndef WIN3264 # include # define HANDLE void* # define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL) # define symbol_from_dll dlsym # define close_dll dlclose #else # define load_dll vimLoadLib # define symbol_from_dll GetProcAddress # define close_dll FreeLibrary #endif /* lauxlib */ #if LUA_VERSION_NUM <= 501 #define luaL_register dll_luaL_register #define luaL_prepbuffer dll_luaL_prepbuffer #define luaL_openlib dll_luaL_openlib #define luaL_typerror dll_luaL_typerror #define luaL_loadfile dll_luaL_loadfile #define luaL_loadbuffer dll_luaL_loadbuffer #else #define luaL_prepbuffsize dll_luaL_prepbuffsize #define luaL_setfuncs dll_luaL_setfuncs #define luaL_loadfilex dll_luaL_loadfilex #define luaL_loadbufferx dll_luaL_loadbufferx #define luaL_argerror dll_luaL_argerror #endif #define luaL_checkany dll_luaL_checkany #define luaL_checklstring dll_luaL_checklstring #define luaL_checkinteger dll_luaL_checkinteger #define luaL_optinteger dll_luaL_optinteger #define luaL_checktype dll_luaL_checktype #define luaL_error dll_luaL_error #define luaL_newstate dll_luaL_newstate #define luaL_buffinit dll_luaL_buffinit #define luaL_addlstring dll_luaL_addlstring #define luaL_pushresult dll_luaL_pushresult /* lua */ #if LUA_VERSION_NUM <= 501 #define lua_tonumber dll_lua_tonumber #define lua_tointeger dll_lua_tointeger #define lua_call dll_lua_call #define lua_pcall dll_lua_pcall #else #define lua_tonumberx dll_lua_tonumberx #define lua_tointegerx dll_lua_tointegerx #define lua_callk dll_lua_callk #define lua_pcallk dll_lua_pcallk #define lua_getglobal dll_lua_getglobal #define lua_setglobal dll_lua_setglobal #endif #if LUA_VERSION_NUM <= 502 #define lua_replace dll_lua_replace #define lua_remove dll_lua_remove #endif #if LUA_VERSION_NUM >= 503 #define lua_rotate dll_lua_rotate #define lua_copy dll_lua_copy #endif #define lua_typename dll_lua_typename #define lua_close dll_lua_close #define lua_gettop dll_lua_gettop #define lua_settop dll_lua_settop #define lua_pushvalue dll_lua_pushvalue #define lua_isnumber dll_lua_isnumber #define lua_isstring dll_lua_isstring #define lua_type dll_lua_type #define lua_rawequal dll_lua_rawequal #define lua_toboolean dll_lua_toboolean #define lua_tolstring dll_lua_tolstring #define lua_touserdata dll_lua_touserdata #define lua_pushnil dll_lua_pushnil #define lua_pushnumber dll_lua_pushnumber #define lua_pushinteger dll_lua_pushinteger #define lua_pushlstring dll_lua_pushlstring #define lua_pushstring dll_lua_pushstring #define lua_pushfstring dll_lua_pushfstring #define lua_pushcclosure dll_lua_pushcclosure #define lua_pushboolean dll_lua_pushboolean #define lua_pushlightuserdata dll_lua_pushlightuserdata #define lua_getfield dll_lua_getfield #define lua_rawget dll_lua_rawget #define lua_rawgeti dll_lua_rawgeti #define lua_createtable dll_lua_createtable #define lua_newuserdata dll_lua_newuserdata #define lua_getmetatable dll_lua_getmetatable #define lua_setfield dll_lua_setfield #define lua_rawset dll_lua_rawset #define lua_rawseti dll_lua_rawseti #define lua_setmetatable dll_lua_setmetatable #define lua_next dll_lua_next /* libs */ #define luaopen_base dll_luaopen_base #define luaopen_table dll_luaopen_table #define luaopen_string dll_luaopen_string #define luaopen_math dll_luaopen_math #define luaopen_io dll_luaopen_io #define luaopen_os dll_luaopen_os #define luaopen_package dll_luaopen_package #define luaopen_debug dll_luaopen_debug #define luaL_openlibs dll_luaL_openlibs /* lauxlib */ #if LUA_VERSION_NUM <= 501 void (*dll_luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l); char *(*dll_luaL_prepbuffer) (luaL_Buffer *B); void (*dll_luaL_openlib) (lua_State *L, const char *libname, const luaL_Reg *l, int nup); int (*dll_luaL_typerror) (lua_State *L, int narg, const char *tname); int (*dll_luaL_loadfile) (lua_State *L, const char *filename); int (*dll_luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, const char *name); #else char *(*dll_luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); void (*dll_luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); int (*dll_luaL_loadfilex) (lua_State *L, const char *filename, const char *mode); int (*dll_luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); int (*dll_luaL_argerror) (lua_State *L, int numarg, const char *extramsg); #endif void (*dll_luaL_checkany) (lua_State *L, int narg); const char *(*dll_luaL_checklstring) (lua_State *L, int numArg, size_t *l); lua_Integer (*dll_luaL_checkinteger) (lua_State *L, int numArg); lua_Integer (*dll_luaL_optinteger) (lua_State *L, int nArg, lua_Integer def); void (*dll_luaL_checktype) (lua_State *L, int narg, int t); int (*dll_luaL_error) (lua_State *L, const char *fmt, ...); lua_State *(*dll_luaL_newstate) (void); void (*dll_luaL_buffinit) (lua_State *L, luaL_Buffer *B); void (*dll_luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); void (*dll_luaL_pushresult) (luaL_Buffer *B); /* lua */ #if LUA_VERSION_NUM <= 501 lua_Number (*dll_lua_tonumber) (lua_State *L, int idx); lua_Integer (*dll_lua_tointeger) (lua_State *L, int idx); void (*dll_lua_call) (lua_State *L, int nargs, int nresults); int (*dll_lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); #else lua_Number (*dll_lua_tonumberx) (lua_State *L, int idx, int *isnum); lua_Integer (*dll_lua_tointegerx) (lua_State *L, int idx, int *isnum); void (*dll_lua_callk) (lua_State *L, int nargs, int nresults, int ctx, lua_CFunction k); int (*dll_lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, int ctx, lua_CFunction k); void (*dll_lua_getglobal) (lua_State *L, const char *var); void (*dll_lua_setglobal) (lua_State *L, const char *var); #endif #if LUA_VERSION_NUM <= 502 void (*dll_lua_replace) (lua_State *L, int idx); void (*dll_lua_remove) (lua_State *L, int idx); #endif #if LUA_VERSION_NUM >= 503 void (*dll_lua_rotate) (lua_State *L, int idx, int n); void (*dll_lua_copy) (lua_State *L, int fromidx, int toidx); #endif const char *(*dll_lua_typename) (lua_State *L, int tp); void (*dll_lua_close) (lua_State *L); int (*dll_lua_gettop) (lua_State *L); void (*dll_lua_settop) (lua_State *L, int idx); void (*dll_lua_pushvalue) (lua_State *L, int idx); int (*dll_lua_isnumber) (lua_State *L, int idx); int (*dll_lua_isstring) (lua_State *L, int idx); int (*dll_lua_type) (lua_State *L, int idx); int (*dll_lua_rawequal) (lua_State *L, int idx1, int idx2); int (*dll_lua_toboolean) (lua_State *L, int idx); const char *(*dll_lua_tolstring) (lua_State *L, int idx, size_t *len); void *(*dll_lua_touserdata) (lua_State *L, int idx); void (*dll_lua_pushnil) (lua_State *L); void (*dll_lua_pushnumber) (lua_State *L, lua_Number n); void (*dll_lua_pushinteger) (lua_State *L, lua_Integer n); void (*dll_lua_pushlstring) (lua_State *L, const char *s, size_t l); void (*dll_lua_pushstring) (lua_State *L, const char *s); const char *(*dll_lua_pushfstring) (lua_State *L, const char *fmt, ...); void (*dll_lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); void (*dll_lua_pushboolean) (lua_State *L, int b); void (*dll_lua_pushlightuserdata) (lua_State *L, void *p); void (*dll_lua_getfield) (lua_State *L, int idx, const char *k); void (*dll_lua_rawget) (lua_State *L, int idx); void (*dll_lua_rawgeti) (lua_State *L, int idx, int n); void (*dll_lua_createtable) (lua_State *L, int narr, int nrec); void *(*dll_lua_newuserdata) (lua_State *L, size_t sz); int (*dll_lua_getmetatable) (lua_State *L, int objindex); void (*dll_lua_setfield) (lua_State *L, int idx, const char *k); void (*dll_lua_rawset) (lua_State *L, int idx); void (*dll_lua_rawseti) (lua_State *L, int idx, int n); int (*dll_lua_setmetatable) (lua_State *L, int objindex); int (*dll_lua_next) (lua_State *L, int idx); /* libs */ int (*dll_luaopen_base) (lua_State *L); int (*dll_luaopen_table) (lua_State *L); int (*dll_luaopen_string) (lua_State *L); int (*dll_luaopen_math) (lua_State *L); int (*dll_luaopen_io) (lua_State *L); int (*dll_luaopen_os) (lua_State *L); int (*dll_luaopen_package) (lua_State *L); int (*dll_luaopen_debug) (lua_State *L); void (*dll_luaL_openlibs) (lua_State *L); typedef void **luaV_function; typedef struct { const char *name; luaV_function func; } luaV_Reg; static const luaV_Reg luaV_dll[] = { /* lauxlib */ #if LUA_VERSION_NUM <= 501 {"luaL_register", (luaV_function) &dll_luaL_register}, {"luaL_prepbuffer", (luaV_function) &dll_luaL_prepbuffer}, {"luaL_openlib", (luaV_function) &dll_luaL_openlib}, {"luaL_typerror", (luaV_function) &dll_luaL_typerror}, {"luaL_loadfile", (luaV_function) &dll_luaL_loadfile}, {"luaL_loadbuffer", (luaV_function) &dll_luaL_loadbuffer}, #else {"luaL_prepbuffsize", (luaV_function) &dll_luaL_prepbuffsize}, {"luaL_setfuncs", (luaV_function) &dll_luaL_setfuncs}, {"luaL_loadfilex", (luaV_function) &dll_luaL_loadfilex}, {"luaL_loadbufferx", (luaV_function) &dll_luaL_loadbufferx}, {"luaL_argerror", (luaV_function) &dll_luaL_argerror}, #endif {"luaL_checkany", (luaV_function) &dll_luaL_checkany}, {"luaL_checklstring", (luaV_function) &dll_luaL_checklstring}, {"luaL_checkinteger", (luaV_function) &dll_luaL_checkinteger}, {"luaL_optinteger", (luaV_function) &dll_luaL_optinteger}, {"luaL_checktype", (luaV_function) &dll_luaL_checktype}, {"luaL_error", (luaV_function) &dll_luaL_error}, {"luaL_newstate", (luaV_function) &dll_luaL_newstate}, {"luaL_buffinit", (luaV_function) &dll_luaL_buffinit}, {"luaL_addlstring", (luaV_function) &dll_luaL_addlstring}, {"luaL_pushresult", (luaV_function) &dll_luaL_pushresult}, /* lua */ #if LUA_VERSION_NUM <= 501 {"lua_tonumber", (luaV_function) &dll_lua_tonumber}, {"lua_tointeger", (luaV_function) &dll_lua_tointeger}, {"lua_call", (luaV_function) &dll_lua_call}, {"lua_pcall", (luaV_function) &dll_lua_pcall}, #else {"lua_tonumberx", (luaV_function) &dll_lua_tonumberx}, {"lua_tointegerx", (luaV_function) &dll_lua_tointegerx}, {"lua_callk", (luaV_function) &dll_lua_callk}, {"lua_pcallk", (luaV_function) &dll_lua_pcallk}, {"lua_getglobal", (luaV_function) &dll_lua_getglobal}, {"lua_setglobal", (luaV_function) &dll_lua_setglobal}, #endif #if LUA_VERSION_NUM <= 502 {"lua_replace", (luaV_function) &dll_lua_replace}, {"lua_remove", (luaV_function) &dll_lua_remove}, #endif #if LUA_VERSION_NUM >= 503 {"lua_rotate", (luaV_function) &dll_lua_rotate}, {"lua_copy", (luaV_function) &dll_lua_copy}, #endif {"lua_typename", (luaV_function) &dll_lua_typename}, {"lua_close", (luaV_function) &dll_lua_close}, {"lua_gettop", (luaV_function) &dll_lua_gettop}, {"lua_settop", (luaV_function) &dll_lua_settop}, {"lua_pushvalue", (luaV_function) &dll_lua_pushvalue}, {"lua_isnumber", (luaV_function) &dll_lua_isnumber}, {"lua_isstring", (luaV_function) &dll_lua_isstring}, {"lua_type", (luaV_function) &dll_lua_type}, {"lua_rawequal", (luaV_function) &dll_lua_rawequal}, {"lua_toboolean", (luaV_function) &dll_lua_toboolean}, {"lua_tolstring", (luaV_function) &dll_lua_tolstring}, {"lua_touserdata", (luaV_function) &dll_lua_touserdata}, {"lua_pushnil", (luaV_function) &dll_lua_pushnil}, {"lua_pushnumber", (luaV_function) &dll_lua_pushnumber}, {"lua_pushinteger", (luaV_function) &dll_lua_pushinteger}, {"lua_pushlstring", (luaV_function) &dll_lua_pushlstring}, {"lua_pushstring", (luaV_function) &dll_lua_pushstring}, {"lua_pushfstring", (luaV_function) &dll_lua_pushfstring}, {"lua_pushcclosure", (luaV_function) &dll_lua_pushcclosure}, {"lua_pushboolean", (luaV_function) &dll_lua_pushboolean}, {"lua_pushlightuserdata", (luaV_function) &dll_lua_pushlightuserdata}, {"lua_getfield", (luaV_function) &dll_lua_getfield}, {"lua_rawget", (luaV_function) &dll_lua_rawget}, {"lua_rawgeti", (luaV_function) &dll_lua_rawgeti}, {"lua_createtable", (luaV_function) &dll_lua_createtable}, {"lua_newuserdata", (luaV_function) &dll_lua_newuserdata}, {"lua_getmetatable", (luaV_function) &dll_lua_getmetatable}, {"lua_setfield", (luaV_function) &dll_lua_setfield}, {"lua_rawset", (luaV_function) &dll_lua_rawset}, {"lua_rawseti", (luaV_function) &dll_lua_rawseti}, {"lua_setmetatable", (luaV_function) &dll_lua_setmetatable}, {"lua_next", (luaV_function) &dll_lua_next}, /* libs */ {"luaopen_base", (luaV_function) &dll_luaopen_base}, {"luaopen_table", (luaV_function) &dll_luaopen_table}, {"luaopen_string", (luaV_function) &dll_luaopen_string}, {"luaopen_math", (luaV_function) &dll_luaopen_math}, {"luaopen_io", (luaV_function) &dll_luaopen_io}, {"luaopen_os", (luaV_function) &dll_luaopen_os}, {"luaopen_package", (luaV_function) &dll_luaopen_package}, {"luaopen_debug", (luaV_function) &dll_luaopen_debug}, {"luaL_openlibs", (luaV_function) &dll_luaL_openlibs}, {NULL, NULL} }; static HANDLE hinstLua = NULL; static void end_dynamic_lua(void) { if (hinstLua) { close_dll(hinstLua); hinstLua = 0; } } static int lua_link_init(char *libname, int verbose) { const luaV_Reg *reg; if (hinstLua) return OK; hinstLua = load_dll(libname); if (!hinstLua) { if (verbose) EMSG2(_(e_loadlib), libname); return FAIL; } for (reg = luaV_dll; reg->func; reg++) { if ((*reg->func = symbol_from_dll(hinstLua, reg->name)) == NULL) { close_dll(hinstLua); hinstLua = 0; if (verbose) EMSG2(_(e_loadfunc), reg->name); return FAIL; } } return OK; } #endif /* DYNAMIC_LUA */ #if defined(DYNAMIC_LUA) || defined(PROTO) int lua_enabled(int verbose) { return lua_link_init((char *)p_luadll, verbose) == OK; } #endif #if LUA_VERSION_NUM > 501 static int luaL_typeerror (lua_State *L, int narg, const char *tname) { const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg)); return luaL_argerror(L, narg, msg); } #endif /* ======= Internal ======= */ static void luaV_newmetatable(lua_State *L, const char *tname) { lua_newtable(L); lua_pushlightuserdata(L, (void *) tname); lua_pushvalue(L, -2); lua_rawset(L, LUA_REGISTRYINDEX); } static void * luaV_toudata(lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) /* value is userdata? */ { if (lua_getmetatable(L, ud)) /* does it have a metatable? */ { luaV_getfield(L, tname); /* get metatable */ if (lua_rawequal(L, -1, -2)) /* MTs match? */ { lua_pop(L, 2); /* MTs */ return p; } } } return NULL; } static void * luaV_checkcache(lua_State *L, void *p) { luaV_getudata(L, p); if (lua_isnil(L, -1)) luaL_error(L, "invalid object"); lua_pop(L, 1); return p; } #define luaV_unbox(L,luatyp,ud) (*((luatyp *) lua_touserdata((L),(ud)))) #define luaV_checkvalid(L,luatyp,ud) \ luaV_checkcache((L), (void *) luaV_unbox((L),luatyp,(ud))) static void * luaV_checkudata(lua_State *L, int ud, const char *tname) { void *p = luaV_toudata(L, ud, tname); if (p == NULL) luaL_typeerror(L, ud, tname); return p; } static void luaV_pushtypval(lua_State *L, typval_T *tv) { if (tv == NULL) { lua_pushnil(L); return; } switch (tv->v_type) { case VAR_STRING: lua_pushstring(L, tv->vval.v_string == NULL ? "" : (char *)tv->vval.v_string); break; case VAR_NUMBER: lua_pushinteger(L, (int) tv->vval.v_number); break; #ifdef FEAT_FLOAT case VAR_FLOAT: lua_pushnumber(L, (lua_Number) tv->vval.v_float); break; #endif case VAR_LIST: luaV_pushlist(L, tv->vval.v_list); break; case VAR_DICT: luaV_pushdict(L, tv->vval.v_dict); break; case VAR_SPECIAL: if (tv->vval.v_number <= VVAL_TRUE) lua_pushinteger(L, (int) tv->vval.v_number); else lua_pushnil(L); break; default: lua_pushnil(L); } } /* converts lua value at 'pos' to typval 'tv' */ static void luaV_totypval (lua_State *L, int pos, typval_T *tv) { switch(lua_type(L, pos)) { case LUA_TBOOLEAN: tv->v_type = VAR_SPECIAL; tv->vval.v_number = (varnumber_T) lua_toboolean(L, pos); break; case LUA_TSTRING: tv->v_type = VAR_STRING; tv->vval.v_string = vim_strsave((char_u *) lua_tostring(L, pos)); break; case LUA_TNUMBER: #ifdef FEAT_FLOAT tv->v_type = VAR_FLOAT; tv->vval.v_float = (float_T) lua_tonumber(L, pos); #else tv->v_type = VAR_NUMBER; tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos); #endif break; case LUA_TUSERDATA: { void *p = lua_touserdata(L, pos); if (lua_getmetatable(L, pos)) /* has metatable? */ { /* check list */ luaV_getfield(L, LUAVIM_LIST); if (lua_rawequal(L, -1, -2)) { tv->v_type = VAR_LIST; tv->vval.v_list = *((luaV_List *) p); ++tv->vval.v_list->lv_refcount; lua_pop(L, 2); /* MTs */ return; } /* check dict */ luaV_getfield(L, LUAVIM_DICT); if (lua_rawequal(L, -1, -3)) { tv->v_type = VAR_DICT; tv->vval.v_dict = *((luaV_Dict *) p); ++tv->vval.v_dict->dv_refcount; lua_pop(L, 3); /* MTs */ return; } lua_pop(L, 3); /* MTs */ } break; } default: tv->v_type = VAR_NUMBER; tv->vval.v_number = 0; } } /* similar to luaL_addlstring, but replaces \0 with \n if toline and * \n with \0 otherwise */ static void luaV_addlstring(luaL_Buffer *b, const char *s, size_t l, int toline) { while (l--) { if (*s == '\0' && toline) luaL_addchar(b, '\n'); else if (*s == '\n' && !toline) luaL_addchar(b, '\0'); else luaL_addchar(b, *s); s++; } } static void luaV_pushline(lua_State *L, buf_T *buf, linenr_T n) { const char *s = (const char *) ml_get_buf(buf, n, FALSE); luaL_Buffer b; luaL_buffinit(L, &b); luaV_addlstring(&b, s, strlen(s), 0); luaL_pushresult(&b); } static char_u * luaV_toline(lua_State *L, int pos) { size_t l; const char *s = lua_tolstring(L, pos, &l); luaL_Buffer b; luaL_buffinit(L, &b); luaV_addlstring(&b, s, l, 1); luaL_pushresult(&b); return (char_u *) lua_tostring(L, -1); } /* pops a string s from the top of the stack and calls mf(t) for pieces t of * s separated by newlines */ static void luaV_msgfunc(lua_State *L, msgfunc_T mf) { luaL_Buffer b; size_t l; const char *p, *s = lua_tolstring(L, -1, &l); luaL_buffinit(L, &b); luaV_addlstring(&b, s, l, 0); luaL_pushresult(&b); /* break string */ p = s = lua_tolstring(L, -1, &l); while (l--) { if (*p++ == '\0') /* break? */ { mf((char_u *) s); s = p; } } mf((char_u *) s); lua_pop(L, 2); /* original and modified strings */ } #define luaV_newtype(typ,tname,luatyp,luatname) \ static luatyp * \ luaV_new##tname (lua_State *L, typ *obj) \ { \ luatyp *o = (luatyp *) lua_newuserdata(L, sizeof(luatyp)); \ *o = obj; \ luaV_setudata(L, obj); /* cache[obj] = udata */ \ luaV_getfield(L, luatname); \ lua_setmetatable(L, -2); \ return o; \ } #define luaV_pushtype(typ,tname,luatyp) \ static luatyp * \ luaV_push##tname (lua_State *L, typ *obj) \ { \ luatyp *o = NULL; \ if (obj == NULL) \ lua_pushnil(L); \ else { \ luaV_getudata(L, obj); \ if (lua_isnil(L, -1)) /* not interned? */ \ { \ lua_pop(L, 1); \ o = luaV_new##tname(L, obj); \ } \ else \ o = (luatyp *) lua_touserdata(L, -1); \ } \ return o; \ } #define luaV_type_tostring(tname,luatname) \ static int \ luaV_##tname##_tostring (lua_State *L) \ { \ lua_pushfstring(L, "%s: %p", luatname, lua_touserdata(L, 1)); \ return 1; \ } /* ======= List type ======= */ static luaV_List * luaV_newlist (lua_State *L, list_T *lis) { luaV_List *l = (luaV_List *) lua_newuserdata(L, sizeof(luaV_List)); *l = lis; lis->lv_refcount++; /* reference in Lua */ luaV_setudata(L, lis); /* cache[lis] = udata */ luaV_getfield(L, LUAVIM_LIST); lua_setmetatable(L, -2); return l; } luaV_pushtype(list_T, list, luaV_List) luaV_type_tostring(list, LUAVIM_LIST) static int luaV_list_len (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushinteger(L, (l == NULL) ? 0 : (int) l->lv_len); return 1; } static int luaV_list_iter (lua_State *L) { listitem_T *li = (listitem_T *) lua_touserdata(L, lua_upvalueindex(2)); if (li == NULL) return 0; luaV_pushtypval(L, &li->li_tv); lua_pushlightuserdata(L, (void *) li->li_next); lua_replace(L, lua_upvalueindex(2)); return 1; } static int luaV_list_call (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */ lua_pushlightuserdata(L, (void *) l->lv_first); lua_pushcclosure(L, luaV_list_iter, 2); return 1; } static int luaV_list_index (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); if (lua_isnumber(L, 2)) /* list item? */ { listitem_T *li = list_find(l, (long) luaL_checkinteger(L, 2)); if (li == NULL) lua_pushnil(L); else luaV_pushtypval(L, &li->li_tv); } else if (lua_isstring(L, 2)) /* method? */ { const char *s = lua_tostring(L, 2); if (strncmp(s, "add", 3) == 0 || strncmp(s, "insert", 6) == 0) { lua_getmetatable(L, 1); lua_getfield(L, -1, s); } else lua_pushnil(L); } else lua_pushnil(L); return 1; } static int luaV_list_newindex (lua_State *L) { list_T *l = luaV_unbox(L, luaV_List, 1); long n = (long) luaL_checkinteger(L, 2); listitem_T *li; if (l->lv_lock) luaL_error(L, "list is locked"); li = list_find(l, n); if (li == NULL) return 0; if (lua_isnil(L, 3)) /* remove? */ { vimlist_remove(l, li, li); clear_tv(&li->li_tv); vim_free(li); } else { typval_T v; luaV_totypval(L, 3, &v); clear_tv(&li->li_tv); copy_tv(&v, &li->li_tv); clear_tv(&v); } return 0; } static int luaV_list_add (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); typval_T v; if (l->lv_lock) luaL_error(L, "list is locked"); lua_settop(L, 2); luaV_totypval(L, 2, &v); if (list_append_tv(l, &v) == FAIL) { clear_tv(&v); luaL_error(L, "Failed to add item to list"); } clear_tv(&v); lua_settop(L, 1); return 1; } static int luaV_list_insert (lua_State *L) { luaV_List *lis = luaV_checkudata(L, 1, LUAVIM_LIST); list_T *l = (list_T *) luaV_checkcache(L, (void *) *lis); long pos = (long) luaL_optinteger(L, 3, 0); listitem_T *li = NULL; typval_T v; if (l->lv_lock) luaL_error(L, "list is locked"); if (pos < l->lv_len) { li = list_find(l, pos); if (li == NULL) luaL_error(L, "invalid position"); } lua_settop(L, 2); luaV_totypval(L, 2, &v); if (list_insert_tv(l, &v, li) == FAIL) { clear_tv(&v); luaL_error(L, "Failed to add item to list"); } clear_tv(&v); lua_settop(L, 1); return 1; } static const luaL_Reg luaV_List_mt[] = { {"__tostring", luaV_list_tostring}, {"__len", luaV_list_len}, {"__call", luaV_list_call}, {"__index", luaV_list_index}, {"__newindex", luaV_list_newindex}, {"add", luaV_list_add}, {"insert", luaV_list_insert}, {NULL, NULL} }; /* ======= Dict type ======= */ static luaV_Dict * luaV_newdict (lua_State *L, dict_T *dic) { luaV_Dict *d = (luaV_Dict *) lua_newuserdata(L, sizeof(luaV_Dict)); *d = dic; dic->dv_refcount++; /* reference in Lua */ luaV_setudata(L, dic); /* cache[dic] = udata */ luaV_getfield(L, LUAVIM_DICT); lua_setmetatable(L, -2); return d; } luaV_pushtype(dict_T, dict, luaV_Dict) luaV_type_tostring(dict, LUAVIM_DICT) static int luaV_dict_len (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); lua_pushinteger(L, (d == NULL) ? 0 : (int) d->dv_hashtab.ht_used); return 1; } static int luaV_dict_iter (lua_State *L UNUSED) { #ifdef FEAT_EVAL hashitem_T *hi = (hashitem_T *) lua_touserdata(L, lua_upvalueindex(2)); int n = lua_tointeger(L, lua_upvalueindex(3)); dictitem_T *di; if (n <= 0) return 0; while (HASHITEM_EMPTY(hi)) hi++; di = dict_lookup(hi); lua_pushstring(L, (char *) hi->hi_key); luaV_pushtypval(L, &di->di_tv); lua_pushlightuserdata(L, (void *) (hi + 1)); lua_replace(L, lua_upvalueindex(2)); lua_pushinteger(L, n - 1); lua_replace(L, lua_upvalueindex(3)); return 2; #else return 0; #endif } static int luaV_dict_call (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); hashtab_T *ht = &d->dv_hashtab; lua_pushvalue(L, lua_upvalueindex(1)); /* pass cache table along */ lua_pushlightuserdata(L, (void *) ht->ht_array); lua_pushinteger(L, ht->ht_used); /* # remaining items */ lua_pushcclosure(L, luaV_dict_iter, 3); return 1; } static int luaV_dict_index (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di = dict_find(d, key, -1); if (di == NULL) lua_pushnil(L); else luaV_pushtypval(L, &di->di_tv); return 1; } static int luaV_dict_newindex (lua_State *L) { dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di; if (d->dv_lock) luaL_error(L, "dict is locked"); di = dict_find(d, key, -1); if (di == NULL) /* non-existing key? */ { if (lua_isnil(L, 3)) return 0; di = dictitem_alloc(key); if (di == NULL) return 0; if (dict_add(d, di) == FAIL) { vim_free(di); return 0; } } else clear_tv(&di->di_tv); if (lua_isnil(L, 3)) /* remove? */ { hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key); hash_remove(&d->dv_hashtab, hi); dictitem_free(di); } else { typval_T v; luaV_totypval(L, 3, &v); copy_tv(&v, &di->di_tv); clear_tv(&v); } return 0; } static const luaL_Reg luaV_Dict_mt[] = { {"__tostring", luaV_dict_tostring}, {"__len", luaV_dict_len}, {"__call", luaV_dict_call}, {"__index", luaV_dict_index}, {"__newindex", luaV_dict_newindex}, {NULL, NULL} }; /* ======= Buffer type ======= */ luaV_newtype(buf_T, buffer, luaV_Buffer, LUAVIM_BUFFER) luaV_pushtype(buf_T, buffer, luaV_Buffer) luaV_type_tostring(buffer, LUAVIM_BUFFER) static int luaV_buffer_len(lua_State *L) { buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1); lua_pushinteger(L, b->b_ml.ml_line_count); return 1; } static int luaV_buffer_call(lua_State *L) { buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1); lua_settop(L, 1); set_curbuf(b, DOBUF_SPLIT); return 1; } static int luaV_buffer_index(lua_State *L) { buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1); linenr_T n = (linenr_T) lua_tointeger(L, 2); if (n > 0 && n <= b->b_ml.ml_line_count) luaV_pushline(L, b, n); else if (lua_isstring(L, 2)) { const char *s = lua_tostring(L, 2); if (strncmp(s, "name", 4) == 0) lua_pushstring(L, (char *) b->b_sfname); else if (strncmp(s, "fname", 5) == 0) lua_pushstring(L, (char *) b->b_ffname); else if (strncmp(s, "number", 6) == 0) lua_pushinteger(L, b->b_fnum); /* methods */ else if (strncmp(s, "insert", 6) == 0 || strncmp(s, "next", 4) == 0 || strncmp(s, "previous", 8) == 0 || strncmp(s, "isvalid", 7) == 0) { lua_getmetatable(L, 1); lua_getfield(L, -1, s); } else lua_pushnil(L); } else lua_pushnil(L); return 1; } static int luaV_buffer_newindex(lua_State *L) { buf_T *b = (buf_T *) luaV_checkvalid(L, luaV_Buffer, 1); linenr_T n = (linenr_T) luaL_checkinteger(L, 2); #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif if (n < 1 || n > b->b_ml.ml_line_count) luaL_error(L, "invalid line number"); if (lua_isnil(L, 3)) /* delete line */ { buf_T *buf = curbuf; curbuf = b; if (u_savedel(n, 1L) == FAIL) { curbuf = buf; luaL_error(L, "cannot save undo information"); } else if (ml_delete(n, FALSE) == FAIL) { curbuf = buf; luaL_error(L, "cannot delete line"); } else { deleted_lines_mark(n, 1L); if (b == curwin->w_buffer) /* fix cursor in current window? */ { if (curwin->w_cursor.lnum >= n) { if (curwin->w_cursor.lnum > n) { curwin->w_cursor.lnum -= 1; check_cursor_col(); } else check_cursor(); changed_cline_bef_curs(); } invalidate_botline(); } } curbuf = buf; } else if (lua_isstring(L, 3)) /* update line */ { buf_T *buf = curbuf; curbuf = b; if (u_savesub(n) == FAIL) { curbuf = buf; luaL_error(L, "cannot save undo information"); } else if (ml_replace(n, luaV_toline(L, 3), TRUE) == FAIL) { curbuf = buf; luaL_error(L, "cannot replace line"); } else changed_bytes(n, 0); curbuf = buf; if (b == curwin->w_buffer) check_cursor_col(); } else luaL_error(L, "wrong argument to change line"); return 0; } static int luaV_buffer_insert(lua_State *L) { luaV_Buffer *lb = luaV_checkudata(L, 1, LUAVIM_BUFFER); buf_T *b = (buf_T *) luaV_checkcache(L, (void *) *lb); linenr_T last = b->b_ml.ml_line_count; linenr_T n = (linenr_T) luaL_optinteger(L, 3, last); buf_T *buf; luaL_checktype(L, 2, LUA_TSTRING); #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif /* fix insertion line */ if (n < 0) n = 0; if (n > last) n = last; /* insert */ buf = curbuf; curbuf = b; if (u_save(n, n + 1) == FAIL) { curbuf = buf; luaL_error(L, "cannot save undo information"); } else if (ml_append(n, luaV_toline(L, 2), 0, FALSE) == FAIL) { curbuf = buf; luaL_error(L, "cannot insert line"); } else appended_lines_mark(n, 1L); curbuf = buf; update_screen(VALID); return 0; } static int luaV_buffer_next(lua_State *L) { luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); buf_T *buf = (buf_T *) luaV_checkcache(L, (void *) *b); luaV_pushbuffer(L, buf->b_next); return 1; } static int luaV_buffer_previous(lua_State *L) { luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); buf_T *buf = (buf_T *) luaV_checkcache(L, (void *) *b); luaV_pushbuffer(L, buf->b_prev); return 1; } static int luaV_buffer_isvalid(lua_State *L) { luaV_Buffer *b = luaV_checkudata(L, 1, LUAVIM_BUFFER); luaV_getudata(L, *b); lua_pushboolean(L, !lua_isnil(L, -1)); return 1; } static const luaL_Reg luaV_Buffer_mt[] = { {"__tostring", luaV_buffer_tostring}, {"__len", luaV_buffer_len}, {"__call", luaV_buffer_call}, {"__index", luaV_buffer_index}, {"__newindex", luaV_buffer_newindex}, {"insert", luaV_buffer_insert}, {"next", luaV_buffer_next}, {"previous", luaV_buffer_previous}, {"isvalid", luaV_buffer_isvalid}, {NULL, NULL} }; /* ======= Window type ======= */ luaV_newtype(win_T, window, luaV_Window, LUAVIM_WINDOW) luaV_pushtype(win_T, window, luaV_Window) luaV_type_tostring(window, LUAVIM_WINDOW) static int luaV_window_call(lua_State *L) { win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1); lua_settop(L, 1); win_goto(w); return 1; } static int luaV_window_index(lua_State *L) { win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1); const char *s = luaL_checkstring(L, 2); if (strncmp(s, "buffer", 6) == 0) luaV_pushbuffer(L, w->w_buffer); else if (strncmp(s, "line", 4) == 0) lua_pushinteger(L, w->w_cursor.lnum); else if (strncmp(s, "col", 3) == 0) lua_pushinteger(L, w->w_cursor.col + 1); else if (strncmp(s, "width", 5) == 0) lua_pushinteger(L, W_WIDTH(w)); else if (strncmp(s, "height", 6) == 0) lua_pushinteger(L, w->w_height); /* methods */ else if (strncmp(s, "next", 4) == 0 || strncmp(s, "previous", 8) == 0 || strncmp(s, "isvalid", 7) == 0) { lua_getmetatable(L, 1); lua_getfield(L, -1, s); } else lua_pushnil(L); return 1; } static int luaV_window_newindex (lua_State *L) { win_T *w = (win_T *) luaV_checkvalid(L, luaV_Window, 1); const char *s = luaL_checkstring(L, 2); int v = luaL_checkinteger(L, 3); if (strncmp(s, "line", 4) == 0) { #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif if (v < 1 || v > w->w_buffer->b_ml.ml_line_count) luaL_error(L, "line out of range"); w->w_cursor.lnum = v; update_screen(VALID); } else if (strncmp(s, "col", 3) == 0) { #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif w->w_cursor.col = v - 1; update_screen(VALID); } else if (strncmp(s, "width", 5) == 0) { win_T *win = curwin; #ifdef FEAT_GUI need_mouse_correct = TRUE; #endif curwin = w; win_setwidth(v); curwin = win; } else if (strncmp(s, "height", 6) == 0) { win_T *win = curwin; #ifdef FEAT_GUI need_mouse_correct = TRUE; #endif curwin = w; win_setheight(v); curwin = win; } else luaL_error(L, "invalid window property: `%s'", s); return 0; } static int luaV_window_next(lua_State *L) { luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); win_T *win = (win_T *) luaV_checkcache(L, (void *) *w); luaV_pushwindow(L, win->w_next); return 1; } static int luaV_window_previous(lua_State *L) { luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); win_T *win = (win_T *) luaV_checkcache(L, (void *) *w); luaV_pushwindow(L, win->w_prev); return 1; } static int luaV_window_isvalid(lua_State *L) { luaV_Window *w = luaV_checkudata(L, 1, LUAVIM_WINDOW); luaV_getudata(L, *w); lua_pushboolean(L, !lua_isnil(L, -1)); return 1; } static const luaL_Reg luaV_Window_mt[] = { {"__tostring", luaV_window_tostring}, {"__call", luaV_window_call}, {"__index", luaV_window_index}, {"__newindex", luaV_window_newindex}, {"next", luaV_window_next}, {"previous", luaV_window_previous}, {"isvalid", luaV_window_isvalid}, {NULL, NULL} }; /* ======= Vim module ======= */ static int luaV_print(lua_State *L) { int i, n = lua_gettop(L); /* nargs */ const char *s; size_t l; luaL_Buffer b; luaL_buffinit(L, &b); lua_getglobal(L, "tostring"); for (i = 1; i <= n; i++) { lua_pushvalue(L, -1); /* tostring */ lua_pushvalue(L, i); /* arg */ lua_call(L, 1, 1); s = lua_tolstring(L, -1, &l); if (s == NULL) return luaL_error(L, "cannot convert to string"); if (i > 1) luaL_addchar(&b, ' '); /* use space instead of tab */ luaV_addlstring(&b, s, l, 0); lua_pop(L, 1); } luaL_pushresult(&b); luaV_msg(L); return 0; } static int luaV_debug(lua_State *L) { lua_settop(L, 0); lua_getglobal(L, "vim"); lua_getfield(L, -1, "eval"); lua_remove(L, -2); /* vim.eval at position 1 */ for (;;) { const char *input; size_t l; lua_pushvalue(L, 1); /* vim.eval */ lua_pushliteral(L, "input('lua_debug> ')"); lua_call(L, 1, 1); /* return string */ input = lua_tolstring(L, -1, &l); if (l == 0 || strcmp(input, "cont") == 0) return 0; msg_putchar('\n'); /* avoid outputting on input line */ if (luaL_loadbuffer(L, input, l, "=(debug command)") || lua_pcall(L, 0, 0, 0)) luaV_emsg(L); lua_settop(L, 1); /* remove eventual returns, but keep vim.eval */ } } static int luaV_command(lua_State *L) { do_cmdline_cmd((char_u *) luaL_checkstring(L, 1)); update_screen(VALID); return 0; } static int luaV_eval(lua_State *L) { typval_T *tv = eval_expr((char_u *) luaL_checkstring(L, 1), NULL); if (tv == NULL) luaL_error(L, "invalid expression"); luaV_pushtypval(L, tv); free_tv(tv); return 1; } static int luaV_beep(lua_State *L UNUSED) { vim_beep(BO_LANG); return 0; } static int luaV_line(lua_State *L) { luaV_pushline(L, curbuf, curwin->w_cursor.lnum); return 1; } static int luaV_list(lua_State *L) { list_T *l = list_alloc(); if (l == NULL) lua_pushnil(L); else luaV_newlist(L, l); return 1; } static int luaV_dict(lua_State *L) { dict_T *d = dict_alloc(); if (d == NULL) lua_pushnil(L); else luaV_newdict(L, d); return 1; } static int luaV_buffer(lua_State *L) { buf_T *buf; if (lua_isstring(L, 1)) /* get by number or name? */ { if (lua_isnumber(L, 1)) /* by number? */ { int n = lua_tointeger(L, 1); FOR_ALL_BUFFERS(buf) if (buf->b_fnum == n) break; } else { /* by name */ size_t l; const char *s = lua_tolstring(L, 1, &l); FOR_ALL_BUFFERS(buf) { if (buf->b_ffname == NULL || buf->b_sfname == NULL) { if (l == 0) break; } else if (strncmp(s, (char *)buf->b_ffname, l) == 0 || strncmp(s, (char *)buf->b_sfname, l) == 0) break; } } } else buf = (lua_toboolean(L, 1)) ? firstbuf : curbuf; /* first buffer? */ luaV_pushbuffer(L, buf); return 1; } static int luaV_window(lua_State *L) { win_T *win; if (lua_isnumber(L, 1)) /* get by number? */ { int n = lua_tointeger(L, 1); for (win = firstwin; win != NULL; win = win->w_next, n--) if (n == 1) break; } else win = (lua_toboolean(L, 1)) ? firstwin : curwin; /* first window? */ luaV_pushwindow(L, win); return 1; } static int luaV_open(lua_State *L) { char_u *s = NULL; #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif if (lua_isstring(L, 1)) s = (char_u *) lua_tostring(L, 1); luaV_pushbuffer(L, buflist_new(s, NULL, 1L, BLN_LISTED)); return 1; } static int luaV_type(lua_State *L) { luaL_checkany(L, 1); if (lua_type(L, 1) == LUA_TUSERDATA) /* check vim udata? */ { lua_settop(L, 1); if (lua_getmetatable(L, 1)) { luaV_getfield(L, LUAVIM_LIST); if (lua_rawequal(L, -1, 2)) { lua_pushstring(L, "list"); return 1; } luaV_getfield(L, LUAVIM_DICT); if (lua_rawequal(L, -1, 2)) { lua_pushstring(L, "dict"); return 1; } luaV_getfield(L, LUAVIM_BUFFER); if (lua_rawequal(L, -1, 2)) { lua_pushstring(L, "buffer"); return 1; } luaV_getfield(L, LUAVIM_WINDOW); if (lua_rawequal(L, -1, 2)) { lua_pushstring(L, "window"); return 1; } } } lua_pushstring(L, luaL_typename(L, 1)); /* fallback */ return 1; } static const luaL_Reg luaV_module[] = { {"command", luaV_command}, {"eval", luaV_eval}, {"beep", luaV_beep}, {"line", luaV_line}, {"list", luaV_list}, {"dict", luaV_dict}, {"buffer", luaV_buffer}, {"window", luaV_window}, {"open", luaV_open}, {"type", luaV_type}, {NULL, NULL} }; /* for freeing list, dict, buffer and window objects; lightuserdata as arg */ static int luaV_free(lua_State *L) { lua_pushnil(L); luaV_setudata(L, lua_touserdata(L, 1)); return 0; } static int luaV_luaeval (lua_State *L) { luaL_Buffer b; size_t l; const char *str = lua_tolstring(L, 1, &l); typval_T *arg = (typval_T *) lua_touserdata(L, 2); typval_T *rettv = (typval_T *) lua_touserdata(L, 3); luaL_buffinit(L, &b); luaL_addlstring(&b, LUAVIM_EVALHEADER, sizeof(LUAVIM_EVALHEADER) - 1); luaL_addlstring(&b, str, l); luaL_pushresult(&b); str = lua_tolstring(L, -1, &l); if (luaL_loadbuffer(L, str, l, LUAVIM_EVALNAME)) /* compile error? */ { luaV_emsg(L); return 0; } luaV_pushtypval(L, arg); if (lua_pcall(L, 1, 1, 0)) /* running error? */ { luaV_emsg(L); return 0; } luaV_totypval(L, -1, rettv); return 0; } static int luaV_setref (lua_State *L) { int copyID = lua_tointeger(L, 1); int abort = FALSE; typval_T tv; luaV_getfield(L, LUAVIM_LIST); luaV_getfield(L, LUAVIM_DICT); lua_pushnil(L); /* traverse cache table */ while (!abort && lua_next(L, lua_upvalueindex(1)) != 0) { lua_getmetatable(L, -1); if (lua_rawequal(L, -1, 2)) /* list? */ { tv.v_type = VAR_LIST; tv.vval.v_list = (list_T *) lua_touserdata(L, 4); /* key */ abort = set_ref_in_item(&tv, copyID, NULL, NULL); } else if (lua_rawequal(L, -1, 3)) /* dict? */ { tv.v_type = VAR_DICT; tv.vval.v_dict = (dict_T *) lua_touserdata(L, 4); /* key */ abort = set_ref_in_item(&tv, copyID, NULL, NULL); } lua_pop(L, 2); /* metatable and value */ } lua_pushinteger(L, abort); return 1; } static int luaopen_vim(lua_State *L) { /* set cache table */ lua_newtable(L); lua_newtable(L); lua_pushstring(L, "v"); lua_setfield(L, -2, "__mode"); lua_setmetatable(L, -2); /* cache is weak-valued */ /* print */ lua_pushcfunction(L, luaV_print); lua_setglobal(L, "print"); /* debug.debug */ lua_getglobal(L, "debug"); lua_pushcfunction(L, luaV_debug); lua_setfield(L, -2, "debug"); lua_pop(L, 1); /* free */ lua_pushlightuserdata(L, (void *) LUAVIM_FREE); lua_pushvalue(L, 1); /* cache table */ lua_pushcclosure(L, luaV_free, 1); lua_rawset(L, LUA_REGISTRYINDEX); /* luaeval */ lua_pushlightuserdata(L, (void *) LUAVIM_LUAEVAL); lua_pushvalue(L, 1); /* cache table */ lua_pushcclosure(L, luaV_luaeval, 1); lua_rawset(L, LUA_REGISTRYINDEX); /* setref */ lua_pushlightuserdata(L, (void *) LUAVIM_SETREF); lua_pushvalue(L, 1); /* cache table */ lua_pushcclosure(L, luaV_setref, 1); lua_rawset(L, LUA_REGISTRYINDEX); /* register */ luaV_newmetatable(L, LUAVIM_LIST); lua_pushvalue(L, 1); luaV_openlib(L, luaV_List_mt, 1); luaV_newmetatable(L, LUAVIM_DICT); lua_pushvalue(L, 1); luaV_openlib(L, luaV_Dict_mt, 1); luaV_newmetatable(L, LUAVIM_BUFFER); lua_pushvalue(L, 1); /* cache table */ luaV_openlib(L, luaV_Buffer_mt, 1); luaV_newmetatable(L, LUAVIM_WINDOW); lua_pushvalue(L, 1); /* cache table */ luaV_openlib(L, luaV_Window_mt, 1); lua_newtable(L); /* vim table */ lua_pushvalue(L, 1); /* cache table */ luaV_openlib(L, luaV_module, 1); lua_setglobal(L, LUAVIM_NAME); return 0; } static lua_State * luaV_newstate(void) { lua_State *L = luaL_newstate(); luaL_openlibs(L); /* core libs */ lua_pushcfunction(L, luaopen_vim); /* vim */ lua_call(L, 0, 0); return L; } static void luaV_setrange(lua_State *L, int line1, int line2) { lua_getglobal(L, LUAVIM_NAME); lua_pushinteger(L, line1); lua_setfield(L, -2, "firstline"); lua_pushinteger(L, line2); lua_setfield(L, -2, "lastline"); lua_pop(L, 1); /* vim table */ } /* ======= Interface ======= */ static lua_State *L = NULL; static int lua_isopen(void) { return L != NULL; } static int lua_init(void) { if (!lua_isopen()) { #ifdef DYNAMIC_LUA if (!lua_enabled(TRUE)) { EMSG(_("Lua library cannot be loaded.")); return FAIL; } #endif L = luaV_newstate(); } return OK; } void lua_end(void) { if (lua_isopen()) { lua_close(L); L = NULL; #ifdef DYNAMIC_LUA end_dynamic_lua(); #endif } } /* ex commands */ void ex_lua(exarg_T *eap) { char *script; if (lua_init() == FAIL) return; script = (char *) script_get(eap, eap->arg); if (!eap->skip) { char *s = (script) ? script : (char *) eap->arg; luaV_setrange(L, eap->line1, eap->line2); if (luaL_loadbuffer(L, s, strlen(s), LUAVIM_CHUNKNAME) || lua_pcall(L, 0, 0, 0)) luaV_emsg(L); } if (script != NULL) vim_free(script); } void ex_luado(exarg_T *eap) { linenr_T l; const char *s = (const char *) eap->arg; luaL_Buffer b; size_t len; buf_T *was_curbuf = curbuf; if (lua_init() == FAIL) return; if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { EMSG(_("cannot save undo information")); return; } luaV_setrange(L, eap->line1, eap->line2); luaL_buffinit(L, &b); luaL_addlstring(&b, "return function(line, linenr) ", 30); /* header */ luaL_addlstring(&b, s, strlen(s)); luaL_addlstring(&b, " end", 4); /* footer */ luaL_pushresult(&b); s = lua_tolstring(L, -1, &len); if (luaL_loadbuffer(L, s, len, LUAVIM_CHUNKNAME)) { luaV_emsg(L); lua_pop(L, 1); /* function body */ return; } lua_call(L, 0, 1); lua_replace(L, -2); /* function -> body */ for (l = eap->line1; l <= eap->line2; l++) { /* Check the line number, the command my have deleted lines. */ if (l > curbuf->b_ml.ml_line_count) break; lua_pushvalue(L, -1); /* function */ luaV_pushline(L, curbuf, l); /* current line as arg */ lua_pushinteger(L, l); /* current line number as arg */ if (lua_pcall(L, 2, 1, 0)) { luaV_emsg(L); break; } /* Catch the command switching to another buffer. */ if (curbuf != was_curbuf) break; if (lua_isstring(L, -1)) /* update line? */ { #ifdef HAVE_SANDBOX luaV_checksandbox(L); #endif ml_replace(l, luaV_toline(L, -1), TRUE); changed_bytes(l, 0); lua_pop(L, 1); /* result from luaV_toline */ } lua_pop(L, 1); /* line */ } lua_pop(L, 1); /* function */ check_cursor(); update_screen(NOT_VALID); } void ex_luafile(exarg_T *eap) { if (lua_init() == FAIL) return; if (!eap->skip) { luaV_setrange(L, eap->line1, eap->line2); if (luaL_loadfile(L, (char *) eap->arg) || lua_pcall(L, 0, 0, 0)) luaV_emsg(L); } } #define luaV_freetype(typ,tname) \ void \ lua_##tname##_free(typ *o) \ { \ if (!lua_isopen()) return; \ luaV_getfield(L, LUAVIM_FREE); \ lua_pushlightuserdata(L, (void *) o); \ lua_call(L, 1, 0); \ } luaV_freetype(buf_T, buffer) luaV_freetype(win_T, window) void do_luaeval (char_u *str, typval_T *arg, typval_T *rettv) { lua_init(); luaV_getfield(L, LUAVIM_LUAEVAL); lua_pushstring(L, (char *) str); lua_pushlightuserdata(L, (void *) arg); lua_pushlightuserdata(L, (void *) rettv); lua_call(L, 3, 0); } int set_ref_in_lua (int copyID) { int aborted = 0; if (lua_isopen()) { luaV_getfield(L, LUAVIM_SETREF); /* call the function with 1 arg, getting 1 result back */ lua_pushinteger(L, copyID); lua_call(L, 1, 1); /* get the result */ aborted = lua_tointeger(L, -1); /* pop result off the stack */ lua_pop(L, 1); } return aborted; } #endif