diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/cpp/snippets.lua | 122 | ||||
-rw-r--r-- | modules/lua/snippets.lua | 62 | ||||
-rw-r--r-- | modules/textadept/init.lua | 2 | ||||
-rw-r--r-- | modules/textadept/key_commands.lua | 4 | ||||
-rw-r--r-- | modules/textadept/lsnippets.lua | 339 |
5 files changed, 434 insertions, 95 deletions
diff --git a/modules/cpp/snippets.lua b/modules/cpp/snippets.lua index 1625cea2..17fd5219 100644 --- a/modules/cpp/snippets.lua +++ b/modules/cpp/snippets.lua @@ -8,69 +8,69 @@ local snippets = _G.snippets if type(snippets) == 'table' then snippets.cpp = { - rc = 'reinterpret_cast<${1:}>(${2:$(selected_text)})', - sc = 'static_cast<${1:}>(${2:$(selected_text)})', - cc = 'const_cast<${1:}>(${2:$(selected_text)})', + rc = 'reinterpret_cast<%1>(%2(%(selected_text)))', + sc = 'static_cast<%1>(%2(%(selected_text)))', + cc = 'const_cast<%1>(%2(%(selected_text)))', -- Lua snippets - lf = 'static int ${1:function}(lua_State *${2:lua}) {\n ${0}\n return 0;\n}', - lff = 'LF ${1:function}(lua_State *${2:lua}) {\n ${0}\n return 0;\n}', + lf = 'static int %1(function)(lua_State *%2(lua)) {\n %0\n return 0;\n}', + lff = 'LF %1(function)(lua_State *%2(lua)) {\n %0\n return 0;\n}', ls = 'lua_State', - lgf = 'lua_getfield(${1:lua}, ${2:-1}, ${3:field});', - lgg = 'lua_getglobal(${1:lua}, ${2:global});', - lgt = 'lua_gettable(${1:lua}, ${2:-2});', - ltop = 'lua_gettop(${1:lua});', - lib = 'lua_isboolean(${1:lua}, ${2:-1});', - licf = 'lua_iscfunction(${1:lua}, ${2:-1});', - lif = 'lua_isfunctionu(${1:lua}, ${2:-1});', - linil = 'lua_isnil(${1:lua}, ${2:-1});', - linone = 'lua_isnone(${1:lua}, ${2:-1});', - linonen = 'lua_isnoneornil(${1:lua}, ${2:-1});', - lin = 'lua_isnumber(${1:lua}, ${2:-1});', - lis = 'lua_isstring(${1:lua}, ${2:-1});', - lit = 'lua_istable(${1:lua}, ${2:-1});', - lith = 'lua_isthread(${1:lua}, ${2:-1});', - liu = 'lua_isuserdata(${1:lua}, ${2:-1});', - llen = 'lua_objlen(${1:lua}, ${2:-1});', - lpop = 'lua_pop(${1:lua}, ${2:1});', - lpb = 'lua_pushboolean(${1:lua}, ${2:boolean});', - lpcc = 'lua_pushcclosure(${1:lua}, ${2:closure_func}, ${3:num_values});', - lpcf = 'lua_pushcfunction(${1:lua}, ${2:cfunction});', - lpi = 'lua_pushinteger(${1:lua}, ${2:integer});', - lplu = 'lua_pushlightuserdata(${1:lua}, ${2:userdata});', - lpnil = 'lua_pushnil(${1:lua});', - lpn = 'lua_pushnumber(${1:lua}, ${2:number});', - lps = 'lua_pushstring(${1:lua}, ${2:string});', - lpth = 'lua_pushthread(${1:lua});', - lpv = 'lua_pushvalue(${1:lua}, ${2:-1});', - lrg = 'lua_rawget(${1:lua}, ${2:-2});', - lrgi = 'lua_rawgeti(${1:lua}, ${2:-2}, ${3:1});', - lrs = 'lua_rawset(${1:lua}, ${2:-3});', - lrsi = 'lua_rawseti(${1:lua}, ${2:-2}, ${3:1});', - lr = 'lua_register(${1:lua}, ${2:fname}, ${3:cfunction});', - lsf = 'lua_setfield(${1:lua}, ${2:-2}, ${3:field});', - lsg = 'lua_setglobal(${1:lua}, ${2:-2}, ${3:global});', - lst = 'lua_settable(${1:lua}, ${2:-3});', - ltb = 'lua_toboolean(${1:lua}, ${2:-1});', - ltcf = 'lua_tocfunction(${1:lua}, ${2:-1});', - lti = 'lua_tointeger(${1:lua}, ${2:-1});', - ltn = 'lua_tonumber(${1:lua}, ${2:-1});', - ltp = 'lua_topointer(${1:lua}, ${2:-1});', - lts = 'lua_tostring(${1:lua}, ${2:-1});', - ltth = 'lua_tothread(${1:lua}, ${2:-1});', - ltu = 'lua_touserdata(${1:lua}, ${2:-1});', - lt = 'lua_type(${1:lua}, ${2:-1});', - llcint = 'luaL_checkint(${1:lua}, ${2:-1});', - llci = 'luaL_checkinteger(${1:lua}, ${2:-1});', - llcl = 'luaL_checklong(${1:lua}, ${2:-1});', - llcn = 'luaL_checknumber(${1:lua}, ${2:-1});', - llcs = 'luaL_checkstring(${1:lua}, ${2:-1});', - llcu = 'luaL_checkudata(${1:lua}, ${2:-1}, ${3:mt_name});', - llerr = 'luaL_error(${1:lua}, ${2:errorstring}${3:, ${4:arg}});', - lloint = 'luaL_optint(${1:lua}, ${2:-1}, ${3:default});', - lloi = 'luaL_optinteger(${1:lua}, ${2:-1}, ${3:default});', - llol = 'luaL_optlong(${1:lua}, ${2:-1}, ${3:default});', - llon = 'luaL_optnumber(${1:lua}, ${2:-1}, ${3:default});', - llos = 'luaL_optstring(${1:lua}, ${2:-1}, ${3:default});', + lgf = 'lua_getfield(%1(lua), %2(-1), %3(field));', + lgg = 'lua_getglobal(%1(lua), %2(global));', + lgt = 'lua_gettable(%1(lua), %2(-2));', + ltop = 'lua_gettop(%1(lua));', + lib = 'lua_isboolean(%1(lua), %2(-1))', + licf = 'lua_iscfunction(%1(lua), %2(-1))', + lif = 'lua_isfunctionu(%1(lua), %2(-1))', + linil = 'lua_isnil(%1(lua), %2(-1))', + linone = 'lua_isnone(%1(lua), %2(-1))', + linonen = 'lua_isnoneornil(%1(lua), %2(-1))', + lin = 'lua_isnumber(%1(lua), %2(-1))', + lis = 'lua_isstring(%1(lua), %2(-1))', + lit = 'lua_istable(%1(lua), %2(-1))', + lith = 'lua_isthread(%1(lua), %2(-1))', + liu = 'lua_isuserdata(%1(lua), %2(-1))', + llen = 'lua_objlen(%1(lua), %2(-1))', + lpop = 'lua_pop(%1(lua), %2(1));', + lpb = 'lua_pushboolean(%1(lua), %2(boolean));', + lpcc = 'lua_pushcclosure(%1(lua), %2(closure_func), %3(num_values));', + lpcf = 'lua_pushcfunction(%1(lua), %2(cfunction));', + lpi = 'lua_pushinteger(%1(lua), %2(integer));', + lplu = 'lua_pushlightuserdata(%1(lua), %2(userdata));', + lpnil = 'lua_pushnil(%1(lua));', + lpn = 'lua_pushnumber(%1(lua), %2(number));', + lps = 'lua_pushstring(%1(lua), %2(string));', + lpth = 'lua_pushthread(%1(lua));', + lpv = 'lua_pushvalue(%1(lua), %2(-1));', + lrg = 'lua_rawget(%1(lua), %2(-2));', + lrgi = 'lua_rawgeti(%1(lua), %2(-2), %3(1));', + lrs = 'lua_rawset(%1(lua), %2(-3));', + lrsi = 'lua_rawseti(%1(lua), %2(-2), %3(1));', + lr = 'lua_register(%1(lua), %2(fname), %3(cfunction));', + lsf = 'lua_setfield(%1(lua), %2(-2), %3(field));', + lsg = 'lua_setglobal(%1(lua), %2(-2), %3(global));', + lst = 'lua_settable(%1(lua), %2(-3));', + ltb = 'lua_toboolean(%1(lua), %2(-1))', + ltcf = 'lua_tocfunction(%1(lua), %2(-1))', + lti = 'lua_tointeger(%1(lua), %2(-1))', + ltn = 'lua_tonumber(%1(lua), %2(-1))', + ltp = 'lua_topointer(%1(lua), %2(-1))', + lts = 'lua_tostring(%1(lua), %2(-1))', + ltth = 'lua_tothread(%1(lua), %2(-1))', + ltu = 'lua_touserdata(%1(lua), %2(-1))', + lt = 'lua_type(%1(lua), %2(-1))', + llcint = 'luaL_checkint(%1(lua), %2(-1))', + llci = 'luaL_checkinteger(%1(lua), %2(-1))', + llcl = 'luaL_checklong(%1(lua), %2(-1))', + llcn = 'luaL_checknumber(%1(lua), %2(-1))', + llcs = 'luaL_checkstring(%1(lua), %2(-1))', + llcu = 'luaL_checkudata(%1(lua), %2(-1), %3(mt_name))', + llerr = 'luaL_error(%1(lua), %2(errorstring)%3(, %4(arg)));', + lloint = 'luaL_optint(%1(lua), %2(-1), %3(default))', + lloi = 'luaL_optinteger(%1(lua), %2(-1), %3(default))', + llol = 'luaL_optlong(%1(lua), %2(-1), %3(default))', + llon = 'luaL_optnumber(%1(lua), %2(-1), %3(default))', + llos = 'luaL_optstring(%1(lua), %2(-1), %3(default))', } end diff --git a/modules/lua/snippets.lua b/modules/lua/snippets.lua index d93ea8ed..fcfb6871 100644 --- a/modules/lua/snippets.lua +++ b/modules/lua/snippets.lua @@ -8,40 +8,40 @@ local snippets = _G.snippets if type(snippets) == 'table' then snippets.lua = { - l = "local ${1:expr}${2: = ${3:value}}", - p = "print(${0})", - f = "function ${1:name}(${2:args})\n ${0}\nend", - fori = "for ${1:i}, ${2:val} in ipairs(${3:table}) do\n ${0}\nend", - ['for'] = "for i=${1:1}, ${2:10}${3:, -1} do\n ${0}\nend", - forp = "for ${1:k}, ${2:v} in pairs(${3:table}) do\n ${0}\nend", - find = "string.find(${1:str}, ${2:pattern})", - len = "string.len(${1:str})", - gsub = "string.gsub(${1:str}, ${2:pattern}, ${3:repl})", - gfind = "for ${1:match} in string.gfind(${2:str}, ${3:pattern}) do\n ${0}\nend", + l = "local %1(expr)%2( = %3(value))", + p = "print(%0)", + f = "function %1(name)(%2(args))\n %0\nend", + fori = "for %1(i), %2(val) in ipairs(%3(table)) do\n %0\nend", + ['for'] = "for i=%1(1), %2(10)%3(, -1) do\n %0\nend", + forp = "for %1(k), %2(v) in pairs(%3(table)) do\n %0\nend", + find = "string.find(%1(str), %2(pattern))", + len = "string.len(%1(str))", + gsub = "string.gsub(%1(str), %2(pattern), %3(repl))", + gfind = "for %1(match) in string.gfind(%2(str), %3(pattern)) do\n %0\nend", c = "-- ", - tc = "local ${1:tc} = lunit.TestCase('${2:description}')", - ae = "lunit.assert_equal(${1:expected}, ${2:actual})", - ane = "lunit.assert_not_equal(${1:unexpected}, ${2:actual})", - at = "lunit.assert_true(${1:actual})", - af = "lunit.assert_false(${1:actual})", + tc = "local %1(tc) = lunit.TestCase('%2(description)')", + ae = "lunit.assert_equal(%1(expected), %2(actual))", + ane = "lunit.assert_not_equal(%1(unexpected), %2(actual))", + at = "lunit.assert_true(%1(actual))", + af = "lunit.assert_false(%1(actual))", run = "lunit.run()", - abool = "lunit.assert_boolean(${1:expr})", - anbool = "lunit.assert_not_boolean(${1:expr})", - ['anil'] = "lunit.assert_nil(${1:expr})", - annil = "lunit.assert_not_nil(${1:expr})", - anum = "lunit.assert_number(${1:expr})", - annum = "lunit.assert_not_number(${1:expr})", - astr = "lunit.assert_string(${1:expr})", - anstr = "lunit.assert_not_string(${1:expr})", - atab = "lunit.assert_table(${1:expr})", - antab = "lunit.assert_not_table(${1:expr})", - athr = "lunit.assert_thread(${1:expr})", - anthr = "lunit.assert_not_thread(${1:expr})", - afunc = "lunit.assert_function(${1:expr})", - anfunc = "lunit.assert_not_function(${1:expr})", - aud = "lunit.assert_userdata(${1:expr})", - anud = "lunit.assert_not_userdata(${1:expr})" + abool = "lunit.assert_boolean(%1(expr))", + anbool = "lunit.assert_not_boolean(%1(expr))", + ['anil'] = "lunit.assert_nil(%1(expr))", + annil = "lunit.assert_not_nil(%1(expr))", + anum = "lunit.assert_number(%1(expr))", + annum = "lunit.assert_not_number(%1(expr))", + astr = "lunit.assert_string(%1(expr))", + anstr = "lunit.assert_not_string(%1(expr))", + atab = "lunit.assert_table(%1(expr))", + antab = "lunit.assert_not_table(%1(expr))", + athr = "lunit.assert_thread(%1(expr))", + anthr = "lunit.assert_not_thread(%1(expr))", + afunc = "lunit.assert_function(%1(expr))", + anfunc = "lunit.assert_not_function(%1(expr))", + aud = "lunit.assert_userdata(%1(expr))", + anud = "lunit.assert_not_userdata(%1(expr))" } end diff --git a/modules/textadept/init.lua b/modules/textadept/init.lua index ea744c75..42240c1d 100644 --- a/modules/textadept/init.lua +++ b/modules/textadept/init.lua @@ -7,7 +7,7 @@ module('_m.textadept', package.seeall) require 'textadept.editing' require 'textadept.keys' +require 'textadept.lsnippets' require 'textadept.macros' require 'textadept.mlines' -require 'textadept.snippets' require 'textadept.key_commands' -- last diff --git a/modules/textadept/key_commands.lua b/modules/textadept/key_commands.lua index c7239c7c..7493a6ec 100644 --- a/modules/textadept/key_commands.lua +++ b/modules/textadept/key_commands.lua @@ -63,11 +63,11 @@ keys.csae = { 'line_end_rect_extend', b } keys.csav = { 'page_down_rect_extend', b } keys.csay = { 'page_up_rect_extend', b } -local m_snippets = _m.textadept.snippets +local m_snippets = _m.textadept.lsnippets keys.ci = { m_snippets.insert } keys.csi = { m_snippets.cancel_current } keys.cai = { m_snippets.list } -keys.ai = { m_snippets.show_scope } +keys.ai = { m_snippets.show_style } local m_editing = _m.textadept.editing keys.cm = { m_editing.match_brace } diff --git a/modules/textadept/lsnippets.lua b/modules/textadept/lsnippets.lua new file mode 100644 index 00000000..f7773639 --- /dev/null +++ b/modules/textadept/lsnippets.lua @@ -0,0 +1,339 @@ +-- Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE. + +--- +-- Provides Lua-centric snippets for Textadept. +-- Snippets are basically pieces of text inserted into a document, but can +-- execute code, contain placeholders a user can enter in dynamic text for, and +-- make transformations on that text. This is much more powerful than standard +-- text templating. +-- There are several option variables used: +-- MARK_SNIPPET: The integer mark used to identify the line that marks the +-- end of a snippet. +-- MARK_SNIPPET_COLOR: The Scintilla color used for the line +-- that marks the end of the snippet. +-- +-- @usage +-- Snippets are defined in the global table 'snippets'. Keys in that table are +-- snippet trigger words, and values are the snippet's text to insert. The +-- exceptions are language names and style names. Language names have table +-- values of either snippets or style keys with table values of snippets. +-- See /lexers/lexer.lua for some default style names. Each lexer's 'add_style' +-- function adds additional styles, the string argument being the style's name. +-- For example: +-- snippets = { +-- file = '%(buffer.filename)', +-- lua = { +-- f = 'function %1(name)(%2(args))\n %0\nend', +-- string = { [string-specific snippets here] } +-- } +-- } +-- Style and lexer insensitive snippets should be placed in the lexer and +-- snippets tables respectively. +-- +-- When searching for a snippet to expand in the snippets table, snippets in the +-- current style have priority, then the ones in the current lexer, and finally +-- the ones in the global table. +-- +-- As mentioned, snippets are key-value pairs, the key being the trigger word +-- and the value being the snippet text: ['trigger'] = 'text'. +-- Snippet text however can contain more than just text. +-- +-- Insert-time Lua and shell code: %(lua_code), `shell_code` +-- The code is executed the moment the snippet is inserted. For Lua code, the +-- result of the code execution is inserted, so print statements are useless. +-- All global variables and a 'selected_text' variable are available. +-- +-- Tab stops/Mirrors: %num +-- These are visited in numeric order with %0 being the final position of the +-- caret, the end of the snippet if not specified. If there is a placeholder +-- (described below) with the specified num, its text is mirrored here. +-- +-- Placeholders: %num(text) +-- These are also visited in numeric order, having precedence over tab stops, +-- and inserting the specified text. If no placeholder is available, the tab +-- stop is visited instead. The specified text can contain Lua code executed +-- at run-time: #(lua_code). +-- +-- Transformations: %num(pattern|replacement) +-- These act like mirrors, but transform the text that would be inserted using +-- a given Lua pattern and replacement. The replacement can contain Lua code +-- executed at run-time: #(lua_code), as well as the standard Lua capture +-- sequences: %n where 1 <= n <= 9. +-- See the Lua documentation for using patterns and replacements. +-- +-- To escape any of the special characters '%', '`', ')', '|', or '#', prepend +-- the standard Lua escape character '%'. Note: +-- * Only '`' needs to be escaped in shell code. +-- * '|'s after the first in transformations do not need to be escaped. +-- * Only unmatched ')'s need to be escaped. Nested ()s are ignored. +module('_m.textadept.lsnippets', package.seeall) + +local MARK_SNIPPET = 4 +local MARK_SNIPPET_COLOR = 0x4D9999 + +--- +-- Global container that holds all snippet definitions. +-- @class table +-- @name snippets +_G.snippets = {} + +_G.snippets.file = "%(buffer.filename)" +_G.snippets.path = "%((buffer.filename or ''):match('^.+/'))" +_G.snippets.tab = "%%%1(1)(%2(default))" +_G.snippets.key = "['%1'] = { %2(func)%3(, %4(arg)) }" + +--- +-- [Local table] The current snippet. +-- @class table +-- @name snippet +local snippet = {} + +--- +-- [Local table] The stack of currently running snippets. +-- @class table +-- @name snippet_stack +local snippet_stack = {} + +-- Local functions. +local snippet_info, run_lua_code, handle_escapes, unhandle_escapes, unescape + +--- +-- Begins expansion of a snippet. +-- The text inserted has escape sequences handled. +-- @param s_text Optional snippet to expand. If none is specified, the snippet +-- is determined from the trigger word (left of the caret), lexer, and style. +function insert(s_text) + local buffer = buffer + local caret = buffer.current_pos + local lexer, style, start, s_name + if not s_text then + lexer = buffer:get_lexer_language() + style = buffer:get_style_name( buffer.style_at[caret] ) + buffer:word_left_extend() + start = buffer.current_pos + s_name = buffer:get_sel_text() + end + if s_name then + local function try_get_snippet(...) + local table = _G.snippets + for _, idx in ipairs{...} do table = table[idx] end + return type(table) == 'string' and table or error() + end + local ret + ret, s_text = pcall(try_get_snippet, lexer, style, s_name) + if not ret then ret, s_text = pcall(try_get_snippet, lexer, s_name) end + if not ret then ret, s_text = pcall(try_get_snippet, s_name) end + if not ret then buffer:goto_pos(caret) end -- restore caret + end + + if s_text then + buffer:begin_undo_action() + s_text = handle_escapes(s_text) + + -- Execute Lua and shell code. + s_text = s_text:gsub('%%(%b())', run_lua_code) + s_text = s_text:gsub('`([^`]+)`', + function(code) return io.popen(code):read('*all'):sub(1, -2) end) + + -- Initialize the new snippet. If one is running, push it onto the stack. + if snippet.index then snippet_stack[#snippet_stack + 1] = snippet end + snippet = {} + snippet.start_pos = start or caret + snippet.prev_sel_text = buffer:get_sel_text() + snippet.index, snippet.max_index = 0, 0 + for i in s_text:gmatch('%%(%d+)') do + i = tonumber(i) + if i > snippet.max_index then snippet.max_index = i end + end + + -- Insert the snippet and set a mark defining the end of it. + buffer:replace_sel(s_text) buffer:add_text('\n') + local line = buffer:line_from_position(buffer.current_pos) + snippet.end_marker = buffer:marker_add(line, MARK_SNIPPET) + buffer:marker_set_back(MARK_SNIPPET, MARK_SNIPPET_COLOR) + + -- Indent all lines inserted. + buffer.current_pos = snippet.start_pos + local count = 0 for _ in s_text:gmatch('\n') do count = count + 1 end + if count > 0 then + local ref_line = buffer:line_from_position(start) + local isize, ibase = buffer.indent, buffer.line_indentation[ref_line] + local inum = ibase / isize -- number of indents needed to match + for i = 1, count do + local linei = buffer.line_indentation[ref_line + i] + buffer.line_indentation[ref_line + i] = linei + isize * inum + end + end + buffer:end_undo_action() + end + + next() +end + +--- +-- If previously at a placeholder or tab stop, attempts to mirror and/or +-- transform the entered text at all appropriate mirrors before moving on to +-- the next placeholder or tab stop. +function next() + if not snippet.index then return end + local buffer = buffer + local s_start, s_end, s_text = snippet_info() + if not s_text then cancel_current() return end + + local index = snippet.index + if index > 0 then + buffer:begin_undo_action() + local caret = math.max(buffer.anchor, buffer.current_pos) + local ph_text = buffer:text_range(snippet.ph_pos, caret) + + -- Transform mirror. + s_text = s_text:gsub('%%'..index..'(%b())', + function(mirror) + local pattern, replacement = mirror:match('^%(([^|]+)|(.+)%)$') + if not pattern and not replacement then return ph_text end + return ph_text:gsub( unhandle_escapes(pattern), + function(...) + local arg = {...} + local repl = replacement:gsub('%%(%d)', + function(i) return arg[ tonumber(i) ] or '' end) + return repl:gsub('#(%b())', run_lua_code) + end ) + end) + + -- Regular mirror. + s_text = s_text:gsub('%%'..index, ph_text) + + buffer:set_sel(s_start, s_end) buffer:replace_sel(s_text) + s_start, s_end = snippet_info() + buffer:end_undo_action() + end + + buffer:begin_undo_action() + index = index + 1 + if index <= snippet.max_index then + local s, e, next_item = s_text:find('%%'..index..'(%b())') + if next_item and not next_item:find('|') then -- placeholder + s, e = buffer:find('%'..index..next_item, 0, s_start) + next_item = next_item:gsub('#(%b())', run_lua_code) + next_item = unhandle_escapes( next_item:sub(2, -2) ) + buffer:set_sel(s, e) buffer:replace_sel(next_item) + buffer:set_sel(s, s + #next_item) + else -- use the first mirror as a placeholder + s, e = buffer:find('%'..index..'[^(]', 2097152, s_start) -- regexp + if not s then snippet.index = index + 1 return next() end + buffer:set_sel(s, e - 1) buffer:replace_sel('') + end + snippet.ph_pos = s + snippet.index = index + else + s_text = unescape( unhandle_escapes( s_text:gsub('%%0', '%%__caret') ) ) + buffer:set_sel(s_start, s_end) buffer:replace_sel(s_text) + s_start, s_end = snippet_info() + if s_end then buffer:goto_pos(s_end + 1) buffer:delete_back() end + local s, e = buffer:find('%__caret', 4, s_start) + if s and s <= s_end then buffer:set_sel(s, e) buffer:replace_sel('') end + buffer:marker_delete_handle(snippet.end_marker) + snippet = #snippet_stack > 0 and table.remove(snippet_stack) or {} + end + buffer:end_undo_action() +end + +--- +-- Cancels the active snippet, reverting to the state before its activation, +-- and restores the previous running snippet (if any). +function cancel_current() + if not snippet.index then return end + local buffer = buffer + local s_start, s_end = snippet_info() + buffer:begin_undo_action() + if s_start and s_end then + buffer:set_sel(s_start, s_end) buffer:replace_sel('') + s_start, s_end = snippet_info() + buffer:goto_pos(s_end + 1) buffer:delete_back() + end + if snippet.prev_sel_text then buffer:add_text(snippet.prev_sel_text) end + buffer:end_undo_action() + buffer:marker_delete_handle(snippet.end_marker) + snippet = #snippet_stack > 0 and table.remove(snippet_stack) or {} +end + +--- +-- Lists available snippets in an autocompletion list. +-- Global snippets and snippets in the current lexer and style are used. +function list() + local buffer = buffer + local list, list_str = {}, '' + local function add_snippets(snippets) + for s_name in pairs(snippets) do list[#list + 1] = s_name end + end + local snippets = _G.snippets + add_snippets(snippets) + local lexer = buffer:get_lexer_language() + local style = buffer:get_style_name( buffer.style_at[buffer.current_pos] ) + if snippets[lexer] and type( snippets[lexer] ) == 'table' then + add_snippets( snippets[lexer] ) + if snippets[lexer][style] then add_snippets( snippets[lexer][style] ) end + end + table.sort(list) + local sep = string.char(buffer.auto_c_separator) + for _, v in ipairs(list) do list_str = list_str..v..sep end + list_str = list_str:sub(1, -2) + local caret = buffer.current_pos + buffer:auto_c_show(caret - buffer:word_start_position(caret, true), list_str) +end + +--- +-- Shows the style at the current caret position in a call tip. +function show_style() + local buffer = buffer + local lexer = buffer:get_lexer_language() + local style_num = buffer.style_at[buffer.current_pos] + local style = buffer:get_style_name(style_num) + local text = 'Lexer: '..lexer..'\nStyle: '..style..' ('..style_num..')' + buffer:call_tip_show(buffer.current_pos, text) +end + +--- +-- [Local function] Gets the start position, end position, and text of the +-- currently running snippet. +-- @return start pos, end pos, and snippet text. +snippet_info = function() + local buffer = buffer + local s = snippet.start_pos + local e = buffer:position_from_line( + buffer:marker_line_from_handle(snippet.end_marker) ) - 1 + if e >= s then return s, e, buffer:text_range(s, e) end +end + +--- +-- [Local function] Runs the given Lua code. +run_lua_code = function(code) + code = unhandle_escapes(code) + local env = setmetatable( + { selected_text = buffer:get_sel_text() }, { __index = _G } ) + local _, val = pcall( setfenv( loadstring('return '..code), env ) ) + return val or '' +end + +--- +-- [Local function] Replaces escaped characters with their octal equivalents in +-- a given string. +-- '%%' is the escape character used. +handle_escapes = function(s) + return s:gsub('%%([%%`%)|#])', + function(char) return ("\\%03d"):format( char:byte() ) end) +end + +--- +-- [Local function] Replaces octal characters with their escaped equivalents in +-- a given string. +unhandle_escapes = function(s) + return s:gsub('\\(%d%d%d)', + function(value) return '%'..string.char(value) end) +end + +--- +-- [Local function] Replaces escaped characters with the actual characters in a +-- given string. +-- This is used when escape sequences are no longer needed. +unescape = function(s) return s:gsub('%%([%%`%)|#])', '%1') end |