diff options
author | Lei Zhu <uhziel@gmail.com> | 2021-01-05 16:53:49 +0800 |
---|---|---|
committer | Lei Zhu <uhziel@gmail.com> | 2021-01-05 16:53:49 +0800 |
commit | eb87cc84f2475911c2086da982902c75f3bfdd3a (patch) | |
tree | 6737c8f955390596314055cd79a6e7eb85688b82 /script/core | |
parent | 39bbefd8b4c98be50955b2484af1a6b6f9541f54 (diff) | |
parent | 525f551abc533b704906fd3a5cb84404fb5cf4de (diff) | |
download | lua-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.lua | 218 | ||||
-rw-r--r-- | script/core/diagnostics/redundant-parameter.lua | 56 | ||||
-rw-r--r-- | script/core/diagnostics/undefined-field.lua | 17 | ||||
-rw-r--r-- | script/core/hover/table.lua | 5 | ||||
-rw-r--r-- | script/core/rename.lua | 2 | ||||
-rw-r--r-- | script/core/semantic-tokens.lua | 10 |
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) |