summaryrefslogtreecommitdiff
path: root/script/core
diff options
context:
space:
mode:
authorLei Zhu <uhziel@gmail.com>2021-01-05 16:53:49 +0800
committerLei Zhu <uhziel@gmail.com>2021-01-05 16:53:49 +0800
commiteb87cc84f2475911c2086da982902c75f3bfdd3a (patch)
tree6737c8f955390596314055cd79a6e7eb85688b82 /script/core
parent39bbefd8b4c98be50955b2484af1a6b6f9541f54 (diff)
parent525f551abc533b704906fd3a5cb84404fb5cf4de (diff)
downloadlua-language-server-eb87cc84f2475911c2086da982902c75f3bfdd3a.zip
Merge branch 'doc-type-table' of https://github.com/uhziel/lua-language-server into doc-type-table
Diffstat (limited to 'script/core')
-rw-r--r--script/core/completion.lua218
-rw-r--r--script/core/diagnostics/redundant-parameter.lua56
-rw-r--r--script/core/diagnostics/undefined-field.lua17
-rw-r--r--script/core/hover/table.lua5
-rw-r--r--script/core/rename.lua2
-rw-r--r--script/core/semantic-tokens.lua10
6 files changed, 253 insertions, 55 deletions
diff --git a/script/core/completion.lua b/script/core/completion.lua
index b1610ff0..ba848272 100644
--- a/script/core/completion.lua
+++ b/script/core/completion.lua
@@ -592,7 +592,8 @@ local function checkTableField(ast, word, start, results)
end)
end
-local function checkCommon(word, text, offset, results)
+local function checkCommon(ast, word, text, offset, results)
+ local myUri = ast.uri
local used = {}
for _, result in ipairs(results) do
used[result.label] = true
@@ -600,14 +601,56 @@ local function checkCommon(word, text, offset, results)
for _, data in ipairs(keyWordMap) do
used[data[1]] = true
end
- for str, pos in text:gmatch '([%a_][%w_]*)()' do
- if not used[str] and pos - 1 ~= offset then
- used[str] = true
- if matchKey(word, str) then
- results[#results+1] = {
- label = str,
- kind = define.CompletionItemKind.Text,
- }
+ if config.config.completion.workspaceWord then
+ for uri in files.eachFile() do
+ local cache = files.getCache(uri)
+ if not cache.commonWords then
+ cache.commonWords = {}
+ local mark = {}
+ for str in files.getText(uri):gmatch '([%a_][%w_]*)' do
+ if not mark[str] then
+ mark[str] = true
+ cache.commonWords[#cache.commonWords+1] = str
+ end
+ end
+ end
+ for _, str in ipairs(cache.commonWords) do
+ if not used[str]
+ and (str ~= word or not files.eq(myUri, uri)) then
+ used[str] = true
+ if matchKey(word, str) then
+ results[#results+1] = {
+ label = str,
+ kind = define.CompletionItemKind.Text,
+ }
+ end
+ end
+ end
+ end
+ for uri in files.eachDll() do
+ local words = files.getDllWords(uri) or {}
+ for _, str in ipairs(words) do
+ if not used[str] and str ~= word then
+ used[str] = true
+ if matchKey(word, str) then
+ results[#results+1] = {
+ label = str,
+ kind = define.CompletionItemKind.Text,
+ }
+ end
+ end
+ end
+ end
+ else
+ for str, pos in text:gmatch '([%a_][%w_]*)()' do
+ if not used[str] and pos - 1 ~= offset then
+ used[str] = true
+ if matchKey(word, str) then
+ results[#results+1] = {
+ label = str,
+ kind = define.CompletionItemKind.Text,
+ }
+ end
end
end
end
@@ -821,6 +864,27 @@ local function checkUri(ast, text, offset, results)
end
::CONTINUE::
end
+ for uri in files.eachDll() do
+ local opens = files.getDllOpens(uri) or {}
+ local path = workspace.getRelativePath(uri)
+ for _, open in ipairs(opens) do
+ if matchKey(literal, open) then
+ if not collect[open] then
+ collect[open] = {
+ textEdit = {
+ start = source.start + #source[2],
+ finish = source.finish - #source[2],
+ newText = open,
+ }
+ }
+ end
+ collect[open][#collect[open]+1] = ([=[* [%s](%s)]=]):format(
+ path,
+ uri
+ )
+ end
+ end
+ end
elseif libName == 'dofile'
or libName == 'loadfile' then
for uri in files.eachFile() do
@@ -970,6 +1034,9 @@ local function checkTypingEnum(ast, text, offset, infers, str, results)
end
local myResults = {}
mergeEnums(myResults, enums, str)
+ table.sort(myResults, function (a, b)
+ return a.label < b.label
+ end)
for _, res in ipairs(myResults) do
results[#results+1] = res
end
@@ -1119,7 +1186,7 @@ local function tryWord(ast, text, offset, results)
end
end
if not hasSpace then
- checkCommon(word, text, offset, results)
+ checkCommon(ast, word, text, offset, results)
end
end
end
@@ -1266,6 +1333,15 @@ local function getComment(ast, offset)
return nil
end
+local function getLuaDoc(ast, offset)
+ for _, doc in ipairs(ast.ast.docs) do
+ if offset >= doc.start and offset <= doc.range then
+ return doc
+ end
+ end
+ return nil
+end
+
local function tryLuaDocCate(line, results)
local word = line:sub(3)
for _, docType in ipairs {
@@ -1347,6 +1423,7 @@ local function tryLuaDocBySource(ast, offset, source, results)
end
end
end
+ return true
elseif source.type == 'doc.type.name' then
for _, doc in ipairs(vm.getDocTypes '*') do
if (doc.type == 'doc.class.name' or doc.type == 'doc.alias.name')
@@ -1358,6 +1435,7 @@ local function tryLuaDocBySource(ast, offset, source, results)
}
end
end
+ return true
elseif source.type == 'doc.param.name' then
local funcs = {}
guide.eachSourceBetween(ast.ast, offset, math.huge, function (src)
@@ -1380,7 +1458,9 @@ local function tryLuaDocBySource(ast, offset, source, results)
}
end
end
+ return true
end
+ return false
end
local function tryLuaDocByErr(ast, offset, err, docState, results)
@@ -1446,40 +1526,121 @@ local function tryLuaDocByErr(ast, offset, err, docState, results)
end
end
-local function tryLuaDocFeatures(line, ast, comm, offset, results)
+local function buildLuaDocOfFunction(func)
+ local index = 1
+ local buf = {}
+ buf[#buf+1] = '${1:comment}'
+ local args = {}
+ local returns = {}
+ if func.args then
+ for _, arg in ipairs(func.args) do
+ args[#args+1] = vm.getInferType(arg)
+ end
+ end
+ if func.returns then
+ for _, rtns in ipairs(func.returns) do
+ for n = 1, #rtns do
+ if not returns[n] then
+ returns[n] = vm.getInferType(rtns[n])
+ end
+ end
+ end
+ end
+ for n, arg in ipairs(args) do
+ index = index + 1
+ buf[#buf+1] = ('---@param %s ${%d:%s}'):format(
+ func.args[n][1],
+ index,
+ arg
+ )
+ end
+ for _, rtn in ipairs(returns) do
+ index = index + 1
+ buf[#buf+1] = ('---@return ${%d:%s}'):format(
+ index,
+ rtn
+ )
+ end
+ local insertText = table.concat(buf, '\n')
+ return insertText
end
-local function tryLuaDoc(ast, text, offset, results)
- local comm = getComment(ast, offset)
- local line = text:sub(comm.start, offset)
- if not line then
+local function tryLuaDocOfFunction(doc, results)
+ if not doc.bindSources then
return
end
- if line:sub(1, 2) ~= '-@' then
- return
+ local func
+ for _, source in ipairs(doc.bindSources) do
+ if source.type == 'function' then
+ func = source
+ break
+ end
end
- -- 尝试 ---@$
- local cate = line:match('%a*', 3)
- if #cate + 2 >= #line then
- tryLuaDocCate(line, results)
+ if not func then
return
end
- -- 尝试一些其他特征
- if tryLuaDocFeatures(line, ast, comm, offset, results) then
+ for _, otherDoc in ipairs(doc.bindGroup) do
+ if otherDoc.type == 'doc.param'
+ or otherDoc.type == 'doc.return' then
+ return
+ end
+ end
+ local insertText = buildLuaDocOfFunction(func)
+ results[#results+1] = {
+ label = '@param;@return',
+ kind = define.CompletionItemKind.Snippet,
+ insertTextFormat = 2,
+ filterText = '---',
+ insertText = insertText
+ }
+end
+
+local function tryLuaDoc(ast, text, offset, results)
+ local doc = getLuaDoc(ast, offset)
+ if not doc then
return
end
+ if doc.type == 'doc.comment' then
+ local line = text:sub(doc.start, doc.range)
+ -- 尝试 ---$
+ if line == '-' then
+ tryLuaDocOfFunction(doc, results)
+ return
+ end
+ -- 尝试 ---@$
+ local cate = line:match('^-@(%a*)$')
+ if cate then
+ tryLuaDocCate(line, results)
+ return
+ end
+ end
-- 根据输入中的source来补全
local source = getLuaDocByContain(ast, offset)
if source then
- tryLuaDocBySource(ast, offset, source, results)
- return
+ local suc = tryLuaDocBySource(ast, offset, source, results)
+ if suc then
+ return
+ end
end
-- 根据附近的错误消息来补全
- local err, doc = getLuaDocByErr(ast, text, comm.start, offset)
+ local err, expectDoc = getLuaDocByErr(ast, text, doc.start, offset)
if err then
- tryLuaDocByErr(ast, offset, err, doc, results)
+ tryLuaDocByErr(ast, offset, err, expectDoc, results)
+ return
+ end
+end
+
+local function tryComment(ast, text, offset, results)
+ local word = findWord(text, offset)
+ local doc = getLuaDoc(ast, offset)
+ local line = text:sub(doc.start, offset)
+ if not word then
+ return
+ end
+ if doc and doc.type ~= 'doc.comment' then
return
end
+ checkCommon(ast, word, text, offset, results)
end
local function completion(uri, offset)
@@ -1490,6 +1651,7 @@ local function completion(uri, offset)
if ast then
if getComment(ast, offset) then
tryLuaDoc(ast, text, offset, results)
+ tryComment(ast, text, offset, results)
else
trySpecial(ast, text, offset, results)
tryWord(ast, text, offset, results)
@@ -1500,7 +1662,7 @@ local function completion(uri, offset)
else
local word = findWord(text, offset)
if word then
- checkCommon(word, text, offset, results)
+ checkCommon(ast, word, text, offset, results)
end
end
diff --git a/script/core/diagnostics/redundant-parameter.lua b/script/core/diagnostics/redundant-parameter.lua
index ac52ab2a..f7b0ae75 100644
--- a/script/core/diagnostics/redundant-parameter.lua
+++ b/script/core/diagnostics/redundant-parameter.lua
@@ -48,12 +48,41 @@ local function countOverLoadArgs(source, doc)
return result
end
+local function getFuncArgs(func)
+ local funcArgs
+ local defs = vm.getDefs(func)
+ for _, def in ipairs(defs) do
+ if def.value then
+ def = def.value
+ end
+ if def.type == 'function' then
+ local args = countFuncArgs(def)
+ if not funcArgs or args > funcArgs then
+ funcArgs = args
+ end
+ if def.bindDocs then
+ for _, doc in ipairs(def.bindDocs) do
+ if doc.type == 'doc.overload' then
+ args = countOverLoadArgs(def, doc)
+ if not funcArgs or args > funcArgs then
+ funcArgs = args
+ end
+ end
+ end
+ end
+ end
+ end
+ return funcArgs
+end
+
return function (uri, callback)
local ast = files.getAst(uri)
if not ast then
return
end
+ local cache = vm.getCache 'redundant-parameter'
+
guide.eachSourceType(ast.ast, 'call', function (source)
local callArgs = countCallArgs(source)
if callArgs == 0 then
@@ -61,27 +90,12 @@ return function (uri, callback)
end
local func = source.node
- local funcArgs
- local defs = vm.getDefs(func)
- for _, def in ipairs(defs) do
- if def.value then
- def = def.value
- end
- if def.type == 'function' then
- local args = countFuncArgs(def)
- if not funcArgs or args > funcArgs then
- funcArgs = args
- end
- if def.bindDocs then
- for _, doc in ipairs(def.bindDocs) do
- if doc.type == 'doc.overload' then
- args = countOverLoadArgs(def, doc)
- if not funcArgs or args > funcArgs then
- funcArgs = args
- end
- end
- end
- end
+ local funcArgs = cache[func]
+ if funcArgs == nil then
+ funcArgs = getFuncArgs(func) or false
+ local refs = vm.getRefs(func, 0)
+ for _, ref in ipairs(refs) do
+ cache[ref] = funcArgs
end
end
diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index 31bd9008..ffa70364 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -11,10 +11,19 @@ return function (uri, callback)
return
end
+ local cache = vm.getCache 'undefined-field'
+
local function getAllDocClassFromInfer(src)
- tracy.ZoneBeginN('undefined-field getInfers')
- local infers = vm.getInfers(src, 0)
- tracy.ZoneEnd()
+ local infers = cache[src]
+ if cache[src] == nil then
+ tracy.ZoneBeginN('undefined-field getInfers')
+ infers = vm.getInfers(src, 0) or false
+ local refs = vm.getRefs(src, 0)
+ for _, ref in ipairs(refs) do
+ cache[ref] = infers
+ end
+ tracy.ZoneEnd()
+ end
if not infers then
return nil
@@ -55,7 +64,9 @@ return function (uri, callback)
local fields = {}
local empty = true
for _, docClass in ipairs(allDocClass) do
+ tracy.ZoneBeginN('undefined-field getDefFields')
local refs = vm.getDefFields(docClass)
+ tracy.ZoneEnd()
for _, ref in ipairs(refs) do
local name = vm.getKeyName(ref)
diff --git a/script/core/hover/table.lua b/script/core/hover/table.lua
index 9fd79a25..58e64951 100644
--- a/script/core/hover/table.lua
+++ b/script/core/hover/table.lua
@@ -31,9 +31,10 @@ local function getKey(src)
end
local function getFieldFull(src)
- local tp = vm.getInferType(src)
+ local value = guide.getObjectValue(src) or src
+ local tp = vm.getInferType(value, 0)
--local class = vm.getClass(src)
- local literal = vm.getInferLiteral(src)
+ local literal = vm.getInferLiteral(value)
if type(literal) == 'string' and #literal >= 50 then
literal = literal:sub(1, 47) .. '...'
end
diff --git a/script/core/rename.lua b/script/core/rename.lua
index b823cb86..3f73c338 100644
--- a/script/core/rename.lua
+++ b/script/core/rename.lua
@@ -292,7 +292,7 @@ local function ofField(source, newname, callback)
else
node = source.node
end
- for _, src in ipairs(vm.getFields(node, 0)) do
+ for _, src in ipairs(vm.getFields(node, 5)) do
ofFieldThen(key, src, newname, callback)
end
end
diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua
index 37eabda7..1c59d80d 100644
--- a/script/core/semantic-tokens.lua
+++ b/script/core/semantic-tokens.lua
@@ -187,6 +187,16 @@ return function (uri, start, finish)
end
end)
+ for _, comm in ipairs(ast.comms) do
+ if comm.semantic then
+ results[#results+1] = {
+ start = comm.start,
+ finish = comm.finish,
+ type = define.TokenTypes.comment,
+ }
+ end
+ end
+
table.sort(results, function (a, b)
return a.start < b.start
end)