summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json11
-rw-r--r--.vscode/tasks.json2
m---------3rd/bee.lua0
m---------3rd/luamake0
-rw-r--r--changelog.md32
-rw-r--r--locale/en-us/script.lua3
-rw-r--r--locale/zh-cn/script.lua5
-rw-r--r--main.lua4
-rw-r--r--meta/template/package.lua2
-rw-r--r--script/config.lua19
-rw-r--r--script/core/code-action.lua9
-rw-r--r--script/core/completion.lua8
-rw-r--r--script/core/diagnostics/close-non-object.lua40
-rw-r--r--script/core/diagnostics/init.lua8
-rw-r--r--script/core/diagnostics/redundant-parameter.lua28
-rw-r--r--script/core/diagnostics/undefined-field.lua10
-rw-r--r--script/core/diagnostics/unused-function.lua24
-rw-r--r--script/core/document-symbol.lua5
-rw-r--r--script/core/hover/description.lua2
-rw-r--r--script/core/keyword.lua140
-rw-r--r--script/core/rename.lua9
-rw-r--r--script/core/semantic-tokens.lua15
-rw-r--r--script/core/signature.lua10
-rw-r--r--script/files.lua11
-rw-r--r--script/glob/gitignore.lua9
-rw-r--r--script/parser/ast.lua193
-rw-r--r--script/parser/grammar.lua41
-rw-r--r--script/parser/guide.lua568
-rw-r--r--script/parser/luadoc.lua42
-rw-r--r--script/proto/define.lua2
-rw-r--r--script/provider/diagnostic.lua3
-rw-r--r--script/provider/provider.lua10
-rw-r--r--script/service/service.lua12
-rw-r--r--script/service/telemetry.lua39
-rw-r--r--script/timer.lua4
-rw-r--r--script/tracy.lua30
-rw-r--r--script/utility.lua16
-rw-r--r--script/vm/eachDef.lua3
-rw-r--r--script/vm/eachField.lua60
-rw-r--r--script/vm/eachRef.lua3
-rw-r--r--script/vm/getDocs.lua3
-rw-r--r--script/vm/guideInterface.lua2
-rw-r--r--script/workspace/require-path.lua2
-rw-r--r--script/workspace/workspace.lua79
-rw-r--r--test.lua4
-rw-r--r--test/completion/init.lua24
-rw-r--r--test/crossfile/definition.lua25
-rw-r--r--test/definition/bug.lua23
-rw-r--r--test/definition/luadoc.lua84
-rw-r--r--test/diagnostics/init.lua37
-rw-r--r--test/full/example.lua3
-rw-r--r--test/full/init.lua1
-rw-r--r--test/full/self.lua29
-rw-r--r--test/signature/init.lua16
-rw-r--r--test/???.md94
55 files changed, 1269 insertions, 589 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 92e6ec8e..54ad1616 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -11,9 +11,11 @@
"ID",
"DEVELOP",
"DBGPORT",
- "DBGWAIT"
+ "DBGWAIT",
+ "tracy"
],
"Lua.diagnostics.disable": [
+ "close-non-object"
],
"Lua.runtime.version": "Lua 5.4",
// Just some comment
@@ -35,5 +37,10 @@
"Lua.awakened.cat": true,
"Lua.develop.enable": true,
"Lua.develop.debuggerPort": 11413,
- "Lua.intelliSense.searchDepth": 0
+ "Lua.intelliSense.searchDepth": 0,
+ "Lua.runtime.path": [
+ "?.lua",
+ "script/?.lua",
+ "script/?/init.lua"
+ ]
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 4771a2ef..dca20fb7 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -35,7 +35,7 @@
"type": "shell",
"label": "Compile",
"windows": {
- "command": "chcp 65001 && ${workspaceFolder}/3rd/luamake/luamake rebuild.bat"
+ "command": "chcp 65001 && ${workspaceFolder}/3rd/luamake/luamake rebuild"
},
"linux": {
"command": "${workspaceFolder}/3rd/luamake/luamake rebuild"
diff --git a/3rd/bee.lua b/3rd/bee.lua
-Subproject bb6094a71d3cd41f0af22704cc1905330d4cc1f
+Subproject 5a88a0aa0a9dc83fb60e913730f1de864438907
diff --git a/3rd/luamake b/3rd/luamake
-Subproject 447d5165d59fcb6470ee2c69468a4549c544346
+Subproject aa20cd1b07006bc50e4a2cc09b5f30134c4a75a
diff --git a/changelog.md b/changelog.md
index 605a7ea3..76681f22 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,6 +1,38 @@
# changelog
+## 1.8.1
+* `FIX` telemetry: connect failed caused not working
+
+## 1.8.0
+`2020-12-23`
+* `NEW` runtime: support nonstandard symbol
+* `NEW` diagnostic: `close-non-object`
+* `FIX` [#318](https://github.com/sumneko/lua-language-server/issues/318)
+
+## 1.7.4
+`2020-12-20`
+* `FIX` workspace: preload may failed
+
+## 1.7.3
+`2020-12-20`
+* `FIX` luadoc: typo of `package.config`
+* `FIX` [#310](https://github.com/sumneko/lua-language-server/issues/310)
+
+## 1.7.2
+`2020-12-17`
+* `CHG` completion: use custom tabsize
+* `FIX` [#307](https://github.com/sumneko/lua-language-server/issues/307)
+* `FIX` a lot of runtime errors
+
+## 1.7.1
+`2020-12-16`
+* `NEW` setting: `diagnostics.neededFileStatus`
+* `FIX` scan workspace may fails
+* `FIX` quickfix: `newline-call` failed
+* `FIX` a lot of other runtime errors
+
## 1.7.0
+`2020-12-16`
* `NEW` diagnostic: `undefined-field`
* `NEW` telemetry:
+ [What data will be sent](https://github.com/sumneko/lua-language-server/blob/master/script/service/telemetry.lua)
diff --git a/locale/en-us/script.lua b/locale/en-us/script.lua
index 20c24c90..1e6f3c03 100644
--- a/locale/en-us/script.lua
+++ b/locale/en-us/script.lua
@@ -37,6 +37,7 @@ DIAG_SET_FOR_STATE = 'Assignment to for-state variable.'
DIAG_CODE_AFTER_BREAK = 'Unable to execute code after `break`.'
DIAG_UNBALANCED_ASSIGNMENTS = 'The value is assigned as `nil` because the number of values is not enough. In Lua, `x, y = 1 ` is equivalent to `x, y = 1, nil` .'
DIAG_REQUIRE_LIKE = 'You can treat `{}` as `require` by setting.'
+DIAG_COSE_NON_OBJECT = 'Cannot close a value of this type. (Unless set `__close` meta method)'
DIAG_CIRCLE_DOC_CLASS = 'Circularly inherited classes.'
DIAG_DOC_FIELD_NO_CLASS = 'The field must be defined after the class.'
@@ -99,6 +100,7 @@ PARSER_ERR_COMMENT_PREFIX = 'Lua should use `--` for annotations.'
PARSER_MISS_SEP_IN_TABLE = 'Miss symbol `,` or `;` .'
PARSER_SET_CONST = 'Assignment to const variable.'
PARSER_UNICODE_NAME = 'Contains Unicode characters.'
+PARSER_ERR_NONSTANDARD_SYMBOL = 'Lua should use `{symbol}` .'
PARSER_LUADOC_MISS_CLASS_NAME = '<class name> expected.'
PARSER_LUADOC_MISS_EXTENDS_SYMBOL = '`:` expected.'
@@ -165,6 +167,7 @@ ACTION_FIX_THEN_AS_DO = 'Modify to `then` .'
ACTION_FIX_DO_AS_THEN = 'Modify to `do` .'
ACTION_ADD_END = 'Add `end` (infer the addition location ny indentations).'
ACTION_FIX_COMMENT_PREFIX = 'Modify to `--` .'
+ACTION_FIX_NONSTANDARD_SYMBOL = 'Modify to `{symbol}` .'
ACTION_RUNTIME_UNICODE_NAME = 'Allow Unicode characters.'
ACTION_SWAP_PARAMS = 'Change to parameter {index} or `{node}`'
diff --git a/locale/zh-cn/script.lua b/locale/zh-cn/script.lua
index 4e43024f..b3b59c85 100644
--- a/locale/zh-cn/script.lua
+++ b/locale/zh-cn/script.lua
@@ -37,6 +37,7 @@ DIAG_SET_FOR_STATE = '修改了循环变量。'
DIAG_CODE_AFTER_BREAK = '无法执行到 `break` 后的代码。'
DIAG_UNBALANCED_ASSIGNMENTS = '由于值的数量不够而被赋值为了 `nil` 。在Lua中, `x, y = 1` 等价于 `x, y = 1, nil` 。'
DIAG_REQUIRE_LIKE = '你可以在设置中将 `{}` 视为 `require`。'
+DIAG_COSE_NON_OBJECT = '无法 close 此类型的值。(除非给此类型设置 `__close` 元方法)'
DIAG_CIRCLE_DOC_CLASS = '循环继承的类。'
DIAG_DOC_FIELD_NO_CLASS = '字段必须定义在类之后。'
@@ -74,7 +75,7 @@ PARSER_MISS_FIELD = '缺少域名。'
PARSER_MISS_METHOD = '缺少方法名。'
PARSER_ARGS_AFTER_DOTS = '`...`必须是最后一个参数。'
PARSER_KEYWORD = '关键字无法作为名称。'
-PARSER_EXP_IN_ACTION = '表达式不能独立存在。'
+PARSER_EXP_IN_ACTION = '该表达式不能作为语句。'
PARSER_BREAK_OUTSIDE = '`break`必须在循环内部。'
PARSER_MALFORMED_NUMBER = '无法构成有效数字。'
PARSER_ACTION_AFTER_RETURN = '`return`之后不能再执行代码。'
@@ -99,6 +100,7 @@ PARSER_ERR_COMMENT_PREFIX = 'Lua应使用`--`来进行注释。'
PARSER_MISS_SEP_IN_TABLE = '需要用`,`或`;`进行分割。'
PARSER_SET_CONST = '不能对常量赋值。'
PARSER_UNICODE_NAME = '包含了 Unicode 字符。'
+PARSER_ERR_NONSTANDARD_SYMBOL = 'Lua中应使用符号 `{symbol}`。'
PARSER_LUADOC_MISS_CLASS_NAME = '缺少类名称。'
PARSER_LUADOC_MISS_EXTENDS_SYMBOL = '缺少符号 `:`。'
@@ -164,6 +166,7 @@ ACTION_FIX_THEN_AS_DO = '改为 `then` 。'
ACTION_FIX_DO_AS_THEN = '改为 `do` 。'
ACTION_ADD_END = '添加 `end` (根据缩进推测添加位置)。'
ACTION_FIX_COMMENT_PREFIX = '改为 `--` 。'
+ACTION_FIX_NONSTANDARD_SYMBOL = '改为 `{symbol}`'
ACTION_RUNTIME_UNICODE_NAME = '允许使用 Unicode 字符。'
ACTION_SWAP_PARAMS = '将其改为 `{node}` 的第 {index} 个参数'
diff --git a/main.lua b/main.lua
index 4ecd4f4a..806c8e06 100644
--- a/main.lua
+++ b/main.lua
@@ -14,10 +14,10 @@ log.init(ROOT, ROOT / 'log' / 'service.log')
log.info('Lua Lsp startup, root: ', ROOT)
log.debug('ROOT:', ROOT:string())
+require 'tracy'
+
xpcall(dofile, log.debug, rootPath .. '/debugger.lua')
local service = require 'service'
--- TODO
---ALL_DEEP = true
service.start()
diff --git a/meta/template/package.lua b/meta/template/package.lua
index fbbd10a4..0d6ab270 100644
--- a/meta/template/package.lua
+++ b/meta/template/package.lua
@@ -16,7 +16,7 @@ function require(modname) end
---#DES 'package'
---@class package*
---#DES 'package.config'
----@field conifg string
+---@field config string
---#DES 'package.cpath'
---@field cpath string
---#DES 'package.loaded'
diff --git a/script/config.lua b/script/config.lua
index 74f2ff4e..eecd8ffa 100644
--- a/script/config.lua
+++ b/script/config.lua
@@ -95,15 +95,16 @@ end
local ConfigTemplate = {
runtime = {
- version = {'Lua 5.4', String},
- path = {{
- "?.lua",
- "?/init.lua",
- "?/?.lua"
- }, Array(String)},
- special = {{}, Hash(String, String)},
- meta = {'${version} ${language}', String},
- unicodeName = {false, Boolean},
+ version = {'Lua 5.4', String},
+ path = {{
+ "?.lua",
+ "?/init.lua",
+ "?/?.lua"
+ }, Array(String)},
+ special = {{}, Hash(String, String)},
+ meta = {'${version} ${language}', String},
+ unicodeName = {false, Boolean},
+ nonstandardSymbol = {{}, Str2Hash ';'},
},
diagnostics = {
enable = {true, Boolean},
diff --git a/script/core/code-action.lua b/script/core/code-action.lua
index 246e1549..2a0d9907 100644
--- a/script/core/code-action.lua
+++ b/script/core/code-action.lua
@@ -156,7 +156,7 @@ local function solveSyntaxByFix(uri, err, results)
}
end
results[#results+1] = {
- title = lang.script['ACTION_' .. err.fix.title],
+ title = lang.script('ACTION_' .. err.fix.title, err.fix),
kind = 'quickfix',
edit = {
changes = {
@@ -207,6 +207,7 @@ end
local function solveNewlineCall(uri, diag, results)
local text = files.getText(uri)
local lines = files.getLines(uri)
+ local start = define.unrange(lines, text, diag.range)
results[#results+1] = {
title = lang.script.ACTION_ADD_SEMICOLON,
kind = 'quickfix',
@@ -214,10 +215,8 @@ local function solveNewlineCall(uri, diag, results)
changes = {
[uri] = {
{
- range = {
- start = diag.range.start,
- ['end'] = diag.range.start,
- },
+ start = start,
+ finish = start,
newText = ';',
}
}
diff --git a/script/core/completion.lua b/script/core/completion.lua
index 5db4aa1b..b1610ff0 100644
--- a/script/core/completion.lua
+++ b/script/core/completion.lua
@@ -1149,10 +1149,10 @@ local function buildInsertDocFunction(doc)
for i, arg in ipairs(doc.args) do
args[i] = ('${%d:%s}'):format(i, arg.name[1])
end
- return ([[
-function (%s)
- $0
-end]]):format(table.concat(args, ', '))
+ return ("\z
+function (%s)\
+\t$0\
+end"):format(table.concat(args, ', '))
end
local function getCallEnums(source, index)
diff --git a/script/core/diagnostics/close-non-object.lua b/script/core/diagnostics/close-non-object.lua
new file mode 100644
index 00000000..cfefb037
--- /dev/null
+++ b/script/core/diagnostics/close-non-object.lua
@@ -0,0 +1,40 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local lang = require 'language'
+local define = require 'proto.define'
+
+return function (uri, callback)
+ local state = files.getAst(uri)
+ if not state then
+ return
+ end
+
+ guide.eachSourceType(state.ast, 'local', function (source)
+ if not source.attrs then
+ return
+ end
+ if source.attrs[1][1] ~= 'close' then
+ return
+ end
+ if not source.value then
+ callback {
+ start = source.start,
+ finish = source.finish,
+ message = lang.script.DIAG_COSE_NON_OBJECT,
+ }
+ return
+ end
+ if source.value.type == 'nil'
+ or source.value.type == 'number'
+ or source.value.type == 'boolean'
+ or source.value.type == 'table'
+ or source.value.type == 'function' then
+ callback {
+ start = source.value.start,
+ finish = source.value.finish,
+ message = lang.script.DIAG_COSE_NON_OBJECT,
+ }
+ return
+ end
+ end)
+end
diff --git a/script/core/diagnostics/init.lua b/script/core/diagnostics/init.lua
index 15884758..bc3f3d8c 100644
--- a/script/core/diagnostics/init.lua
+++ b/script/core/diagnostics/init.lua
@@ -4,7 +4,8 @@ local config = require 'config'
local await = require 'await'
-- 把耗时最长的诊断放到最后面
-local diagLevel = {
+local diagSort = {
+ ['undefined-field'] = 99,
['redundant-parameter'] = 100,
}
@@ -13,7 +14,7 @@ for k in pairs(define.DiagnosticDefaultSeverity) do
diagList[#diagList+1] = k
end
table.sort(diagList, function (a, b)
- return (diagLevel[a] or 0) < (diagLevel[b] or 0)
+ return (diagSort[a] or 0) < (diagSort[b] or 0)
end)
local function check(uri, name, results)
@@ -23,7 +24,8 @@ local function check(uri, name, results)
local level = config.config.diagnostics.severity[name]
or define.DiagnosticDefaultSeverity[name]
- local neededFileStatus = define.DiagnosticDefaultNeededFileStatus[name]
+ local neededFileStatus = config.config.diagnostics.neededFileStatus[name]
+ or define.DiagnosticDefaultNeededFileStatus[name]
if neededFileStatus == 'Opened' and not files.isOpen(uri) then
return
end
diff --git a/script/core/diagnostics/redundant-parameter.lua b/script/core/diagnostics/redundant-parameter.lua
index 2fae20e8..ac52ab2a 100644
--- a/script/core/diagnostics/redundant-parameter.lua
+++ b/script/core/diagnostics/redundant-parameter.lua
@@ -22,7 +22,7 @@ local function countFuncArgs(source)
if source.parent and source.parent.type == 'setmethod' then
result = result + 1
end
- if not source.args then
+ if not source.args or #source.args == 0 then
return result
end
if source.args[#source.args].type == '...' then
@@ -32,6 +32,22 @@ local function countFuncArgs(source)
return result
end
+local function countOverLoadArgs(source, doc)
+ local result = 0
+ if source.parent and source.parent.type == 'setmethod' then
+ result = result + 1
+ end
+ local func = doc.overload
+ if not func.args or #func.args == 0 then
+ return result
+ end
+ if func.args[#func.args].type == '...' then
+ return math.maxinteger
+ end
+ result = result + #func.args
+ return result
+end
+
return function (uri, callback)
local ast = files.getAst(uri)
if not ast then
@@ -56,6 +72,16 @@ return function (uri, callback)
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
diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index a8fadfed..31bd9008 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -12,7 +12,9 @@ return function (uri, callback)
end
local function getAllDocClassFromInfer(src)
+ tracy.ZoneBeginN('undefined-field getInfers')
local infers = vm.getInfers(src, 0)
+ tracy.ZoneEnd()
if not infers then
return nil
@@ -29,7 +31,7 @@ return function (uri, callback)
local allDocClass = {}
for i = 1, #infers do
local infer = infers[i]
- if infer.type ~= '_G' and infer.type ~= 'any' then
+ if infer.type ~= '_G' and infer.type ~= 'any' and infer.type ~= 'table' then
local inferSource = infer.source
if inferSource.type == 'doc.class' then
addTo(allDocClass, inferSource)
@@ -53,7 +55,7 @@ return function (uri, callback)
local fields = {}
local empty = true
for _, docClass in ipairs(allDocClass) do
- local refs = vm.getFieldsOfDocClassAnyNotGet(docClass)
+ local refs = vm.getDefFields(docClass)
for _, ref in ipairs(refs) do
local name = vm.getKeyName(ref)
@@ -90,13 +92,13 @@ return function (uri, callback)
if not fields[fieldName] then
local message = lang.script('DIAG_UNDEF_FIELD', fieldName)
- if src.type == 'getfield' then
+ if src.type == 'getfield' and src.field then
callback {
start = src.field.start,
finish = src.field.finish,
message = message,
}
- elseif src.type == 'getmethod' then
+ elseif src.type == 'getmethod' and src.method then
callback {
start = src.method.start,
finish = src.method.finish,
diff --git a/script/core/diagnostics/unused-function.lua b/script/core/diagnostics/unused-function.lua
index 1463d081..e6ae9386 100644
--- a/script/core/diagnostics/unused-function.lua
+++ b/script/core/diagnostics/unused-function.lua
@@ -4,6 +4,7 @@ local vm = require 'vm'
local define = require 'proto.define'
local lang = require 'language'
local await = require 'await'
+local client = require 'provider.client'
local function isToBeClosed(source)
if not source.attrs then
@@ -32,7 +33,7 @@ return function (uri, callback)
and parent.type ~= 'setlocal' then
return
end
- if isToBeClosed(source) then
+ if isToBeClosed(parent) then
return
end
local hasGet
@@ -44,12 +45,21 @@ return function (uri, callback)
end
end
if not hasGet then
- callback {
- start = source.start,
- finish = source.finish,
- tags = { define.DiagnosticTag.Unnecessary },
- message = lang.script.DIAG_UNUSED_FUNCTION,
- }
+ if client.isVSCode() then
+ callback {
+ start = source.start,
+ finish = source.finish,
+ tags = { define.DiagnosticTag.Unnecessary },
+ message = lang.script.DIAG_UNUSED_FUNCTION,
+ }
+ else
+ callback {
+ start = source.keyword[1],
+ finish = source.keyword[2],
+ tags = { define.DiagnosticTag.Unnecessary },
+ message = lang.script.DIAG_UNUSED_FUNCTION,
+ }
+ end
end
end)
end
diff --git a/script/core/document-symbol.lua b/script/core/document-symbol.lua
index 7392b337..f06e613c 100644
--- a/script/core/document-symbol.lua
+++ b/script/core/document-symbol.lua
@@ -76,8 +76,9 @@ local function buildTable(tbl)
if not field then
break
end
- if field.type == 'tablefield' then
- buf[i] = ('%s'):format(field.field[1])
+ if field.type == 'tablefield'
+ and field.field then
+ buf[#buf+1] = ('%s'):format(field.field[1])
end
end
return table.concat(buf, ', ')
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index f9a0fc1d..570dec3f 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -109,7 +109,7 @@ end
local function buildEnumChunk(docType, name)
local enums = vm.getDocEnums(docType)
- if #enums == 0 then
+ if not enums or #enums == 0 then
return
end
local types = {}
diff --git a/script/core/keyword.lua b/script/core/keyword.lua
index cace541b..08600868 100644
--- a/script/core/keyword.lua
+++ b/script/core/keyword.lua
@@ -15,10 +15,10 @@ local keyWordMap = {
label = 'do .. end',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-do
- $0
-end]],
+ insertText = "\z
+do\
+\t$0\
+end",
}
end
return true
@@ -64,38 +64,38 @@ end]],
label = 'for .. in',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-${1:key, value} in ${2:pairs(${3:t})} do
- $0
-end]]
+ insertText = "\z
+${1:key, value} in ${2:pairs(${3:t})} do\
+\t$0\
+end"
}
results[#results+1] = {
label = 'for i = ..',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-${1:i} = ${2:1}, ${3:10, 1} do
- $0
-end]]
+ insertText = "\z
+${1:i} = ${2:1}, ${3:10, 1} do\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'for .. in',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-for ${1:key, value} in ${2:pairs(${3:t})} do
- $0
-end]]
+ insertText = "\z
+for ${1:key, value} in ${2:pairs(${3:t})} do\
+\t$0\
+end"
}
results[#results+1] = {
label = 'for i = ..',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-for ${1:i} = ${2:1}, ${3:10, 1} do
- $0
-end]]
+ insertText = "\z
+for ${1:i} = ${2:1}, ${3:10, 1} do\
+\t$0\
+end"
}
end
return true
@@ -106,26 +106,26 @@ end]]
label = 'function ()',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = isExp and [[
-($1)
- $0
-end]] or [[
-$1($2)
- $0
-end]]
+ insertText = isExp and "\z
+($1)\
+\t$0\
+end" or "\z
+$1($2)\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'function ()',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = isExp and [[
-function ($1)
- $0
-end]] or [[
-function $1($2)
- $0
-end]]
+ insertText = isExp and "\z
+function ($1)\
+\t$0\
+end" or "\z
+function $1($2)\
+\t$0\
+end"
}
end
return true
@@ -137,20 +137,20 @@ end]]
label = 'if .. then',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-$1 then
- $0
-end]]
+ insertText = "\z
+$1 then\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'if .. then',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-if $1 then
- $0
-end]]
+ insertText = "\z
+if $1 then\
+\t$0\
+end"
}
end
return true
@@ -161,20 +161,20 @@ end]]
label = 'in ..',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-${1:pairs(${2:t})} do
- $0
-end]]
+ insertText = "\z
+${1:pairs(${2:t})} do\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'in ..',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-in ${1:pairs(${2:t})} do
- $0
-end]]
+ insertText = "\z
+in ${1:pairs(${2:t})} do\
+\t$0\
+end"
}
end
return true
@@ -185,20 +185,20 @@ end]]
label = 'local function',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-function $1($2)
- $0
-end]]
+ insertText = "\z
+function $1($2)\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'local function',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-local function $1($2)
- $0
-end]]
+ insertText = "\z
+local function $1($2)\
+\t$0\
+end"
}
end
return false
@@ -219,10 +219,10 @@ end]]
label = 'repeat .. until',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-repeat
- $0
-until $1]]
+ insertText = "\z
+repeat\
+\t$0\
+until $1"
}
end
return true
@@ -247,20 +247,20 @@ until $1]]
label = 'while .. do',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-${1:true} do
- $0
-end]]
+ insertText = "\z
+${1:true} do\
+\t$0\
+end"
}
else
results[#results+1] = {
label = 'while .. do',
kind = define.CompletionItemKind.Snippet,
insertTextFormat = 2,
- insertText = [[
-while ${1:true} do
- $0
-end]]
+ insertText = "\z
+while ${1:true} do\
+\t$0\
+end"
}
end
return true
diff --git a/script/core/rename.lua b/script/core/rename.lua
index 54867f77..b823cb86 100644
--- a/script/core/rename.lua
+++ b/script/core/rename.lua
@@ -113,10 +113,16 @@ local function trim(str)
end
local function isValidName(str)
+ if not str then
+ return false
+ end
return str:match '^[%a_][%w_]*$'
end
local function isValidGlobal(str)
+ if not str then
+ return false
+ end
for s in str:gmatch '[^%.]*' do
if not isValidName(trim(s)) then
return false
@@ -431,6 +437,9 @@ local accept = {
local m = {}
function m.rename(uri, pos, newname)
+ if not newname then
+ return nil
+ end
local ast = files.getAst(uri)
if not ast then
return nil
diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua
index 170bffa8..37eabda7 100644
--- a/script/core/semantic-tokens.lua
+++ b/script/core/semantic-tokens.lua
@@ -120,6 +120,21 @@ Care['doc.type.name'] = function (source, results)
end
end
+Care['nonstandardSymbol.comment'] = function (source, results)
+ results[#results+1] = {
+ start = source.start,
+ finish = source.finish,
+ type = define.TokenTypes.comment,
+ }
+end
+Care['nonstandardSymbol.continue'] = function (source, results)
+ results[#results+1] = {
+ start = source.start,
+ finish = source.finish,
+ type = define.TokenTypes.keyword,
+ }
+end
+
local function buildTokens(results, text, lines)
local tokens = {}
local lastLine = 0
diff --git a/script/core/signature.lua b/script/core/signature.lua
index 2f652f39..6e3d7e11 100644
--- a/script/core/signature.lua
+++ b/script/core/signature.lua
@@ -6,16 +6,16 @@ local hoverDesc = require 'core.hover.description'
local function findNearCall(uri, ast, pos)
local text = files.getText(uri)
- -- 检查 `f()$` 的情况,注意要区别于 `f($`
- if text:sub(pos, pos) == ')' then
- return nil
- end
-
local nearCall
guide.eachSourceContain(ast.ast, pos, function (src)
if src.type == 'call'
or src.type == 'table'
or src.type == 'function' then
+ -- call()$
+ if src.finish <= pos
+ and text:sub(src.finish, src.finish) == ')' then
+ return
+ end
if not nearCall or nearCall.start < src.start then
nearCall = src
end
diff --git a/script/files.lua b/script/files.lua
index 71981a83..9ffcd2a1 100644
--- a/script/files.lua
+++ b/script/files.lua
@@ -211,7 +211,11 @@ end
--- 遍历文件
function m.eachFile()
- return pairs(m.fileMap)
+ local map = {}
+ for uri, file in pairs(m.fileMap) do
+ map[uri] = file
+ end
+ return pairs(map)
end
function m.compileAst(uri, text)
@@ -242,8 +246,9 @@ function m.compileAst(uri, text)
, 'lua'
, config.config.runtime.version
, {
- special = config.config.runtime.special,
- unicodeName = config.config.runtime.unicodeName,
+ special = config.config.runtime.special,
+ unicodeName = config.config.runtime.unicodeName,
+ nonstandardSymbol = config.config.runtime.nonstandardSymbol,
}
)
local passed = os.clock() - clock
diff --git a/script/glob/gitignore.lua b/script/glob/gitignore.lua
index 3f942bfb..7dfd4591 100644
--- a/script/glob/gitignore.lua
+++ b/script/glob/gitignore.lua
@@ -116,6 +116,7 @@ function mt:checkDirectory(catch, path, matcher)
end
function mt:simpleMatch(path)
+ path = path:gsub('^[/\\]+', '')
for i = #self.matcher, 1, -1 do
local matcher = self.matcher[i]
local catch = matcher(path)
@@ -147,18 +148,18 @@ function mt:finishMatch(path)
return false
end
-function mt:scan(callback)
+function mt:scan(root, callback)
local files = {}
if type(callback) ~= 'function' then
callback = nil
end
local list = {}
- local result = self:callInterface('list', '')
+ local result = self:callInterface('list', root)
if type(result) ~= 'table' then
return files
end
for _, path in ipairs(result) do
- list[#list+1] = path:match '([^/\\]+)[/\\]*$'
+ list[#list+1] = path
end
while #list > 0 do
local current = list[#list]
@@ -181,7 +182,7 @@ function mt:scan(callback)
if filename
and filename ~= '.'
and filename ~= '..' then
- list[#list+1] = current .. '/' .. filename
+ list[#list+1] = path
end
end
end
diff --git a/script/parser/ast.lua b/script/parser/ast.lua
index 2fdb99ef..127bb08c 100644
--- a/script/parser/ast.lua
+++ b/script/parser/ast.lua
@@ -48,6 +48,13 @@ local VersionOp = {
['//'] = {'Lua 5.3', 'Lua 5.4'},
}
+local SymbolAlias = {
+ ['||'] = 'or',
+ ['&&'] = 'and',
+ ['!='] = '~=',
+ ['!'] = 'not',
+}
+
local function checkOpVersion(op)
local versions = VersionOp[op.type]
if not versions then
@@ -305,41 +312,84 @@ local Defs = {
end
end,
CLongComment = function (start1, finish1, start2, finish2)
- PushError {
- type = 'ERR_C_LONG_COMMENT',
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['/**/'] then
+ else
+ PushError {
+ type = 'ERR_C_LONG_COMMENT',
+ start = start1,
+ finish = finish2 - 1,
+ fix = {
+ title = 'FIX_C_LONG_COMMENT',
+ {
+ start = start1,
+ finish = finish1 - 1,
+ text = '--[[',
+ },
+ {
+ start = start2,
+ finish = finish2 - 1,
+ text = '--]]'
+ },
+ }
+ }
+ end
+ return {
+ type = 'nonstandardSymbol.comment',
start = start1,
finish = finish2 - 1,
- fix = {
- title = 'FIX_C_LONG_COMMENT',
- {
- start = start1,
- finish = finish1 - 1,
- text = '--[[',
- },
- {
- start = start2,
- finish = finish2 - 1,
- text = '--]]'
- },
- }
}
end,
- CCommentPrefix = function (start, finish)
- PushError {
- type = 'ERR_COMMENT_PREFIX',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_COMMENT_PREFIX',
- {
- start = start,
- finish = finish - 1,
- text = '--',
- },
+ CCommentPrefix = function (start, finish, commentFinish)
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['//'] then
+ else
+ PushError {
+ type = 'ERR_COMMENT_PREFIX',
+ start = start,
+ finish = finish - 1,
+ fix = {
+ title = 'FIX_COMMENT_PREFIX',
+ {
+ start = start,
+ finish = finish - 1,
+ text = '--',
+ },
+ }
}
+ end
+ return {
+ type = 'nonstandardSymbol.comment',
+ start = start,
+ finish = commentFinish - 1,
}
end,
String = function (start, quote, str, finish)
+ if quote == '`' then
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['`'] then
+ else
+ PushError {
+ type = 'ERR_NONSTANDARD_SYMBOL',
+ start = start,
+ finish = finish - 1,
+ info = {
+ symbol = '"',
+ },
+ fix = {
+ title = 'FIX_NONSTANDARD_SYMBOL',
+ symbol = '"',
+ {
+ start = start,
+ finish = start,
+ text = '"',
+ },
+ {
+ start = finish - 1,
+ finish = finish - 1,
+ text = '"',
+ },
+ }
+ }
+ end
+ end
return {
type = 'string',
start = start,
@@ -642,6 +692,29 @@ local Defs = {
return call
end,
BinaryOp = function (start, op)
+ if SymbolAlias[op] then
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol[op] then
+ else
+ PushError {
+ type = 'ERR_NONSTANDARD_SYMBOL',
+ start = start,
+ finish = start + #op - 1,
+ info = {
+ symbol = SymbolAlias[op],
+ },
+ fix = {
+ title = 'FIX_NONSTANDARD_SYMBOL',
+ symbol = SymbolAlias[op],
+ {
+ start = start,
+ finish = start + #op - 1,
+ text = SymbolAlias[op],
+ },
+ }
+ }
+ end
+ op = SymbolAlias[op]
+ end
return {
type = op,
start = start,
@@ -649,6 +722,29 @@ local Defs = {
}
end,
UnaryOp = function (start, op)
+ if SymbolAlias[op] then
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol[op] then
+ else
+ PushError {
+ type = 'ERR_NONSTANDARD_SYMBOL',
+ start = start,
+ finish = start + #op - 1,
+ info = {
+ symbol = SymbolAlias[op],
+ },
+ fix = {
+ title = 'FIX_NONSTANDARD_SYMBOL',
+ symbol = SymbolAlias[op],
+ {
+ start = start,
+ finish = start + #op - 1,
+ text = SymbolAlias[op],
+ },
+ }
+ }
+ end
+ op = SymbolAlias[op]
+ end
return {
type = op,
start = start,
@@ -855,6 +951,19 @@ local Defs = {
finish = start,
}
end,
+ ASSIGN = function (start, symbol)
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol[symbol] then
+ else
+ PushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = start,
+ finish = start + #symbol - 1,
+ info = {
+ version = 'Lua',
+ }
+ }
+ end
+ end,
DOT = function (start)
return {
type = '.',
@@ -1133,7 +1242,7 @@ local Defs = {
end,
LocalName = function (name, attrs)
if not name then
- return name
+ return
end
name.attrs = attrs
return name
@@ -1197,7 +1306,7 @@ local Defs = {
return
end
if not name then
- return nil
+ return
end
name.type = 'label'
return name
@@ -1216,7 +1325,7 @@ local Defs = {
return
end
if not name then
- return nil
+ return
end
name.type = 'goto'
return name
@@ -1356,6 +1465,20 @@ local Defs = {
}
return block
end,
+ RTContinue = function (_, pos, ...)
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['continue'] then
+ return pos, ...
+ else
+ return false
+ end
+ end,
+ Continue = function (start, finish)
+ return {
+ type = 'nonstandardSymbol.continue',
+ start = start,
+ finish = finish - 1,
+ }
+ end,
Lua = function (start, actions, finish)
actions.type = 'main'
actions.start = start
@@ -1435,6 +1558,16 @@ local Defs = {
}
}
end,
+ MissQuote3 = function (pos)
+ PushError {
+ type = 'MISS_SYMBOL',
+ start = pos,
+ finish = pos,
+ info = {
+ symbol = "`"
+ }
+ }
+ end,
MissEscX = function (pos)
PushError {
type = 'MISS_ESC_X',
diff --git a/script/parser/grammar.lua b/script/parser/grammar.lua
index ad107ef5..f222a283 100644
--- a/script/parser/grammar.lua
+++ b/script/parser/grammar.lua
@@ -58,6 +58,12 @@ end
defs.None = function () end
defs.np = m.Cp() / function (n) return n+1 end
defs.NameBody = m.R('az', 'AZ', '__', '\x80\xff') * m.R('09', 'az', 'AZ', '__', '\x80\xff')^0
+defs.NoNil = function (o)
+ if o == nil then
+ return
+ end
+ return o
+end
m.setmaxstack(1000)
@@ -118,6 +124,7 @@ NOT <- Sp 'not' Cut
OR <- Sp {'or'} Cut
RETURN <- Sp 'return' Cut
TRUE <- Sp 'true' Cut
+CONTINUE <- Sp 'continue' Cut
DO <- Sp {} 'do' {} Cut
/ Sp({} 'then' {} Cut) -> ErrDo
@@ -180,9 +187,9 @@ UnaryList <- NOT
/ '~' !'='
POWER <- Sp {'^'}
-BinaryOp <-( Sp {} {'or'} Cut
- / Sp {} {'and'} Cut
- / Sp {} {'<=' / '>=' / '<'!'<' / '>'!'>' / '~=' / '=='}
+BinaryOp <-( Sp {} {'or' / '||'} Cut
+ / Sp {} {'and' / '&&'} Cut
+ / Sp {} {'<=' / '>=' / '<'!'<' / '>'!'>' / '~=' / '==' / '!='}
/ Sp {} ({} '=' {}) -> ErrEQ
/ Sp {} ({} '!=' {}) -> ErrUEQ
/ Sp {} {'|'}
@@ -194,7 +201,7 @@ BinaryOp <-( Sp {} {'or'} Cut
/ Sp {} {'*' / '//' / '/' / '%'}
/ Sp {} {'^'}
)-> BinaryOp
-UnaryOp <-( Sp {} {'not' Cut / '#' / '~' !'=' / '-' !'-'}
+UnaryOp <-( Sp {} {'not' Cut / '#' / '~' !'=' / '-' !'-' / '!' !'='}
)-> UnaryOp
PL <- Sp '('
@@ -215,9 +222,11 @@ COLON <- Sp ({} ':' !':')
-> COLON
LABEL <- Sp '::'
ASSIGN <- Sp '=' !'='
+ / Sp ({} {'+=' / '-=' / '*=' / '\='})
+ -> ASSIGN
AssignOrEQ <- Sp ({} '==' {})
-> ErrAssign
- / Sp '='
+ / ASSIGN
DirtyBR <- BR / {} -> MissBR
DirtyTR <- TR / {} -> MissTR
@@ -250,6 +259,9 @@ StringDef <- {'"'}
/ {"'"}
{~(Esc / !%nl !"'" .)*~} -> 1
("'" / {} -> MissQuote2)
+ / {'`'}
+ {(!%nl !'`' .)*} -> 1
+ ('`' / {} -> MissQuote3)
/ ('[' {} {:eq: '='* :} {} '[' %nl?
{(!StringClose .)*} -> 1
(StringClose / {}))
@@ -321,7 +333,7 @@ Single <- FreeName
Suffix <- SuffixWithoutCall
/ ({} PL SuffixCall DirtyPR {})
-> Call
-SuffixCall <- Sp ({} {| (COMMA / Exp)+ |} {})
+SuffixCall <- Sp ({} {| (COMMA / Exp->NoNil)+ |} {})
-> PackExpList
/ %nil
SuffixWithoutCall
@@ -359,7 +371,7 @@ TableField <- COMMA
/ SEMICOLON
/ NewIndex
/ NewField
- / Exp
+ / Exp->NoNil
Index <- BL DirtyExp DirtyBR
NewIndex <- Sp ({} Index NeedAssign DirtyExp {})
-> NewIndex
@@ -402,11 +414,12 @@ CrtAction <- Semicolon
/ LocalFunction
/ Local
/ Set
+ / Continue
/ Call
/ ExpInAction
UnkAction <- ({} {Word+})
-> UnknownAction
- / ({} '//' {} (LongComment / ShortComment))
+ / ({} '//' {} (LongComment / ShortComment) {})
-> CCommentPrefix
/ ({} {. (!Sps !CrtAction .)*})
-> UnknownAction
@@ -425,10 +438,14 @@ Do <- Sp ({}
Break <- Sp ({} BREAK {})
-> Break
+Continue <- Sp ({} CONTINUE {})
+ => RTContinue
+ -> Continue
+
Return <- Sp ({} RETURN ReturnExpList {})
-> Return
ReturnExpList
- <- Sp {| Exp (Sp ',' MaybeExp)* |}
+ <- Sp {| Exp->NoNil (Sp ',' MaybeExp)* |}
/ Sp {| !Exp !',' |}
/ ExpList
@@ -461,7 +478,7 @@ LoopBody <- FOR LoopArgs NeedDo
{} {| (!END Action)* |}
NeedEnd
LoopArgs <- MustName AssignOrEQ
- ({} {| (COMMA / !DO !END Exp)* |} {})
+ ({} {| (COMMA / !DO !END Exp->NoNil)* |} {})
-> PackLoopArgs
In <- InBody
@@ -469,9 +486,9 @@ In <- InBody
InBody <- FOR InNameList NeedIn InExpList NeedDo
{} {| (!END Action)* |}
NeedEnd
-InNameList <- ({} {| (COMMA / !IN !DO !END Name)* |} {})
+InNameList <- ({} {| (COMMA / !IN !DO !END Name->NoNil)* |} {})
-> PackInNameList
-InExpList <- ({} {| (COMMA / !DO !DO !END Exp)* |} {})
+InExpList <- ({} {| (COMMA / !DO !DO !END Exp->NoNil)* |} {})
-> PackInExpList
While <- WhileBody
diff --git a/script/parser/guide.lua b/script/parser/guide.lua
index a6b11744..a6149dd7 100644
--- a/script/parser/guide.lua
+++ b/script/parser/guide.lua
@@ -17,6 +17,8 @@ local setmetatable = setmetatable
local assert = assert
local select = select
local osClock = os.clock
+local tonumber = tonumber
+local tointeger = math.tointeger
local DEVELOP = _G.DEVELOP
local log = log
local _G = _G
@@ -25,13 +27,9 @@ local function logWarn(...)
log.warn(...)
end
-_ENV = nil
-
local m = {}
-m.ANY = {"ANY"}
-
-m.ANYNOTGET = {"ANYNOTGET"}
+m.ANY = {"<ANY>"}
local blockTypes = {
['while'] = true,
@@ -99,6 +97,7 @@ m.childMap = {
['doc.vararg'] = {'vararg', 'comment'},
['doc.type.table'] = {'key', 'value', 'comment'},
['doc.type.function'] = {'#args', '#returns', 'comment'},
+ ['doc.type.typeliteral'] = {'node'},
['doc.overload'] = {'overload', 'comment'},
['doc.see'] = {'name', 'field'},
}
@@ -178,6 +177,9 @@ function m.getBlock(obj)
if blockTypes[tp] then
return obj
end
+ if obj == obj.parent then
+ error('obj == obj.parent?', obj.type)
+ end
obj = obj.parent
end
error('guide.getBlock overstack')
@@ -664,6 +666,10 @@ function m.lineRange(lines, row, ignoreNL)
end
end
+function m.lineData(lines, row)
+ return lines[row]
+end
+
function m.getKeyTypeOfLiteral(obj)
if not obj then
return nil
@@ -714,6 +720,8 @@ function m.getKeyType(obj)
return 'string'
elseif tp == 'doc.field' then
return 'string'
+ elseif tp == 'dummy' then
+ return 'string'
end
return m.getKeyTypeOfLiteral(obj)
end
@@ -781,14 +789,24 @@ function m.getKeyName(obj)
return obj.alias[1]
elseif tp == 'doc.field' then
return obj.field[1]
+ elseif tp == 'dummy' then
+ return obj[1]
end
return m.getKeyNameOfLiteral(obj)
end
function m.getSimpleName(obj)
if obj.type == 'call' then
- local key = obj.args and obj.args[2]
- return m.getKeyName(key)
+ local node = obj.node
+ if not node then
+ return
+ end
+ if node.special == 'rawset'
+ or node.special == 'rawget' then
+ local key = obj.args and obj.args[2]
+ return m.getKeyName(key)
+ end
+ return ('%p'):format(obj)
elseif obj.type == 'table' then
return ('%p'):format(obj)
elseif obj.type == 'select' then
@@ -1136,7 +1154,8 @@ local function buildSimpleList(obj, max)
list[i] = cur
break
elseif cur.type == 'select'
- or cur.type == 'table' then
+ or cur.type == 'table'
+ or cur.type == 'call' then
list[i] = cur
break
elseif cur.type == 'string' then
@@ -1177,6 +1196,7 @@ function m.getSimple(obj, max)
or obj.type == 'tablefield'
or obj.type == 'tableindex'
or obj.type == 'select'
+ or obj.type == 'call'
or obj.type == 'table'
or obj.type == 'string'
or obj.type == 'doc.class.name'
@@ -1359,16 +1379,13 @@ function m.getNextRef(ref)
return nil
end
-function m.checkSameSimpleInValueOfTable(status, value, start, queue)
+function m.checkSameSimpleInValueOfTable(status, value, start, pushQueue)
if value.type ~= 'table' then
return
end
for i = 1, #value do
local field = value[i]
- queue[#queue+1] = {
- obj = field,
- start = start + 1,
- }
+ pushQueue(field, start + 1)
end
end
@@ -1382,6 +1399,16 @@ function m.searchFields(status, obj, key)
m.cleanResults(status.results)
end
+function m.searchDefFields(status, obj, key)
+ local simple = m.getSimple(obj)
+ if not simple then
+ return
+ end
+ simple[#simple+1] = key or m.ANY
+ m.searchSameFields(status, simple, 'deffield')
+ m.cleanResults(status.results)
+end
+
function m.getObjectValue(obj)
while obj.type == 'paren' do
obj = obj.exp
@@ -1400,11 +1427,13 @@ function m.getObjectValue(obj)
end
if obj.type == 'field'
or obj.type == 'method' then
- return obj.parent.value
+ return obj.parent and obj.parent.value
end
if obj.type == 'call' then
if obj.node.special == 'rawset' then
return obj.args[3]
+ else
+ return obj
end
end
if obj.type == 'select' then
@@ -1413,9 +1442,9 @@ function m.getObjectValue(obj)
return nil
end
-function m.checkSameSimpleInValueInMetaTable(status, mt, start, queue)
+function m.checkSameSimpleInValueInMetaTable(status, mt, start, pushQueue)
local newStatus = m.status(status)
- m.searchFields(newStatus, mt, '__index')
+ m.searchDefFields(newStatus, mt, '__index')
local refsStatus = m.status(status)
for i = 1, #newStatus.results do
local indexValue = m.getObjectValue(newStatus.results[i])
@@ -1425,14 +1454,10 @@ function m.checkSameSimpleInValueInMetaTable(status, mt, start, queue)
end
for i = 1, #refsStatus.results do
local obj = refsStatus.results[i]
- queue[#queue+1] = {
- obj = obj,
- start = start,
- force = true,
- }
+ pushQueue(obj, start, true)
end
end
-function m.checkSameSimpleInValueOfSetMetaTable(status, func, start, queue)
+function m.checkSameSimpleInValueOfSetMetaTable(status, func, start, pushQueue)
if not func or func.special ~= 'setmetatable' then
return
end
@@ -1441,48 +1466,60 @@ function m.checkSameSimpleInValueOfSetMetaTable(status, func, start, queue)
local obj = args[1]
local mt = args[2]
if obj then
- queue[#queue+1] = {
- obj = obj,
- start = start,
- force = true,
- }
+ pushQueue(obj, start, true)
end
if mt then
- m.checkSameSimpleInValueInMetaTable(status, mt, start, queue)
+ m.checkSameSimpleInValueInMetaTable(status, mt, start, pushQueue)
end
end
-function m.checkSameSimpleInValueOfCallMetaTable(status, call, start, queue)
+function m.checkSameSimpleInValueOfCallMetaTable(status, call, start, pushQueue)
if status.crossMetaTableMark then
return
end
status.crossMetaTableMark = true
if call.type == 'call' then
- m.checkSameSimpleInValueOfSetMetaTable(status, call.node, start, queue)
+ m.checkSameSimpleInValueOfSetMetaTable(status, call.node, start, pushQueue)
end
status.crossMetaTableMark = false
end
-function m.checkSameSimpleInSpecialBranch(status, obj, start, queue)
+function m.checkSameSimpleInSpecialBranch(status, obj, start, pushQueue)
if status.interface.index then
local results = status.interface.index(obj)
if not results then
return
end
for _, res in ipairs(results) do
- queue[#queue+1] = {
- obj = res,
- start = start + 1,
- }
+ pushQueue(obj, start + 1)
end
end
end
+local function appendValidGenericType(results, status, typeName, obj)
+ if typeName.parent.type == 'doc.type.typeliteral' then
+ if obj.type == 'string' and status.interface.docType then
+ local docs = status.interface.docType(obj[1])
+ for i = 1, #docs do
+ local doc = docs[i]
+ if doc.type == 'doc.class.name'
+ or doc.type == 'doc.alias.name' then
+ results[#results+1] = doc
+ break
+ end
+ end
+ end
+ else
+ -- 发现没有使用 `T`,则沿用既有逻辑直接返回实参
+ results[#results+1] = obj
+ end
+end
+
local function stepRefOfGeneric(status, typeUnit, args, mode)
+ local results = {}
if not args then
- return nil
+ return results
end
- local results = {}
local myName = typeUnit[1]
for _, typeName in ipairs(typeUnit.typeGeneric[myName]) do
if typeName == typeUnit then
@@ -1502,7 +1539,7 @@ local function stepRefOfGeneric(status, typeUnit, args, mode)
and source.parent.type == 'funcargs' then
for index, arg in ipairs(source.parent) do
if arg == source then
- results[#results+1] = args[index]
+ appendValidGenericType(results, status, typeName, args[index])
end
end
end
@@ -1536,7 +1573,7 @@ function m.checkSameSimpleByDocType(status, doc, args)
return results
end
-function m.checkSameSimpleByBindDocs(status, obj, start, queue, mode)
+function m.checkSameSimpleByBindDocs(status, obj, start, pushQueue, mode)
if not obj.bindDocs then
return
end
@@ -1567,30 +1604,19 @@ function m.checkSameSimpleByBindDocs(status, obj, start, queue, mode)
for _, res in ipairs(results) do
if res.type == 'doc.class'
or res.type == 'doc.type' then
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
skipInfer = true
end
if res.type == 'doc.type.function' then
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
elseif res.type == 'doc.field' then
- queue[#queue+1] = {
- obj = res,
- start = start + 1,
- }
+ pushQueue(res, start + 1)
end
end
return skipInfer
end
-function m.checkSameSimpleOfRefByDocSource(status, obj, start, queue, mode)
+function m.checkSameSimpleOfRefByDocSource(status, obj, start, pushQueue, mode)
if status.share.searchingBindedDoc then
return
end
@@ -1608,15 +1634,11 @@ function m.checkSameSimpleOfRefByDocSource(status, obj, start, queue, mode)
end
status.share.searchingBindedDoc = nil
for _, res in ipairs(newStatus.results) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
-function m.checkSameSimpleByDoc(status, obj, start, queue, mode)
+function m.checkSameSimpleByDoc(status, obj, start, pushQueue, mode)
if obj.type == 'doc.class.name'
or obj.type == 'doc.class' then
if obj.type == 'doc.class.name' then
@@ -1630,30 +1652,19 @@ function m.checkSameSimpleByDoc(status, obj, start, queue, mode)
classStart = false
end
if classStart and doc.type == 'doc.field' then
- queue[#queue+1] = {
- obj = doc,
- start = start + 1,
- }
+ pushQueue(doc, start + 1)
end
end
- m.checkSameSimpleOfRefByDocSource(status, obj, start, queue, mode)
+ m.checkSameSimpleOfRefByDocSource(status, obj, start, pushQueue, mode)
if mode == 'ref' then
local pieceResult = stepRefOfDocType(status, obj.class, 'ref')
for _, res in ipairs(pieceResult) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
if obj.extends then
local pieceResult = stepRefOfDocType(status, obj.extends, 'def')
for _, res in ipairs(pieceResult) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
end
@@ -1662,47 +1673,36 @@ function m.checkSameSimpleByDoc(status, obj, start, queue, mode)
for _, piece in ipairs(obj.types) do
local pieceResult = stepRefOfDocType(status, piece, 'def')
for _, res in ipairs(pieceResult) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
if mode == 'ref' then
- m.checkSameSimpleOfRefByDocSource(status, obj, start, queue, mode)
+ m.checkSameSimpleOfRefByDocSource(status, obj, start, pushQueue, mode)
end
return true
elseif obj.type == 'doc.type.name'
or obj.type == 'doc.see.name' then
local pieceResult = stepRefOfDocType(status, obj, 'def')
for _, res in ipairs(pieceResult) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
local state = m.getDocState(obj)
if state.type == 'doc.type' and mode == 'ref' then
- m.checkSameSimpleOfRefByDocSource(status, state, start, queue, mode)
+ m.checkSameSimpleOfRefByDocSource(status, state, start, pushQueue, mode)
end
return true
elseif obj.type == 'doc.field' then
- if mode ~= 'field' then
- return m.checkSameSimpleByDoc(status, obj.extends, start, queue, mode)
+ if mode ~= 'field'
+ and mode ~= 'deffield' then
+ return m.checkSameSimpleByDoc(status, obj.extends, start, pushQueue, mode)
end
elseif obj.type == 'doc.type.array' then
- queue[#queue+1] = {
- obj = obj.node,
- start = start + 1,
- force = true,
- }
+ pushQueue(obj.node, start + 1, true)
return true
end
end
-function m.checkSameSimpleInArg1OfSetMetaTable(status, obj, start, queue)
+function m.checkSameSimpleInArg1OfSetMetaTable(status, obj, start, pushQueue)
local args = obj.parent
if not args or args.type ~= 'callargs' then
return
@@ -1715,11 +1715,11 @@ function m.checkSameSimpleInArg1OfSetMetaTable(status, obj, start, queue)
if m.checkValueMark(status, obj, mt) then
return
end
- m.checkSameSimpleInValueInMetaTable(status, mt, start, queue)
+ m.checkSameSimpleInValueInMetaTable(status, mt, start, pushQueue)
end
end
-function m.searchSameMethodCrossSelf(ref, mark)
+function m.searchSameMethodOutSelf(ref, mark)
local selfNode
if ref.tag == 'self' then
selfNode = ref
@@ -1737,70 +1737,76 @@ function m.searchSameMethodCrossSelf(ref, mark)
return nil
end
mark[selfNode] = true
- return selfNode.method.node
+ local method = selfNode.method.node
+ if mark[method] then
+ return nil
+ end
+ mark[method] = true
+ return method
end
end
-function m.searchSameMethod(ref, mark)
- if mark['method'] then
- return nil
- end
+function m.searchSameMethodIntoSelf(ref, mark)
local nxt = ref.next
if not nxt then
return nil
end
- if nxt.type == 'setmethod' then
- mark['method'] = true
- return ref
+ if nxt.type ~= 'setmethod' then
+ return nil
end
- return nil
+ if mark[ref] then
+ return nil
+ end
+ mark[ref] = true
+ local value = nxt.value
+ if not value or value.type ~= 'function' then
+ return nil
+ end
+ local selfRef = value.locals and value.locals[1]
+ if not selfRef or selfRef.tag ~= 'self' then
+ return nil
+ end
+ if mark[selfRef] then
+ return nil
+ end
+ mark[selfRef] = true
+ return selfRef
end
-function m.searchSameFieldsCrossMethod(status, ref, start, queue)
+function m.searchSameFieldsCrossMethod(status, ref, start, pushQueue)
+ if status.share.crossMethodLock then
+ return
+ end
local mark = status.crossMethodMark
if not mark then
mark = {}
status.crossMethodMark = mark
end
- local method = m.searchSameMethod(ref, mark)
- or m.searchSameMethodCrossSelf(ref, mark)
- if not method then
+ local selfRef = m.searchSameMethodIntoSelf(ref, mark)
+ if selfRef then
+ tracy.ZoneBeginN 'searchSameFieldsCrossMethod'
+ local _ <close> = tracy.ZoneEnd
+ -- 如果自己是method,则只检查自己内部的self引用
+ local results = m.getStepRef(status, selfRef, 'ref')
+ for _, res in ipairs(results) do
+ pushQueue(res, start, true)
+ end
return
end
- local methodStatus = m.status(status)
- m.searchRefs(methodStatus, method, 'ref')
- for _, md in ipairs(methodStatus.results) do
- queue[#queue+1] = {
- obj = md,
- start = start,
- force = true,
- }
- local nxt = md.next
- if not nxt then
- goto CONTINUE
- end
- if nxt.type == 'setmethod' then
- local func = nxt.value
- if not func then
- goto CONTINUE
- end
- local selfNode = func.locals and func.locals[1]
- if not selfNode or not selfNode.ref then
- goto CONTINUE
- end
- if mark[selfNode] then
- goto CONTINUE
- end
- mark[selfNode] = true
- for _, selfRef in ipairs(selfNode.ref) do
- queue[#queue+1] = {
- obj = selfRef,
- start = start,
- force = true,
- }
- end
+ local method = m.searchSameMethodOutSelf(ref, mark)
+ if method then
+ tracy.ZoneBeginN 'searchSameFieldsCrossMethod'
+ local _ <close> = tracy.ZoneEnd
+ -- 如果自己是self,则找出父级的method,以及父级method的引用
+ local newStatus = m.status(status)
+ newStatus.crossMethodLock = true
+ m.searchRefs(newStatus, method, 'ref')
+ newStatus.crossMethodLock = false
+ for _, res in ipairs(newStatus.results) do
+ mark[res] = true
+ pushQueue(res, start, true)
end
- ::CONTINUE::
+ return
end
end
@@ -1885,7 +1891,7 @@ function m.checkSameSimpleInCallInSameFile(status, func, args, index)
return results
end
-function m.checkSameSimpleInCall(status, ref, start, queue, mode)
+function m.checkSameSimpleInCall(status, ref, start, pushQueue, mode)
local func, args, index = m.getCallValue(ref)
if not func then
return
@@ -1899,7 +1905,7 @@ function m.checkSameSimpleInCall(status, ref, start, queue, mode)
end
status.share.crossCallCount = status.share.crossCallCount + 1
-- 检查赋值是 semetatable() 的情况
- m.checkSameSimpleInValueOfSetMetaTable(status, func, start, queue)
+ m.checkSameSimpleInValueOfSetMetaTable(status, func, start, pushQueue)
-- 检查赋值是 func() 的情况
local objs = m.checkSameSimpleInCallInSameFile(status, func, args, index)
if status.interface.call then
@@ -1916,19 +1922,11 @@ function m.checkSameSimpleInCall(status, ref, start, queue, mode)
local newStatus = m.status(status)
for _, obj in ipairs(objs) do
m.searchRefs(newStatus, obj, mode)
- queue[#queue+1] = {
- obj = obj,
- start = start,
- force = true,
- }
+ pushQueue(obj, start, true)
end
status.share.crossCallCount = status.share.crossCallCount - 1
for _, obj in ipairs(newStatus.results) do
- queue[#queue+1] = {
- obj = obj,
- start = start,
- force = true,
- }
+ pushQueue(obj, start, true)
end
end
@@ -1997,7 +1995,7 @@ function m.findGlobalsOfName(ast, name)
return results
end
-function m.checkSameSimpleInGlobal(status, name, source, start, queue)
+function m.checkSameSimpleInGlobal(status, name, source, start, pushQueue)
if not name then
return
end
@@ -2009,11 +2007,7 @@ function m.checkSameSimpleInGlobal(status, name, source, start, queue)
end
if objs then
for _, obj in ipairs(objs) do
- queue[#queue+1] = {
- obj = obj,
- start = start,
- force = true,
- }
+ pushQueue(obj, start, true)
end
end
end
@@ -2054,7 +2048,7 @@ function m.checkReturnMark(status, a, mark)
return result
end
-function m.searchSameFieldsInValue(status, ref, start, queue, mode)
+function m.searchSameFieldsInValue(status, ref, start, pushQueue, mode)
local value = m.getObjectValue(ref)
if not value then
return
@@ -2065,22 +2059,14 @@ function m.searchSameFieldsInValue(status, ref, start, queue, mode)
local newStatus = m.status(status)
m.searchRefs(newStatus, value, mode)
for _, res in ipairs(newStatus.results) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
- queue[#queue+1] = {
- obj = value,
- start = start,
- force = true,
- }
+ pushQueue(value, start, true)
-- 检查形如 a = f() 的分支情况
- m.checkSameSimpleInCall(status, value, start, queue, mode)
+ m.checkSameSimpleInCall(status, value, start, pushQueue, mode)
end
-function m.checkSameSimpleAsTableField(status, ref, start, queue)
+function m.checkSameSimpleAsTableField(status, ref, start, pushQueue)
if not status.deep then
--return
end
@@ -2094,11 +2080,7 @@ function m.checkSameSimpleAsTableField(status, ref, start, queue)
local newStatus = m.status(status)
m.searchRefs(newStatus, parent.field, 'ref')
for _, res in ipairs(newStatus.results) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
@@ -2112,7 +2094,7 @@ function m.checkSearchLevel(status)
return false
end
-function m.checkSameSimpleAsReturn(status, ref, start, queue)
+function m.checkSameSimpleAsReturn(status, ref, start, pushQueue)
if not status.deep then
return
end
@@ -2129,16 +2111,15 @@ function m.checkSameSimpleAsReturn(status, ref, start, queue)
m.searchRefsAsFunctionReturn(newStatus, ref, 'ref')
for _, res in ipairs(newStatus.results) do
if not m.checkCallMark(status, res) then
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
end
-function m.checkSameSimpleAsSetValue(status, ref, start, queue)
+function m.checkSameSimpleAsSetValue(status, ref, start, pushQueue)
+ if not status.deep then
+ --return
+ end
if ref.type == 'select' then
return
end
@@ -2171,11 +2152,7 @@ function m.checkSameSimpleAsSetValue(status, ref, start, queue)
local newStatus = m.status(status)
m.searchRefs(newStatus, obj, 'ref')
for _, res in ipairs(newStatus.results) do
- queue[#queue+1] = {
- obj = res,
- start = start,
- force = true,
- }
+ pushQueue(res, start, true)
end
end
@@ -2192,7 +2169,7 @@ local function hasTypeName(doc, name)
return false
end
-function m.checkSameSimpleInString(status, ref, start, queue, mode)
+function m.checkSameSimpleInString(status, ref, start, pushQueue, mode)
-- 特殊处理 ('xxx').xxx 的形式
if ref.type ~= 'string'
and not hasTypeName(ref, 'string') then
@@ -2204,6 +2181,13 @@ function m.checkSameSimpleInString(status, ref, start, queue, mode)
if status.share.searchingBindedDoc then
return
end
+ if not status.share.markString then
+ status.share.markString = {}
+ end
+ if status.share.markString[ref] then
+ return
+ end
+ status.share.markString[ref] = true
local newStatus = m.status(status)
local docs = status.interface.docType('string*')
local mark = {}
@@ -2216,12 +2200,10 @@ function m.checkSameSimpleInString(status, ref, start, queue, mode)
goto CONTINUE
end
mark[res] = true
- queue[#queue+1] = {
- obj = res,
- start = start + 1,
- }
+ pushQueue(res, start + 1)
::CONTINUE::
end
+ status.share.markString[ref] = nil
return true
end
@@ -2259,7 +2241,7 @@ function m.pushResult(status, mode, ref, simple)
end
end
if m.isLiteral(ref)
- and ref.parent.type == 'callargs'
+ and ref.parent and ref.parent.type == 'callargs'
and ref ~= simple.node then
results[#results+1] = ref
end
@@ -2316,7 +2298,7 @@ function m.pushResult(status, mode, ref, simple)
results[#results+1] = ref
elseif ref.type == 'getindex' then
-- do not trust `t[1]`
- if ref.index.type == 'string' then
+ if ref.index and ref.index.type == 'string' then
results[#results+1] = ref
end
elseif ref.type == 'setglobal'
@@ -2336,6 +2318,30 @@ function m.pushResult(status, mode, ref, simple)
or ref.type == 'doc.field' then
results[#results+1] = ref
end
+ elseif mode == 'deffield' then
+ if ref.type == 'setfield'
+ or ref.type == 'tablefield' then
+ results[#results+1] = ref
+ elseif ref.type == 'setmethod' then
+ results[#results+1] = ref
+ elseif ref.type == 'setindex'
+ or ref.type == 'tableindex' then
+ results[#results+1] = ref
+ elseif ref.type == 'setglobal' then
+ results[#results+1] = ref
+ elseif ref.type == 'function' then
+ results[#results+1] = ref
+ elseif ref.type == 'table' then
+ results[#results+1] = ref
+ elseif ref.type == 'call' then
+ if ref.node.special == 'rawset' then
+ results[#results+1] = ref
+ end
+ elseif ref.type == 'doc.type.function'
+ or ref.type == 'doc.class.name'
+ or ref.type == 'doc.field' then
+ results[#results+1] = ref
+ end
end
end
@@ -2344,7 +2350,7 @@ function m.checkSameSimpleName(ref, sm)
return true
end
- if sm == m.ANYNOTGET and not m.isGet(ref) then
+ if sm == m.ANY_DEF and m.isSet(ref) then
return true
end
@@ -2358,10 +2364,7 @@ function m.checkSameSimpleName(ref, sm)
return false
end
-function m.checkSameSimple(status, simple, data, mode, queue)
- local ref = data.obj
- local start = data.start
- local force = data.force
+function m.checkSameSimple(status, simple, ref, start, force, mode, pushQueue)
if start > #simple then
return
end
@@ -2376,30 +2379,30 @@ function m.checkSameSimple(status, simple, data, mode, queue)
cmode = 'ref'
end
-- 检查 doc
- local skipInfer = m.checkSameSimpleByBindDocs(status, ref, i, queue, cmode)
- or m.checkSameSimpleByDoc(status, ref, i, queue, cmode)
+ local skipInfer = m.checkSameSimpleByBindDocs(status, ref, i, pushQueue, cmode)
+ or m.checkSameSimpleByDoc(status, ref, i, pushQueue, cmode)
-- 检查自己是字符串的分支情况
- m.checkSameSimpleInString(status, ref, i, queue, cmode)
+ m.checkSameSimpleInString(status, ref, i, pushQueue, cmode)
if not skipInfer then
-- 穿透 self:func 与 mt:func
- m.searchSameFieldsCrossMethod(status, ref, i, queue)
+ m.searchSameFieldsCrossMethod(status, ref, i, pushQueue)
-- 穿透赋值
- m.searchSameFieldsInValue(status, ref, i, queue, cmode)
+ m.searchSameFieldsInValue(status, ref, i, pushQueue, cmode)
-- 检查自己是字面量表的情况
- m.checkSameSimpleInValueOfTable(status, ref, i, queue)
+ m.checkSameSimpleInValueOfTable(status, ref, i, pushQueue)
-- 检查自己作为 setmetatable 第一个参数的情况
- m.checkSameSimpleInArg1OfSetMetaTable(status, ref, i, queue)
+ m.checkSameSimpleInArg1OfSetMetaTable(status, ref, i, pushQueue)
-- 检查自己作为 setmetatable 调用的情况
- m.checkSameSimpleInValueOfCallMetaTable(status, ref, i, queue)
+ m.checkSameSimpleInValueOfCallMetaTable(status, ref, i, pushQueue)
-- 检查自己是特殊变量的分支的情况
- m.checkSameSimpleInSpecialBranch(status, ref, i, queue)
+ m.checkSameSimpleInSpecialBranch(status, ref, i, pushQueue)
if cmode == 'ref' then
-- 检查形如 { a = f } 的情况
- m.checkSameSimpleAsTableField(status, ref, i, queue)
+ m.checkSameSimpleAsTableField(status, ref, i, pushQueue)
-- 检查形如 return m 的情况
- m.checkSameSimpleAsReturn(status, ref, i, queue)
+ m.checkSameSimpleAsReturn(status, ref, i, pushQueue)
-- 检查形如 a = f 的情况
- m.checkSameSimpleAsSetValue(status, ref, i, queue)
+ m.checkSameSimpleAsSetValue(status, ref, i, pushQueue)
end
end
if i == #simple then
@@ -2417,55 +2420,81 @@ function m.checkSameSimple(status, simple, data, mode, queue)
end
end
+local queuesPool = {}
+local startsPool = {}
+local forcesPool = {}
+local poolSize = 0
+
+local function allocQueue()
+ if poolSize <= 0 then
+ return {}, {}, {}
+ else
+ local queues = queuesPool[poolSize]
+ local starts = startsPool[poolSize]
+ local forces = forcesPool[poolSize]
+ poolSize = poolSize - 1
+ return queues, starts, forces
+ end
+end
+
+local function deallocQueue(queues, starts, forces)
+ poolSize = poolSize + 1
+ queuesPool[poolSize] = queues
+ startsPool[poolSize] = starts
+ forcesPool[poolSize] = forces
+end
+
function m.searchSameFields(status, simple, mode)
- local queue = {}
+ local queues, starts, forces = allocQueue()
+ local queueLen = 0
+ local function pushQueue(obj, start, force)
+ queueLen = queueLen + 1
+ queues[queueLen] = obj
+ starts[queueLen] = start
+ forces[queueLen] = force
+ end
if simple.mode == 'global' then
-- 全局变量开头
- m.checkSameSimpleInGlobal(status, simple[1], simple.node, 1, queue)
+ m.checkSameSimpleInGlobal(status, simple[1], simple.node, 1, pushQueue)
elseif simple.mode == 'local' then
-- 局部变量开头
- queue[1] = {
- obj = simple.node,
- start = 1,
- }
+ pushQueue(simple.node, 1)
local refs = simple.node.ref
if refs then
for i = 1, #refs do
- queue[#queue+1] = {
- obj = refs[i],
- start = 1,
- }
+ pushQueue(refs[i], 1)
end
end
else
- queue[1] = {
- obj = simple.node,
- start = 1,
- }
+ pushQueue(simple.node, 1)
end
local max = 0
local locks = {}
for i = 1, 1e6 do
- local data = queue[i]
- if not data then
- return
+ if queueLen <= 0 then
+ break
end
- local lock = locks[data.start]
+ local obj = queues[queueLen]
+ local start = starts[queueLen]
+ local force = forces[queueLen]
+ queueLen = queueLen - 1
+ local lock = locks[start]
if not lock then
lock = {}
- locks[data.start] = lock
+ locks[start] = lock
end
- if not lock[data.obj] then
- lock[data.obj] = true
+ if not lock[obj] then
+ lock[obj] = true
max = max + 1
status.share.count = status.share.count + 1
- m.checkSameSimple(status, simple, data, mode, queue)
+ m.checkSameSimple(status, simple, obj, start, force, mode, pushQueue)
if max >= 10000 then
logWarn('Queue too large!')
break
end
end
end
+ deallocQueue(queues, starts, forces)
end
function m.getCallerInSameFile(status, func)
@@ -2655,6 +2684,9 @@ function m.getRefCache(status, obj, mode)
end
function m.searchRefs(status, obj, mode)
+ if not obj then
+ return
+ end
local cache, makeCache = m.getRefCache(status, obj, mode)
if cache then
for i = 1, #cache do
@@ -2664,13 +2696,16 @@ function m.searchRefs(status, obj, mode)
end
-- 检查单步引用
+ tracy.ZoneBeginN('searchRefs getStepRef')
local res = m.getStepRef(status, obj, mode)
if res then
for i = 1, #res do
status.results[#status.results+1] = res[i]
end
end
+ tracy.ZoneEnd()
-- 检查simple
+ tracy.ZoneBeginN('searchRefs searchSameFields')
if status.depth <= 100 then
local simple = m.getSimple(obj)
if simple then
@@ -2684,6 +2719,7 @@ function m.searchRefs(status, obj, mode)
logWarn('status.depth overflow')
end
end
+ tracy.ZoneEnd()
m.cleanResults(status.results)
@@ -3283,6 +3319,8 @@ local function mathCheck(status, a, b)
or m.getInferLiteral(status, a, 'number')
local v2 = m.getInferLiteral(status, b, 'integer')
or m.getInferLiteral(status, a, 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local int = m.hasType(status, a, 'integer')
and m.hasType(status, b, 'integer')
and not m.hasType(status, a, 'number')
@@ -3374,6 +3412,8 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
if v1 and v2 then
v = v1 <= v2
@@ -3390,6 +3430,8 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
if v1 and v2 then
v = v1 >= v2
@@ -3406,6 +3448,8 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
if v1 and v2 then
v = v1 < v2
@@ -3422,6 +3466,8 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
if v1 and v2 then
v = v1 > v2
@@ -3436,6 +3482,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '|' then
local v1 = m.getInferLiteral(status, source[1], 'integer')
local v2 = m.getInferLiteral(status, source[2], 'integer')
+ v1 = tointeger(v1)
+ v2 = tointeger(v2)
local v
if v1 and v2 then
v = v1 | v2
@@ -3450,6 +3498,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '~' then
local v1 = m.getInferLiteral(status, source[1], 'integer')
local v2 = m.getInferLiteral(status, source[2], 'integer')
+ v1 = tointeger(v1)
+ v2 = tointeger(v2)
local v
if v1 and v2 then
v = v1 ~ v2
@@ -3464,6 +3514,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '&' then
local v1 = m.getInferLiteral(status, source[1], 'integer')
local v2 = m.getInferLiteral(status, source[2], 'integer')
+ v1 = tointeger(v1)
+ v2 = tointeger(v2)
local v
if v1 and v2 then
v = v1 & v2
@@ -3478,6 +3530,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '<<' then
local v1 = m.getInferLiteral(status, source[1], 'integer')
local v2 = m.getInferLiteral(status, source[2], 'integer')
+ v1 = tointeger(v1)
+ v2 = tointeger(v2)
local v
if v1 and v2 then
v = v1 << v2
@@ -3492,6 +3546,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '>>' then
local v1 = m.getInferLiteral(status, source[1], 'integer')
local v2 = m.getInferLiteral(status, source[2], 'integer')
+ v1 = tointeger(v1)
+ v2 = tointeger(v2)
local v
if v1 and v2 then
v = v1 >> v2
@@ -3506,6 +3562,8 @@ function m.inferCheckBinary(status, source)
elseif op.type == '..' then
local v1 = m.getInferLiteral(status, source[1], 'string')
local v2 = m.getInferLiteral(status, source[2], 'string')
+ v1 = type(v1) == 'string' and v1 or nil
+ v2 = type(v2) == 'string' and v2 or nil
local v
if v1 and v2 then
v = v1 .. v2
@@ -3522,6 +3580,8 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
if v1 and v2 then
v = v1 ^ v2
@@ -3538,9 +3598,11 @@ function m.inferCheckBinary(status, source)
or m.getInferLiteral(status, source[1], 'number')
local v2 = m.getInferLiteral(status, source[2], 'integer')
or m.getInferLiteral(status, source[2], 'number')
+ v1 = tonumber(v1)
+ v2 = tonumber(v2)
local v
- if v1 and v2 then
- v = v1 > v2
+ if v1 and v2 and v2 ~= 0 then
+ v = v1 / v2
end
status.results = m.allocInfer {
type = 'number',
@@ -3581,7 +3643,7 @@ function m.inferCheckBinary(status, source)
local int, v1, v2 = mathCheck(status, source[1], source[2])
status.results = m.allocInfer {
type = int,
- value = (v1 and v2) and (v1 % v2) or nil,
+ value = (v1 and v2 and v2 ~= 0) and (v1 % v2) or nil,
source = source,
level = 100,
}
@@ -3590,7 +3652,7 @@ function m.inferCheckBinary(status, source)
local int, v1, v2 = mathCheck(status, source[1], source[2])
status.results = m.allocInfer {
type = int,
- value = (v1 and v2) and (v1 // v2) or nil,
+ value = (v1 and v2 and v2 ~= 0) and (v1 // v2) or nil,
source = source,
level = 100,
}
@@ -3608,7 +3670,9 @@ function m.inferByDef(status, obj)
status.share.inferedDef[obj] = true
local mark = {}
local newStatus = m.status(status, status.interface)
+ tracy.ZoneBeginN('inferByDef searchRefs')
m.searchRefs(newStatus, obj, 'def')
+ tracy.ZoneEnd()
for _, src in ipairs(newStatus.results) do
local inferStatus = m.status(newStatus)
m.searchInfer(inferStatus, src)
@@ -3913,6 +3977,9 @@ function m.inferByPCallReturn(status, source)
if not call or call.type ~= 'call' then
return
end
+ if not call.args then
+ return
+ end
local node = call.node
local specialName = node.special
local func, index
@@ -3989,6 +4056,9 @@ function m.searchInfer(status, obj)
end
obj = value
end
+ if not obj then
+ return
+ end
local cache, makeCache = m.getRefCache(status, obj, 'infer')
if cache then
@@ -4024,7 +4094,9 @@ function m.searchInfer(status, obj)
end
if status.deep then
+ tracy.ZoneBeginN('inferByDef')
m.inferByDef(status, obj)
+ tracy.ZoneEnd()
end
m.inferBySet(status, obj)
m.inferByCall(status, obj)
@@ -4068,8 +4140,8 @@ function m.requestDefinition(obj, interface, deep)
return status.results, status.share.count
end
---- 请求对象的域
----@param filterKey nil|string|table nil表fields不做限制;string表fields必须同名;table取值为guild.ANYSET表fields必须满足isSet()
+--- 请求对象的字段
+---@param filterKey nil|string|table
function m.requestFields(obj, interface, deep, filterKey)
local status = m.status(nil, interface, deep)
@@ -4078,6 +4150,16 @@ function m.requestFields(obj, interface, deep, filterKey)
return status.results, status.share.count
end
+--- 请求对象的定义字段
+---@param filterKey nil|string|table
+function m.requestDefFields(obj, interface, deep, filterKey)
+ local status = m.status(nil, interface, deep)
+
+ m.searchDefFields(status, obj, filterKey)
+
+ return status.results, status.share.count
+end
+
--- 请求对象的类型推测
function m.requestInfer(obj, interface, deep)
local status = m.status(nil, interface, deep)
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua
index ae644a65..990b4606 100644
--- a/script/parser/luadoc.lua
+++ b/script/parser/luadoc.lua
@@ -56,6 +56,7 @@ Symbol <- ({} {
/ '...'
/ '+'
/ '#'
+ / '`'
} {})
-> Symbol
]], {
@@ -450,12 +451,41 @@ function parseType(parent)
if not tp then
break
end
+
+ -- 处理 `T` 的情况
+ local typeLiteral = nil
+ if tp == 'symbol' and content == '`' then
+ nextToken()
+ if not checkToken('symbol', '`', 2) then
+ break
+ end
+ tp, content = peekToken()
+ if not tp then
+ break
+ end
+ -- TypeLiteral,指代类型的字面值。比如,对于类 Cat 来说,它的 TypeLiteral 是 "Cat"
+ typeLiteral = {
+ type = 'doc.type.typeliteral',
+ parent = result,
+ start = getStart(),
+ finish = nil,
+ node = nil,
+ }
+ end
+
if tp == 'name' then
nextToken()
local typeUnit = parseTypeUnit(result, content)
if not typeUnit then
break
end
+ if typeLiteral then
+ nextToken()
+ typeLiteral.finish = getFinish()
+ typeLiteral.node = typeUnit
+ typeUnit.parent = typeLiteral
+ typeUnit = typeLiteral
+ end
result.types[#result.types+1] = typeUnit
if not result.start then
result.start = typeUnit.start
@@ -907,11 +937,22 @@ local function buildLuaDoc(comment)
return result
end
+---当前行在注释doc前是否有代码
+local function haveCodeBeforeDocInCurLine(lineData, docStartCol)
+ return docStartCol > lineData.sp + lineData.tab + 3
+end
+
local function isNextLine(lns, binded, doc)
if not binded then
return false
end
local lastDoc = binded[#binded]
+ local lastDocStartRow, lastDocStartCol = guide.positionOf(lns, lastDoc.originalComment.start)
+ local lastDocStartLineData = guide.lineData(lns, lastDocStartRow)
+ if haveCodeBeforeDocInCurLine(lastDocStartLineData, lastDocStartCol) then
+ return false
+ end
+
local lastRow = guide.positionOf(lns, lastDoc.finish)
local newRow = guide.positionOf(lns, doc.start)
return newRow - lastRow == 1
@@ -1034,6 +1075,7 @@ return function (_, state)
if ast.finish < doc.finish then
ast.finish = doc.finish
end
+ doc.originalComment = comment
end
end
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 88493abe..50f4cd87 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -157,6 +157,7 @@ m.DiagnosticDefaultSeverity = {
['redundant-value'] = 'Hint',
['code-after-break'] = 'Hint',
['unbalanced-assignments'] = 'Warning',
+ ['close-non-object'] = 'Warning',
['duplicate-doc-class'] = 'Warning',
['undefined-doc-class'] = 'Warning',
@@ -196,6 +197,7 @@ m.DiagnosticDefaultNeededFileStatus = {
['redundant-value'] = 'Opened',
['code-after-break'] = 'Opened',
['unbalanced-assignments'] = 'Any',
+ ['close-non-object'] = 'Any',
['duplicate-doc-class'] = 'Any',
['undefined-doc-class'] = 'Any',
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index b155a591..1d8b779d 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -64,6 +64,9 @@ end
local function buildDiagnostic(uri, diag)
local lines = files.getLines(uri)
local text = files.getText(uri)
+ if not text or not lines then
+ return
+ end
local relatedInformation
if diag.related then
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index 2fb999e7..32778399 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -42,6 +42,11 @@ local function updateConfig()
exclude = configs[3],
}
+ if not updated then
+ log.warn('No config?', util.dump(configs))
+ return
+ end
+
local oldConfig = util.deepCopy(config.config)
local oldOther = util.deepCopy(config.other)
config.setConfig(updated, other)
@@ -206,7 +211,10 @@ proto.on('textDocument/hover', function (params)
end
local md = markdown()
md:add('lua', hover.label)
- md:add('md', "---")
+ if hover.label and #hover.label > 0
+ and hover.description and #hover.description > 0 then
+ md:add('md', "---")
+ end
md:add('md', hover.description)
return {
contents = {
diff --git a/script/service/service.lua b/script/service/service.lua
index 82c192b6..41c8e7bf 100644
--- a/script/service/service.lua
+++ b/script/service/service.lua
@@ -145,12 +145,24 @@ function m.startTimer()
end
end
+function m.testVersion()
+ local stack = debug.setcstacklimit(200)
+ debug.setcstacklimit(stack + 1)
+ if debug.setcstacklimit(stack) == stack + 1 then
+ proto.notify('window/showMessage', {
+ type = 2,
+ message = 'It seems to be running in Lua 5.4.0 or Lua 5.4.1 . Please upgrade to Lua 5.4.2 or above. Otherwise, it may encounter weird "C stack overflow", resulting in failure to work properly',
+ })
+ end
+end
+
function m.start()
util.enableCloseFunction()
await.setErrorHandle(log.error)
pub.recruitBraves(4)
proto.listen()
m.report()
+ m.testVersion()
require 'provider'
diff --git a/script/service/telemetry.lua b/script/service/telemetry.lua
index ec004a27..52ad193b 100644
--- a/script/service/telemetry.lua
+++ b/script/service/telemetry.lua
@@ -1,9 +1,10 @@
-local net = require 'service.net'
-local timer = require 'timer'
-local config = require 'config'
-local client = require 'provider.client'
-local nonil = require 'without-check-nil'
-local util = require 'utility'
+local net = require 'service.net'
+local timer = require 'timer'
+local config = require 'config'
+local client = require 'provider.client'
+local nonil = require 'without-check-nil'
+local util = require 'utility'
+local platform = require 'bee.platform'
local tokenPath = (ROOT / 'log' / 'token'):string()
local token = util.loadFile(tokenPath)
@@ -34,6 +35,16 @@ local function pushClientInfo(link)
))
end
+local function pushPlatformInfo(link)
+ send(link, string.pack('zzzzz'
+ , 'platform'
+ , token
+ , ('%s %s'):format(platform.OS, platform.Arch)
+ , ('%s %s'):format(platform.CRT, platform.CRTVersion)
+ , ('%s %s'):format(platform.Compiler, platform.CompilerVersion)
+ ))
+end
+
local function pushErrorLog(link)
if not log.firstError then
return
@@ -53,9 +64,19 @@ timer.wait(5, function ()
if not config.config.telemetry.enable then
return
end
- local link = net.connect('tcp', '119.45.194.183', 11577)
- pushClientInfo(link)
- pushErrorLog(link)
+ local suc, link = pcall(net.connect, 'tcp', 'moe-loli.love', 11577)
+ if not suc then
+ suc, link = pcall(net.connect, 'tcp', '119.45.194.183', 11577)
+ end
+ if not suc then
+ return
+ end
+ function link:on_connect()
+ pushClientInfo(link)
+ pushPlatformInfo(link)
+ pushErrorLog(link)
+ self:close()
+ end
end)()
timer.loop(1, function ()
if not config.config.telemetry.enable then
diff --git a/script/timer.lua b/script/timer.lua
index 1d4343f1..7857be6f 100644
--- a/script/timer.lua
+++ b/script/timer.lua
@@ -2,6 +2,8 @@ local setmetatable = setmetatable
local mathMax = math.max
local mathFloor = math.floor
local osClock = os.clock
+local xpcall = xpcall
+local logError = log.error
_ENV = nil
@@ -43,7 +45,7 @@ local function mWakeup(self)
end
self._running = false
if self._onTimer then
- self:_onTimer()
+ xpcall(self._onTimer, logError, self)
end
if self._removed then
return
diff --git a/script/tracy.lua b/script/tracy.lua
new file mode 100644
index 00000000..bf93a103
--- /dev/null
+++ b/script/tracy.lua
@@ -0,0 +1,30 @@
+local originTracy
+
+local function enable()
+ if not originTracy then
+ local suc = pcall(require, 'luatracy')
+ if suc then
+ originTracy = tracy
+ else
+ originTracy = {
+ ZoneBeginN = function (info) end,
+ ZoneEnd = function () end,
+ }
+ end
+ end
+ tracy = originTracy
+end
+
+local function disable()
+ tracy = {
+ ZoneBeginN = function (info) end,
+ ZoneEnd = function () end,
+ }
+end
+
+disable()
+
+return {
+ enable = enable,
+ disable = disable,
+}
diff --git a/script/utility.lua b/script/utility.lua
index 2386998b..a1eec7eb 100644
--- a/script/utility.lua
+++ b/script/utility.lua
@@ -19,6 +19,7 @@ local utf8Len = utf8.len
local mathHuge = math.huge
local inf = 1 / 0
local nan = 0 / 0
+local utf8 = utf8
_ENV = nil
@@ -477,11 +478,18 @@ function m.viewLiteral(v)
end
function m.utf8Len(str, start, finish)
- local len, pos = utf8Len(str, start, finish, true)
- if len then
- return len
+ local len = 0
+ for _ = 1, 10000 do
+ local clen, pos = utf8Len(str, start, finish, true)
+ if clen then
+ len = len + clen
+ break
+ else
+ len = len + 1 + utf8Len(str, start, pos - 1, true)
+ start = pos + 1
+ end
end
- return 1 + m.utf8Len(str, start, pos-1) + m.utf8Len(str, pos+1, finish)
+ return len
end
function m.revertTable(t)
diff --git a/script/vm/eachDef.lua b/script/vm/eachDef.lua
index 7825d2b1..8d031f42 100644
--- a/script/vm/eachDef.lua
+++ b/script/vm/eachDef.lua
@@ -33,6 +33,9 @@ function vm.getDefs(source, deep)
deep = deep or -999
if guide.isGlobal(source) then
local key = guide.getKeyName(source)
+ if not key then
+ return {}
+ end
return vm.getGlobalSets(key)
else
local cache = vm.getCache('eachDef')[source]
diff --git a/script/vm/eachField.lua b/script/vm/eachField.lua
index 2620aa41..690f6aa4 100644
--- a/script/vm/eachField.lua
+++ b/script/vm/eachField.lua
@@ -25,6 +25,27 @@ local function getFields(source, deep, filterKey)
return results
end
+local function getDefFields(source, deep, filterKey)
+ local unlock = vm.lock('eachDefField', source)
+ if not unlock then
+ return {}
+ end
+
+ while source.type == 'paren' do
+ source = source.exp
+ if not source then
+ return {}
+ end
+ end
+ deep = config.config.intelliSense.searchDepth + (deep or 0)
+
+ await.delay()
+ local results = guide.requestDefFields(source, vm.interface, deep, filterKey)
+
+ unlock()
+ return results
+end
+
local function getFieldsBySource(source, deep, filterKey)
deep = deep or -999
local cache = vm.getCache('eachField')[source]
@@ -38,12 +59,28 @@ local function getFieldsBySource(source, deep, filterKey)
return cache
end
+local function getDefFieldsBySource(source, deep, filterKey)
+ deep = deep or -999
+ local cache = vm.getCache('eachDefField')[source]
+ if not cache or cache.deep < deep then
+ cache = getDefFields(source, deep, filterKey)
+ cache.deep = deep
+ if not filterKey then
+ vm.getCache('eachDefField')[source] = cache
+ end
+ end
+ return cache
+end
+
function vm.getFields(source, deep)
if source.special == '_G' then
return vm.getGlobals '*'
end
if guide.isGlobal(source) then
local name = guide.getKeyName(source)
+ if not name then
+ return {}
+ end
local cache = vm.getCache('eachFieldOfGlobal')[name]
or getFieldsBySource(source, deep)
vm.getCache('eachFieldOfGlobal')[name] = cache
@@ -53,13 +90,20 @@ function vm.getFields(source, deep)
end
end
-function vm.getFieldsOfDocClassAnyNotGet(source, deep)
- if not guide.isDocClass(source) then
- return {}
+function vm.getDefFields(source, deep)
+ if source.special == '_G' then
+ return vm.getGlobalSets '*'
+ end
+ if guide.isGlobal(source) then
+ local name = guide.getKeyName(source)
+ if not name then
+ return {}
+ end
+ local cache = vm.getCache('eachDefFieldOfGlobal')[name]
+ or getDefFieldsBySource(source, deep)
+ vm.getCache('eachDefFieldOfGlobal')[name] = cache
+ return cache
+ else
+ return getDefFieldsBySource(source, deep)
end
-
- local cache = vm.getCache('eachFieldOfDocClass')[source]
- or getFieldsBySource(source, deep, guide.ANYNOTGET)
- vm.getCache('eachFieldOfDocClass')[source] = cache
- return cache
end
diff --git a/script/vm/eachRef.lua b/script/vm/eachRef.lua
index e9229c38..1073ecbe 100644
--- a/script/vm/eachRef.lua
+++ b/script/vm/eachRef.lua
@@ -32,6 +32,9 @@ function vm.getRefs(source, deep)
deep = deep or -999
if guide.isGlobal(source) then
local key = guide.getKeyName(source)
+ if not key then
+ return {}
+ end
return vm.getGlobals(key)
else
local cache = vm.getCache('eachRef')[source]
diff --git a/script/vm/getDocs.lua b/script/vm/getDocs.lua
index 1c54d593..632dd1c2 100644
--- a/script/vm/getDocs.lua
+++ b/script/vm/getDocs.lua
@@ -51,6 +51,9 @@ local function getDocTypes(name)
end
function vm.getDocEnums(doc, mark, results)
+ if not doc then
+ return nil
+ end
mark = mark or {}
if mark[doc] then
return nil
diff --git a/script/vm/guideInterface.lua b/script/vm/guideInterface.lua
index a73067c5..7fd515eb 100644
--- a/script/vm/guideInterface.lua
+++ b/script/vm/guideInterface.lua
@@ -22,7 +22,7 @@ function m.searchFileReturn(results, ast, index)
end
function m.require(args, index)
- local reqName = args[1] and args[1][1]
+ local reqName = args and args[1] and args[1][1]
if not reqName then
return nil
end
diff --git a/script/workspace/require-path.lua b/script/workspace/require-path.lua
index f1dc2fb9..4503f0ee 100644
--- a/script/workspace/require-path.lua
+++ b/script/workspace/require-path.lua
@@ -17,7 +17,7 @@ local function getOnePath(path, searcher)
local start = stemSearcher:match '()%?' or 1
for pos = start, #stemPath do
local word = stemPath:sub(start, pos)
- local newSearcher = stemSearcher:gsub('%?', word)
+ local newSearcher = stemSearcher:gsub('%?', (word:gsub('%%', '%%%%')))
if newSearcher == stemPath then
return word
end
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index c76eec55..c493823d 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -34,30 +34,32 @@ function m.init(uri)
log.init(ROOT, logPath)
end
-local function interfaceFactory(root)
- return {
- type = function (path)
- if fs.is_directory(fs.path(root .. '/' .. path)) then
- return 'directory'
+local globInteferFace = {
+ type = function (path)
+ local result
+ pcall(function ()
+ if fs.is_directory(fs.path(path)) then
+ result = 'directory'
else
- return 'file'
+ result = 'file'
end
- end,
- list = function (path)
- local fullPath = fs.path(root .. '/' .. path)
- if not fs.exists(fullPath) then
- return nil
- end
- local paths = {}
- pcall(function ()
- for fullpath in fullPath:list_directory() do
- paths[#paths+1] = fullpath:string()
- end
- end)
- return paths
+ end)
+ return result
+ end,
+ list = function (path)
+ local fullPath = fs.path(path)
+ if not fs.exists(fullPath) then
+ return nil
end
- }
-end
+ local paths = {}
+ pcall(function ()
+ for fullpath in fullPath:list_directory() do
+ paths[#paths+1] = fullpath:string()
+ end
+ end)
+ return paths
+ end
+}
--- 创建排除文件匹配器
function m.getNativeMatcher()
@@ -68,7 +70,6 @@ function m.getNativeMatcher()
return m.nativeMatcher
end
- local interface = interfaceFactory(m.path)
local pattern = {}
-- config.workspace.ignoreDir
for path in pairs(config.config.workspace.ignoreDir) do
@@ -119,7 +120,7 @@ function m.getNativeMatcher()
pattern[#pattern+1] = path
end
- m.nativeMatcher = glob.gitignore(pattern, m.matchOption, interface)
+ m.nativeMatcher = glob.gitignore(pattern, m.matchOption, globInteferFace)
m.nativeVersion = config.version
return m.nativeMatcher
@@ -140,16 +141,18 @@ function m.getLibraryMatchers()
end
m.libraryMatchers = {}
for path, pattern in pairs(librarys) do
- local nPath = fs.absolute(fs.path(path)):string()
- local matcher = glob.gitignore(pattern, m.matchOption)
- if platform.OS == 'Windows' then
- matcher:setOption 'ignoreCase'
+ if fs.exists(fs.path(path)) then
+ local nPath = fs.absolute(fs.path(path)):string()
+ local matcher = glob.gitignore(pattern, m.matchOption, globInteferFace)
+ if platform.OS == 'Windows' then
+ matcher:setOption 'ignoreCase'
+ end
+ log.debug('getLibraryMatchers', path, nPath)
+ m.libraryMatchers[#m.libraryMatchers+1] = {
+ path = nPath,
+ matcher = matcher
+ }
end
- log.debug('getLibraryMatchers', path, nPath)
- m.libraryMatchers[#m.libraryMatchers+1] = {
- path = nPath,
- matcher = matcher
- }
end
m.libraryVersion = config.version
@@ -168,7 +171,7 @@ end
local function loadFileFactory(root, progress, isLibrary)
return function (path)
- local uri = furi.encode(root .. '/' .. path)
+ local uri = furi.encode(path)
if not files.isLua(uri) then
return
end
@@ -246,15 +249,11 @@ function m.awaitPreload()
local native = m.getNativeMatcher()
local librarys = m.getLibraryMatchers()
if native then
- native:scan(nativeLoader)
+ native:scan(m.path, nativeLoader)
end
for _, library in ipairs(librarys) do
- local libraryInterface = interfaceFactory(library.path)
- local libraryLoader = loadFileFactory(library.path, progress, true)
- for k, v in pairs(libraryInterface) do
- library.matcher:setInterface(k, v)
- end
- library.matcher:scan(libraryLoader)
+ local libraryLoader = loadFileFactory(library.path, progress, true)
+ library.matcher:scan(library.path, libraryLoader)
end
log.info(('Found %d files.'):format(progress.max))
diff --git a/test.lua b/test.lua
index ed8d8dfc..cc69416d 100644
--- a/test.lua
+++ b/test.lua
@@ -18,6 +18,7 @@ log.debug('测试开始')
ac = {}
--dofile((ROOT / 'build_package.lua'):string())
+require 'tracy'
local function loadAllLibs()
assert(require 'bee.filesystem')
@@ -46,6 +47,7 @@ local function main()
debug.setcstacklimit(1000)
require 'parser.guide'.debugMode = true
require 'language' 'zh-cn'
+ require 'utility'.enableCloseFunction()
local function test(name)
local clock = os.clock()
print(('测试[%s]...'):format(name))
@@ -80,3 +82,5 @@ loadAllLibs()
main()
log.debug('测试完成')
+require 'bee.thread'.sleep(1)
+os.exit()
diff --git a/test/completion/init.lua b/test/completion/init.lua
index 662df84e..69ca83c3 100644
--- a/test/completion/init.lua
+++ b/test/completion/init.lua
@@ -1071,10 +1071,10 @@ function$
{
label = 'function ()',
kind = define.CompletionItemKind.Snippet,
- insertText = [[
-function $1($2)
- $0
-end]],
+ insertText = "\z
+function $1($2)\
+\t$0\
+end",
},
}
@@ -1089,10 +1089,10 @@ local t = function$
{
label = 'function ()',
kind = define.CompletionItemKind.Snippet,
- insertText = [[
-function ($1)
- $0
-end]],
+ insertText = "\z
+function ($1)\
+\t$0\
+end",
},
}
Cared['insertText'] = false
@@ -1964,10 +1964,10 @@ f($)
{
label = 'fun(x: number, y: number):string',
kind = define.CompletionItemKind.Function,
- insertText = [[
-function (${1:x}, ${2:y})
- $0
-end]],
+ insertText = "\z
+function (${1:x}, ${2:y})\
+\t$0\
+end",
},
}
Cared['insertText'] = nil
diff --git a/test/crossfile/definition.lua b/test/crossfile/definition.lua
index 839a3e89..30d796c8 100644
--- a/test/crossfile/definition.lua
+++ b/test/crossfile/definition.lua
@@ -615,3 +615,28 @@ TEST {
]]
},
}
+
+TEST {
+ {
+ path = 'a.lua',
+ content = [[
+ local lib = {}
+
+ function lib:fn1()
+ return self
+ end
+
+ function lib:<!fn2!>()
+ end
+
+ return lib:fn1()
+ ]]
+ },
+ {
+ path = 'b.lua',
+ content = [[
+ local app = require 'a'
+ print(app.<?fn2?>)
+ ]]
+ },
+}
diff --git a/test/definition/bug.lua b/test/definition/bug.lua
index e7158848..8c446123 100644
--- a/test/definition/bug.lua
+++ b/test/definition/bug.lua
@@ -146,3 +146,26 @@ t.<!f1!> = t.f2
print(t.<?f2?>)
]]
+
+TEST [[
+---@type string
+string.xx = ''
+string.xx:<?format?>()
+]]
+
+TEST [[
+---@class Foo
+Foo = {}
+function Foo:Constructor()
+ self.<!bar1!> = 1
+end
+
+---@class Foo2: Foo
+Foo2 = {}
+function Foo2:Constructor()
+end
+
+---@type Foo2
+local v
+v.<?bar1?>
+]]
diff --git a/test/definition/luadoc.lua b/test/definition/luadoc.lua
index 31134135..1f3dae00 100644
--- a/test/definition/luadoc.lua
+++ b/test/definition/luadoc.lua
@@ -169,3 +169,87 @@ end
AAAA.a.<?SSDF?>
]]
+
+TEST [[
+---@class Cat
+local <!m!> ---hahaha
+---@class Dog
+local m2
+---@type Cat
+local <?<!v!>?>
+]]
+
+TEST [[
+---@class Cat
+local <!m!> --hahaha
+---@class Dog
+local m2
+---@type Cat
+local <?<!v!>?>
+]]
+
+TEST [[
+---@class Cat
+ local <!m!> ---hahaha
+
+ ---@class Dog
+ local m2
+ ---@type Cat
+ local <?<!v!>?>
+]]
+
+TEST [[
+---@class Foo
+local Foo = {}
+function Foo:<!bar1!>() end
+
+---@generic T
+---@param arg1 T
+---@return T
+function Generic(arg1) print(arg1) end
+
+local v1 = Generic(Foo)
+print(v1.<?bar1?>)
+]]
+
+TEST [[
+---@class Foo
+local Foo = {}
+function Foo:bar1() end
+
+---@generic T
+---@param arg1 T
+---@return T
+function Generic(arg1) print(arg1) end
+
+local v1 = Generic("Foo")
+print(v1.<?bar1?>)
+]]
+
+TEST [[
+---@class Foo
+local Foo = {}
+function Foo:bar1() end
+
+---@generic T
+---@param arg1 `T`
+---@return T
+function Generic(arg1) print(arg1) end
+
+local v1 = Generic(Foo)
+print(v1.<?bar1?>)
+]]
+
+TEST [[
+---@class Foo
+local Foo = {}
+function Foo:<!bar1!>() end
+
+---@generic T
+---@param arg1 `T`
+---@return T
+function Generic(arg1) print(arg1) end
+
+local v1 = Generic("Foo")
+print(v1.<?bar1?>)
+]]
diff --git a/test/diagnostics/init.lua b/test/diagnostics/init.lua
index cfd0f4cb..d95bf380 100644
--- a/test/diagnostics/init.lua
+++ b/test/diagnostics/init.lua
@@ -76,7 +76,7 @@ local <!x!>
]]
TEST [[
-local x <close>
+local x <close> = print
]]
TEST [[
@@ -319,6 +319,16 @@ return [[
]]
]=]
+config.config.diagnostics.disable['close-non-object'] = true
+TEST [[
+local _ <close> = function () end
+]]
+
+config.config.diagnostics.disable['close-non-object'] = nil
+TEST [[
+local _ <close> = <!1!>
+]]
+
config.config.diagnostics.disable['unused-local'] = true
TEST [[
local f = <!function () end!>
@@ -340,8 +350,7 @@ TEST [[
--<!function F() end!>
--]]
-config.config.diagnostics.disable['unused-local'] = false
-config.config.diagnostics.disable['unused-function'] = true
+config.config.diagnostics.disable['unused-local'] = nil
TEST [[
local mt, x
function mt:m()
@@ -823,11 +832,7 @@ TEST [[
---@class class
local t
]]
-
-TEST [[
-local _ <close> = function () end
-]]
-
+---[==[
-- checkUndefinedField 通用
TEST [[
---@class Foo
@@ -945,3 +950,19 @@ v2 = v
v2:method1()
v2:method2() -- 这个感觉实际应该报错更合适
]]
+
+TEST [[
+---@type table
+T1 = {}
+print(T1.f1)
+---@type table*
+T2 = {}
+print(T2.<!f2!>)
+]]
+--]==]
+TEST [[
+---@overload fun(...)
+local function f() end
+
+f(1)
+]]
diff --git a/test/full/example.lua b/test/full/example.lua
index b19f0485..4f6090ee 100644
--- a/test/full/example.lua
+++ b/test/full/example.lua
@@ -41,6 +41,7 @@ local function testIfExit(path)
local lines = parser:lines(buf)
for i = 1, max do
files.removeAll()
+ files.open('')
files.setText('', buf)
diag('', function () end)
local passed = os.clock() - clock
@@ -52,6 +53,8 @@ local function testIfExit(path)
print(('基准诊断测试[%s]单次耗时:%.10f'):format(path:filename():string(), need))
end
end
+
+require 'tracy' .enable()
testIfExit(ROOT / 'test' / 'example' / 'vm.txt')
testIfExit(ROOT / 'test' / 'example' / 'largeGlobal.txt')
testIfExit(ROOT / 'test' / 'example' / 'guide.txt')
diff --git a/test/full/init.lua b/test/full/init.lua
index f370671e..ad34da7d 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -11,3 +11,4 @@ end
require 'full.normal'
require 'full.example'
require 'full.dirty'
+require 'full.self'
diff --git a/test/full/self.lua b/test/full/self.lua
new file mode 100644
index 00000000..247702ae
--- /dev/null
+++ b/test/full/self.lua
@@ -0,0 +1,29 @@
+local files = require 'files'
+local fsu = require 'fs-utility'
+local furi = require 'file-uri'
+local diag = require 'provider.diagnostic'
+local config = require 'config'
+files.removeAll()
+
+fsu.scanDirectory(ROOT, function (path)
+ if path:extension():string() ~= '.lua' then
+ return
+ end
+ local uri = furi.encode(path:string())
+ local text = fsu.loadFile(path)
+ files.setText(uri, text)
+ files.open(uri)
+end)
+
+config.config.diagnostics.disable['undefined-field'] = true
+config.config.diagnostics.disable['redundant-parameter'] = true
+diag.start()
+
+local clock = os.clock()
+
+for uri in files.eachFile() do
+ diag.doDiagnostic(uri)
+end
+
+local passed = os.clock() - clock
+print('基准全量诊断用时:', passed)
diff --git a/test/signature/init.lua b/test/signature/init.lua
index 765e0814..fca995bd 100644
--- a/test/signature/init.lua
+++ b/test/signature/init.lua
@@ -174,3 +174,19 @@ function Foo(param01: any, param02: any)
]],
arg = {14, 25},
}
+
+TEST [[
+function f1(a, b)
+end
+
+function f2(c, d)
+end
+
+f2(f1(),$)
+]]
+{
+ label = [[
+function f2(c: any, d: any)
+]],
+ arg = {21, 26},
+}
diff --git a/test/???.md b/test/???.md
deleted file mode 100644
index 46a4b58a..00000000
--- a/test/???.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# 如何搜索引用
-
-```lua
-local x = 1
-print(x) -- 通过语法搜索到 local x
-```
-
-```lua
-local function f()
-end
-
-local x = f
-print(x) -- 通过 x 的赋值搜索到函数
-```
-
-```lua
-X.Y.Z = 1
-print(X.Y.Z) -- 通过 field 的赋值行为搜索
-```
-
-```lua
-local function f()
- return f
-end
-
-local x = f()
-print(x) -- 引用不穿透函数调用?
-```
-
-```lua
-local t = {
- x = 1
-}
-
-print(t.x) -- 引用穿透表
-```
-
-```lua
-local function f()
- return {
- x = 1
- }
-end
-
-local t = f()
-print(t.x) -- 是否穿透函数返回的表?
-```
-
-在栈帧上标记值?
-
-```lua
-X.Y.Z = 1
-local t = X.Y
-print(t.Z)
-```
-
-字符串匹配?
-1. t -> Z
-2. X.Y -> Z
-3. X.Y.Z = 1
-
-语义匹配?
-1. t -> Z
-2. X.Y -> Z
-3. X -> Y -> Z
-4. X.Y.Z = 1
-
-建立标记?
-```lua
-{
- type = 'set',
- key = {
- 's|X',
- 's|Y',
- 's|Z',
- }
- v = 1,
-},
-{
- type = 'local',
- key = 't',
- v = {
- 's|X',
- 's|Y',
- },
-},
-{
- type = 'get',
- key = {
- 'l|t',
- 's|Z',
- }
-}
-```