diff options
Diffstat (limited to 'config/essentials/vis/vis-ultisnips')
-rw-r--r-- | config/essentials/vis/vis-ultisnips/init.lua | 149 | ||||
-rw-r--r-- | config/essentials/vis/vis-ultisnips/snipmate-parser.lua | 128 | ||||
-rw-r--r-- | config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua | 160 | ||||
-rw-r--r-- | config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua | 230 | ||||
-rw-r--r-- | config/essentials/vis/vis-ultisnips/ultisnips-parser.lua | 211 |
5 files changed, 878 insertions, 0 deletions
diff --git a/config/essentials/vis/vis-ultisnips/init.lua b/config/essentials/vis/vis-ultisnips/init.lua new file mode 100644 index 0000000..52faa55 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/init.lua @@ -0,0 +1,149 @@ +-------------------------------------------------------------------------------- +-- Modules + +local M = {} +local cwd = ... +local SnipMate = require(cwd .. '.snipmate-parser') +local UltiSnips = require(cwd .. '.ultisnips-parser') + + + +-------------------------------------------------------------------------------- +-- Config + +M.snipmate = '' +M.ultisnips = '' + + + +-------------------------------------------------------------------------------- +-- Helper functions + +-- Takes list of snippets and concatenates them into the string suitable +-- for passing to dmenu (or, very probably, vis-menu) +local function snippetslist(snippets) + local list = '' + + for k,v in pairs(snippets) do + if not v.description then + list = list .. k .. '\n' + else + list = list .. k .. ' - ' .. v.description .. '\n' + end + end + + return list +end + + + +local function load_ultisnips() + local snippetfile = M.ultisnips .. vis.win.syntax .. '.snippets' + local snippets, success = UltiSnips.load_snippets(snippetfile) + if not success then + vis:info('Failed to load a correct UltiSnip: ' .. snippetfile) + end + return snippets, success +end + + + +local function load_snipmate() + local snippetfile = M.snipmate .. vis.win.syntax .. '.snippets' + local snippets, success = SnipMate.load_snippets(snippetfile) + if not success then + vis:info('Failed to load a correct SnipMate: ' .. snippetfile) + end + return snippets, success +end + + + +-- Second will append to first using suffix for distinguishing +local function merge_and_override(snips1, snips2, suffix) + for k,v in pairs(snips2) do + snips1[k .. suffix] = v + end + return snips1 +end + + + +-------------------------------------------------------------------------------- +-- Plugging it all in + +vis:map(vis.modes.INSERT, "<C-x><C-j>", function() + local snippets = merge_and_override(load_snipmate(), load_ultisnips(), '_us') + + local win = vis.win + local file = win.file + local pos = win.selection.pos + + if not pos then + return + end + -- TODO do something clever here + + -- Use prefix W if exists + local initial = ' ' + local range = file:text_object_longword(pos > 0 and pos - 1 or pos) + if range then + initial = initial .. file:content(range) + end + + -- Note, for one reason or another, using vis-menu corrupts my terminal + -- (urxvt) for exact amount of lines that vis-menu takes + -- dmenu has no such problems, but can't take initial input :-\ + --local stdout = io.popen("echo '" .. snippetslist(snippets) .. "' | dmenu -l 5", "r") + local stdout = io.popen("echo '" .. snippetslist(snippets) .. "' | vis-menu " .. initial, "r") + local chosen = stdout:lines()() + local _, msg, status = stdout:close() + if status ~= 0 or not chosen then + vis:message(msg) + return + end + + local trigger = chosen:gmatch('[^ ]+')() + local snipcontent = snippets[trigger].content + if range then + file:delete(range) + -- Update position after deleting the range + pos = pos - (range.finish - range.start) + vis:redraw() + end + + vis:insert(snipcontent.str) + + + if #snipcontent.tags > 0 then + vis:info("Use 'g>' and 'g<' to navigate between anchors.") + + -- Create selections iteratively using `:#n,#n2 p` command and `gs` to + -- save it in the jumplist + for _,v in ipairs(snipcontent.tags) do + -- Can't use 'x' command because it'd select stuff across + -- whole file + vis:command('#' .. pos + v.selstart ..',#' .. pos + v.selend .. ' p') + --vis:feedkeys('gs') -- Tested, works without this too, but just in case + --vis:message('Command: ' .. cmd) + end + + -- Backtrack through all selections we've made first + -- (so that we can use g> to move us forward)... + for _ in ipairs(snipcontent.tags) do + vis:feedkeys('g<') + end + + -- ... then set us on the first selection + vis:feedkeys('g>') + else + win.selection.pos = pos + #snipcontent.str + end +end, "Insert a snippet") + + + +-------------------------------------------------------------------------------- +-- End module + +return M
\ No newline at end of file diff --git a/config/essentials/vis/vis-ultisnips/snipmate-parser.lua b/config/essentials/vis/vis-ultisnips/snipmate-parser.lua new file mode 100644 index 0000000..9d735f1 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/snipmate-parser.lua @@ -0,0 +1,128 @@ +-------------------------------------------------------------------------------- +-- Module table + +local M = {} + +local lpeg = require('lpeg') + + + +-------------------------------------------------------------------------------- +-- lpeg rules + +-- Base definitions +-- local tws = lpeg.S' ' ^ 1 +local tnewline = lpeg.S'\n' +-- local tlowcasedword = lpeg.R'az' ^ 1 +local tdigit = lpeg.locale()['digit'] +local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()['print'] +-- local tcontrol = lpeg.locale()['cntrl'] +local ttabtrigger = tanyprintable ^ 1 +local ttag = lpeg.Cg(lpeg.Cp(), 'selstart') + * lpeg.P'${' + * lpeg.Cg(tdigit^1, 'tag-order') + * ( + (lpeg.S':' * lpeg.Cg(talphanum^1, 'default-value') * lpeg.S'}') + + lpeg.S'}' + ) + * lpeg.Cg(lpeg.Cp(), 'selend') +local tsnippetdecl = lpeg.P'snippet' * lpeg.S' ' * lpeg.Cg(ttabtrigger, 'tabtrigger') * tnewline +local tsnippetcontent = lpeg.C( + lpeg.Cp() * + (lpeg.S'\t '^1 + * (lpeg.Ct(ttag) + tanyprintable)^1 + * tnewline + )^1 + ) + +-- Constructs +local tsnippet = tsnippetdecl * tsnippetcontent +local tcomment = lpeg.S'#' * tanyprintable^0 * tnewline + +-- The way grammar captures: +-- Every snippet gets its own table, and every table has: +-- 'tabtrigger' - the tabtrigger +-- [1] - full content +-- [2] - start of snippet content (need to subtract from selstart/selend +-- [3..n] - tags +local tsnippetsfile = lpeg.Ct((tcomment + lpeg.Ct(tsnippet) + tnewline) ^1) + +-------------------------------------------------------------------------------- +-- Functions + +local function trim_tabs(content) + local trim = function (s) + return (string.gsub(s, "^\t(.-)$", "%1")) + end + + local ret='' + for str in string.gmatch(content, '([^\n]+)') do + ret = ret .. trim(str) .. '\n' + end + return ret +end + +-- Tags are on the top level of th table, +-- defined starting with index '3' +-- Index '2' is start of the content +-- Structure: +-- { tag-order: int +-- , selstart: int +-- , selend: int +-- , default-value: str +-- } +local function extract_tags(tableau) + local tags = {} + for k,v in ipairs(tableau) do + if k >= 3 then -- Only process starting with ix 2 + tags[k - 2] = { selstart = v.selstart - tableau[2] - 1 + , selend = v.selend - tableau[2] - 1 + , default = v['default-value'] + , order = v['tag-order'] + } +-- vis:message('snippet ' .. tableau.tabtrigger .. ' tag ' .. +-- tostring(tags[k - 1].order) .. ' has start/end: ' .. +-- tostring(tags[k - 1].selstart) .. '/' .. +-- tostring(tags[k - 1].selend)) + end + end + return tags +end + +M.load_snippets = function(snippetfile) + local snippets = {} + + local f = io.open(snippetfile, 'r') + if f then + local content = f:read("*all") + + -- TODO hmmm, this'll make whole file unsuable, when it could + -- in fact have usable snippets + local m = tsnippetsfile:match(content) + if not m then + vis:info('Failed to parse SnipMate file: '.. snippetfile) + return nil + else + -- k is index of snippet definition, v is table of snippet def + for _,v in pairs(m) do + snippets[v.tabtrigger] = { description = nil + , options = {} + , content = { str = trim_tabs(v[1]) + , tags = extract_tags(v) + } + } + end + end + + f:close() + return snippets, true + else + return snippets, false + end +end + +-------------------------------------------------------------------------------- +-- End module + +return M diff --git a/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua b/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua new file mode 100644 index 0000000..997365f --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/testlpeg-snipmate.lua @@ -0,0 +1,160 @@ +local lpeg = require("lpeg") + +-------------------------------------------------------------------------------- + +-- Base definitions +-- local tws = lpeg.S(" ") ^ 1 +local tnewline = lpeg.S("\n") +-- local tlowcasedword = lpeg.R("az") ^ 1 +local tdigit = lpeg.locale()["digit"] +local talphanum = lpeg.locale()["alnum"] +local tanyprintable = lpeg.locale()["print"] +-- local tcontrol = lpeg.locale()["cntrl"] +local ttabtrigger = tanyprintable ^ 1 +local ttag = lpeg.Cg(lpeg.Cp(), "selstart") + * lpeg.P("${") + * lpeg.Cg(tdigit ^ 1, "tag-order") + * ((lpeg.S(":") * lpeg.Cg(talphanum ^ 1, "default-value") * lpeg.S("}")) + lpeg.S("}")) + * lpeg.Cg(lpeg.Cp(), "selend") +local tsnippetdecl = lpeg.P("snippet") * lpeg.S(" ") * lpeg.Cg(ttabtrigger, "tabtrigger") * tnewline +local tsnippetcontent = lpeg.C(lpeg.Cp() * (lpeg.S("\t ") ^ 1 * (lpeg.Ct(ttag) + tanyprintable) ^ 1 * tnewline) ^ 1) + +-- Constructs +local tsnippet = tsnippetdecl * tsnippetcontent +local tcomment = lpeg.S("#") * tanyprintable ^ 0 * tnewline + +-- The way grammar captures: +-- Every snippet gets its own table, and every table has: +-- 'tabtrigger' - the tabtrigger +-- [1] - full content +-- [2..n] - tags +local tsnippetsfile = lpeg.Ct((tcomment + lpeg.Ct(tsnippet) + tnewline) ^ 1) +-------------------------------------------------------------------------------- + +-- local testsingle = [[ +-- snippet sim +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- ]] + +-- local testmulti = [[ +-- snippet sim +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- snippet simc +-- public class Application +-- { +-- ${1:public} static int Main(string[] args) +-- { +-- ${0} +-- return 0; +-- } +-- } +-- snippet svm +-- ${1:public} static void Main(string[] args) +-- { +-- ${0} +-- } +-- ]] + +local testfile = [[ +# I'll most propably add more stuff in here like +# * List/Array constructio +# * Mostly used generics +# * Linq +# * Funcs, Actions, Predicates +# * Lambda +# * Events +# +# Feedback is welcome! +# +# Main +snippet sim + ${1:public} static int Main(string[] args) + { + ${0} + return 0; + } +snippet simc + public class Application + { + ${1:public} static int Main(string[] args) + { + ${0} + return 0; + } + } +snippet svm + ${1:public} static void Main(string[] args) + { + ${0} + } +# if condition +snippet if + if (${1:true}) + { + ${0:${VISUAL}} + } +snippet el + else + { + ${0:${VISUAL}} + } +]] + +-------------------------------------------------------------------------------- +-- Test + +local function print_table(tableau, tabwidth) + if tabwidth == nil then + tabwidth = 0 + end + + -- Iterate + for k, v in pairs(tableau) do + local tabs = ("\t"):rep(tabwidth) + + print(tabs .. k .. ':"' .. tostring(v) .. '"') + if type(v) == "table" then + print_table(v, tabwidth + 1) + end + end +end + +--print("------------ header ------------------------------------") +--p = lpeg.Ct(tsnippetdecl) +--t = p:match([[ +--snippet classy +--]]) +--print_table(t) +--print("--------------------------------------------------------------") + +--print("------------ tag ------------------------------------") +--print_table( +-- lpeg.Ct(ttag):match('${0:VISUAL}') +--) +--print_table( +-- lpeg.Ct(ttag):match('${12:Badonkadong}') +--) +--print_table( +-- lpeg.Ct(ttag):match('${1}') +--) +--print("--------------------------------------------------------------") + +--print("------------ single snippet test ------------------------------------") +--print_table(lpeg.Ct(tsnippet):match(testsingle)) +--print("--------------------------------------------------------------") + +--print("------------ multi snippet test ------------------------------------") +--print_table(lpeg.Ct(tsnippetsfile):match(testmulti)) +--print("--------------------------------------------------------------") + +print("------------ file with comments -------------------------------------") +print_table(tsnippetsfile:match(testfile)) +print("--------------------------------------------------------------") diff --git a/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua b/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua new file mode 100644 index 0000000..79df900 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/testlpeg-ultisnips.lua @@ -0,0 +1,230 @@ +local lpeg = require("lpeg") + +-------------------------------------------------------------------------------- + +local tsep = lpeg.S(" ") +local tws = tsep ^ 1 +local tnewline = lpeg.S("\n") +local tlowcasedword = lpeg.R("az") ^ 1 +local tdigit = lpeg.locale()["digit"] +-- local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()["print"] +local tcontrol = lpeg.locale()["cntrl"] +local function quoted(p) + return lpeg.S('"') * p * lpeg.S('"') +end +local function anythingbut(ch) + return (tanyprintable + tcontrol) - lpeg.S(ch) +end + +local ttabtriggercomplex = quoted(tlowcasedword * lpeg.S("()[]?0123456789-") ^ 1) +-- TODO This is just retarded +local ttabtriggerweird = lpeg.S("!") * (lpeg.R("az") + lpeg.S("?()")) ^ 1 * lpeg.S("!") +local ttabtriggerweird2 = lpeg.P("#!") +local ttabtrigger = ttabtriggercomplex + ttabtriggerweird + ttabtriggerweird2 + tlowcasedword +local tdescription = quoted(lpeg.Cg((tanyprintable - lpeg.S('"')) ^ 1, "description")) +local toption = lpeg.R("az") + +local tstartsnippet = lpeg.P("snippet") + * tws + * lpeg.Cg(ttabtrigger, "tabtrigger") + * tws + * tdescription + * tws ^ 0 + * lpeg.Cg(toption ^ 0, "options") +local tendsnippet = lpeg.P("endsnippet") + +-- The content parsing needs cleanup, its really convoluted due to me learning +-- lpeg while using it +--tcontent = ((tanyprintable + tcontrol)^1 - tendsnippet) * tnewline +local tcontent = ((lpeg.S(" \t") + tanyprintable) ^ 1 - tendsnippet) * tnewline +local tsnippet = tstartsnippet * tnewline * ((tendsnippet * tnewline) + lpeg.Cg(tcontent ^ 1, "content")) + +local tcomment = lpeg.S("#") * tanyprintable ^ 0 * tnewline +local tpriority = lpeg.P("priority") * tws * lpeg.Cg(lpeg.S("-") ^ 0 * tdigit ^ 1, "priority") + +-- TODO doesn't work +local tsnippetsfile = (lpeg.Ct(tsnippet) + tpriority + tcomment + tnewline) ^ 1 + +-- TODO does parse values correctly, but parsing out nested tags will +-- require recursion at the callsite since I have no clue how to do it +local ttag = { + "T", + Expr = lpeg.C((lpeg.V("T") + anythingbut("}")) ^ 1), + Tnum = lpeg.Cg(tdigit ^ 1, "tagnum"), + Ps = lpeg.Cg(lpeg.Cp(), "selstart"), + Pe = lpeg.Cg(lpeg.Cp(), "selend"), + Tc = lpeg.V("Ps") + * lpeg.P("${") + * lpeg.V("Tnum") + * lpeg.S(":") + * lpeg.Cg(lpeg.V("Expr"), "expr") + * lpeg.V("Pe") + * lpeg.S("}"), + Ts = lpeg.V("Ps") * lpeg.S("$") * lpeg.V("Pe") * lpeg.V("Tnum"), + T = lpeg.V("Tc") + lpeg.V("Ts"), +} + +-------------------------------------------------------------------------------- + +-- local testheader = [[ +-- snippet #! "#!/usr/bin/env lua" b +-- ]] + +local testcontent = [[ +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +]] + +local testsnippet = [[ +snippet fori "ipair for foop" b +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +endsnippet +]] + +local luasnippetfile = [[ +priority -50 + +################################# +# Snippets for the Lua language # +################################# +snippet #! "#!/usr/bin/env lua" b +#!/usr/bin/env lua +$0 +endsnippet + +snippet !fun(ction)?! "New function" br +local function ${1:new_function}(${2:args}) + $0 +end +endsnippet + +snippet forp "pair for loop" b +for ${1:name},${2:val} in pairs(${3:table_name}) do + $0 +end +endsnippet + +snippet fori "ipair for foop" b +for ${1:idx},${2:val} in ipairs(${3:table_name}) do + $0 +end +endsnippet + +snippet for "numeric for loop" b +for ${1:i}=${2:first},${3:last}${4/^..*/(?0:,:)/}${4:step} do + $0 +end +endsnippet + +snippet do "do block" +do + $0 +end +endsnippet + +snippet repeat "repeat loop" b +repeat + $1 +until $0 +endsnippet + +snippet while "while loop" b +while $1 do + $0 +end +endsnippet + +snippet if "if statement" b +if $1 then + $0 +end +endsnippet + +snippet ife "if/else statement" b +if $1 then + $2 +else + $0 +end +endsnippet + +snippet eif "if/elseif statement" b +if $1 then + $2 +elseif $3 then + $0 +end +endsnippet + +snippet eife "if/elseif/else statement" b +if $1 then + $2 +elseif $3 then + $4 +else + $0 +end +endsnippet + +snippet pcall "pcall statement" b +local ok, err = pcall(${1:your_function}) +if not ok then + handler(${2:ok, err}) +${3:else + success(${4:ok, err}) +}end +endsnippet + +snippet local "local x = 1" +local ${1:x} = ${0:1} +endsnippet + +# vim:ft=snippets: +]] + +-------------------------------------------------------------------------------- +-- Test + +local function print_table(tableau, tabwidth) + if tabwidth == nil then + tabwidth = 0 + end + + -- Iterate + for k, v in pairs(tableau) do + local tabs = ("\t"):rep(tabwidth) + + print(tabs .. k .. ': "' .. tostring(v) .. '"') + if type(v) == "table" then + print_table(v, tabwidth + 1) + end + end +end + +do + print("------------ snippet test ------------------------------------") + local p = lpeg.Ct(tsnippet) + local t = p:match(testsnippet) + print_table(t) + print("--------------------------------------------------------------") +end + +do + print("------------ snippetfile test ------------------------------------") + local p = lpeg.Ct(tsnippetsfile) + local t = p:match(luasnippetfile) + print_table(t) + print("--------------------------------------------------------------") +end + +do + print("------------ tags test -------------------------------------") + local p = lpeg.Ct((lpeg.Ct(ttag) + tanyprintable + tcontrol) ^ 1) + local t = p:match(testcontent) + print_table(t) + print("--------------------------------------------------------------") +end diff --git a/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua b/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua new file mode 100644 index 0000000..a4240b8 --- /dev/null +++ b/config/essentials/vis/vis-ultisnips/ultisnips-parser.lua @@ -0,0 +1,211 @@ +-------------------------------------------------------------------------------- +-- Module table + +local M = {} + +local lpeg = require('lpeg') + + + +-------------------------------------------------------------------------------- +-- lpeg rules + +local tsep = lpeg.S' \t' +local tws = tsep ^ 1 +local tnewline = lpeg.S'\n' +local tlowcasedword = lpeg.R'az' ^ 1 +local tdigit = lpeg.locale()['digit'] +-- local talphanum = lpeg.locale()['alnum'] +local tanyprintable = lpeg.locale()['print'] +local tcontrol = lpeg.locale()['cntrl'] +local function surrounded(ch, p) return lpeg.S(ch) * p * lpeg.S(ch) end +local function anythingbut(ch) return (tanyprintable + tcontrol) - lpeg.S(ch) end + +local ttabtriggercomplex = surrounded ('"', + tlowcasedword * lpeg.S'()[]?0123456789-'^1 + ) +-- TODO This is just retarded +-- Check the actual grammar and see what special starting chars are +-- then relax the grammar a bit +local ttabtriggerweird = surrounded('!', + (lpeg.R'az' + lpeg.S'?()') ^ 1 + ) +local ttabtriggerweird2 = lpeg.P'#!' +local ttabtriggerweird3 = surrounded('/', + (anythingbut'/') ^1 + ) +local ttabtrigger = ttabtriggercomplex + + ttabtriggerweird + + ttabtriggerweird2 + + ttabtriggerweird3 + + (tlowcasedword + lpeg.S'.') +local tdescription = surrounded ('"', + lpeg.Cg( (tanyprintable - lpeg.S'"')^1, 'description') + ) +local toption = lpeg.R'az' + +local tstartsnippet = lpeg.P'snippet' + * tws + * lpeg.Cg(ttabtrigger, 'tabtrigger') + * tws + * tdescription + * tws ^ 0 + * lpeg.Cg(toption^0, 'options') +local tendsnippet = lpeg.P'endsnippet' + +-- The content parsing needs cleanup, its really convoluted due to me learning +-- lpeg while using it +--tcontent = ((tanyprintable + tcontrol)^1 - tendsnippet) * tnewline +local tcontent = ((lpeg.S' \t' + tanyprintable)^1 - tendsnippet) + * tnewline +local tsnippet = tstartsnippet + * tnewline + * ((tendsnippet * tnewline) + lpeg.Cg(tcontent ^ 1, 'content')) + +-- local tcomment = lpeg.S'#' +-- * tanyprintable^0 +-- * tnewline +-- local tpriority = lpeg.P'priority' +-- * tws +-- * lpeg.Cg(lpeg.S('-')^0 * tdigit^1, 'priority') + +-- TODO doesn't work +-- local tsnippetsfile = (lpeg.Ct(tsnippet) + tpriority + tcomment + tnewline) ^ 1 + + +-- TODO does parse values correctly, but parsing out nested tags will +-- require recursion at the callsite since I have no clue how to do it +local ttag = { 'T' + ; Expr = lpeg.C((lpeg.V'T' + ((tanyprintable + tcontrol) - lpeg.S'}'))^1) + , Tnum = lpeg.Cg(tdigit ^ 1, 'tagnum') + , Ps = lpeg.Cg(lpeg.Cp(), 'selstart') + , Pe = lpeg.Cg(lpeg.Cp(), 'selend') + , Tc = lpeg.V'Ps' + * lpeg.P'${' + * lpeg.V'Tnum' + * lpeg.S(':') + * lpeg.Cg(lpeg.V'Expr', 'expr') + * lpeg.V'Pe' + * lpeg.S'}' + , Ts = lpeg.V'Ps' * lpeg.S'$' * lpeg.V'Pe' * lpeg.V'Tnum' + , T = lpeg.V'Tc' + lpeg.V'Ts' + } + + + +-------------------------------------------------------------------------------- +-- Functions + +-- Parses the snippet's content to create a table we later use +-- to corrently insert the text, the selections, and the default values +local function create_content(str) + local content = {} + content.str = str + content.tags = {} + + local p = vis.lpeg.Ct((lpeg.Ct(ttag) + tanyprintable + tcontrol) ^ 1) + local m = p:match(str) + + local s = 1 -- We start from 1 to adjust position from $^0 to ^$0 + for k,v in ipairs(m) do + content.tags[k] = v + -- TODO recurse over tag.expr to extract nested tags + -- Of course this will actually have to be used later on, depending + -- on whether the tag is added or not + + -- We need to keep track of how much we remove, and readjust all + -- subsequent selection points + -- Note to self, I hate all this bookkeeping + local tagtext = string.sub(str, v.selstart, v.selend) + if v.expr ~= nil then + content.str = string.gsub(content.str, tagtext, v.expr) + content.tags[k].selstart = content.tags[k].selstart - s + content.tags[k].selend = content.tags[k].selstart + #v.expr + s = s + #'${' + #tostring(k) + #':' + 1 + else + content.str = string.gsub(content.str, tagtext, '') + content.tags[k].selstart = content.tags[k].selstart - s + content.tags[k].selend = content.tags[k].selstart + s = s + #'$' + 1 + end + end + + return content +end + + + +-- Takes a line starting with 'snippet' and a lines iterator, and creates +-- a 'snippet' table to be used +-- If it fails it returns nil, otherwise returns two values, a tabtrigger +-- and a snippet +local function create_snippet(start_line, linesit) + local snippetstr = start_line .. '\n' + -- Read content into list of lines until we hit `endsnippet` + for line in linesit do + local s, _ = string.find(line, 'endsnippet') + if s == 1 then + snippetstr = snippetstr .. 'endsnippet' .. '\n' + break + else + snippetstr = snippetstr .. line .. '\n' + end + end + + local p = vis.lpeg.Ct(tsnippet) + local m = p:match(snippetstr) + + if not m then + -- Enable this when debugging, otherwise it nukes whole app + vis:info('Failed to parse some snippets!') + -- vis:message('Failed to parse snippet: ' .. snippetstr) + return nil + else + local tabtrigger = m.tabtrigger + local snippet = {} + snippet.description = m.description + snippet.options = m.options + snippet.content = create_content(m.content) + return tabtrigger, snippet + end +end + + + +-- Loads all snippets from passed '.snippets' file. Should probably be +-- triggered when new file is loaded or when syntax is set/changed +M.load_snippets = function(snippetfile) + local snippets = {} + + local f = io.open(snippetfile, 'r') + if f then + io.input(f) + local linesit = io.lines() + + for line in linesit do + -- TODO read whole file, then apply lpeg grammar that parses all + -- snippets out rather than being pedestrian about it like this + local s, _ = string.find(line, 'snippet') + -- Find lines that start with 'snippet' and enter + -- snippet reading loop + if s == 1 then + local tabtrigger, snippet = create_snippet(line, linesit) + if tabtrigger then + snippets[tabtrigger] = snippet + end + end + end + + io.close(f) + return snippets, true + else + return snippets, false + end +end + + + +-------------------------------------------------------------------------------- +-- End module + +return M
\ No newline at end of file |