-
-
Save Lexikos/bc17b8d55ae8a8102e35 to your computer and use it in GitHub Desktop.
-- AutoComplete v0.8 by Lexikos | |
--[[ | |
Tested on SciTE4AutoHotkey 3.0.06.01; may also work on SciTE 3.1.0 or later. | |
To use this script with SciTE4AutoHotkey: | |
- Place this file in your SciTE user settings folder. | |
- Add the following to UserLuaScript.lua: | |
dofile(props['SciteUserHome'].."/AutoComplete.lua") | |
- Restart SciTE. | |
]] | |
-- List of styles per lexer that autocomplete should not occur within. | |
local IGNORE_STYLES = { -- Should include comments, strings and errors. | |
[SCLEX_AHK1] = {1,2,3,6,20}, | |
[SCLEX_AHK2] = {1,2,3,5,15}, | |
[SCLEX_LUA] = {1,2,3,6,7,8,12} | |
} | |
local INCREMENTAL = true | |
local IGNORE_CASE = true | |
local CASE_CORRECT = true | |
local CASE_CORRECT_INSTANT = false | |
local WRAP_ARROW_KEYS = false | |
local CHOOSE_SINGLE = props["autocomplete.choose.single"] | |
-- Number of chars to type before the autocomplete list appears: | |
local MIN_PREFIX_LEN = 2 | |
-- Length of shortest word to add to the autocomplete list: | |
local MIN_IDENTIFIER_LEN = 2 | |
-- List of regex patterns for finding suggestions for the autocomplete menu: | |
local IDENTIFIER_PATTERNS = {"[a-z_][a-z_0-9]+"} | |
-- Override settings that interfere with this script: | |
props["autocomplete.ahk1.start.characters"] = "" | |
props["autocomplete.ahk2.start.characters"] = "" | |
-- This feature is very awkward when combined with automatic popups: | |
props["autocomplete.choose.single"] = "0" | |
local names = {} | |
local notempty = next | |
local shouldIgnorePos -- init'd by buildNames(). | |
local normalize | |
if IGNORE_CASE then | |
normalize = string.upper | |
else | |
normalize = function(word) return word end | |
end | |
local function setLexerSpecificStuff() | |
-- Disable collection of words in comments, strings, etc. | |
-- Also disables autocomplete popups while typing there. | |
if IGNORE_STYLES[editor.Lexer] then | |
-- Define a function for calling later: | |
shouldIgnorePos = function(pos) | |
return isInTable(IGNORE_STYLES[editor.Lexer], editor.StyleAt[pos]) | |
end | |
else | |
-- Optional: Disable autocomplete popups for unknown lexers. | |
shouldIgnorePos = function(pos) return true end | |
end | |
end | |
local apiCache = {} -- Names from api files, stored by lexer name. | |
local function getApiNames() | |
local lexer = editor.LexerLanguage | |
if apiCache[lexer] then | |
return apiCache[lexer] | |
end | |
local apiNames = {} | |
local apiFiles = props["APIPath"] or "" | |
apiFiles:gsub("[^;]+", function(apiFile) -- For each in ;-delimited list. | |
for name in io.lines(apiFile) do | |
name = name:gsub("[\(, ].*", "") -- Discard parameters/comments. | |
if string.len(name) > 0 then | |
apiNames[name] = true | |
end | |
end | |
return "" | |
end) | |
apiCache[lexer] = apiNames -- Even if it's empty. | |
return apiNames | |
end | |
local function buildNames() | |
setLexerSpecificStuff() | |
-- Reset our array of names. | |
names = {} | |
-- Collect all words matching the given patterns. | |
local unique = {} | |
for i, pattern in ipairs(IDENTIFIER_PATTERNS) do | |
local startPos, endPos | |
endPos = 0 | |
while true do | |
startPos, endPos = editor:findtext(pattern, SCFIND_REGEXP, endPos + 1) | |
if not startPos then | |
break | |
end | |
if not shouldIgnorePos(startPos) then | |
if endPos-startPos+1 >= MIN_IDENTIFIER_LEN then | |
-- Create one key-value pair per unique word: | |
local name = editor:textrange(startPos, endPos) | |
unique[normalize(name)] = name | |
end | |
end | |
end | |
end | |
-- Build an ordered array from the table of names. | |
for name in pairs(getApiNames()) do | |
-- This also "case-corrects"; e.g. "gui" -> "Gui". | |
unique[normalize(name)] = name | |
end | |
for _,name in pairs(unique) do | |
table.insert(names, name) | |
end | |
table.sort(names, function(a,b) return normalize(a) < normalize(b) end) | |
buffer.namesForAutoComplete = names -- Cache it for OnSwitchFile. | |
end | |
local lastAutoCItem = 0 -- Used by handleKey(). | |
local menuItems | |
local function handleChar(char, calledByHotkey) | |
local pos = editor.CurrentPos | |
local startPos = editor:WordStartPosition(pos, true) | |
local len = pos - startPos | |
if not INCREMENTAL and editor:AutoCActive() then | |
-- Nothing to do. | |
return | |
end | |
if len < MIN_PREFIX_LEN then | |
if editor:AutoCActive() then | |
if len == 0 then | |
-- Happens sometimes after typing ")". | |
editor:AutoCCancel() | |
return | |
end | |
-- Otherwise, autocomplete is already showing so may as well | |
-- keep it updated even though len < MIN_PREFIX_LEN. | |
else | |
if char then | |
-- Not enough text to trigger autocomplete, so return. | |
return | |
end | |
-- Otherwise, we were called explicitly without a param. | |
end | |
end | |
if not editor:AutoCActive() and shouldIgnorePos(startPos) and not calledByHotkey then | |
-- User is typing in a comment or string, so don't automatically | |
-- pop up the auto-complete window. | |
return | |
end | |
local prefix = normalize(editor:textrange(startPos, pos)) | |
menuItems = {} | |
for i, name in ipairs(names) do | |
local s = normalize(string.sub(name, 1, len)) | |
if s >= prefix then | |
if s == prefix then | |
table.insert(menuItems, name) | |
else | |
break -- There will be no more matches. | |
end | |
end | |
end | |
if notempty(menuItems) then | |
-- Show or update the auto-complete list. | |
local list = table.concat(menuItems, "\1") | |
editor.AutoCIgnoreCase = IGNORE_CASE | |
editor.AutoCCaseInsensitiveBehaviour = 1 -- Do NOT pre-select a case-sensitive match | |
editor.AutoCSeparator = 1 | |
editor.AutoCMaxHeight = 10 | |
editor:AutoCShow(len, list) | |
-- Check if we should auto-auto-complete. | |
if normalize(menuItems[1]) == prefix and not calledByHotkey then | |
-- User has completely typed the only item, so cancel. | |
if CASE_CORRECT then | |
if CASE_CORRECT_INSTANT or #menuItems == 1 then | |
-- Make sure the correct item is selected. | |
editor:AutoCShow(len, menuItems[1]) | |
editor:AutoCComplete() | |
end | |
if #menuItems > 1 then | |
editor:AutoCShow(len, list) | |
end | |
end | |
if #menuItems == 1 then | |
editor:AutoCCancel() | |
return | |
end | |
end | |
lastAutoCItem = #menuItems - 1 | |
if lastAutoCItem == 0 and calledByHotkey and CHOOSE_SINGLE then | |
editor:AutoCComplete() | |
end | |
else | |
-- No relevant items. | |
if editor:AutoCActive() then | |
editor:AutoCCancel() | |
end | |
end | |
end | |
local function handleKey(key, shift, ctrl, alt) | |
if key == 0x20 and ctrl and not (shift or alt) then -- ^Space | |
handleChar(nil, true) | |
return true | |
end | |
if alt or not editor:AutoCActive() then return end | |
if key == 0x8 then -- VK_BACK | |
if not ctrl then | |
-- Need to handle it here rather than relying on the default | |
-- processing, which would occur after handleChar() returns: | |
editor:DeleteBack() | |
handleChar() | |
return true | |
end | |
elseif key == 0x25 then -- VK_LEFT | |
if not shift then | |
if ctrl then | |
editor:WordLeft() -- See VK_BACK for comments. | |
else | |
editor:CharLeft() -- See VK_BACK for comments. | |
end | |
handleChar() | |
return true | |
end | |
elseif key == 0x26 then -- VK_UP | |
if editor.AutoCCurrent == 0 then | |
-- User pressed UP when already at the top of the list. | |
if WRAP_ARROW_KEYS then | |
-- Select the last item. | |
editor:AutoCSelect(menuItems[#menuItems]) | |
return true | |
end | |
-- Cancel the list and let the caret move up. | |
editor:AutoCCancel() | |
end | |
elseif key == 0x28 then -- VK_DOWN | |
if editor.AutoCCurrent == lastAutoCItem then | |
-- User pressed DOWN when already at the bottom of the list. | |
if WRAP_ARROW_KEYS then | |
-- Select the first item. | |
editor:AutoCSelect(menuItems[1]) | |
return true | |
end | |
-- Cancel the list and let the caret move down. | |
editor:AutoCCancel() | |
end | |
elseif key == 0x5A and ctrl then -- ^z | |
editor:AutoCCancel() | |
end | |
end | |
-- Event handlers | |
local events = { | |
OnChar = handleChar, | |
OnKey = handleKey, | |
OnSave = buildNames, | |
OnSwitchFile = function() | |
-- Use this file's cached list if possible: | |
names = buffer.namesForAutoComplete | |
if not names then | |
-- Otherwise, build a new list. | |
buildNames() | |
else | |
setLexerSpecificStuff() | |
end | |
end, | |
OnOpen = function() | |
-- Ensure the document is styled first, so we can filter out | |
-- words in comments and strings. | |
editor:Colourise(0, editor.Length) | |
-- Then do the real work. | |
buildNames() | |
end | |
} | |
-- Add event handlers in a cooperative fashion: | |
for evt, func in pairs(events) do | |
local oldfunc = _G[evt] | |
if oldfunc then | |
_G[evt] = function(...) return func(...) or oldfunc(...) end | |
else | |
_G[evt] = func | |
end | |
end |
@telppa , thank you so much, i fixed it.
Can you do me one more favor please? How can i install this function for my SciTE4AutoHotkey v3.1.0
This is a complicated issue.
Because it involves a lot of files named in Chinese and I don't know how to tell you which one is you need, later I will try to merge them with scite 3.1.0. Maybe in two months.
@telppa
Oh. I will wait for that. It's a very cool feature for SciTE 3.1.0 since SciTE 3.1.0 has removed Calltip functionality.
Thank you a lot.
@telppa
It sounds like you've made some great improvements, but I think posting it entirely within a comment on this gist was the wrong way to share it. Perhaps you can edit your comment to replace the code with a link?
I created an updated version that fixes bugs,
Which bugs?
retrieves unicode words (including Chinese, Japanese, Korean, etc.),
How are words delineated in those languages? Did it not work before because of some issue with handling Unicode, or are the words not space-delimited?
and automatically retrieves new words.
Do you mean while they are typed, as opposed to when the file is saved?
In Lexikos -- AutoComplete v0.8 by Lexikos,
In Scite4AutoHotkey Version 3.1.00, I get an error message for line 80, which reads:
AutoComplete.lua:80: invalid escape sequence near '"[('
Line 80 in the original Lexikos code reads:
name = name:gsub("[(, ].*", "") -- Discard parameters/comments.
In Lexicos code, on line 80, the ( characters are highlighted with a red frame.
How to fix this error?
THANKS.
- `local function getApiNames()
-
local lexer = editor.LexerLanguage
-
if apiCache[lexer] then
-
return apiCache[lexer]
-
end
-
local apiNames = {}
-
local apiFiles = props["APIPath"] or ""
-
apiFiles:gsub("[^;]+", function(apiFile) -- For each in ;-delimited list.
-
for name in io.lines(apiFile) do
-
name = name:gsub("[\(, ].*", "") -- Discard parameters/comments.
-
if string.len(name) > 0 then
-
apiNames[name] = true
-
end
-
end
-
return ""
-
end)
-
apiCache[lexer] = apiNames -- Even if it's empty.
-
return apiNames
- end`
@thaihoa89
if you use SciTE4AutoHotkey v3.1.0
try download
ahk.api
ahk.lua
AutoCompletePlus.lua
ahk.api overwrite SciTE\ahk.api
ahk.lua overwrite SciTE\ahk.lua
AutoCompletePlus.lua overwrite SciTE\AutoCompletePlus.lua
then
ahk.lua comment line 677 678 681 682
ahk.lua line 679 -> dofile(props['SciteDefaultHome'].."/AutoCompletePlus.lua")
AutoCompletePlus.lua line 166 -> name = name:gsub("[(, ].*", "")
the other way is use https://github.com/telppa/SciTE4AutoHotkey-Plus
but SciTE4AutoHotkey-Plus only have chinese now.