Cool, thanks for the script.
I tried to make a small change so pressing ctrl+space in comments lets you use the auto complete then only, but for some reason it only seems to work for the lua language, and not in ahk scripts. In ahk the auto-complete closes as soon as you type.
All I did was added module var
explicitAutoC which is set true when ^Space is pressed and false whenever
editor:AutoCCancel() is called, and used it to force
shouldIgnorePos() result to not ignore comments after ^Space. Or that was what I intended anyway, but I don't know lua, or much about scite either. Changes are highlighted red below.
-- v0.1
local SCLEX_AHK1 = 200
local IGNORE_STYLES = { -- comment or string styles
[SCLEX_AHK1] = {1,2,6,20},
[SCLEX_LUA] = {1,2,3,6,7,8,12} -- comments, strings or error
}
local INCREMENTAL = true
local IGNORE_CASE = true
-- 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]+"}
local names = {}
local notempty = next
local shouldIgnorePos -- init'd by buildNames().
[color=red]local explicitAutoC = false -- set when auto complete is explicitly shown by pressing ctrl+space[/color]
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])[color=red] and not explicitAutoC[/color]
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:GetLexerLanguage()
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:
unique[editor] = true
end
end
end
end
-- Build an ordered array from the table of names.
for name in pairs(getApiNames()) do
unique[name] = true
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 function handleChar(char)
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()[color=red]
explicitAutoC = false[/color]
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) 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))
local 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
if #menuItems == 1 and normalize(menuItems[1]) == prefix then
-- User has completely typed the only item, so cancel.
editor:AutoCCancel()[color=red]
explicitAutoC = false[/color]
else
-- Show or update the auto-complete list.
editor.AutoCIgnoreCase = IGNORE_CASE
editor.AutoCSeparator = 1
editor:AutoCShow(len, table.concat(menuItems, "\1"))
lastAutoCItem = #menuItems - 1
end
else
-- No relevant items.
if editor:AutoCActive() then
editor:AutoCCancel()[color=red]
explicitAutoC = false[/color]
end
end
end
local function handleKey(key, shift, ctrl, alt)
if key == 0x20 and ctrl and not (shift or alt) then -- ^Space[color=red]
explicitAutoC = true -- auto complete was explicitly displayed[/color]
handleChar()
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:AutoCGetCurrent() == 0 then
-- User is probably trying to move the caret, since they're
-- already at the top of the list. So cancel the list:
editor:AutoCCancel()[color=red]
explicitAutoC = false[/color]
end
elseif key == 0x28 then -- VK_DOWN
if editor:AutoCGetCurrent() == lastAutoCItem then
-- As above, but for the bottom of the list.
editor:AutoCCancel()[color=red]
explicitAutoC = false[/color]
end
elseif key == 0x5A and ctrl then -- ^z
editor:AutoCCancel()[color=red]
explicitAutoC = false[/color]
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
Anyone got any ideas why it's behaving this way?
P.S I was not sure if I should post this in ask for help or here.