aboutsummaryrefslogtreecommitdiff
path: root/modules/textadept
diff options
context:
space:
mode:
Diffstat (limited to 'modules/textadept')
-rw-r--r--modules/textadept/keys.lua14
-rw-r--r--modules/textadept/menu.lua8
-rw-r--r--modules/textadept/snippets.lua172
3 files changed, 103 insertions, 91 deletions
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua
index 01669279..7eb2bde9 100644
--- a/modules/textadept/keys.lua
+++ b/modules/textadept/keys.lua
@@ -320,8 +320,7 @@ keys[not OSX and (GUI and 'c|' or 'c\\')
-- Select.
local m_sel = m_edit[_L['Select']]
keys[GUI and 'cM' or 'mM'] = m_sel[_L['Select between Matching Delimiters']][2]
-keys[not OSX and GUI and 'c<'
- or 'm<'] = m_sel[_L['Select between XML Tags']][2]
+keys[not OSX and GUI and 'c<' or 'm<'] = m_sel[_L['Select between XML Tags']][2]
if GUI then
keys[not OSX and 'c>' or 'm>'] = m_sel[_L['Select in XML Tag']][2]
end
@@ -369,8 +368,7 @@ end
-- Find in Files is ai when find pane is focused in GUI.
if GUI then
keys[not OSX and 'cag' or 'cmg'] = m_search[_L['Goto Next File Found']][2]
- keys[not OSX and 'caG'
- or 'cmG'] = m_search[_L['Goto Previous File Found']][2]
+ keys[not OSX and 'caG' or 'cmG'] = m_search[_L['Goto Previous File Found']][2]
end
keys[not OSX and 'cj' or 'mj'] = textadept.editing.goto_line
@@ -410,10 +408,10 @@ keys[not OSX and (GUI and 'caO' or 'mO')
keys[not OSX and (GUI and 'caP' or 'cmp') or 'cmP'] = io.quick_open
-- Snippets.
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 '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.
diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua
index 37c7cd59..25341bbe 100644
--- a/modules/textadept/menu.lua
+++ b/modules/textadept/menu.lua
@@ -233,10 +233,10 @@ local default_menubar = {
},
{
title = _L['Snippets'],
- {_L['Insert Snippet...'], textadept.snippets._select},
- {_L['Expand Snippet/Next Placeholder'], textadept.snippets._insert},
- {_L['Previous Snippet Placeholder'], textadept.snippets._previous},
- {_L['Cancel Snippet'], textadept.snippets._cancel_current},
+ {_L['Insert Snippet...'], textadept.snippets.select},
+ {_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')
diff --git a/modules/textadept/snippets.lua b/modules/textadept/snippets.lua
index 598e603a..7bdfab7e 100644
--- a/modules/textadept/snippets.lua
+++ b/modules/textadept/snippets.lua
@@ -119,12 +119,20 @@ M.INDIC_PLACEHOLDER = _SCINTILLA.next_indic_number()
-- Note: If a directory has multiple snippets with the same trigger, the snippet
-- chosen for insertion is not defined and may not be constant.
-- @class table
--- @name _paths
-M._paths = {}
+-- @name paths
+M.paths = {}
local INDIC_SNIPPET = _SCINTILLA.next_indic_number()
local INDIC_CURRENTPLACEHOLDER = _SCINTILLA.next_indic_number()
+
+-- 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.
+-- @class table
+-- @name snippets
+-- @see _G.snippets
+local snippets = {}
+
-- 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
@@ -137,27 +145,29 @@ local INDIC_CURRENTPLACEHOLDER = _SCINTILLA.next_indic_number()
-- 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 matching_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
+ local s = snippets
+ for _, v in ipairs{type(s[lexer]) == 'table' and s[lexer] or {}, s} 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
+ if name:find(name_patt) and
+ (type(text) == 'string' or type(text) == 'function') then
+ matching_snippets[name] = tostring(text)
end
end
end
end
-- Search in snippet files.
- for i = 1, #M._paths do
- for basename in lfs.dir(M._paths[i]) do
+ 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('^([^.]+)%.?([^.]*)%.?([^.]*)$')
@@ -165,17 +175,22 @@ local function find_snippet(grep, no_trigger)
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)
+ 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
+ matching_snippets[p1 == lexer and p2 or p1] = text
end
end
end
- if not grep then return nil, nil else return trigger, snippets end
+ if not grep then return nil, nil else return trigger, matching_snippets end
end
+-- Metatable for a snippet object.
+-- @class table
+-- @name snippet_mt
+local snippet_mt -- defined later
+
-- The stack of currently running snippets.
local snippet_stack = {}
@@ -220,7 +235,7 @@ local function new_snippet(text, trigger)
return buffer:indicator_all_on_for(pos) &
1 << INDIC_CURRENTPLACEHOLDER > 0 and pos + 1 or pos
else
- return M._snippet_mt[k]
+ return snippet_mt[k]
end
end})
snippet_stack[#snippet_stack + 1] = snippet
@@ -365,70 +380,7 @@ local function new_snippet(text, trigger)
return snippet
end
----
--- Inserts snippet text *text* or the snippet assigned to the trigger word
--- behind the caret.
--- Otherwise, if a snippet is active, goes to the active snippet's next
--- placeholder. Returns `false` if no action was taken.
--- @param text Optional snippet text to insert. If `nil`, attempts to insert a
--- new snippet based on the trigger, the word behind caret, and the current
--- lexer.
--- @return `false` if no action was taken; `nil` otherwise.
--- @see buffer.word_chars
--- @name _insert
-function M._insert(text)
- local trigger
- 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]
- if snippet then snippet:next() else return false end
-end
-
----
--- Jumps back to the previous snippet placeholder, reverting any changes from
--- the current one.
--- Returns `false` if no snippet is active.
--- @return `false` if no snippet is active; `nil` otherwise.
--- @name _previous
-function M._previous()
- if #snippet_stack == 0 then return false end
- snippet_stack[#snippet_stack]:previous()
-end
-
----
--- Cancels the active snippet, removing all inserted text.
--- Returns `false` if no snippet is active.
--- @return `false` if no snippet is active; `nil` otherwise.
--- @name _cancel_current
-function M._cancel_current()
- if #snippet_stack == 0 then return false end
- snippet_stack[#snippet_stack]:finish(true)
-end
-
----
--- Prompts the user to select a snippet to insert from a list of global and
--- language-specific snippets.
--- @name _select
-function M._select()
- 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']},
- items = items, width = CURSES and ui.size[1] - 2 or nil
- }
- if button == 1 and i then M._insert(items[i * 2]) end
-end
-
--- Metatable for a snippet object.
--- @class table
--- @name _snippet_mt
-M._snippet_mt = {
+snippet_mt = {
-- Inserts the current snapshot (based on `self.index`) of this snippet into
-- the buffer and marks placeholders.
insert = function(self)
@@ -630,6 +582,68 @@ M._snippet_mt = {
end,
}
+---
+-- Inserts snippet text *text* or the snippet assigned to the trigger word
+-- behind the caret.
+-- Otherwise, if a snippet is active, goes to the active snippet's next
+-- placeholder. Returns `false` if no action was taken.
+-- @param text Optional snippet text to insert. If `nil`, attempts to insert a
+-- new snippet based on the trigger, the word behind caret, and the current
+-- lexer.
+-- @return `false` if no action was taken; `nil` otherwise.
+-- @see buffer.word_chars
+-- @name insert
+function M.insert(text)
+ local trigger
+ 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]
+ if snippet then snippet:next() else return false end
+end
+
+---
+-- Jumps back to the previous snippet placeholder, reverting any changes from
+-- the current one.
+-- Returns `false` if no snippet is active.
+-- @return `false` if no snippet is active; `nil` otherwise.
+-- @name previous
+function M.previous()
+ if #snippet_stack == 0 then return false end
+ snippet_stack[#snippet_stack]:previous()
+end
+
+---
+-- Cancels the active snippet, removing all inserted text.
+-- Returns `false` if no snippet is active.
+-- @return `false` if no snippet is active; `nil` otherwise.
+-- @name cancel_current
+function M.cancel_current()
+ if #snippet_stack == 0 then return false end
+ snippet_stack[#snippet_stack]:finish(true)
+end
+
+---
+-- Prompts the user to select a snippet to insert from a list of global and
+-- language-specific snippets.
+-- @name select
+function M.select()
+ local all_snippets, items = {}, {}
+ for trigger, snippet in pairs(select(2, find_snippet(true, true))) do
+ all_snippets[#all_snippets + 1], all_snippets[trigger] = trigger, snippet
+ end
+ table.sort(all_snippets)
+ for i = 1, #all_snippets do
+ local trigger = all_snippets[i]
+ items[#items + 1], items[#items + 2] = trigger, all_snippets[trigger]
+ end
+ local button, i = ui.dialogs.filteredlist{
+ title = _L['Select Snippet'], columns = {_L['Trigger'], _L['Snippet Text']},
+ items = items, width = CURSES and ui.size[1] - 2 or nil
+ }
+ if button == 1 and i then M.insert(items[i * 2]) end
+end
+
-- Update snippet transforms when text is added or deleted.
events.connect(events.UPDATE_UI, function(updated)
if #snippet_stack > 0 and updated and updated & buffer.UPDATE_CONTENT > 0 then
@@ -647,10 +661,10 @@ end)
-- @see textadept.editing.autocomplete
textadept.editing.autocompleters.snippet = function()
local list = {}
- local trigger, snippets = find_snippet(true)
+ local trigger, matching_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
+ for name in pairs(matching_snippets) do
list[#list + 1] = string.format('%s%s%d', name, sep, xpm)
end
return #trigger, list
@@ -662,6 +676,6 @@ end
-- This table also contains the `textadept.snippets` module.
-- @class table
-- @name _G.snippets
-_G.snippets = M
+_G.snippets = snippets
return M