summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2019-12-09 20:28:18 +0800
committer最萌小汐 <sumneko@hotmail.com>2019-12-09 20:28:18 +0800
commitd803249a1aedafad9b08349ed16c941bf4c3ba4e (patch)
treee25c274c1c1f16eddefa3ca572202f7536c6b7d8
parent9e193c1d17d64137191c5aa9e4f39c18a795396f (diff)
downloadlua-language-server-d803249a1aedafad9b08349ed16c941bf4c3ba4e.zip
自动完成搜索局部变量
-rw-r--r--script-beta/core/completion.lua51
-rw-r--r--script-beta/core/matchkey.lua30
-rw-r--r--script-beta/parser/guide.lua33
-rw-r--r--test-beta.lua2
-rw-r--r--test-beta/completion/init.lua2
5 files changed, 116 insertions, 2 deletions
diff --git a/script-beta/core/completion.lua b/script-beta/core/completion.lua
index e69de29b..d3a7a022 100644
--- a/script-beta/core/completion.lua
+++ b/script-beta/core/completion.lua
@@ -0,0 +1,51 @@
+local ckind = require 'define.CompletionItemKind'
+local files = require 'files'
+local guide = require 'parser.guide'
+local matchKey = require 'core.matchKey'
+
+local function findWord(text, offset)
+ for i = offset, 1, -1 do
+ if not text:sub(i, i):match '[%w_]' then
+ if i == offset then
+ return nil
+ end
+ return text:sub(i+1, offset)
+ end
+ end
+ return nil
+end
+
+local function checkLocal(ast, word, offset, results)
+ guide.getVisibleLocalNames(ast.ast, offset, function (name)
+ if matchKey(word, name) then
+ results[#results+1] = {
+ label = name,
+ kind = ckind.Variable,
+ }
+ end
+ end)
+end
+
+local function tryWord(ast, text, offset, results)
+ local word = findWord(text, offset)
+ if not word then
+ return nil
+ end
+ checkLocal(ast, word, offset, results)
+end
+
+return function (uri, offset)
+ local ast = files.getAst(uri)
+ if not ast then
+ return nil
+ end
+ local text = files.getText(uri)
+ local results = {}
+
+ tryWord(ast, text, offset, results)
+
+ if #results == 0 then
+ return nil
+ end
+ return results
+end
diff --git a/script-beta/core/matchkey.lua b/script-beta/core/matchkey.lua
new file mode 100644
index 00000000..b46250cb
--- /dev/null
+++ b/script-beta/core/matchkey.lua
@@ -0,0 +1,30 @@
+return function (me, other)
+ if me == other then
+ return true
+ end
+ if me == '' then
+ return true
+ end
+ if #me > #other then
+ return false
+ end
+ local lMe = me:lower()
+ local lOther = other:lower()
+ if lMe == lOther:sub(1, #lMe) then
+ return true
+ end
+ local chars = {}
+ for i = 1, #lOther do
+ local c = lOther:sub(i, i)
+ chars[c] = (chars[c] or 0) + 1
+ end
+ for i = 1, #lMe do
+ local c = lMe:sub(i, i)
+ if chars[c] and chars[c] > 0 then
+ chars[c] = chars[c] - 1
+ else
+ return false
+ end
+ end
+ return true
+end
diff --git a/script-beta/parser/guide.lua b/script-beta/parser/guide.lua
index 2e26a3da..4ccfd23d 100644
--- a/script-beta/parser/guide.lua
+++ b/script-beta/parser/guide.lua
@@ -235,6 +235,39 @@ function m.getLocal(block, name, pos)
error('guide.getLocal overstack')
end
+--- 获取指定区块中所有的可见局部变量名称
+function m.getVisibleLocalNames(block, pos, callback)
+ block = m.getBlock(block)
+ local used = {}
+ for _ = 1, 1000 do
+ if not block then
+ return nil
+ end
+ local locals = block.locals
+ local res
+ if not locals then
+ goto CONTINUE
+ end
+ for i = 1, #locals do
+ local loc = locals[i]
+ if loc.effect > pos then
+ break
+ end
+ local name = loc[1]
+ if not used[name] then
+ used[name] = true
+ callback(name)
+ end
+ end
+ if res then
+ return res, res
+ end
+ ::CONTINUE::
+ block = m.getParentBlock(block)
+ end
+ error('guide.getLocal overstack')
+end
+
--- 获取指定区块中可见的标签
---@param block table
---@param name string {comment = '标签名'}
diff --git a/test-beta.lua b/test-beta.lua
index cf9f9ea3..ccd5c681 100644
--- a/test-beta.lua
+++ b/test-beta.lua
@@ -43,7 +43,7 @@ local function main()
test 'rename'
test 'type_inference'
test 'hover'
- --test 'completion'
+ test 'completion'
--test 'signature'
--test 'document_symbol'
test 'crossfile'
diff --git a/test-beta/completion/init.lua b/test-beta/completion/init.lua
index 18920704..e834e73a 100644
--- a/test-beta/completion/init.lua
+++ b/test-beta/completion/init.lua
@@ -35,7 +35,7 @@ rawset(_G, 'TEST', true)
function TEST(script)
return function (expect)
files.removeAll()
- local pos = script:find('$', 1, true)
+ local pos = script:find('$', 1, true) - 1
local new_script = script:gsub('%$', '')
files.setText('', new_script)