aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--modules/textadept/editing.lua126
-rw-r--r--modules/textadept/keys.lua4
-rw-r--r--modules/textadept/menu.lua2
4 files changed, 72 insertions, 66 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3eb1f71..e742fe23 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -612,7 +612,7 @@ Changes:
* Renamed `_G.buffer_new()` to [`buffer.new()`][].
* Changed the display of highlighted words, including removing
`_M.textadept.editing.INDIC_HIGHLIGHT_ALPHA`.
-* Changed [`_M.textadept.editing.autocomplete_word()`][] API.
+* Changed `_M.textadept.editing.autocomplete_word()` API.
* Removed `_M.textadept.menu.menubar`, `_M.textadept.menu.contextmenu`, and
`events.handlers` tables from the API.
* Moved `_M.textadept.filter_through` module functionality into
@@ -634,7 +634,6 @@ Changes:
[key modes]: api/keys.html#Modes
[Scintilla]: http://scintilla.org
[`buffer.new()`]: api/buffer.html#new
-[`_M.textadept.editing.autocomplete_word()`]: api/textadept.editing.html#autocomplete_word
[`_M.textadept.editing.filter_through()`]: api/textadept.editing.html#filter_through
[`_M.textadept.run.goto_error()`]: api/textadept.run.html#goto_error
[`gui.find.goto_file_found()`]: api/gui.find.html#goto_file_found
@@ -1121,7 +1120,7 @@ Changes:
* [`_G.timeout()`][] accepts fractional seconds.
* Replaced `scripts/update_doc` with `src/Makefile` targets.
* New Manual and LuaDoc HTML page formatting.
-* [`_M.textadept.editing.autocomplete_word()`][] accepts default words.
+* `_M.textadept.editing.autocomplete_word()` accepts default words.
* Added documentation on [generating LuaDoc][] and [Lua Adeptsense][].
* Moved `Markdown:` comments into LuaDoc.
* Added Spanish and German translations.
@@ -1139,7 +1138,6 @@ Changes:
[`_M.set_buffer_properties()`]: api/_M.html#Buffer.Properties
[`keys.KEYSYMS`]: api/keys.html#KEYSYMS
[`_G.timeout()`]: api/_G.html#timeout
-[`_M.textadept.editing.autocomplete_word()`]: api/textadept.editing.html#autocomplete_word
[generating LuaDoc]: 11_Scripting.html#Generating.LuaDoc
[Lua Adeptsense]: api/textadept.adeptsense.html#Generating.Lua.Adeptsense
[GTK]: http://gtk.org
diff --git a/modules/textadept/editing.lua b/modules/textadept/editing.lua
index 1619456d..81ad1d34 100644
--- a/modules/textadept/editing.lua
+++ b/modules/textadept/editing.lua
@@ -27,6 +27,10 @@ local M = {}
-- @field STRIP_TRAILING_SPACES (bool)
-- Strip trailing whitespace before saving files.
-- The default value is `false`.
+-- @field AUTOCOMPLETE_ALL (bool)
+-- Autocomplete the current word using words from all open buffers.
+-- If `true`, performance may be slow when many buffers are open.
+-- The default value is `false`.
-- @field INDIC_BRACEMATCH (number)
-- The matching brace highlight indicator number.
-- @field INDIC_HIGHLIGHT (number)
@@ -38,6 +42,7 @@ M.HIGHLIGHT_BRACES = true
M.TYPEOVER_CHARS = true
M.AUTOINDENT = true
M.STRIP_TRAILING_SPACES = false
+M.AUTOCOMPLETE_ALL = false
M.INDIC_BRACEMATCH = _SCINTILLA.next_indic_number()
M.INDIC_HIGHLIGHT = _SCINTILLA.next_indic_number()
@@ -86,6 +91,17 @@ M.braces = {[40] = 1, [41] = 1, [91] = 1, [93] = 1, [123] = 1, [125] = 1}
-- @see TYPEOVER_CHARS
M.typeover_chars = {[41] = 1, [93] = 1, [125] = 1, [39] = 1, [34] = 1}
+---
+-- Map of autocompleter names to autocompletion functions.
+-- Autocompletion functions must return two values: the number of characters
+-- behind the caret that are used as the prefix of the entity to autocomplete,
+-- and a list of completions to show. Autocompletion lists are automatically
+-- sorted.
+-- @class table
+-- @name autocompleters
+-- @see autocomplete
+M.autocompleters = {}
+
-- Matches characters specified in char_matches.
events.connect(events.CHAR_ADDED, function(c)
if not M.AUTOPAIR then return end
@@ -202,65 +218,6 @@ function M.match_brace(select)
end
---
--- Displays an autocompletion list for the word behind the caret, returning
--- `true` if completions were found.
--- The displayed list is built from existing words in the buffer and the set of
--- words in string *words*.
--- @param words Optional list of words considered to be in the buffer,
--- even if they are not. Words may contain [registered images][].
---
--- [registered images]: buffer.html#register_image
--- @return `true` if there were completions to show; `false` otherwise.
--- @see buffer.word_chars
--- @name autocomplete_word
-function M.autocomplete_word(words)
- local buffer = buffer
- local pos, length = buffer.current_pos, buffer.length
- local completions, c_list = {}, {}
- local buffer_text = buffer:get_text()
- local root = buffer_text:sub(1, pos):match('['..buffer.word_chars..']+$')
- if not root or root == '' then return end
- for _, word in ipairs(words or {}) do
- if word:match('^'..root) then
- c_list[#c_list + 1], completions[word:match('^(.-)%??%d*$')] = word, true
- end
- end
- local patt = '^['..buffer.word_chars..']+'
- buffer.target_start, buffer.target_end = 0, buffer.length
- buffer.search_flags = buffer.FIND_WORDSTART
- if not buffer.auto_c_ignore_case then
- buffer.search_flags = buffer.search_flags + buffer.FIND_MATCHCASE
- end
- local match_pos = buffer:search_in_target(root)
- while match_pos ~= -1 do
- local s, e = buffer_text:find(patt, match_pos + 1)
- local match = buffer_text:sub(s, e)
- if not completions[match] and #match > #root then
- c_list[#c_list + 1], completions[match] = match, true
- end
- buffer.target_start, buffer.target_end = match_pos + 1, buffer.length
- match_pos = buffer:search_in_target(root)
- end
- if not buffer.auto_c_ignore_case then
- table.sort(c_list)
- else
- table.sort(c_list, function(a, b) return a:upper() < b:upper() end)
- end
- if #c_list > 0 then
- if not buffer.auto_c_choose_single or #c_list ~= 1 then
- buffer.auto_c_order = 0 -- pre-sorted
- buffer:auto_c_show(#root, table.concat(c_list, ' '))
- else
- -- Scintilla does not emit AUTO_C_SELECTION in this case. This is
- -- necessary for autocompletion with multiple selections.
- local text = c_list[1]:match('^(.-)%??%d*$')
- events.emit(events.AUTO_C_SELECTION, text, pos - #root)
- end
- return true
- end
-end
-
----
-- Comments or uncomments the selected lines based on the current language.
-- As long as any part of a line is selected, the entire line is eligible for
-- commenting/uncommenting.
@@ -562,4 +519,55 @@ function M.filter_through(command)
os.remove(tmpfile)
end
+---
+-- Displays an autocompletion list provided by the autocompleter function
+-- associated with string *name*, and returns `true` if completions were found.
+-- @param name The name of an autocompleter function in the `autocompleters`
+-- table to use for providing autocompletions.
+-- @name autocomplete
+-- @see autocompleters
+function M.autocomplete(name)
+ if not M.autocompleters[name] then return end
+ local len_entered, list = M.autocompleters[name]()
+ if not len_entered or not list or #list == 0 then return end
+ if not buffer.auto_c_choose_single or #list ~= 1 then
+ buffer.auto_c_order = buffer.ORDER_PERFORMSORT
+ buffer:auto_c_show(len_entered, table.concat(list, ' '))
+ else
+ -- Scintilla does not emit AUTO_C_SELECTION in this case. This is
+ -- necessary for autocompletion with multiple selections.
+ local text = list[1]:match('^(.-)%??%d*$')
+ events.emit(events.AUTO_C_SELECTION, text, buffer.current_pos - len_entered)
+ end
+ return true
+end
+
+-- Returns for the word behind the caret a list of completions constructed from
+-- the current buffer or all open buffers (depending on `M.AUTOCOMPLETE_ALL`).
+-- @see buffer.word_chars
+-- @see autocomplete
+M.autocompleters.word = function()
+ local list, ignore_case = {}, buffer.auto_c_ignore_case
+ local line, pos = buffer:get_cur_line()
+ local word_char = '['..buffer.word_chars:gsub('(%p)', '%%%1')..']'
+ local word = line:sub(1, pos):match(word_char..'*$')
+ if word == '' then return nil end
+ if ignore_case then word = word:lower() end
+ for i = 1, #_BUFFERS do
+ if _BUFFERS[i] == buffer or M.AUTOCOMPLETE_ALL then
+ local text = _BUFFERS[i]:get_text()
+ -- Frontier pattern (%f) is too slow, so check prior char after a match.
+ local patt = '()('..word:gsub('(%p)', '%%%1')..word_char..'+)'
+ local nonword_char = '^[^'..buffer.word_chars:gsub('(%p)', '%%%1')..']'
+ for i, word in (not ignore_case and text or text:lower()):gmatch(patt) do
+ if (i == 1 or text:find(nonword_char, i - 1)) and not list[word] then
+ list[#list + 1], list[word] = word, true
+ end
+ end
+ end
+ end
+ if #list == 0 then return nil end
+ return #word, list
+end
+
return M
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua
index e8315246..781d8860 100644
--- a/modules/textadept/keys.lua
+++ b/modules/textadept/keys.lua
@@ -261,7 +261,7 @@ M.utils = {
events.emit(events.UPDATE_UI) -- for updating statusbar
end,
set_encoding = function(encoding)
- io.set_buffer_encoding(encoding)
+ buffer:set_encoding(encoding)
events.emit(events.UPDATE_UI) -- for updating statusbar
end,
set_eol_mode = function(mode)
@@ -388,7 +388,7 @@ keys[not OSX and (not CURSES and 'adel' or 'mdel')
keys[not OSX and not CURSES and 'ca' or 'ma'] = buffer.select_all
keys[not CURSES and 'cm' or 'mm'] = editing.match_brace
keys[not OSX and (not CURSES and 'c\n' or 'cmj')
- or 'cesc'] = editing.autocomplete_word
+ or 'cesc'] = {editing.autocomplete, 'word'}
if CURSES and WIN32 then keys['c\r'] = keys['cmj'] end
if not CURSES then
keys[not OSX and 'caH' or 'mH'] = editing.highlight_word
diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua
index c6d5d5af..6dc3bc1b 100644
--- a/modules/textadept/menu.lua
+++ b/modules/textadept/menu.lua
@@ -47,7 +47,7 @@ local menubar = {
{_L['Select _All'], buffer.select_all},
SEPARATOR,
{_L['_Match Brace'], editing.match_brace},
- {_L['Complete _Word'], editing.autocomplete_word},
+ {_L['Complete _Word'], {editing.autocomplete, 'word'}},
{_L['_Highlight Word'], editing.highlight_word},
{_L['Toggle _Block Comment'], editing.block_comment},
{_L['T_ranspose Characters'], editing.transpose_chars},