diff options
Diffstat (limited to 'modules/textadept')
-rw-r--r-- | modules/textadept/keys.lua | 14 | ||||
-rw-r--r-- | modules/textadept/menu.lua | 4 | ||||
-rw-r--r-- | modules/textadept/snippets.lua | 113 |
3 files changed, 82 insertions, 49 deletions
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua index 2ed43ed3..fe3ab785 100644 --- a/modules/textadept/keys.lua +++ b/modules/textadept/keys.lua @@ -83,9 +83,10 @@ local M = {} -- Ctrl+Space |⌥Esc |^Space |Complete symbol -- Ctrl+H |^H |M-H<br/>M-S-H|Show documentation -- Tab |⇥ |Tab |Expand snippet or next placeholder --- Ctrl+K |⌥⇥ |M-K |Insert snippet... +-- Ctrl+Shift+K |⌥⇧⇥ |M-S-K |Insert snippet... -- Shift+Tab |⇧⇥ |S-Tab |Previous snippet placeholder -- Esc |Esc |Esc |Cancel snippet +-- Ctrl+K |⌥⇥ |M-K |Complete trigger word -- Ctrl+F2 |⌘F2 |F1 |Toggle bookmark -- Ctrl+Shift+F2 |⌘⇧F2 |F6 |Clear bookmarks -- F2 |F2 |F2 |Next bookmark @@ -219,7 +220,7 @@ module('textadept.keys')]] -- Windows and Linux key bindings. -- -- Unassigned keys (~ denotes keys reserved by the operating system): --- c: C H I K p Q T ~ V Y _ ) ] } + +-- c: C H I p Q T ~ V Y _ ) ] } + -- a: aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_ ) ] } *+-/=\n\s -- ca: aAbBcCdD F jJkKlLmM N qQ t xXy zZ_"'()[]{}<>* / \s -- @@ -256,14 +257,14 @@ module('textadept.keys')]] -- * Control+Shift and Control+Meta+Shift keys are not recognized. -- * Modifiers for function keys F1-F12 are not recognized. -- For pdcurses (Win32): --- * Many Control+Symbol keys are not recognized, but most +-- * Many Control+Symbol keys are not recognized, but most -- Control+Shift+Symbol keys are. -- * Ctrl+Meta+Symbol keys are not recognized. -- -- Unassigned keys (~ denotes keys reserved by the operating system): -- c: g~~ ~ ~ -- cm: cd g~~ k ~ q t yz --- m: e J K qQ sS vVw yY _ + +-- m: e J qQ sS vVw yY _ + -- Note: m[befhstv] may be used by Linux/BSD GUI terminals for menu access. -- -- CTRL = 'c' (Control ^) @@ -408,10 +409,13 @@ keys[not OSX and (GUI and 'caO' or 'mO') or 'cmO'] = m_quick_open[_L['Quickly Open _Current Directory']][2] keys[not OSX and (GUI and 'caP' or 'cmp') or 'cmP'] = io.quick_open -- Snippets. -keys[not OSX and (GUI and 'ck' or 'mk') or 'a\t'] = textadept.snippets._select +local m_snippets = m_tools[_L['_Snippets']] +keys[not OSX and (GUI and 'cK' or 'mK') or 'sa\t'] = textadept.snippets._select keys['\t'] = textadept.snippets._insert keys['s\t'] = textadept.snippets._previous keys.esc = textadept.snippets._cancel_current +keys[not OSX and (GUI and 'ck' or 'mk') + or 'a\t'] = m_snippets[_L['Complete Trigger _Word']][2] -- Other. keys[not OSX and 'c ' or 'aesc'] = m_tools[_L['_Complete Symbol']][2] keys[GUI and 'ch' or 'mh'] = textadept.editing.show_documentation diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua index 3806e14b..5edc1683 100644 --- a/modules/textadept/menu.lua +++ b/modules/textadept/menu.lua @@ -239,6 +239,10 @@ local default_menubar = { {_L['_Expand Snippet/Next Placeholder'], textadept.snippets._insert}, {_L['_Previous Snippet Placeholder'], textadept.snippets._previous}, {_L['_Cancel Snippet'], textadept.snippets._cancel_current}, + SEPARATOR, + {_L['Complete Trigger _Word'], function() + textadept.editing.autocomplete('snippets') + end} }, SEPARATOR, {_L['_Complete Symbol'], function() diff --git a/modules/textadept/snippets.lua b/modules/textadept/snippets.lua index 7fcfe55e..46a97dd6 100644 --- a/modules/textadept/snippets.lua +++ b/modules/textadept/snippets.lua @@ -123,6 +123,57 @@ M._paths = {} local INDIC_SNIPPET = _SCINTILLA.next_indic_number() local INDIC_CURRENTPLACEHOLDER = _SCINTILLA.next_indic_number() +-- Finds the snippet assigned to the trigger word behind the caret and returns +-- the trigger word and snippet text. If *grep* is `true`, returns a table of +-- snippets (trigger-text key-value pairs) that match the trigger word +-- instead of snippet text. +-- Snippets are searched for in the global snippets table followed by snippet +-- directories. Lexer-specific snippets are preferred. +-- @param grep Flag that indicates whether or not to return a table of snippets +-- that match the trigger word. +-- @param no_trigger Flag that indicates whether or not to ignore the trigger +-- word and return all snippets. +-- @return trigger word, snippet text or table of matching snippets +local function find_snippet(grep, no_trigger) + local snippets = {} + local pos = buffer.current_pos + local trigger = buffer:text_range(buffer:word_start_position(pos), pos) + if no_trigger then grep, trigger = true, '' end + local lexer = buffer:get_lexer(true) + local name_patt = '^'..trigger + -- Search in the snippet tables, ignoring this module's non-string members. + for _, v in ipairs{type(M[lexer]) == 'table' and M[lexer] or {}, M} do + if not grep and v[trigger] then + return trigger, v[trigger] + elseif grep then + for name, text in pairs(v) do + if name:find(name_patt) and (v ~= M or type(text) == 'string') then + snippets[name] = text + end + end + end + end + -- Search in snippet files. + for i = 1, #M._paths do + for basename in lfs.dir(M._paths[i]) do + -- Snippet files are either of the form "lexer.trigger.ext" or + -- "trigger.ext". Prefer "lexer."-prefixed snippets. + local p1, p2, p3 = basename:match('^([^.]+)%.?([^.]*)%.?([^.]*)$') + if not grep and (p1 == lexer and p2 == trigger or + p1 == trigger and p3 == '') or + grep and (p1 == lexer and p2 and p2:find(name_patt) or + p1 and p1:find(name_patt) and p3 == '') then + local f = io.open(M._paths[i]..'/'..basename) + text = f:read('a') + f:close() + if not grep then return trigger, text end + snippets[p1 == lexer and p2 or p1] = text + end + end + end + if not grep then return nil, nil else return trigger, snippets end +end + -- The stack of currently running snippets. local snippet_stack = {} @@ -325,27 +376,7 @@ end -- @name _insert function M._insert(text) local trigger - if not text then - local lexer = buffer:get_lexer(true) - trigger = buffer:text_range(buffer:word_start_position(buffer.current_pos), - buffer.current_pos) - text = type(M[lexer]) == 'table' and M[lexer][trigger] or M[trigger] - if not text then - for i = 1, #M._paths do - for basename in lfs.dir(M._paths[i]) do - -- Snippet files are either of the form "lexer.trigger.ext" or - -- "trigger.ext". Prefer "lexer."-prefixed snippets. - local first, second = basename:match('^([^.]+)%.?([^.]*)') - if first == lexer and second == trigger or - first == trigger and second == '' and not text then - local f = io.open(M._paths[i]..'/'..basename) - text = f:read('a') - f:close() - end - end - end - end - end + if not text then trigger, text = find_snippet(trigger) end if type(text) == 'function' and not trigger:find('^_') then text = text() end local snippet = type(text) == 'string' and new_snippet(text, trigger) or snippet_stack[#snippet_stack] @@ -378,29 +409,12 @@ end -- language-specific snippets. -- @name _select function M._select() - local list, items = {}, {} - for trigger, text in pairs(snippets) do - if type(text) == 'string' then list[#list + 1] = trigger..'|'..text end - end - local lexer = buffer:get_lexer(true) - if snippets[lexer] then - for trigger, text in pairs(snippets[lexer]) do - if type(text) == 'string' then list[#list + 1] = trigger..'|'..text end - end - end - for i = 1, #M._paths do - for basename in lfs.dir(M._paths[i]) do - local first, second = basename:match('^([^.]+)%.?([^.]*)') - if second == '' or first == lexer then - local f = io.open(M._paths[i]..'/'..basename) - list[#list + 1] = (second ~= '' and second or first)..'|'..f:read('a') - f:close() - end - end - end - table.sort(list) - for i = 1, #list do - items[#items + 1], items[#items + 2] = list[i]:match('^([^|]+)|(.+)$') + local snippets = select(2, find_snippet(true, true)) + local triggers, items = {}, {} + for trigger in pairs(snippets) do triggers[#triggers + 1] = trigger end + table.sort(triggers) + for i = 1, #triggers do + items[#items + 1], items[#items + 2] = triggers[i], snippets[triggers[i]] end local button, i = ui.dialogs.filteredlist{ title = _L['Select Snippet'], columns = {_L['Trigger'], _L['Snippet Text']}, @@ -626,6 +640,17 @@ events.connect(events.VIEW_NEW, function() buffer.indic_style[INDIC_CURRENTPLACEHOLDER] = buffer.INDIC_HIDDEN end) +textadept.editing.autocompleters.snippet = function() + local list = {} + local trigger, snippets = find_snippet(true) + local sep = string.char(buffer.auto_c_type_separator) + local xpm = textadept.editing.XPM_IMAGES.NAMESPACE + for name in pairs(snippets) do + list[#list + 1] = string.format('%s%s%d', name, sep, xpm) + end + return #trigger, list +end + --- -- Map of snippet triggers with their snippet text or functions that return such -- text, with language-specific snippets tables assigned to a lexer name key. |