aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/cpp/snippets.lua122
-rw-r--r--modules/lua/snippets.lua62
-rw-r--r--modules/textadept/init.lua2
-rw-r--r--modules/textadept/key_commands.lua4
-rw-r--r--modules/textadept/lsnippets.lua339
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