summaryrefslogtreecommitdiff
path: root/script
diff options
context:
space:
mode:
Diffstat (limited to 'script')
-rw-r--r--script/await.lua9
-rw-r--r--script/client.lua19
-rw-r--r--script/config/config.lua2
-rw-r--r--script/config/loader.lua22
-rw-r--r--script/core/code-action.lua40
-rw-r--r--script/core/command/autoRequire.lua4
-rw-r--r--script/core/command/jsonToLua.lua1
-rw-r--r--script/core/command/removeSpace.lua1
-rw-r--r--script/core/command/solve.lua1
-rw-r--r--script/core/completion.lua218
-rw-r--r--script/core/diagnostics/await-in-sync.lua30
-rw-r--r--script/core/diagnostics/deprecated.lua3
-rw-r--r--script/core/diagnostics/discard-returns.lua29
-rw-r--r--script/core/diagnostics/duplicate-doc-class.lua3
-rw-r--r--script/core/diagnostics/init.lua1
-rw-r--r--script/core/diagnostics/not-yieldable.lua63
-rw-r--r--script/core/diagnostics/redundant-value.lua3
-rw-r--r--script/core/diagnostics/type-check.lua30
-rw-r--r--script/core/diagnostics/undefined-doc-param.lua3
-rw-r--r--script/core/diagnostics/undefined-field.lua2
-rw-r--r--script/core/diagnostics/undefined-global.lua3
-rw-r--r--script/core/diagnostics/unused-function.lua4
-rw-r--r--script/core/document-symbol.lua6
-rw-r--r--script/core/folding.lua3
-rw-r--r--script/core/generic.lua2
-rw-r--r--script/core/hint.lua44
-rw-r--r--script/core/hover/arg.lua22
-rw-r--r--script/core/hover/description.lua2
-rw-r--r--script/core/hover/init.lua8
-rw-r--r--script/core/hover/label.lua14
-rw-r--r--script/core/hover/table.lua1
-rw-r--r--script/core/infer.lua2
-rw-r--r--script/core/noder.lua40
-rw-r--r--script/core/semantic-tokens.lua3
-rw-r--r--script/core/signature.lua5
-rw-r--r--script/core/workspace-symbol.lua1
-rw-r--r--script/files.lua30
-rw-r--r--script/filewatch.lua1
-rw-r--r--script/glob/gitignore.lua3
-rw-r--r--script/global.d.lua7
-rw-r--r--script/library.lua5
-rw-r--r--script/parser/luadoc.lua111
-rw-r--r--script/parser/newparser.lua32
-rw-r--r--script/plugin.lua5
-rw-r--r--script/proto/converter.lua5
-rw-r--r--script/proto/define.lua6
-rw-r--r--script/proto/proto.lua26
-rw-r--r--script/provider/capability.lua1
-rw-r--r--script/provider/diagnostic.lua36
-rw-r--r--script/provider/provider.lua1463
-rw-r--r--script/pub/pub.lua1
-rw-r--r--script/service/telemetry.lua2
-rw-r--r--script/utility.lua1
-rw-r--r--script/vm/eachDef.lua2
-rw-r--r--script/vm/eachRef.lua2
-rw-r--r--script/vm/getDocs.lua136
-rw-r--r--script/vm/getGlobals.lua2
-rw-r--r--script/vm/getLinks.lua2
-rw-r--r--script/vm/vm.lua2
-rw-r--r--script/workspace/require-path.lua8
-rw-r--r--script/workspace/workspace.lua24
61 files changed, 1609 insertions, 948 deletions
diff --git a/script/await.lua b/script/await.lua
index e92af272..ff840956 100644
--- a/script/await.lua
+++ b/script/await.lua
@@ -25,7 +25,7 @@ local function setID(id, co, callback)
end
--- 设置错误处理器
----@param errHandle function {comment = '当有错误发生时,会以错误堆栈为参数调用该函数'}
+---@param errHandle function # 当有错误发生时,会以错误堆栈为参数调用该函数
function m.setErrorHandle(errHandle)
m.errorHandle = errHandle
end
@@ -39,6 +39,7 @@ function m.checkResult(co, ...)
end
--- 创建一个任务
+---@param callback async fun()
function m.call(callback, ...)
local co = coroutine.create(callback)
local closers = {}
@@ -66,6 +67,7 @@ function m.call(callback, ...)
end
--- 创建一个任务,并挂起当前线程,当任务完成后再延续当前线程/若任务被关闭,则返回nil
+---@async
function m.await(callback, ...)
if not coroutine.isyieldable() then
return callback(...)
@@ -109,6 +111,7 @@ end
--- 休眠一段时间
---@param time number
+---@async
function m.sleep(time)
if not coroutine.isyieldable() then
if m.errorHandle then
@@ -128,6 +131,7 @@ end
--- 等待直到唤醒
---@param callback function
+---@async
function m.wait(callback, ...)
if not coroutine.isyieldable() then
return
@@ -148,6 +152,7 @@ function m.wait(callback, ...)
end
--- 延迟
+---@async
function m.delay()
if not m._enable then
return
@@ -174,6 +179,7 @@ function m.delay()
end
--- stop then close
+---@async
function m.stop()
if not coroutine.isyieldable() then
return
@@ -239,6 +245,7 @@ function m.disable()
end
--- 注册事件
+---@param callback async fun(ev: string, ...)
function m.watch(callback)
m.watchList[#m.watchList+1] = callback
end
diff --git a/script/client.lua b/script/client.lua
index 93ac38b3..4d39cd0d 100644
--- a/script/client.lua
+++ b/script/client.lua
@@ -59,6 +59,23 @@ function m.getAbility(name)
return current
end
+function m.getOffsetEncoding()
+ if m._offsetEncoding then
+ return m._offsetEncoding
+ end
+ local clientEncodings = m.getAbility 'offsetEncoding'
+ if type(clientEncodings) == 'table' then
+ for _, encoding in ipairs(clientEncodings) do
+ if encoding == 'utf-8' then
+ m._offsetEncoding = 'utf-8'
+ return m._offsetEncoding
+ end
+ end
+ end
+ m._offsetEncoding = 'utf-16'
+ return m._offsetEncoding
+end
+
local function packMessage(...)
local strs = table.pack(...)
for i = 1, strs.n do
@@ -88,6 +105,7 @@ end
---@param titles string[]
---@return string action
---@return integer index
+---@async
function m.awaitRequestMessage(type, message, titles)
proto.notify('window/logMessage', {
type = define.MessageType[type] or 3,
@@ -254,6 +272,7 @@ function m.init(t)
m.client(t.clientInfo.name)
nonil.disable()
lang(LOCALE or t.locale)
+ converter.setOffsetEncoding(m.getOffsetEncoding())
hookPrint()
end
diff --git a/script/config/config.lua b/script/config/config.lua
index a361d68f..0e18428f 100644
--- a/script/config/config.lua
+++ b/script/config/config.lua
@@ -151,6 +151,7 @@ local Template = {
"?.lua",
"?/init.lua",
},
+ ['Lua.runtime.pathStrict'] = Type.Boolean >> false,
['Lua.runtime.special'] = Type.Hash(Type.String, Type.String),
['Lua.runtime.meta'] = Type.String >> '${version} ${language} ${encoding}',
['Lua.runtime.unicodeName'] = Type.Boolean,
@@ -198,6 +199,7 @@ local Template = {
['Lua.hint.paramType'] = Type.Boolean >> true,
['Lua.hint.setType'] = Type.Boolean >> false,
['Lua.hint.paramName'] = Type.String >> 'All',
+ ['Lua.hint.await'] = Type.Boolean >> true,
['Lua.window.statusBar'] = Type.Boolean >> true,
['Lua.window.progressBar'] = Type.Boolean >> true,
['Lua.IntelliSense.traceLocalSet'] = Type.Boolean >> false,
diff --git a/script/config/loader.lua b/script/config/loader.lua
index 03fe9456..e754be49 100644
--- a/script/config/loader.lua
+++ b/script/config/loader.lua
@@ -19,37 +19,43 @@ local m = {}
function m.loadRCConfig(filename)
local path = workspace.getAbsolutePath(filename)
if not path then
- return
+ m.lastRCConfig = nil
+ return nil
end
local buf = util.loadFile(path)
if not buf then
- return
+ m.lastRCConfig = nil
+ return nil
end
local suc, res = pcall(json.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return
+ return m.lastRCConfig
end
+ m.lastRCConfig = res
return res
end
function m.loadLocalConfig(filename)
local path = workspace.getAbsolutePath(filename)
if not path then
- return
+ m.lastLocalConfig = nil
+ return nil
end
local buf = util.loadFile(path)
if not buf then
errorMessage(lang.script('CONFIG_LOAD_FAILED', path))
- return
+ m.lastLocalConfig = nil
+ return nil
end
local firstChar = buf:match '%S'
if firstChar == '{' then
local suc, res = pcall(json.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return
+ return m.lastLocalConfig
end
+ m.lastLocalConfig = res
return res
else
local suc, res = pcall(function ()
@@ -57,12 +63,14 @@ function m.loadLocalConfig(filename)
end)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return
+ return m.lastLocalConfig
end
+ m.lastLocalConfig = res
return res
end
end
+---@async
function m.loadClientConfig()
local configs = proto.awaitRequest('workspace/configuration', {
items = {
diff --git a/script/core/code-action.lua b/script/core/code-action.lua
index 8256107e..ad048c48 100644
--- a/script/core/code-action.lua
+++ b/script/core/code-action.lua
@@ -308,6 +308,44 @@ local function solveTrailingSpace(uri, diag, results)
}
end
+local function solveAwaitInSync(uri, diag, results)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+ local start, finish = converter.unpackRange(uri, diag.range)
+ local parentFunction
+ guide.eachSourceType(state.ast, 'function', function (source)
+ if source.start > finish
+ or source.finish < start then
+ return
+ end
+ if not parentFunction or parentFunction.start < source.start then
+ parentFunction = source
+ end
+ end)
+ if not parentFunction then
+ return
+ end
+ local row = guide.rowColOf(parentFunction.start)
+ local pos = guide.positionOf(row, 0)
+ results[#results+1] = {
+ title = lang.script.ACTION_MARK_ASYNC,
+ kind = 'quickfix',
+ edit = {
+ changes = {
+ [uri] = {
+ {
+ start = pos,
+ finish = pos,
+ newText = '---@async\n',
+ }
+ }
+ }
+ },
+ }
+end
+
local function solveDiagnostic(uri, diag, start, results)
if diag.source == lang.script.DIAG_SYNTAX_CHECK then
solveSyntax(uri, diag, results)
@@ -326,6 +364,8 @@ local function solveDiagnostic(uri, diag, start, results)
solveAmbiguity1(uri, diag, results)
elseif diag.code == 'trailing-space' then
solveTrailingSpace(uri, diag, results)
+ elseif diag.code == 'await-in-sync' then
+ solveAwaitInSync(uri, diag, results)
end
disableDiagnostic(uri, diag.code, start, results)
end
diff --git a/script/core/command/autoRequire.lua b/script/core/command/autoRequire.lua
index aa641967..30bd13a1 100644
--- a/script/core/command/autoRequire.lua
+++ b/script/core/command/autoRequire.lua
@@ -63,6 +63,7 @@ local function findInsertRow(uri)
return row or 0, fmt
end
+---@async
local function askAutoRequire(visiblePaths)
local selects = {}
local nameMap = {}
@@ -125,6 +126,7 @@ local function applyAutoRequire(uri, row, name, result, fmt)
})
end
+---@async
return function (data)
local uri = data.uri
local target = data.target
@@ -135,7 +137,7 @@ return function (data)
end
local path = furi.decode(target)
- local visiblePaths = rpath.getVisiblePath(path, config.get 'Lua.runtime.path')
+ local visiblePaths = rpath.getVisiblePath(path)
if not visiblePaths or #visiblePaths == 0 then
return
end
diff --git a/script/core/command/jsonToLua.lua b/script/core/command/jsonToLua.lua
index 6aecee2c..d29ad608 100644
--- a/script/core/command/jsonToLua.lua
+++ b/script/core/command/jsonToLua.lua
@@ -7,6 +7,7 @@ local lang = require 'language'
local converter = require 'proto.converter'
local guide = require 'parser.guide'
+---@async
return function (data)
local state = files.getState(data.uri)
local text = files.getText(data.uri)
diff --git a/script/core/command/removeSpace.lua b/script/core/command/removeSpace.lua
index 3021d4a4..aa565f7f 100644
--- a/script/core/command/removeSpace.lua
+++ b/script/core/command/removeSpace.lua
@@ -12,6 +12,7 @@ local function isInString(ast, offset)
end) or false
end
+---@async
return function (data)
local uri = data.uri
local text = files.getText(uri)
diff --git a/script/core/command/solve.lua b/script/core/command/solve.lua
index f7831f7f..8065aa9d 100644
--- a/script/core/command/solve.lua
+++ b/script/core/command/solve.lua
@@ -27,6 +27,7 @@ local literalMap = {
['table'] = true,
}
+---@async
return function (data)
local uri = data.uri
local text = files.getText(uri)
diff --git a/script/core/completion.lua b/script/core/completion.lua
index cdd01f98..4dd55070 100644
--- a/script/core/completion.lua
+++ b/script/core/completion.lua
@@ -20,6 +20,7 @@ local lookBackward = require 'core.look-backward'
local guide = require 'parser.guide'
local infer = require 'core.infer'
local noder = require 'core.noder'
+local await = require 'await'
local DiagnosticModes = {
'disable-next-line',
@@ -30,6 +31,8 @@ local DiagnosticModes = {
local stackID = 0
local stacks = {}
+
+---@param callback async fun()
local function stack(callback)
stackID = stackID + 1
stacks[stackID] = callback
@@ -40,6 +43,7 @@ local function clearStack()
stacks = {}
end
+---@async
local function resolveStack(id)
local callback = stacks[id]
if not callback then
@@ -87,7 +91,8 @@ local function findNearestTableField(state, position)
return source
end
-local function findParent(state, text, position)
+local function findParent(state, position)
+ local text = state.lua
local offset = guide.positionToOffset(state, position)
for i = offset, 1, -1 do
local char = text:sub(i, i)
@@ -124,7 +129,7 @@ local function findParent(state, text, position)
return nil, nil
end
-local function findParentInStringIndex(state, text, position)
+local function findParentInStringIndex(state, position)
local near, nearStart
guide.eachSourceContain(state.ast, position, function (source)
local start = guide.getStartFinish(source)
@@ -202,6 +207,7 @@ local function getSnip(source)
end
end
+---@async
local function buildDesc(source)
if source.type == 'dummy' then
return
@@ -228,7 +234,7 @@ local function buildFunction(results, source, value, oop, data)
title = 'trigger signature',
command = 'editor.action.triggerParameterHints',
}
- snipData.id = stack(function ()
+ snipData.id = stack(function () ---@async
return {
detail = buildDetail(source),
description = buildDesc(source),
@@ -291,7 +297,7 @@ local function checkLocal(state, word, position, results)
match = name,
insertText = name,
kind = define.CompletionItemKind.Function,
- id = stack(function ()
+ id = stack(function () ---@async
return {
detail = buildDetail(source),
description = buildDesc(source),
@@ -304,7 +310,7 @@ local function checkLocal(state, word, position, results)
results[#results+1] = {
label = name,
kind = define.CompletionItemKind.Variable,
- id = stack(function ()
+ id = stack(function () ---@async
return {
detail = buildDetail(source),
description = buildDesc(source),
@@ -369,7 +375,7 @@ local function checkModule(state, word, position, results)
},
},
},
- id = stack(function ()
+ id = stack(function () ---@async
local md = markdown()
md:add('md', lang.script('COMPLETION_IMPORT_FROM', ('[%s](%s)'):format(
workspace.getRelativePath(uri),
@@ -388,19 +394,16 @@ local function checkModule(state, word, position, results)
end
end
-local function checkFieldFromFieldToIndex(name, src, parent, word, startPos, position)
+local function checkFieldFromFieldToIndex(state, name, src, parent, word, startPos, position)
if name:match '^[%a_][%w_]*$' then
return nil
end
local textEdit, additionalTextEdits
- local uri = guide.getUri(parent)
- local text = files.getText(uri)
- local state = files.getState(uri)
local startOffset = guide.positionToOffset(state, startPos)
local offset = guide.positionToOffset(state, position)
local wordStartOffset
if word == '' then
- wordStartOffset = text:match('()%S', startOffset + 1)
+ wordStartOffset = state.lua:match('()%S', startOffset + 1)
if wordStartOffset then
wordStartOffset = wordStartOffset - 1
else
@@ -444,8 +447,8 @@ local function checkFieldFromFieldToIndex(name, src, parent, word, startPos, pos
}
end
else
- if config.get 'Lua.runtime.version' == 'Lua 5.1'
- or config.get 'Lua.runtime.version' == 'LuaJIT' then
+ if config.get 'Lua.runtime.version' == 'lua 5.1'
+ or config.get 'Lua.runtime.version' == 'luaJIT' then
textEdit.newText = '_G' .. textEdit.newText
else
textEdit.newText = '_ENV' .. textEdit.newText
@@ -454,7 +457,7 @@ local function checkFieldFromFieldToIndex(name, src, parent, word, startPos, pos
return textEdit, additionalTextEdits
end
-local function checkFieldThen(name, src, word, startPos, position, parent, oop, results)
+local function checkFieldThen(state, name, src, word, startPos, position, parent, oop, results)
local value = searcher.getObjectValue(src) or src
local kind = define.CompletionItemKind.Field
if value.type == 'function'
@@ -470,7 +473,7 @@ local function checkFieldThen(name, src, word, startPos, position, parent, oop,
match = name:match '^[^(]+',
insertText = name:match '^[^(]+',
deprecated = vm.isDeprecated(src) or nil,
- id = stack(function ()
+ id = stack(function () ---@async
return {
detail = buildDetail(src),
description = buildDesc(src),
@@ -495,7 +498,7 @@ local function checkFieldThen(name, src, word, startPos, position, parent, oop,
newText = name,
}
else
- textEdit, additionalTextEdits = checkFieldFromFieldToIndex(name, src, parent, word, startPos, position)
+ textEdit, additionalTextEdits = checkFieldFromFieldToIndex(state, name, src, parent, word, startPos, position)
end
results[#results+1] = {
label = name,
@@ -503,7 +506,7 @@ local function checkFieldThen(name, src, word, startPos, position, parent, oop,
deprecated = vm.isDeprecated(src) or nil,
textEdit = textEdit,
additionalTextEdits = additionalTextEdits,
- id = stack(function ()
+ id = stack(function () ---@async
return {
detail = buildDetail(src),
description = buildDesc(src),
@@ -512,6 +515,7 @@ local function checkFieldThen(name, src, word, startPos, position, parent, oop,
}
end
+---@async
local function checkFieldOfRefs(refs, state, word, startPos, position, parent, oop, results, locals, isGlobal)
local fields = {}
local funcs = {}
@@ -570,17 +574,20 @@ local function checkFieldOfRefs(refs, state, word, startPos, position, parent, o
end
for name, src in util.sortPairs(fields) do
if src then
- checkFieldThen(name, src, word, startPos, position, parent, oop, results)
+ checkFieldThen(state, name, src, word, startPos, position, parent, oop, results)
+ await.delay()
end
end
end
+---@async
local function checkGlobal(state, word, startPos, position, parent, oop, results)
local locals = guide.getVisibleLocals(state.ast, position)
local globals = vm.getGlobalSets '*'
checkFieldOfRefs(globals, state, word, startPos, position, parent, oop, results, locals, 'global')
end
+---@async
local function checkField(state, word, start, position, parent, oop, results)
if parent.tag == '_ENV' or parent.special == '_G' then
local globals = vm.getGlobalSets '*'
@@ -620,7 +627,8 @@ local function checkTableField(state, word, start, results)
end)
end
-local function checkCommon(myUri, word, text, position, results)
+local function checkCommon(state, word, position, results)
+ local myUri = state.uri
local showWord = config.get 'Lua.completion.showWord'
if showWord == 'Disable' then
return
@@ -688,8 +696,7 @@ local function checkCommon(myUri, word, text, position, results)
end
end
end
- local state = files.getState(myUri)
- for str, offset in text:gmatch '([%a_][%w_]+)()' do
+ for str, offset in state.lua:gmatch '([%a_][%w_]+)()' do
if #results >= 100 then
break
end
@@ -719,7 +726,8 @@ local function isInString(state, position)
end)
end
-local function checkKeyWord(state, text, start, position, word, hasSpace, afterLocal, results)
+local function checkKeyWord(state, start, position, word, hasSpace, afterLocal, results)
+ local text = state.lua
local snipType = config.get 'Lua.completion.keywordSnippet'
local symbol = lookBackward.findSymbol(text, guide.positionToOffset(state, start))
local isExp = symbol == '(' or symbol == ',' or symbol == '='
@@ -871,7 +879,8 @@ local function checkFunctionArgByDocParam(state, word, startPos, results)
end
end
-local function isAfterLocal(state, text, startPos)
+local function isAfterLocal(state, startPos)
+ local text = state.lua
local offset = guide.positionToOffset(state, startPos)
local pos = lookBackward.skipSpace(text, offset)
local word = lookBackward.findWord(text, pos)
@@ -886,7 +895,7 @@ local function collectRequireNames(mode, myUri, literal, source, smark, position
goto CONTINUE
end
local path = workspace.getRelativePath(uri)
- local infos = rpath.getVisiblePath(path, config.get 'Lua.runtime.path')
+ local infos = rpath.getVisiblePath(path)
for _, info in ipairs(infos) do
if matchKey(literal, info.expect) then
if not collect[info.expect] then
@@ -977,7 +986,7 @@ local function collectRequireNames(mode, myUri, literal, source, smark, position
end
end
-local function checkUri(state, text, position, results)
+local function checkUri(state, position, results)
local myUri = guide.getUri(state.ast)
guide.eachSourceContain(state.ast, position, function (source)
if source.type ~= 'string' then
@@ -1005,7 +1014,8 @@ local function checkUri(state, text, position, results)
end)
end
-local function checkLenPlusOne(state, text, position, results)
+local function checkLenPlusOne(state, position, results)
+ local text = state.lua
guide.eachSourceContain(state.ast, position, function (source)
if source.type == 'getindex'
or source.type == 'setindex' then
@@ -1097,7 +1107,7 @@ local function mergeEnums(a, b, source)
end
end
-local function checkTypingEnum(state, text, position, defs, str, results)
+local function checkTypingEnum(state, position, defs, str, results)
local enums = {}
for _, def in ipairs(defs) do
if def.type == 'doc.type.enum'
@@ -1119,7 +1129,7 @@ local function checkTypingEnum(state, text, position, defs, str, results)
end
end
-local function checkEqualEnumLeft(state, text, position, source, results)
+local function checkEqualEnumLeft(state, position, source, results)
if not source then
return
end
@@ -1129,10 +1139,11 @@ local function checkEqualEnumLeft(state, text, position, source, results)
end
end)
local defs = vm.getDefs(source)
- checkTypingEnum(state, text, position, defs, str, results)
+ checkTypingEnum(state, position, defs, str, results)
end
-local function checkEqualEnum(state, text, position, results)
+local function checkEqualEnum(state, position, results)
+ local text = state.lua
local start = lookBackward.findTargetSymbol(text, guide.positionToOffset(state, position), '=')
if not start then
return
@@ -1154,10 +1165,10 @@ local function checkEqualEnum(state, text, position, results)
if source.type == 'call' and not eqOrNeq then
return
end
- checkEqualEnumLeft(state, text, position, source, results)
+ checkEqualEnumLeft(state, position, source, results)
end
-local function checkEqualEnumInString(state, text, position, results)
+local function checkEqualEnumInString(state, position, results)
local source = findNearestSource(state, position)
local parent = source.parent
if parent.type == 'binary' then
@@ -1170,16 +1181,16 @@ local function checkEqualEnumInString(state, text, position, results)
if parent.op.type ~= '==' and parent.op.type ~= '~=' then
return
end
- checkEqualEnumLeft(state, text, position, parent[1], results)
+ checkEqualEnumLeft(state, position, parent[1], results)
end
if parent.type == 'local' then
- checkEqualEnumLeft(state, text, position, parent, results)
+ checkEqualEnumLeft(state, position, parent, results)
end
if parent.type == 'setlocal'
or parent.type == 'setglobal'
or parent.type == 'setfield'
or parent.type == 'setindex' then
- checkEqualEnumLeft(state, text, position, parent.node, results)
+ checkEqualEnumLeft(state, position, parent.node, results)
end
end
@@ -1191,20 +1202,21 @@ local function isFuncArg(state, position)
end)
end
-local function trySpecial(state, text, position, results)
+local function trySpecial(state, position, results)
if isInString(state, position) then
- checkUri(state, text, position, results)
- checkEqualEnumInString(state, text, position, results)
+ checkUri(state, position, results)
+ checkEqualEnumInString(state, position, results)
return
end
-- x[#x+1]
- checkLenPlusOne(state, text, position, results)
+ checkLenPlusOne(state, position, results)
-- type(o) ==
- checkEqualEnum(state, text, position, results)
+ checkEqualEnum(state, position, results)
end
-local function tryIndex(state, text, position, results)
- local parent, oop = findParentInStringIndex(state, text, position)
+---@async
+local function tryIndex(state, position, results)
+ local parent, oop = findParentInStringIndex(state, position)
if not parent then
return
end
@@ -1212,7 +1224,9 @@ local function tryIndex(state, text, position, results)
checkField(state, word, position, position, parent, oop, results)
end
-local function tryWord(state, text, position, triggerCharacter, results)
+---@async
+local function tryWord(state, position, triggerCharacter, results)
+ local text = state.lua
local offset = guide.positionToOffset(state, position)
local finish = lookBackward.skipSpace(text, offset)
local word, start = lookBackward.findWord(text, offset)
@@ -1226,11 +1240,11 @@ local function tryWord(state, text, position, triggerCharacter, results)
if isInString(state, position) then
if not hasSpace then
if #results == 0 then
- checkCommon(state.uri, word, text, position, results)
+ checkCommon(state, word, position, results)
end
end
else
- local parent, oop = findParent(state, text, startPos)
+ local parent, oop = findParent(state, startPos)
if parent then
if not hasSpace then
checkField(state, word, startPos, position, parent, oop, results)
@@ -1239,8 +1253,8 @@ local function tryWord(state, text, position, triggerCharacter, results)
checkProvideLocal(state, word, startPos, results)
checkFunctionArgByDocParam(state, word, startPos, results)
else
- local afterLocal = isAfterLocal(state, text, startPos)
- local stop = checkKeyWord(state, text, startPos, position, word, hasSpace, afterLocal, results)
+ local afterLocal = isAfterLocal(state, startPos)
+ local stop = checkKeyWord(state, startPos, position, word, hasSpace, afterLocal, results)
if stop then
return
end
@@ -1257,12 +1271,14 @@ local function tryWord(state, text, position, triggerCharacter, results)
end
end
if not hasSpace then
- checkCommon(state.uri, word, text, position, results)
+ checkCommon(state, word, position, results)
end
end
end
-local function trySymbol(state, text, position, results)
+---@async
+local function trySymbol(state, position, results)
+ local text = state.lua
local symbol, start = lookBackward.findSymbol(text, guide.positionToOffset(state, position))
if not symbol then
return nil
@@ -1273,7 +1289,7 @@ local function trySymbol(state, text, position, results)
local startPos = guide.offsetToPosition(state, start)
if symbol == '.'
or symbol == ':' then
- local parent, oop = findParent(state, text, startPos)
+ local parent, oop = findParent(state, startPos)
if parent then
tracy.ZoneBeginN 'completion.trySymbol'
checkField(state, '', startPos, position, parent, oop, results)
@@ -1411,7 +1427,7 @@ local function getCallEnumsAndFuncs(source, index, oop, call)
end
end
-local function findCall(state, text, position)
+local function findCall(state, position)
local call
guide.eachSourceContain(state.ast, position, function (src)
if src.type == 'call' then
@@ -1423,7 +1439,7 @@ local function findCall(state, text, position)
return call
end
-local function getCallArgInfo(call, text, position)
+local function getCallArgInfo(call, position)
if not call.args then
return 1, nil, nil
end
@@ -1448,7 +1464,8 @@ local function getFuncParamByCallIndex(func, index)
return func.args[index]
end
-local function checkTableLiteralField(state, text, position, tbl, fields, results)
+local function checkTableLiteralField(state, position, tbl, fields, results)
+ local text = state.lua
local mark = {}
for _, field in ipairs(tbl) do
if field.type == 'tablefield'
@@ -1480,7 +1497,7 @@ local function checkTableLiteralField(state, text, position, tbl, fields, result
label = guide.getKeyName(field),
kind = define.CompletionItemKind.Property,
insertText = ('%s = $0'):format(guide.getKeyName(field)),
- id = stack(function ()
+ id = stack(function () ---@async
return {
detail = buildDetail(field),
description = buildDesc(field),
@@ -1492,7 +1509,7 @@ local function checkTableLiteralField(state, text, position, tbl, fields, result
end
end
-local function checkTableLiteralFieldByCall(state, text, position, call, defs, index, results)
+local function checkTableLiteralFieldByCall(state, position, call, defs, index, results)
local source = findNearestTableField(state, position)
if not source then
return
@@ -1526,16 +1543,16 @@ local function checkTableLiteralFieldByCall(state, text, position, call, defs, i
end
::CONTINUE::
end
- checkTableLiteralField(state, text, position, tbl, fields, results)
+ checkTableLiteralField(state, position, tbl, fields, results)
end
-local function tryCallArg(state, text, position, results)
- local call = findCall(state, text, position)
+local function tryCallArg(state, position, results)
+ local call = findCall(state, position)
if not call then
return
end
local myResults = {}
- local argIndex, arg, oop = getCallArgInfo(call, text, position)
+ local argIndex, arg, oop = getCallArgInfo(call, position)
if arg and arg.type == 'function' then
return
end
@@ -1550,10 +1567,10 @@ local function tryCallArg(state, text, position, results)
for _, enum in ipairs(myResults) do
results[#results+1] = enum
end
- checkTableLiteralFieldByCall(state, text, position, call, defs, argIndex, results)
+ checkTableLiteralFieldByCall(state, position, call, defs, argIndex, results)
end
-local function tryTable(state, text, position, results)
+local function tryTable(state, position, results)
local source = findNearestTableField(state, position)
if not source then
return
@@ -1577,7 +1594,7 @@ local function tryTable(state, text, position, results)
fields[#fields+1] = field
end
end
- checkTableLiteralField(state, text, position, tbl, fields, results)
+ checkTableLiteralField(state, position, tbl, fields, results)
end
local function getComment(state, position)
@@ -1589,7 +1606,7 @@ local function getComment(state, position)
return nil
end
-local function getLuaDoc(state, position)
+local function getluaDoc(state, position)
for _, doc in ipairs(state.ast.docs) do
if position >= doc.start and position <= doc.range then
return doc
@@ -1598,7 +1615,7 @@ local function getLuaDoc(state, position)
return nil
end
-local function tryLuaDocCate(word, results)
+local function tryluaDocCate(word, results)
for _, docType in ipairs {
'class',
'type',
@@ -1615,6 +1632,8 @@ local function tryLuaDocCate(word, results)
'see',
'diagnostic',
'module',
+ 'async',
+ 'nodiscard',
} do
if matchKey(word, docType) then
results[#results+1] = {
@@ -1625,7 +1644,7 @@ local function tryLuaDocCate(word, results)
end
end
-local function getLuaDocByContain(state, position)
+local function getluaDocByContain(state, position)
local result
local range = math.huge
guide.eachSourceContain(state.ast.docs, position, function (src)
@@ -1641,12 +1660,12 @@ local function getLuaDocByContain(state, position)
return result
end
-local function getLuaDocByErr(state, text, start, position)
+local function getluaDocByErr(state, start, position)
local targetError
for _, err in ipairs(state.errs) do
if err.finish <= position
and err.start >= start then
- if not text:sub(err.finish + 1, position):find '%S' then
+ if not state.lua:sub(err.finish + 1, position):find '%S' then
targetError = err
break
end
@@ -1666,13 +1685,16 @@ local function getLuaDocByErr(state, text, start, position)
return targetError, targetDoc
end
-local function tryLuaDocBySource(state, position, source, results)
+local function tryluaDocBySource(state, position, source, results)
if source.type == 'doc.extends.name' then
if source.parent.type == 'doc.class' then
+ local used = {}
for _, doc in ipairs(vm.getDocDefines '*') do
if doc.type == 'doc.class.name'
and doc.parent ~= source.parent
+ and not used[doc[1]]
and matchKey(source[1], doc[1]) then
+ used[doc[1]] = true
results[#results+1] = {
label = doc[1],
kind = define.CompletionItemKind.Class,
@@ -1687,10 +1709,13 @@ local function tryLuaDocBySource(state, position, source, results)
end
return true
elseif source.type == 'doc.type.name' then
+ local used = {}
for _, doc in ipairs(vm.getDocDefines '*') do
if (doc.type == 'doc.class.name' or doc.type == 'doc.alias.name')
and doc.parent ~= source.parent
+ and not used[doc[1]]
and matchKey(source[1], doc[1]) then
+ used[doc[1]] = true
results[#results+1] = {
label = doc[1],
kind = define.CompletionItemKind.Class,
@@ -1761,7 +1786,7 @@ local function tryLuaDocBySource(state, position, source, results)
return false
end
-local function tryLuaDocByErr(state, position, err, docState, results)
+local function tryluaDocByErr(state, position, err, docState, results)
if err.type == 'LUADOC_MISS_CLASS_EXTENDS_NAME' then
for _, doc in ipairs(vm.getDocDefines '*') do
if doc.type == 'doc.class.name'
@@ -1840,7 +1865,7 @@ local function tryLuaDocByErr(state, position, err, docState, results)
end
end
-local function buildLuaDocOfFunction(func)
+local function buildluaDocOfFunction(func)
local index = 1
local buf = {}
buf[#buf+1] = '${1:comment}'
@@ -1882,7 +1907,7 @@ local function buildLuaDocOfFunction(func)
return insertText
end
-local function tryLuaDocOfFunction(doc, results)
+local function tryluaDocOfFunction(doc, results)
if not doc.bindSources then
return
end
@@ -1902,7 +1927,7 @@ local function tryLuaDocOfFunction(doc, results)
return
end
end
- local insertText = buildLuaDocOfFunction(func)
+ local insertText = buildluaDocOfFunction(func)
results[#results+1] = {
label = '@param;@return',
kind = define.CompletionItemKind.Snippet,
@@ -1912,8 +1937,8 @@ local function tryLuaDocOfFunction(doc, results)
}
end
-local function tryLuaDoc(state, text, position, results)
- local doc = getLuaDoc(state, position)
+local function tryluaDoc(state, position, results)
+ local doc = getluaDoc(state, position)
if not doc then
return
end
@@ -1921,38 +1946,38 @@ local function tryLuaDoc(state, text, position, results)
local line = doc.originalComment.text
-- 尝试 ---$
if line == '-' then
- tryLuaDocOfFunction(doc, results)
+ tryluaDocOfFunction(doc, results)
return
end
-- 尝试 ---@$
local cate = line:match('^-+%s*@(%a*)$')
if cate then
- tryLuaDocCate(cate, results)
+ tryluaDocCate(cate, results)
return
end
end
-- 根据输入中的source来补全
- local source = getLuaDocByContain(state, position)
+ local source = getluaDocByContain(state, position)
if source then
- local suc = tryLuaDocBySource(state, position, source, results)
+ local suc = tryluaDocBySource(state, position, source, results)
if suc then
return
end
end
-- 根据附近的错误消息来补全
- local err, expectDoc = getLuaDocByErr(state, text, doc.start, position)
+ local err, expectDoc = getluaDocByErr(state, doc.start, position)
if err then
- tryLuaDocByErr(state, position, err, expectDoc, results)
+ tryluaDocByErr(state, position, err, expectDoc, results)
return
end
end
-local function tryComment(state, text, position, results)
+local function tryComment(state, position, results)
if #results > 0 then
return
end
- local word = lookBackward.findWord(text, guide.positionToOffset(state, position))
- local doc = getLuaDoc(state, position)
+ local word = lookBackward.findWord(state.lua, guide.positionToOffset(state, position))
+ local doc = getluaDoc(state, position)
if not word then
local comment = getComment(state, position)
if comment.type == 'comment.short'
@@ -1973,7 +1998,7 @@ local function tryComment(state, text, position, results)
if doc and doc.type ~= 'doc.comment' then
return
end
- checkCommon(state.uri, word, text, position, results)
+ checkCommon(state, word, position, results)
end
local function makeCache(uri, position, results)
@@ -2043,7 +2068,7 @@ local function getCache(uri, position)
end
if results.enableCommon then
- checkCommon(uri, word, text, position, results)
+ checkCommon(state, word, position, results)
end
return cache.results
@@ -2054,6 +2079,7 @@ local function clearCache()
cache.results = nil
end
+---@async
local function completion(uri, position, triggerCharacter)
tracy.ZoneBeginN 'completion cache'
local results = getCache(uri, position)
@@ -2061,29 +2087,30 @@ local function completion(uri, position, triggerCharacter)
if results then
return results
end
+ await.delay()
tracy.ZoneBeginN 'completion #1'
local state = files.getState(uri)
local text = files.getText(uri)
- results = {}
+ local results = {}
clearStack()
tracy.ZoneEnd()
tracy.ZoneBeginN 'completion #2'
if state then
if getComment(state, position) then
- tryLuaDoc(state, text, position, results)
- tryComment(state, text, position, results)
+ tryluaDoc(state, position, results)
+ tryComment(state, position, results)
else
- trySpecial(state, text, position, results)
- tryCallArg(state, text, position, results)
- tryTable(state, text, position, results)
- tryWord(state, text, position, triggerCharacter, results)
- tryIndex(state, text, position, results)
- trySymbol(state, text, position, results)
+ trySpecial(state, position, results)
+ tryCallArg(state, position, results)
+ tryTable(state, position, results)
+ tryWord(state, position, triggerCharacter, results)
+ tryIndex(state, position, results)
+ trySymbol(state, position, results)
end
else
local word = lookBackward.findWord(text, guide.positionToOffset(state, position))
if word then
- checkCommon(nil, word, text, position, results)
+ checkCommon(nil, word, position, results)
end
end
tracy.ZoneEnd()
@@ -2099,6 +2126,7 @@ local function completion(uri, position, triggerCharacter)
return results
end
+---@async
local function resolve(id)
local item = resolveStack(id)
local cache = workspace.getCache 'completion'
diff --git a/script/core/diagnostics/await-in-sync.lua b/script/core/diagnostics/await-in-sync.lua
new file mode 100644
index 00000000..558a5ff0
--- /dev/null
+++ b/script/core/diagnostics/await-in-sync.lua
@@ -0,0 +1,30 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local lang = require 'language'
+local await = require 'await'
+
+---@async
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ ---@async
+ guide.eachSourceType(state.ast, 'call', function (source)
+ local currentFunc = guide.getParentFunction(source)
+ if currentFunc and vm.isAsync(currentFunc, false) then
+ return
+ end
+ await.delay()
+ if vm.isAsyncCall(source) then
+ callback {
+ start = source.node.start,
+ finish = source.node.finish,
+ message = lang.script('DIAG_AWAIT_IN_SYNC'),
+ }
+ return
+ end
+ end)
+end
diff --git a/script/core/diagnostics/deprecated.lua b/script/core/diagnostics/deprecated.lua
index 0aeac9e9..e9a1fef7 100644
--- a/script/core/diagnostics/deprecated.lua
+++ b/script/core/diagnostics/deprecated.lua
@@ -8,6 +8,7 @@ local await = require 'await'
local noder = require 'core.noder'
local types = {'getglobal', 'getfield', 'getindex', 'getmethod'}
+---@async
return function (uri, callback)
local ast = files.getState(uri)
if not ast then
@@ -16,7 +17,7 @@ return function (uri, callback)
local cache = {}
- guide.eachSourceTypes(ast.ast, types, function (src)
+ guide.eachSourceTypes(ast.ast, types, function (src) ---@async
if src.type == 'getglobal' then
local key = src[1]
if not key then
diff --git a/script/core/diagnostics/discard-returns.lua b/script/core/diagnostics/discard-returns.lua
new file mode 100644
index 00000000..cef7ece5
--- /dev/null
+++ b/script/core/diagnostics/discard-returns.lua
@@ -0,0 +1,29 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local await = require 'await'
+local lang = require 'language'
+
+---@async
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+ ---@async
+ guide.eachSourceType(state.ast, 'call', function (source)
+ local parent = source.parent
+ if parent.type ~= 'function'
+ and parent.type ~= 'main' then
+ return
+ end
+ await.delay()
+ if vm.isNoDiscard(source.node, true) then
+ callback {
+ start = source.start,
+ finish = source.finish,
+ message = lang.script('DIAG_DISCARD_RETURNS'),
+ }
+ end
+ end)
+end
diff --git a/script/core/diagnostics/duplicate-doc-class.lua b/script/core/diagnostics/duplicate-doc-class.lua
index 780d15b9..97e2089a 100644
--- a/script/core/diagnostics/duplicate-doc-class.lua
+++ b/script/core/diagnostics/duplicate-doc-class.lua
@@ -16,8 +16,7 @@ return function (uri, callback)
local cache = {}
for _, doc in ipairs(state.ast.docs) do
- if doc.type == 'doc.class'
- or doc.type == 'doc.alias' then
+ if doc.type == 'doc.alias' then
local name = guide.getKeyName(doc)
if not cache[name] then
local docs = vm.getDocDefines(name)
diff --git a/script/core/diagnostics/init.lua b/script/core/diagnostics/init.lua
index 63a1bcf0..4950900b 100644
--- a/script/core/diagnostics/init.lua
+++ b/script/core/diagnostics/init.lua
@@ -64,6 +64,7 @@ local function check(uri, name, results)
end
end
+---@async
return function (uri, response)
local ast = files.getState(uri)
if not ast then
diff --git a/script/core/diagnostics/not-yieldable.lua b/script/core/diagnostics/not-yieldable.lua
new file mode 100644
index 00000000..612b5b98
--- /dev/null
+++ b/script/core/diagnostics/not-yieldable.lua
@@ -0,0 +1,63 @@
+local files = require 'files'
+local await = require 'await'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local lang = require 'language'
+local infer = require 'core.infer'
+
+local function isYieldAble(defs, i)
+ local hasFuncDef
+ for _, def in ipairs(defs) do
+ if def.type == 'function' then
+ hasFuncDef = true
+ local arg = def.args and def.args[i]
+ if arg then
+ if infer.hasType(arg, 'any')
+ or vm.isAsync(arg, true) then
+ return true
+ end
+ end
+ end
+ if def.type == 'doc.type.function' then
+ hasFuncDef = true
+ local arg = def.args and def.args[i]
+ if arg then
+ if infer.hasType(arg.extends, 'any')
+ or vm.isAsync(arg.extends, true) then
+ return true
+ end
+ end
+ end
+ end
+ return not hasFuncDef
+end
+
+---@async
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ guide.eachSourceType(state.ast, 'call', function (source) ---@async
+ if not source.args then
+ return
+ end
+ await.delay()
+ local defs = vm.getDefs(source.node)
+ if #defs == 0 then
+ return
+ end
+ for i, arg in ipairs(source.args) do
+ if vm.isAsync(arg, true)
+ and not vm.isLinkedCall(source.node, i)
+ and not isYieldAble(defs, i) then
+ callback {
+ start = arg.start,
+ finish = arg.finish,
+ message = lang.script('DIAG_NOT_YIELDABLE', i),
+ }
+ end
+ end
+ end)
+end
diff --git a/script/core/diagnostics/redundant-value.lua b/script/core/diagnostics/redundant-value.lua
index 4c913330..6f60303b 100644
--- a/script/core/diagnostics/redundant-value.lua
+++ b/script/core/diagnostics/redundant-value.lua
@@ -4,13 +4,14 @@ local lang = require 'language'
local guide = require 'parser.guide'
local await = require 'await'
+---@async
return function (uri, callback)
local state = files.getState(uri)
if not state then
return
end
- guide.eachSource(state.ast, function (src)
+ guide.eachSource(state.ast, function (src) ---@async
await.delay()
if src.redundant then
callback {
diff --git a/script/core/diagnostics/type-check.lua b/script/core/diagnostics/type-check.lua
index 11678b38..8728b169 100644
--- a/script/core/diagnostics/type-check.lua
+++ b/script/core/diagnostics/type-check.lua
@@ -23,6 +23,9 @@ local typeNameMap = {
}
local function isTable(name)
+ if type(name) ~= 'string' then
+ return
+ end
if tableMap[name]
---table<K: number, V: string> table
or tableMap[name:sub(1, 5)]
@@ -84,7 +87,8 @@ local function compatibleType(param, args)
if param[1] and param[1]:sub(1,1) == '"' then
return true
end
- elseif isTable(v.type or v[1]) and isTable(param[1] or param.type) then
+ elseif (isTable(v.type) or isTable(v[1]))
+ and (isTable(param[1]) or isTable(param.type)) then
return true
end
end
@@ -114,14 +118,16 @@ end
local function addFatherClass(infers)
for k in pairs(infers) do
- local docDefs = vm.getDocDefines(k)
- for _, doc in ipairs(docDefs) do
- if doc.parent
- and doc.parent.type == 'doc.class'
- and doc.parent.extends then
- for _, tp in ipairs(doc.parent.extends) do
- if tp.type == 'doc.extends.name' then
- infers[tp[1]] = true
+ if type(k) == 'string' then
+ local docDefs = vm.getDocDefines(k)
+ for _, doc in ipairs(docDefs) do
+ if doc.parent
+ and doc.parent.type == 'doc.class'
+ and doc.parent.extends then
+ for _, tp in ipairs(doc.parent.extends) do
+ if tp.type == 'doc.extends.name' then
+ infers[tp[1]] = true
+ end
end
end
end
@@ -238,9 +244,6 @@ local function getInfoFromDefs(defs)
and isClassOralias(v.type) then
plusAlias[#plusAlias+1] = v
end
- if not v[1] or not v.type then
- log.warn('type-check: if not v[1] or not v.type')
- end
end
plusAlias[#plusAlias+1] = types[i]
end
@@ -409,12 +412,13 @@ local function matchParams(paramsTypes, i, arg)
return false, messages
end
+---@async
return function (uri, callback)
local ast = files.getState(uri)
if not ast then
return
end
- guide.eachSourceType(ast.ast, 'call', function (source)
+ guide.eachSourceType(ast.ast, 'call', function (source) ---@async
if not source.args then
return
end
diff --git a/script/core/diagnostics/undefined-doc-param.lua b/script/core/diagnostics/undefined-doc-param.lua
index 6140b4f0..86bf3871 100644
--- a/script/core/diagnostics/undefined-doc-param.lua
+++ b/script/core/diagnostics/undefined-doc-param.lua
@@ -12,6 +12,9 @@ local function hasParamName(func, name)
if arg[1] == name then
return true
end
+ if arg.type == '...' and name == '...' then
+ return true
+ end
end
return false
end
diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index ff02728f..2e64f0cc 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -18,6 +18,7 @@ local SkipCheckClass = {
['lightuserdata'] = true,
}
+---@async
return function (uri, callback)
local ast = files.getState(uri)
if not ast then
@@ -26,6 +27,7 @@ return function (uri, callback)
local cache = {}
+ ---@async
local function checkUndefinedField(src)
local id = noder.getID(src)
if not id then
diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua
index 14754c16..48c8a226 100644
--- a/script/core/diagnostics/undefined-global.lua
+++ b/script/core/diagnostics/undefined-global.lua
@@ -14,6 +14,7 @@ local requireLike = {
['load'] = true,
}
+---@async
return function (uri, callback)
local ast = files.getState(uri)
if not ast then
@@ -21,7 +22,7 @@ return function (uri, callback)
end
-- 遍历全局变量,检查所有没有 set 模式的全局变量
- guide.eachSourceType(ast.ast, 'getglobal', function (src)
+ guide.eachSourceType(ast.ast, 'getglobal', function (src) ---@async
local key = src[1]
if not key then
return
diff --git a/script/core/diagnostics/unused-function.lua b/script/core/diagnostics/unused-function.lua
index 8f6ccaac..0e0c0003 100644
--- a/script/core/diagnostics/unused-function.lua
+++ b/script/core/diagnostics/unused-function.lua
@@ -18,6 +18,7 @@ local function isToBeClosed(source)
return false
end
+---@async
return function (uri, callback)
local ast = files.getState(uri)
if not ast then
@@ -25,6 +26,7 @@ return function (uri, callback)
end
local cache = {}
+ ---@async
local function checkFunction(source)
if cache[source] ~= nil then
return cache[source]
@@ -81,7 +83,7 @@ return function (uri, callback)
end
-- 只检查局部函数
- guide.eachSourceType(ast.ast, 'function', function (source)
+ guide.eachSourceType(ast.ast, 'function', function (source) ---@async
checkFunction(source)
end)
end
diff --git a/script/core/document-symbol.lua b/script/core/document-symbol.lua
index cfabedab..07794e60 100644
--- a/script/core/document-symbol.lua
+++ b/script/core/document-symbol.lua
@@ -219,6 +219,7 @@ local function buildAnonymousFunction(source, text, used, symbols)
}
end
+---@async
local function buildSource(source, text, used, symbols)
if source.type == 'local'
or source.type == 'setlocal'
@@ -234,6 +235,7 @@ local function buildSource(source, text, used, symbols)
end
end
+---@async
local function makeSymbol(uri)
local ast = files.getState(uri)
local text = files.getText(uri)
@@ -243,7 +245,7 @@ local function makeSymbol(uri)
local symbols = {}
local used = {}
- guide.eachSource(ast.ast, function (source)
+ guide.eachSource(ast.ast, function (source) ---@async
buildSource(source, text, used, symbols)
end)
@@ -279,6 +281,7 @@ local function packChild(symbols)
return root
end
+---@async
local function packSymbols(symbols)
await.delay()
table.sort(symbols, function (a, b)
@@ -291,6 +294,7 @@ local function packSymbols(symbols)
return packChild(symbols)
end
+---@async
return function (uri)
local symbols = makeSymbol(uri)
if not symbols then
diff --git a/script/core/folding.lua b/script/core/folding.lua
index bd3ba5f3..2cf06d46 100644
--- a/script/core/folding.lua
+++ b/script/core/folding.lua
@@ -145,6 +145,7 @@ local Care = {
end,
}
+---@async
return function (uri)
local state = files.getState(uri)
local text = files.getText(uri)
@@ -154,7 +155,7 @@ return function (uri)
local regions = {}
local status = {}
- guide.eachSource(state.ast, function (source)
+ guide.eachSource(state.ast, function (source) ---@async
local tp = source.type
if Care[tp] then
await.delay()
diff --git a/script/core/generic.lua b/script/core/generic.lua
index 92e97362..b383ee5d 100644
--- a/script/core/generic.lua
+++ b/script/core/generic.lua
@@ -7,7 +7,7 @@ local noder = require "core.noder"
---@field proto parser.guide.object
---@field parent parser.guide.object
----@class generic.closure
+---@class generic.closure: parser.guide.object
---@field type string
---@field proto parser.guide.object
---@field upvalues table<string, generic.value[]>
diff --git a/script/core/hint.lua b/script/core/hint.lua
index 62d2f7bf..42390443 100644
--- a/script/core/hint.lua
+++ b/script/core/hint.lua
@@ -6,13 +6,14 @@ local guide = require 'parser.guide'
local await = require 'await'
local define = require 'proto.define'
+---@async
local function typeHint(uri, results, start, finish)
- local ast = files.getState(uri)
- if not ast then
+ local state = files.getState(uri)
+ if not state then
return
end
local mark = {}
- guide.eachSourceBetween(ast.ast, start, finish, function (source)
+ guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
if source.type ~= 'local'
and source.type ~= 'setglobal'
and source.type ~= 'tablefield'
@@ -96,17 +97,18 @@ local function hasLiteralArgInCall(call)
return false
end
+---@async
local function paramName(uri, results, start, finish)
local paramConfig = config.get 'Lua.hint.paramName'
if not paramConfig or paramConfig == 'None' then
return
end
- local ast = files.getState(uri)
- if not ast then
+ local state = files.getState(uri)
+ if not state then
return
end
local mark = {}
- guide.eachSourceBetween(ast.ast, start, finish, function (source)
+ guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
if source.type ~= 'call' then
return
end
@@ -158,9 +160,39 @@ local function paramName(uri, results, start, finish)
end)
end
+---@async
+local function awaitHint(uri, results, start, finish)
+ local awaitConfig = config.get 'Lua.hint.await'
+ if not awaitConfig then
+ return
+ end
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+ guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
+ if source.type ~= 'call' then
+ return
+ end
+ await.delay()
+ local node = source.node
+ if not vm.isAsyncCall(source) then
+ return
+ end
+ results[#results+1] = {
+ text = 'await ',
+ offset = node.start,
+ kind = define.InlayHintKind.Other,
+ where = 'left',
+ }
+ end)
+end
+
+---@async
return function (uri, start, finish)
local results = {}
typeHint(uri, results, start, finish)
+ awaitHint(uri, results, start, finish)
paramName(uri, results, start, finish)
return results
end
diff --git a/script/core/hover/arg.lua b/script/core/hover/arg.lua
index 822be2b6..4e6a1ace 100644
--- a/script/core/hover/arg.lua
+++ b/script/core/hover/arg.lua
@@ -38,7 +38,10 @@ local function asFunction(source, oop)
infer.searchAndViewInfers(arg)
)
elseif arg.type == '...' then
- args[#args+1] = '...'
+ args[#args+1] = ('%s: %s'):format(
+ '...',
+ infer.searchAndViewInfers(arg)
+ )
else
args[#args+1] = ('%s'):format(infer.searchAndViewInfers(arg))
end
@@ -60,18 +63,11 @@ local function asDocFunction(source)
for i = 1, #source.args do
local arg = source.args[i]
local name = arg.name[1]
- if arg.extends then
- args[i] = ('%s%s: %s'):format(
- name,
- arg.optional and '?' or '',
- infer.searchAndViewInfers(arg.extends)
- )
- else
- args[i] = ('%s%s'):format(
- name,
- arg.optional and '?' or ''
- )
- end
+ args[i] = ('%s%s: %s'):format(
+ name,
+ arg.optional and '?' or '',
+ arg.extends and infer.searchAndViewInfers(arg.extends) or 'any'
+ )
end
return table.concat(args, ', ')
end
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index 576ed42a..725ea7a5 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -31,8 +31,6 @@ local function collectRequire(mode, literal)
if vm.isMetaFile(uri) then
shows[i] = ('* [[meta]](%s)'):format(uri)
elseif searcher then
- searcher = searcher:sub(#rootPath + 1)
- searcher = ws.normalize(searcher)
searcher = searcher:gsub('^[/\\]+', '')
shows[i] = ('* [%s](%s) %s'):format(path, uri, lang.script('HOVER_USE_LUA_PATH', searcher))
else
diff --git a/script/core/hover/init.lua b/script/core/hover/init.lua
index 784ef75d..7d99a006 100644
--- a/script/core/hover/init.lua
+++ b/script/core/hover/init.lua
@@ -7,12 +7,14 @@ local findSource = require 'core.find-source'
local markdown = require 'provider.markdown'
local infer = require 'core.infer'
+---@async
local function getHover(source)
local md = markdown()
local defMark = {}
local labelMark = {}
local descMark = {}
+ ---@async
local function addHover(def, checkLable)
if defMark[def] then
return
@@ -37,12 +39,17 @@ local function getHover(source)
end
if infer.searchAndViewInfers(source) == 'function' then
+ local hasFunc
for _, def in ipairs(vm.getDefs(source)) do
if def.type == 'function'
or def.type == 'doc.type.function' then
+ hasFunc = true
addHover(def, true)
end
end
+ if not hasFunc then
+ addHover(source, true)
+ end
else
addHover(source, true)
for _, def in ipairs(vm.getDefs(source)) do
@@ -74,6 +81,7 @@ local accept = {
['doc.module'] = true,
}
+---@async
local function getHoverByUri(uri, position)
local ast = files.getState(uri)
if not ast then
diff --git a/script/core/hover/label.lua b/script/core/hover/label.lua
index 8906d54d..0bb4fe89 100644
--- a/script/core/hover/label.lua
+++ b/script/core/hover/label.lua
@@ -16,7 +16,11 @@ local function asFunction(source, oop)
local arg = buildArg(source, oop)
local rtn = buildReturn(source)
local lines = {}
- lines[1] = ('%s %s(%s)'):format(oop and 'method' or 'function', name or '', arg)
+ lines[1] = ('%s%s %s(%s)'):format(
+ vm.isAsync(source) and 'async ' or '',
+ oop and 'method' or 'function',
+ name or '', arg
+ )
lines[2] = rtn
return table.concat(lines, '\n')
end
@@ -44,6 +48,7 @@ local function asDocTypeName(source)
end
end
+---@async
local function asValue(source, title)
local name = buildName(source, false) or ''
local type = infer.searchAndViewInfers(source)
@@ -59,6 +64,9 @@ local function asValue(source, title)
local pack = {}
pack[#pack+1] = title
pack[#pack+1] = name .. ':'
+ if vm.isAsync(source, true) then
+ pack[#pack+1] = 'async'
+ end
if cont
and ( type == 'table'
or type == 'any'
@@ -76,10 +84,12 @@ local function asValue(source, title)
return table.concat(pack, ' ')
end
+---@async
local function asLocal(source)
return asValue(source, 'local')
end
+---@async
local function asGlobal(source)
return asValue(source, 'global')
end
@@ -111,6 +121,7 @@ local function isGlobalField(source)
end
end
+---@async
local function asField(source)
if isGlobalField(source) then
return asGlobal(source)
@@ -175,6 +186,7 @@ local function asNumber(source)
return formatNumber(num)
end
+---@async
return function (source, oop)
if source.type == 'function' then
return asFunction(source, oop)
diff --git a/script/core/hover/table.lua b/script/core/hover/table.lua
index 68a04b40..285d5c02 100644
--- a/script/core/hover/table.lua
+++ b/script/core/hover/table.lua
@@ -134,6 +134,7 @@ local function getOptionalMap(fields)
return optionals
end
+---@async
return function (source)
local maxFields = config.get 'Lua.hover.previewFields'
if maxFields <= 0 then
diff --git a/script/core/infer.lua b/script/core/infer.lua
index 2825578e..8da35289 100644
--- a/script/core/infer.lua
+++ b/script/core/infer.lua
@@ -385,7 +385,7 @@ function m.viewDocFunction(doc)
end
local args = {}
for i, arg in ipairs(doc.args) do
- args[i] = ('%s: %s'):format(arg.name[1], m.viewDocName(arg.extends))
+ args[i] = ('%s: %s'):format(arg.name[1], arg.extends and m.viewDocName(arg.extends) or 'any')
end
local label = ('fun(%s)'):format(table.concat(args, ', '))
if #doc.returns > 0 then
diff --git a/script/core/noder.lua b/script/core/noder.lua
index 59c103e8..374d2c9b 100644
--- a/script/core/noder.lua
+++ b/script/core/noder.lua
@@ -1188,46 +1188,6 @@ compileNodeMap = util.switch()
: call(function (noders, id, source)
local uri = guide.getUri(source)
collector.subscribe(uri, id, noders)
-
- local parent = source.parent
- if parent.type ~= 'doc.type' then
- goto BREAK
- end
- local bindSources = parent.bindSources
- if not bindSources then
- goto BREAK
- end
- for i = 1, #bindSources do
- local src = bindSources[i]
- if src.type ~= 'local' then
- goto CONTINUE1
- end
- local refs = src.ref
- if not refs then
- goto CONTINUE1
- end
- for j = 1, #refs do
- local ref = refs[j]
- if ref.type ~= 'getlocal' then
- goto CONTINUE2
- end
- local nxt = ref.next
- if not nxt then
- goto CONTINUE2
- end
- local nxtType = nxt.type
- if nxtType == 'setfield'
- or nxtType == 'setmethod'
- or nxtType == 'setindex' then
- local defID = 'def:' .. id
- collector.subscribe(uri, defID, noders)
- goto BREAK
- end
- ::CONTINUE2::
- end
- ::CONTINUE1::
- end
- ::BREAK::
end)
: case 'doc.class.name'
: case 'doc.alias.name'
diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua
index dc0649d1..8389cbb4 100644
--- a/script/core/semantic-tokens.lua
+++ b/script/core/semantic-tokens.lua
@@ -373,6 +373,7 @@ config.watch(function (key, value)
end
end)
+---@async
return function (uri, start, finish)
local state = files.getState(uri)
local text = files.getText(uri)
@@ -381,7 +382,7 @@ return function (uri, start, finish)
end
local results = {}
- guide.eachSourceBetween(state.ast, start, finish, function (source)
+ guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
local method = Care[source.type]
if not method then
return
diff --git a/script/core/signature.lua b/script/core/signature.lua
index 007a3787..d55866f5 100644
--- a/script/core/signature.lua
+++ b/script/core/signature.lua
@@ -39,6 +39,7 @@ local function findNearCall(uri, ast, pos)
return nearCall
end
+---@async
local function makeOneSignature(source, oop, index)
local label = hoverLabel(source, oop)
-- 去掉返回值
@@ -65,7 +66,7 @@ local function makeOneSignature(source, oop, index)
if index > i and i > 0 then
local lastLabel = params[i].label
local text = label:sub(lastLabel[1] + 1, lastLabel[2])
- if text == '...' then
+ if text:sub(1, 3) == '...' then
index = i
end
end
@@ -77,6 +78,7 @@ local function makeOneSignature(source, oop, index)
}
end
+---@async
local function makeSignatures(text, call, pos)
local node = call.node
local oop = node.type == 'method'
@@ -136,6 +138,7 @@ local function makeSignatures(text, call, pos)
return signs
end
+---@async
return function (uri, pos)
local state = files.getState(uri)
if not state then
diff --git a/script/core/workspace-symbol.lua b/script/core/workspace-symbol.lua
index 265a8d92..5fb4a741 100644
--- a/script/core/workspace-symbol.lua
+++ b/script/core/workspace-symbol.lua
@@ -57,6 +57,7 @@ local function searchFile(uri, key, results)
end)
end
+---@async
return function (key)
local results = {}
diff --git a/script/files.lua b/script/files.lua
index 4d649b29..a779f0f6 100644
--- a/script/files.lua
+++ b/script/files.lua
@@ -120,16 +120,22 @@ end
--- 设置文件文本
---@param uri uri
---@param text string
-function m.setText(uri, text, isTrust, instance)
+---@param isTrust boolean
+---@param version integer
+function m.setText(uri, text, isTrust, version)
if not text then
return
end
+ if #text > 1024 * 1024 * 10 then
+ local client = require 'client'
+ client.showMessage('Warning', lang.script('WORKSPACE_SKIP_HUGE_FILE', uri))
+ return
+ end
--log.debug('setText', uri)
local create
if not m.fileMap[uri] then
m.fileMap[uri] = {
uri = uri,
- version = 0,
}
m.fileCount = m.fileCount + 1
create = true
@@ -154,7 +160,7 @@ function m.setText(uri, text, isTrust, instance)
m.astMap[uri] = nil
file.cache = {}
file.cacheActiveTime = math.huge
- file.version = file.version + 1
+ file.version = version
m.globalVersion = m.globalVersion + 1
await.close('files.version')
m.onWatch('version')
@@ -457,6 +463,7 @@ function m.compileState(uri, text)
--await.delay()
if state then
state.uri = uri
+ state.lua = text
state.ast.uri = uri
local clock = os.clock()
parser.luadoc(state)
@@ -488,14 +495,14 @@ function m.getState(uri)
if not file then
return nil
end
- local ast = m.astMap[uri]
- if not ast then
- ast = m.compileState(uri, file.text)
- m.astMap[uri] = ast
+ local state = m.astMap[uri]
+ if not state then
+ state = m.compileState(uri, file.text)
+ m.astMap[uri] = state
--await.delay()
end
file.cacheActiveTime = timer.clock()
- return ast
+ return state
end
---设置文件的当前可见范围
@@ -703,13 +710,16 @@ function m.getDllWords(uri)
end
--- 注册事件
+---@param callback async fun(ev: string, uri: uri)
function m.watch(callback)
m.watchList[#m.watchList+1] = callback
end
-function m.onWatch(ev, ...)
+function m.onWatch(ev, uri)
for _, callback in ipairs(m.watchList) do
- xpcall(callback, log.error, ev, ...)
+ await.call(function ()
+ callback(ev, uri)
+ end)
end
end
diff --git a/script/filewatch.lua b/script/filewatch.lua
index e2050dd9..dd46d1a3 100644
--- a/script/filewatch.lua
+++ b/script/filewatch.lua
@@ -30,6 +30,7 @@ function m.watch(path)
end
end
+---@param callback async fun()
function m.event(callback)
m._eventList[#m._eventList+1] = callback
end
diff --git a/script/glob/gitignore.lua b/script/glob/gitignore.lua
index 73e20cbf..09be1415 100644
--- a/script/glob/gitignore.lua
+++ b/script/glob/gitignore.lua
@@ -163,6 +163,8 @@ function mt:getRelativePath(path)
return path
end
+---@param callback async fun()
+---@async
function mt:scan(path, callback)
local files = {}
if type(callback) ~= 'function' then
@@ -170,6 +172,7 @@ function mt:scan(path, callback)
end
local list = {}
+ ---@async
local function check(current)
local fileType = self:callInterface('type', current)
if fileType == 'file' then
diff --git a/script/global.d.lua b/script/global.d.lua
index cc0aafbd..0435293e 100644
--- a/script/global.d.lua
+++ b/script/global.d.lua
@@ -11,7 +11,7 @@ DBGPORT = 0
DBGWAIT = false
---displayed language, use command line: --locale="en-us"
----@type '"en-us"'|'"zh-cn"'
+---@type '"en-us"'|'"zh-cn"'|'"pt-br"'
LOCALE = 'en-us'
---path of local config file, use command line: --configpath="config.lua"
@@ -27,4 +27,9 @@ SHOWSOURCE = false
TRACE = false
---trace searching with `too deep!` into log, use command line: --footprint=true
+---@type boolean
FOOTPRINT = false
+
+---trace rpc, use command line: --rpclog=true
+---@type boolean
+RPCLOG = false
diff --git a/script/library.lua b/script/library.lua
index 943e6d2d..81242a91 100644
--- a/script/library.lua
+++ b/script/library.lua
@@ -341,6 +341,7 @@ local function apply3rd(cfg, onlyMemory)
end
local hasAsked
+---@async
local function askFor3rd(cfg)
hasAsked = true
local yes1 = lang.script.WINDOW_APPLY_WHIT_SETTING
@@ -395,7 +396,7 @@ local function check3rdByWords(text, configs)
if hasAsked then
return
end
- await.call(function ()
+ await.call(function () ---@async
for _, cfg in ipairs(configs) do
if cfg.words then
for _, word in ipairs(cfg.words) do
@@ -419,7 +420,7 @@ local function check3rdByFileName(uri, configs)
if not path then
return
end
- await.call(function ()
+ await.call(function () ---@async
for _, cfg in ipairs(configs) do
if cfg.files then
for _, filename in ipairs(cfg.files) do
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua
index e21e7c19..d206e6d7 100644
--- a/script/parser/luadoc.lua
+++ b/script/parser/luadoc.lua
@@ -10,9 +10,8 @@ local Parser = re.compile([[
Main <- (Token / Sp)*
Sp <- %s+
X16 <- [a-fA-F0-9]
-Word <- [a-zA-Z0-9_]
Token <- Integer / Name / String / Symbol
-Name <- ({} {[a-zA-Z_0-9] [a-zA-Z0-9_.*-]*} {})
+Name <- ({} {%name} {})
-> Name
Integer <- ({} {[0-9]+} !'.' {})
-> Integer
@@ -45,7 +44,7 @@ EChar <- 'a' -> ea
/ ('z' (%nl / %s)*) -> ''
/ ('x' {X16 X16}) -> Char16
/ ([0-9] [0-9]? [0-9]?) -> Char10
- / ('u{' {Word*} '}') -> CharUtf8
+ / ('u{' {X16*} '}') -> CharUtf8
Symbol <- ({} {
[:|,<>()?+#`{}]
/ '[]'
@@ -63,6 +62,7 @@ Symbol <- ({} {
er = '\r',
et = '\t',
ev = '\v',
+ name = (m.R('az', 'AZ', '09', '\x80\xff') + m.S('_')) * (m.R('az', 'AZ', '__', '09', '\x80\xff') + m.S('_.*-'))^0,
Char10 = function (char)
char = tonumber(char)
if not char or char < 0 or char > 255 then
@@ -342,6 +342,21 @@ local function parseTypeUnitTable(parent, node)
return result
end
+local function parseDots(tp, parent)
+ if not checkToken('symbol', '...', 1) then
+ return
+ end
+ nextToken()
+ local dots = {
+ type = tp,
+ start = getStart(),
+ finish = getFinish(),
+ parent = parent,
+ [1] = '...',
+ }
+ return dots
+end
+
local function parseTypeUnitFunction()
local typeUnit = {
type = 'doc.type.function',
@@ -361,47 +376,29 @@ local function parseTypeUnitFunction()
type = 'doc.type.arg',
parent = typeUnit,
}
- if checkToken('symbol', '...', 1) then
- nextToken()
- local vararg = {
- type = 'doc.type.name',
- start = getStart(),
+ arg.name = parseName('doc.type.name', arg)
+ or parseDots('doc.type.name', arg)
+ if not arg.name then
+ pushError {
+ type = 'LUADOC_MISS_ARG_NAME',
+ start = getFinish(),
finish = getFinish(),
- parent = arg,
- [1] = '...',
}
- arg.name = vararg
- if not arg.start then
- arg.start = arg.name.start
- end
- arg.finish = getFinish()
- else
- arg.name = parseName('doc.type.name', arg)
- if not arg.name then
- pushError {
- type = 'LUADOC_MISS_ARG_NAME',
- start = getFinish(),
- finish = getFinish(),
- }
- break
- end
- if not arg.start then
- arg.start = arg.name.start
- end
- if checkToken('symbol', '?', 1) then
- nextToken()
- arg.optional = true
- end
- arg.finish = getFinish()
- if not nextSymbolOrError(':') then
- break
- end
+ break
+ end
+ if not arg.start then
+ arg.start = arg.name.start
+ end
+ if checkToken('symbol', '?', 1) then
+ nextToken()
+ arg.optional = true
+ end
+ arg.finish = getFinish()
+ if checkToken('symbol', ':', 1) then
+ nextToken()
arg.extends = parseType(arg)
- if not arg.extends then
- break
- end
- arg.finish = getFinish()
end
+ arg.finish = getFinish()
typeUnit.args[#typeUnit.args+1] = arg
if checkToken('symbol', ',', 1) then
nextToken()
@@ -492,6 +489,19 @@ local function parseTypeUnitLiteralTable()
end
local function parseTypeUnit(parent, content)
+ if content == 'async' then
+ local tp, cont = peekToken()
+ if tp == 'name' then
+ if cont == 'fun' then
+ nextToken()
+ local func = parseTypeUnit(parent, cont)
+ if func then
+ func.async = true
+ return func
+ end
+ end
+ end
+ end
local result
if content == 'fun' then
result = parseTypeUnitFunction()
@@ -749,6 +759,7 @@ local function parseParam()
type = 'doc.param',
}
result.param = parseName('doc.param.name', result)
+ or parseDots('doc.param.name', result)
if not result.param then
pushError {
type = 'LUADOC_MISS_PARAM_NAME',
@@ -1098,6 +1109,22 @@ local function parseModule()
return result
end
+local function parseAsync()
+ return {
+ type = 'doc.async',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+end
+
+local function parseNoDiscard()
+ return {
+ type = 'doc.nodiscard',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+end
+
local function convertTokens()
local tp, text = nextToken()
if not tp then
@@ -1141,6 +1168,10 @@ local function convertTokens()
return parseDiagnostic()
elseif text == 'module' then
return parseModule()
+ elseif text == 'async' then
+ return parseAsync()
+ elseif text == 'nodiscard' then
+ return parseNoDiscard()
end
end
diff --git a/script/parser/newparser.lua b/script/parser/newparser.lua
index 0b721a7a..8eb97b17 100644
--- a/script/parser/newparser.lua
+++ b/script/parser/newparser.lua
@@ -487,7 +487,13 @@ end
local function skipComment(isAction)
local token = Tokens[Index + 1]
if token == '--'
- or (token == '//' and isAction) then
+ or (
+ token == '//'
+ and (
+ isAction
+ or State.options.nonstandardSymbol['//']
+ )
+ ) then
local start = Tokens[Index]
local left = getPosition(start, 'left')
local chead = false
@@ -1527,7 +1533,7 @@ local function parseTable()
local index = 0
local wantSep = false
while true do
- skipSpace()
+ skipSpace(true)
local token = Tokens[Index + 1]
if token == '}' then
Index = Index + 2
@@ -2094,6 +2100,7 @@ local function parseParams(params)
start = getPosition(Tokens[Index], 'left'),
finish = getPosition(Tokens[Index] + 2, 'right'),
parent = params,
+ [1] = '...',
}
local chunk = Chunk[#Chunk]
chunk.vararg = vararg
@@ -2161,7 +2168,7 @@ local function parseFunction(isLocal, isAction)
Index = Index + 2
local LastLocalCount = LocalCount
LocalCount = 0
- skipSpace()
+ skipSpace(true)
local hasLeftParen = Tokens[Index + 1] == '('
if not hasLeftParen then
local name = parseName()
@@ -2191,7 +2198,7 @@ local function parseFunction(isLocal, isAction)
finish = simple.finish,
}
end
- skipSpace()
+ skipSpace(true)
hasLeftParen = Tokens[Index + 1] == '('
end
end
@@ -2222,7 +2229,7 @@ local function parseFunction(isLocal, isAction)
params.parent = func
func.args = params
end
- skipSpace()
+ skipSpace(true)
if Tokens[Index + 1] == ')' then
local parenRight = getPosition(Tokens[Index], 'right')
func.finish = parenRight
@@ -2230,7 +2237,7 @@ local function parseFunction(isLocal, isAction)
params.finish = parenRight
end
Index = Index + 2
- skipSpace()
+ skipSpace(true)
else
func.finish = lastRightPosition()
if params then
@@ -2339,6 +2346,9 @@ local function parseBinaryOP(asAction, level)
if not symbol then
return nil
end
+ if symbol == '//' and State.options.nonstandardSymbol['//'] then
+ return nil
+ end
local myLevel = BinarySymbol[symbol]
if level and myLevel < level then
return nil
@@ -3551,6 +3561,16 @@ function parseAction()
name.vstart = exp.start
name.range = exp.finish
exp.parent = name
+ if name.type == 'setlocal' then
+ local loc = name.node
+ if loc.attrs then
+ pushError {
+ type = 'SET_CONST',
+ start = name.start,
+ finish = name.finish,
+ }
+ end
+ end
pushActionIntoCurrentChunk(name)
return name
else
diff --git a/script/plugin.lua b/script/plugin.lua
index 26f39226..f56dc9f9 100644
--- a/script/plugin.lua
+++ b/script/plugin.lua
@@ -50,6 +50,7 @@ local function resetFiles()
end
end
+---@async
local function checkTrustLoad()
local filePath = LOGPATH .. '/trusted'
local trusted = util.loadFile(filePath)
@@ -79,7 +80,7 @@ function m.init()
return
end
m.hasInited = true
- await.call(function ()
+ await.call(function () ---@async
local ws = require 'workspace'
m.interface = {}
@@ -100,7 +101,7 @@ function m.init()
m.showError(err)
return
end
- if not checkTrustLoad() then
+ if not client.isVSCode() and not checkTrustLoad() then
return
end
local suc, err = xpcall(f, log.error, f)
diff --git a/script/proto/converter.lua b/script/proto/converter.lua
index cf6331f1..9c75f056 100644
--- a/script/proto/converter.lua
+++ b/script/proto/converter.lua
@@ -2,7 +2,6 @@ local guide = require 'parser.guide'
local files = require 'files'
local encoder = require 'encoder'
--- TODO
local offsetEncoding = 'utf16'
local m = {}
@@ -178,4 +177,8 @@ function m.textEdit(range, newtext)
}
end
+function m.setOffsetEncoding(encoding)
+ offsetEncoding = encoding:lower():gsub('%-', '')
+end
+
return m
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 713857af..dbb6ba85 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -44,6 +44,9 @@ m.DiagnosticDefaultSeverity = {
['no-implicit-any'] = 'Information',
['deprecated'] = 'Warning',
['different-requires'] = 'Warning',
+ ['await-in-sync'] = 'Warning',
+ ['not-yieldable'] = 'Warning',
+ ['discard-returns'] = 'Warning',
['type-check'] = 'Warning',
['duplicate-doc-class'] = 'Warning',
@@ -98,6 +101,9 @@ m.DiagnosticDefaultNeededFileStatus = {
['no-implicit-any'] = 'None',
['deprecated'] = 'Opened',
['different-requires'] = 'Any',
+ ['await-in-sync'] = 'None',
+ ['not-yieldable'] = 'None',
+ ['discard-returns'] = 'Opened',
['type-check'] = 'None',
['duplicate-doc-class'] = 'Any',
diff --git a/script/proto/proto.lua b/script/proto/proto.lua
index e380f54f..7cdc461c 100644
--- a/script/proto/proto.lua
+++ b/script/proto/proto.lua
@@ -8,6 +8,20 @@ local json = require 'json'
local reqCounter = util.counter()
+local function logSend(buf)
+ if not RPCLOG then
+ return
+ end
+ log.debug('rpc send:', buf)
+end
+
+local function logRecieve(proto)
+ if not RPCLOG then
+ return
+ end
+ log.debug('rpc recieve:', json.encode(proto))
+end
+
local m = {}
m.ability = {}
@@ -22,6 +36,7 @@ function m.getMethodName(proto)
end
end
+---@param callback async fun()
function m.on(method, callback)
m.ability[method] = callback
end
@@ -38,6 +53,7 @@ function m.response(id, res)
data.result = res == nil and json.null or res
local buf = jsonrpc.encode(data)
--log.debug('Response', id, #buf)
+ logSend(buf)
io.write(buf)
end
@@ -56,6 +72,7 @@ function m.responseErr(id, code, message)
}
}
--log.debug('ResponseErr', id, #buf)
+ logSend(buf)
io.write(buf)
end
@@ -65,9 +82,11 @@ function m.notify(name, params)
params = params,
}
--log.debug('Notify', name, #buf)
+ logSend(buf)
io.write(buf)
end
+---@async
function m.awaitRequest(name, params)
local id = reqCounter()
local buf = jsonrpc.encode {
@@ -76,6 +95,7 @@ function m.awaitRequest(name, params)
params = params,
}
--log.debug('Request', name, #buf)
+ logSend(buf)
io.write(buf)
local result, error = await.wait(function (resume)
m.waiting[id] = resume
@@ -94,6 +114,7 @@ function m.request(name, params, callback)
params = params,
}
--log.debug('Request', name, #buf)
+ logSend(buf)
io.write(buf)
m.waiting[id] = function (result, error)
if error then
@@ -106,6 +127,7 @@ function m.request(name, params, callback)
end
function m.doMethod(proto)
+ logRecieve(proto)
local method, optional = m.getMethodName(proto)
local abil = m.ability[method]
if not abil then
@@ -120,7 +142,7 @@ function m.doMethod(proto)
if proto.id then
m.holdon[proto.id] = proto
end
- await.call(function ()
+ await.call(function () ---@async
--log.debug('Start method:', method)
if proto.id then
await.setID('proto:' .. proto.id)
@@ -146,6 +168,7 @@ function m.doMethod(proto)
end
end
ok, res = xpcall(abil, log.error, proto.params)
+ await.delay()
end)
end
@@ -159,6 +182,7 @@ function m.close(id, reason)
end
function m.doResponse(proto)
+ logRecieve(proto)
local id = proto.id
local resume = m.waiting[id]
if not resume then
diff --git a/script/provider/capability.lua b/script/provider/capability.lua
index 76cadc0d..b712defc 100644
--- a/script/provider/capability.lua
+++ b/script/provider/capability.lua
@@ -48,6 +48,7 @@ end
function m.getIniter()
local initer = {
+ offsetEncoding = client.getOffsetEncoding(),
-- 文本同步方式
textDocumentSync = {
-- 打开关闭文本时通知
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index 492b3048..ca4bcdb8 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -11,6 +11,7 @@ local progress = require "progress"
local client = require 'client'
local converter = require 'proto.converter'
+---@class diagnosticProvider
local m = {}
m._start = false
m.cache = {}
@@ -139,6 +140,10 @@ function m.clear(uri)
log.debug('clearDiagnostics', uri)
end
+function m.clearCache(uri)
+ m.cache[uri] = false
+end
+
function m.clearAll()
for luri in pairs(m.cache) do
m.clear(luri)
@@ -152,11 +157,13 @@ function m.syntaxErrors(uri, ast)
local results = {}
- for _, err in ipairs(ast.errs) do
- if not config.get 'Lua.diagnostics.disable'[err.type:lower():gsub('_', '-')] then
- results[#results+1] = buildSyntaxError(uri, err)
+ pcall(function ()
+ for _, err in ipairs(ast.errs) do
+ if not config.get 'Lua.diagnostics.disable'[err.type:lower():gsub('_', '-')] then
+ results[#results+1] = buildSyntaxError(uri, err)
+ end
end
- end
+ end)
return results
end
@@ -180,6 +187,7 @@ function m.diagnostics(uri, diags)
end)
end
+---@async
function m.doDiagnostic(uri)
if not config.get 'Lua.diagnostics.enable' then
return
@@ -213,6 +221,8 @@ function m.doDiagnostic(uri)
return
end
+ local version = files.getVersion(uri)
+
await.setID('diag:' .. uri)
local prog <close> = progress.create(lang.script.WINDOW_DIAGNOSING, 0.5)
@@ -236,6 +246,7 @@ function m.doDiagnostic(uri)
proto.notify('textDocument/publishDiagnostics', {
uri = uri,
+ version = version,
diagnostics = full,
})
if #full > 0 then
@@ -265,15 +276,17 @@ function m.refresh(uri)
return
end
await.close('diag:' .. uri)
- await.call(function ()
+ await.call(function () ---@async
await.delay()
if uri then
- m.doDiagnostic(uri)
+ m.clearCache(uri)
+ xpcall(m.doDiagnostic, log.error, uri)
end
m.diagnosticsAll()
end, 'files.version')
end
+---@async
local function askForDisable()
if m.dontAskedForDisable then
return
@@ -332,7 +345,7 @@ function m.diagnosticsAll(force)
return
end
await.close 'diagnosticsAll'
- await.call(function ()
+ await.call(function () ---@async
await.sleep(delay)
m.diagnosticsAllClock = os.clock()
local clock = os.clock()
@@ -347,7 +360,7 @@ function m.diagnosticsAll(force)
for i, uri in ipairs(uris) do
bar:setMessage(('%d/%d'):format(i, #uris))
bar:setPercentage(i / #uris * 100)
- m.doDiagnostic(uri)
+ xpcall(m.doDiagnostic, log.error, uri)
await.delay()
if cancelled then
log.debug('Break workspace diagnostics')
@@ -375,6 +388,7 @@ function m.checkStepResult()
end
end
+---@async
function m.checkWorkspaceDiag()
if not await.hasID 'diagnosticsAll' then
return
@@ -400,7 +414,7 @@ function m.checkWorkspaceDiag()
return false
end
-files.watch(function (ev, uri)
+files.watch(function (ev, uri) ---@async
if ev == 'remove' then
m.clear(uri)
m.refresh(uri)
@@ -410,7 +424,7 @@ files.watch(function (ev, uri)
end
elseif ev == 'open' then
if ws.isReady() then
- m.doDiagnostic(uri)
+ xpcall(m.doDiagnostic, log.error, uri)
end
elseif ev == 'close' then
if files.isLibrary(uri)
@@ -420,7 +434,7 @@ files.watch(function (ev, uri)
end
end)
-await.watch(function (ev, co)
+await.watch(function (ev, co) ---@async
if ev == 'delay' then
if m.checkStepResult then
m.checkStepResult()
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index 8932c373..d732f3c2 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -16,6 +16,7 @@ local cfgLoader = require 'config.loader'
local converter = require 'proto.converter'
local filewatch = require 'filewatch'
+---@async
local function updateConfig()
local new
if CONFIGPATH then
@@ -43,7 +44,19 @@ local function updateConfig()
log.debug('loaded config dump:', util.dump(new))
end
-filewatch.event(function (changes)
+---@class provider
+local m = {}
+
+m.attributes = {}
+
+function m.register(method)
+ return function (attrs)
+ m.attributes[method] = attrs
+ proto.on(method, attrs[1])
+ end
+end
+
+filewatch.event(function (changes) ---@async
local configPath = workspace.getAbsolutePath(CONFIGPATH or '.luarc.json')
if not configPath then
return
@@ -56,777 +69,886 @@ filewatch.event(function (changes)
end
end)
-proto.on('initialize', function (params)
- client.init(params)
- config.init()
- workspace.initPath(params.rootUri)
- return {
- capabilities = cap.getIniter(),
- serverInfo = {
- name = 'sumneko.lua',
- },
- }
-end)
-
-proto.on('initialized', function (params)
- files.init()
- local _ <close> = progress.create(lang.script.WINDOW_INITIALIZING, 0.5)
- updateConfig()
- local registrations = {}
-
- if client.getAbility 'workspace.didChangeConfiguration.dynamicRegistration' then
- -- 监视配置变化
- registrations[#registrations+1] = {
- id = 'workspace/didChangeConfiguration',
- method = 'workspace/didChangeConfiguration',
+m.register 'initialize' {
+ function (params)
+ client.init(params)
+ config.init()
+ workspace.initPath(params.rootUri)
+ return {
+ capabilities = cap.getIniter(),
+ serverInfo = {
+ name = 'sumneko.lua',
+ },
}
end
+}
- if #registrations ~= 0 then
- proto.awaitRequest('client/registerCapability', {
- registrations = registrations
- })
- end
- library.init()
- workspace.init()
- return true
-end)
+m.register 'initialized'{
+ ---@async
+ function (params)
+ files.init()
+ local _ <close> = progress.create(lang.script.WINDOW_INITIALIZING, 0.5)
+ updateConfig()
+ local registrations = {}
-proto.on('exit', function ()
- log.info('Server exited.')
- os.exit(true)
-end)
+ if client.getAbility 'workspace.didChangeConfiguration.dynamicRegistration' then
+ -- 监视配置变化
+ registrations[#registrations+1] = {
+ id = 'workspace/didChangeConfiguration',
+ method = 'workspace/didChangeConfiguration',
+ }
+ end
-proto.on('shutdown', function ()
- log.info('Server shutdown.')
- return true
-end)
+ if #registrations ~= 0 then
+ proto.awaitRequest('client/registerCapability', {
+ registrations = registrations
+ })
+ end
+ library.init()
+ workspace.init()
+ return true
+ end
+}
-proto.on('workspace/didChangeConfiguration', function ()
- if CONFIGPATH then
- return
+m.register 'exit' {
+ function ()
+ log.info('Server exited.')
+ os.exit(true)
end
- updateConfig()
-end)
+}
-proto.on('workspace/didCreateFiles', function (params)
- log.debug('workspace/didCreateFiles', util.dump(params))
- for _, file in ipairs(params.files) do
- if workspace.isValidLuaUri(file.uri) then
- files.setText(file.uri, pub.awaitTask('loadFile', file.uri), false)
- end
+m.register 'shutdown' {
+ function ()
+ log.info('Server shutdown.')
+ return true
end
-end)
+}
-proto.on('workspace/didDeleteFiles', function (params)
- log.debug('workspace/didDeleteFiles', util.dump(params))
- for _, file in ipairs(params.files) do
- files.remove(file.uri)
- local childs = files.getChildFiles(file.uri)
- for _, uri in ipairs(childs) do
- log.debug('workspace/didDeleteFiles#child', uri)
- files.remove(uri)
+m.register 'workspace/didChangeConfiguration' {
+ function () ---@async
+ if CONFIGPATH then
+ return
end
+ updateConfig()
end
-end)
+}
-proto.on('workspace/didRenameFiles', function (params)
- log.debug('workspace/didRenameFiles', util.dump(params))
- for _, file in ipairs(params.files) do
- local text = files.getOriginText(file.oldUri)
- if text then
- files.remove(file.oldUri)
- if workspace.isValidLuaUri(file.newUri) then
- files.setText(file.newUri, text, false)
+m.register 'workspace/didCreateFiles' {
+ ---@async
+ function (params)
+ log.debug('workspace/didCreateFiles', util.dump(params))
+ for _, file in ipairs(params.files) do
+ if workspace.isValidLuaUri(file.uri) then
+ files.setText(file.uri, pub.awaitTask('loadFile', file.uri), false)
end
end
- local childs = files.getChildFiles(file.oldUri)
- for _, uri in ipairs(childs) do
- local ctext = files.getOriginText(uri)
- if ctext then
- local ouri = uri
- local tail = ouri:sub(#file.oldUri)
- local nuri = file.newUri .. tail
- log.debug('workspace/didRenameFiles#child', ouri, nuri)
+ end
+}
+
+m.register 'workspace/didDeleteFiles' {
+ function (params)
+ log.debug('workspace/didDeleteFiles', util.dump(params))
+ for _, file in ipairs(params.files) do
+ files.remove(file.uri)
+ local childs = files.getChildFiles(file.uri)
+ for _, uri in ipairs(childs) do
+ log.debug('workspace/didDeleteFiles#child', uri)
files.remove(uri)
- if workspace.isValidLuaUri(nuri) then
- files.setText(nuri, text, false)
+ end
+ end
+ end
+}
+
+m.register 'workspace/didRenameFiles' {
+ ---@async
+ function (params)
+ log.debug('workspace/didRenameFiles', util.dump(params))
+ for _, file in ipairs(params.files) do
+ local text = files.getOriginText(file.oldUri)
+ if text then
+ files.remove(file.oldUri)
+ if workspace.isValidLuaUri(file.newUri) then
+ files.setText(file.newUri, text, false)
+ end
+ end
+ local childs = files.getChildFiles(file.oldUri)
+ for _, uri in ipairs(childs) do
+ local ctext = files.getOriginText(uri)
+ if ctext then
+ local ouri = uri
+ local tail = ouri:sub(#file.oldUri)
+ local nuri = file.newUri .. tail
+ log.debug('workspace/didRenameFiles#child', ouri, nuri)
+ files.remove(uri)
+ if workspace.isValidLuaUri(nuri) then
+ files.setText(nuri, text, false)
+ end
end
end
end
end
-end)
+}
-proto.on('textDocument/didOpen', function (params)
- workspace.awaitReady()
- local doc = params.textDocument
- local uri = doc.uri
- local text = doc.text
- log.debug('didOpen', uri)
- files.setText(uri, text, true)
- files.open(uri)
-end)
+m.register 'textDocument/didOpen' {
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local doc = params.textDocument
+ local uri = doc.uri
+ local text = doc.text
+ log.debug('didOpen', uri)
+ files.setText(uri, text, true)
+ files.open(uri)
+ end
+}
-proto.on('textDocument/didClose', function (params)
- local doc = params.textDocument
- local uri = doc.uri
- log.debug('didClose', uri)
- files.close(uri)
- if not files.isLua(uri) then
- files.remove(uri)
+m.register 'textDocument/didClose' {
+ function (params)
+ local doc = params.textDocument
+ local uri = doc.uri
+ log.debug('didClose', uri)
+ files.close(uri)
+ if not files.isLua(uri) then
+ files.remove(uri)
+ end
end
-end)
+}
-proto.on('textDocument/didChange', function (params)
- workspace.awaitReady()
- local doc = params.textDocument
- local changes = params.contentChanges
- local uri = doc.uri
- --log.debug('changes', util.dump(changes))
- local text = tm(uri, changes)
- files.setText(uri, text, true)
-end)
+m.register 'textDocument/didChange' {
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local doc = params.textDocument
+ local changes = params.contentChanges
+ local uri = doc.uri
+ --log.debug('changes', util.dump(changes))
+ local text = tm(uri, changes)
+ files.setText(uri, text, true, doc.version)
+ end
+}
-proto.on('textDocument/hover', function (params)
- local doc = params.textDocument
- local uri = doc.uri
- if not workspace.isReady() then
- local count, max = workspace.getLoadProcess()
+m.register 'textDocument/hover' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ local doc = params.textDocument
+ local uri = doc.uri
+ if not workspace.isReady() then
+ local count, max = workspace.getLoadProcess()
+ return {
+ contents = {
+ value = lang.script('HOVER_WS_LOADING', count, max),
+ kind = 'markdown',
+ }
+ }
+ end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_HOVER, 0.5)
+ local core = require 'core.hover'
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local hover, source = core.byUri(uri, pos)
+ if not hover then
+ return nil
+ end
return {
contents = {
- value = lang.script('HOVER_WS_LOADING', count, max),
+ value = tostring(hover),
kind = 'markdown',
- }
+ },
+ range = converter.packRange(uri, source.start, source.finish),
}
end
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_HOVER, 0.5)
- local core = require 'core.hover'
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local hover, source = core.byUri(uri, pos)
- if not hover then
- return nil
- end
- return {
- contents = {
- value = tostring(hover),
- kind = 'markdown',
- },
- range = converter.packRange(uri, source.start, source.finish),
- }
-end)
+}
-proto.on('textDocument/definition', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_DEFINITION, 0.5)
- local core = require 'core.definition'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core(uri, pos)
- if not result then
- return nil
- end
- local response = {}
- for i, info in ipairs(result) do
- local targetUri = info.uri
- if targetUri then
- if files.exists(targetUri) then
- if client.getAbility 'textDocument.definition.linkSupport' then
- response[i] = converter.locationLink(targetUri
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- , converter.packRange(uri, info.source.start, info.source.finish)
- )
- else
- response[i] = converter.location(targetUri
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- )
+m.register 'textDocument/definition' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_DEFINITION, 0.5)
+ local core = require 'core.definition'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
+ if not result then
+ return nil
+ end
+ local response = {}
+ for i, info in ipairs(result) do
+ local targetUri = info.uri
+ if targetUri then
+ if files.exists(targetUri) then
+ if client.getAbility 'textDocument.definition.linkSupport' then
+ response[i] = converter.locationLink(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(uri, info.source.start, info.source.finish)
+ )
+ else
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ )
+ end
end
end
end
+ return response
end
- return response
-end)
+}
-proto.on('textDocument/typeDefinition', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_DEFINITION, 0.5)
- local core = require 'core.type-definition'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core(uri, pos)
- if not result then
- return nil
- end
- local response = {}
- for i, info in ipairs(result) do
- local targetUri = info.uri
- if targetUri then
- if files.exists(targetUri) then
- if client.getAbility 'textDocument.typeDefinition.linkSupport' then
- response[i] = converter.locationLink(targetUri
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- , converter.packRange(uri, info.source.start, info.source.finish)
- )
- else
- response[i] = converter.location(targetUri
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- )
+m.register 'textDocument/typeDefinition' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_DEFINITION, 0.5)
+ local core = require 'core.type-definition'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
+ if not result then
+ return nil
+ end
+ local response = {}
+ for i, info in ipairs(result) do
+ local targetUri = info.uri
+ if targetUri then
+ if files.exists(targetUri) then
+ if client.getAbility 'textDocument.typeDefinition.linkSupport' then
+ response[i] = converter.locationLink(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(uri, info.source.start, info.source.finish)
+ )
+ else
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ )
+ end
end
end
end
+ return response
end
- return response
-end)
+}
-proto.on('textDocument/references', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_REFERENCE, 0.5)
- local core = require 'core.reference'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core(uri, pos)
- if not result then
- return nil
- end
- local response = {}
- for i, info in ipairs(result) do
- local targetUri = info.uri
- response[i] = converter.location(targetUri
- , converter.packRange(targetUri, info.target.start, info.target.finish)
- )
- end
- return response
-end)
-
-proto.on('textDocument/documentHighlight', function (params)
- local core = require 'core.highlight'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core(uri, pos)
- if not result then
- return nil
- end
- local response = {}
- for _, info in ipairs(result) do
- response[#response+1] = {
- range = converter.packRange(uri, info.start, info.finish),
- kind = info.kind,
- }
+m.register 'textDocument/references' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_REFERENCE, 0.5)
+ local core = require 'core.reference'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
+ if not result then
+ return nil
+ end
+ local response = {}
+ for i, info in ipairs(result) do
+ local targetUri = info.uri
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ )
+ end
+ return response
end
- return response
-end)
+}
-proto.on('textDocument/rename', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_RENAME, 0.5)
- local core = require 'core.rename'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core.rename(uri, pos, params.newName)
- if not result then
- return nil
- end
- local workspaceEdit = {
- changes = {},
- }
- for _, info in ipairs(result) do
- local ruri = info.uri
- if not workspaceEdit.changes[ruri] then
- workspaceEdit.changes[ruri] = {}
- end
- local textEdit = converter.textEdit(converter.packRange(ruri, info.start, info.finish), info.text)
- workspaceEdit.changes[ruri][#workspaceEdit.changes[ruri]+1] = textEdit
- end
- return workspaceEdit
-end)
+m.register 'textDocument/documentHighlight' {
+ abortByFileUpdate = true,
+ function (params)
+ local core = require 'core.highlight'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
+ if not result then
+ return nil
+ end
+ local response = {}
+ for _, info in ipairs(result) do
+ response[#response+1] = {
+ range = converter.packRange(uri, info.start, info.finish),
+ kind = info.kind,
+ }
+ end
+ return response
+ end
+}
-proto.on('textDocument/prepareRename', function (params)
- local core = require 'core.rename'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local result = core.prepareRename(uri, pos)
- if not result then
- return nil
- end
- return {
- range = converter.packRange(uri, result.start, result.finish),
- placeholder = result.text,
- }
-end)
+m.register 'textDocument/rename' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_RENAME, 0.5)
+ local core = require 'core.rename'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.rename(uri, pos, params.newName)
+ if not result then
+ return nil
+ end
+ local workspaceEdit = {
+ changes = {},
+ }
+ for _, info in ipairs(result) do
+ local ruri = info.uri
+ if not workspaceEdit.changes[ruri] then
+ workspaceEdit.changes[ruri] = {}
+ end
+ local textEdit = converter.textEdit(converter.packRange(ruri, info.start, info.finish), info.text)
+ workspaceEdit.changes[ruri][#workspaceEdit.changes[ruri]+1] = textEdit
+ end
+ return workspaceEdit
+ end
+}
-proto.on('textDocument/completion', function (params)
- local uri = params.textDocument.uri
- if not workspace.isReady() then
- local count, max = workspace.getLoadProcess()
+m.register 'textDocument/prepareRename' {
+ abortByFileUpdate = true,
+ function (params)
+ local core = require 'core.rename'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.prepareRename(uri, pos)
+ if not result then
+ return nil
+ end
return {
- {
- label = lang.script('HOVER_WS_LOADING', count, max),textEdit = {
- range = {
- start = params.position,
- ['end'] = params.position,
- },
- newText = '',
- },
- }
+ range = converter.packRange(uri, result.start, result.finish),
+ placeholder = result.text,
}
end
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_COMPLETION, 0.5)
- --log.info(util.dump(params))
- local core = require 'core.completion'
- --log.debug('textDocument/completion')
- --log.debug('completion:', params.context and params.context.triggerKind, params.context and params.context.triggerCharacter)
- if not files.exists(uri) then
- return nil
- end
- local triggerCharacter = params.context and params.context.triggerCharacter
- if config.get 'editor.acceptSuggestionOnEnter' ~= 'off' then
- if triggerCharacter == '\n'
- or triggerCharacter == '{'
- or triggerCharacter == ',' then
- return
+}
+
+m.register 'textDocument/completion' {
+ ---@async
+ function (params)
+ local uri = params.textDocument.uri
+ if not workspace.isReady() then
+ local count, max = workspace.getLoadProcess()
+ return {
+ {
+ label = lang.script('HOVER_WS_LOADING', count, max),textEdit = {
+ range = {
+ start = params.position,
+ ['end'] = params.position,
+ },
+ newText = '',
+ },
+ }
+ }
end
- end
- await.setPriority(1000)
- local clock = os.clock()
- local pos = converter.unpackPosition(uri, params.position)
- local result = core.completion(uri, pos, triggerCharacter)
- local passed = os.clock() - clock
- if passed > 0.1 then
- log.warn(('Completion takes %.3f sec.'):format(passed))
- end
- if not result then
- return nil
- end
- tracy.ZoneBeginN 'completion make'
- local _ <close> = tracy.ZoneEnd
- local easy = false
- local items = {}
- for i, res in ipairs(result) do
- local item = {
- label = res.label,
- kind = res.kind,
- detail = res.detail,
- deprecated = res.deprecated,
- sortText = ('%04d'):format(i),
- filterText = res.filterText,
- insertText = res.insertText,
- insertTextFormat = 2,
- commitCharacters = res.commitCharacters,
- command = res.command,
- textEdit = res.textEdit and {
- range = converter.packRange(
- uri,
- res.textEdit.start,
- res.textEdit.finish
- ),
- newText = res.textEdit.newText,
- },
- additionalTextEdits = res.additionalTextEdits and (function ()
- local t = {}
- for j, edit in ipairs(res.additionalTextEdits) do
- t[j] = {
- range = converter.packRange(
- uri,
- edit.start,
- edit.finish
- ),
- newText = edit.newText,
- }
- end
- return t
- end)(),
- documentation = res.description and {
- value = tostring(res.description),
- kind = 'markdown',
- },
- }
- if res.id then
- if easy and os.clock() - clock < 0.05 then
- local resolved = core.resolve(res.id)
- if resolved then
- item.detail = resolved.detail
- item.documentation = resolved.description and {
- value = tostring(resolved.description),
- kind = 'markdown',
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_COMPLETION, 0.5)
+ --log.info(util.dump(params))
+ local core = require 'core.completion'
+ --log.debug('textDocument/completion')
+ --log.debug('completion:', params.context and params.context.triggerKind, params.context and params.context.triggerCharacter)
+ if not files.exists(uri) then
+ return nil
+ end
+ local triggerCharacter = params.context and params.context.triggerCharacter
+ if config.get 'editor.acceptSuggestionOnEnter' ~= 'off' then
+ if triggerCharacter == '\n'
+ or triggerCharacter == '{'
+ or triggerCharacter == ',' then
+ return
+ end
+ end
+ --await.setPriority(1000)
+ local clock = os.clock()
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.completion(uri, pos, triggerCharacter)
+ local passed = os.clock() - clock
+ if passed > 0.1 then
+ log.warn(('Completion takes %.3f sec.'):format(passed))
+ end
+ if not result then
+ return nil
+ end
+ tracy.ZoneBeginN 'completion make'
+ local _ <close> = tracy.ZoneEnd
+ local easy = false
+ local items = {}
+ for i, res in ipairs(result) do
+ local item = {
+ label = res.label,
+ kind = res.kind,
+ detail = res.detail,
+ deprecated = res.deprecated,
+ sortText = ('%04d'):format(i),
+ filterText = res.filterText,
+ insertText = res.insertText,
+ insertTextFormat = 2,
+ commitCharacters = res.commitCharacters,
+ command = res.command,
+ textEdit = res.textEdit and {
+ range = converter.packRange(
+ uri,
+ res.textEdit.start,
+ res.textEdit.finish
+ ),
+ newText = res.textEdit.newText,
+ },
+ additionalTextEdits = res.additionalTextEdits and (function ()
+ local t = {}
+ for j, edit in ipairs(res.additionalTextEdits) do
+ t[j] = {
+ range = converter.packRange(
+ uri,
+ edit.start,
+ edit.finish
+ ),
+ newText = edit.newText,
+ }
+ end
+ return t
+ end)(),
+ documentation = res.description and {
+ value = tostring(res.description),
+ kind = 'markdown',
+ },
+ }
+ if res.id then
+ if easy and os.clock() - clock < 0.05 then
+ local resolved = core.resolve(res.id)
+ if resolved then
+ item.detail = resolved.detail
+ item.documentation = resolved.description and {
+ value = tostring(resolved.description),
+ kind = 'markdown',
+ }
+ end
+ else
+ easy = false
+ item.data = {
+ uri = uri,
+ id = res.id,
}
end
- else
- easy = false
- item.data = {
- uri = uri,
- id = res.id,
- }
end
+ items[i] = item
end
- items[i] = item
+ return {
+ isIncomplete = not result.complete,
+ items = items,
+ }
end
- return {
- isIncomplete = not result.complete,
- items = items,
- }
-end)
+}
-proto.on('completionItem/resolve', function (item)
- local core = require 'core.completion'
- if not item.data then
+m.register 'completionItem/resolve' {
+ ---@async
+ function (item)
+ local core = require 'core.completion'
+ if not item.data then
+ return item
+ end
+ local id = item.data.id
+ local uri = item.data.uri
+ --await.setPriority(1000)
+ local resolved = core.resolve(id)
+ if not resolved then
+ return nil
+ end
+ item.detail = resolved.detail or item.detail
+ item.documentation = resolved.description and {
+ value = tostring(resolved.description),
+ kind = 'markdown',
+ } or item.documentation
+ item.additionalTextEdits = resolved.additionalTextEdits and (function ()
+ local t = {}
+ for j, edit in ipairs(resolved.additionalTextEdits) do
+ t[j] = {
+ range = converter.packRange(
+ uri,
+ edit.start,
+ edit.finish
+ ),
+ newText = edit.newText,
+ }
+ end
+ return t
+ end)() or item.additionalTextEdits
return item
end
- local id = item.data.id
- local uri = item.data.uri
- --await.setPriority(1000)
- local resolved = core.resolve(id)
- if not resolved then
- return nil
- end
- item.detail = resolved.detail or item.detail
- item.documentation = resolved.description and {
- value = tostring(resolved.description),
- kind = 'markdown',
- } or item.documentation
- item.additionalTextEdits = resolved.additionalTextEdits and (function ()
- local t = {}
- for j, edit in ipairs(resolved.additionalTextEdits) do
- t[j] = {
- range = converter.packRange(
- uri,
- edit.start,
- edit.finish
- ),
- newText = edit.newText,
- }
- end
- return t
- end)() or item.additionalTextEdits
- return item
-end)
+}
-proto.on('textDocument/signatureHelp', function (params)
- if not config.get 'Lua.signatureHelp.enable' then
- return nil
- end
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SIGNATURE, 0.5)
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local pos = converter.unpackPosition(uri, params.position)
- local core = require 'core.signature'
- local results = core(uri, pos)
- if not results then
- return nil
- end
- local infos = {}
- for i, result in ipairs(results) do
- local parameters = {}
- for j, param in ipairs(result.params) do
- parameters[j] = {
- label = {
- param.label[1],
- param.label[2],
+m.register 'textDocument/signatureHelp' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ if not config.get 'Lua.signatureHelp.enable' then
+ return nil
+ end
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SIGNATURE, 0.5)
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local pos = converter.unpackPosition(uri, params.position)
+ local core = require 'core.signature'
+ local results = core(uri, pos)
+ if not results then
+ return nil
+ end
+ local infos = {}
+ for i, result in ipairs(results) do
+ local parameters = {}
+ for j, param in ipairs(result.params) do
+ parameters[j] = {
+ label = {
+ param.label[1],
+ param.label[2],
+ }
}
+ end
+ infos[i] = {
+ label = result.label,
+ parameters = parameters,
+ activeParameter = result.index - 1,
+ documentation = result.description and {
+ value = tostring(result.description),
+ kind = 'markdown',
+ },
}
end
- infos[i] = {
- label = result.label,
- parameters = parameters,
- activeParameter = result.index - 1,
- documentation = result.description and {
- value = tostring(result.description),
- kind = 'markdown',
- },
+ return {
+ signatures = infos,
}
end
- return {
- signatures = infos,
- }
-end)
+}
-proto.on('textDocument/documentSymbol', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SYMBOL, 0.5)
- local core = require 'core.document-symbol'
- local uri = params.textDocument.uri
+m.register 'textDocument/documentSymbol' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SYMBOL, 0.5)
+ local core = require 'core.document-symbol'
+ local uri = params.textDocument.uri
- local symbols = core(uri)
- if not symbols then
- return nil
- end
+ local symbols = core(uri)
+ if not symbols then
+ return nil
+ end
- local function convert(symbol)
- await.delay()
- symbol.range = converter.packRange(
- uri,
- symbol.range[1],
- symbol.range[2]
- )
- symbol.selectionRange = converter.packRange(
- uri,
- symbol.selectionRange[1],
- symbol.selectionRange[2]
- )
- if symbol.name == '' then
- symbol.name = lang.script.SYMBOL_ANONYMOUS
- end
- symbol.valueRange = nil
- if symbol.children then
- for _, child in ipairs(symbol.children) do
- convert(child)
+ ---@async
+ local function convert(symbol)
+ await.delay()
+ symbol.range = converter.packRange(
+ uri,
+ symbol.range[1],
+ symbol.range[2]
+ )
+ symbol.selectionRange = converter.packRange(
+ uri,
+ symbol.selectionRange[1],
+ symbol.selectionRange[2]
+ )
+ if symbol.name == '' then
+ symbol.name = lang.script.SYMBOL_ANONYMOUS
+ end
+ symbol.valueRange = nil
+ if symbol.children then
+ for _, child in ipairs(symbol.children) do
+ convert(child)
+ end
end
end
- end
-
- for _, symbol in ipairs(symbols) do
- convert(symbol)
- end
- return symbols
-end)
+ for _, symbol in ipairs(symbols) do
+ convert(symbol)
+ end
-proto.on('textDocument/codeAction', function (params)
- local core = require 'core.code-action'
- local uri = params.textDocument.uri
- local range = params.range
- local diagnostics = params.context.diagnostics
- if not files.exists(uri) then
- return nil
+ return symbols
end
+}
- local start, finish = converter.unpackRange(uri, range)
- local results = core(uri, start, finish, diagnostics)
+m.register 'textDocument/codeAction' {
+ abortByFileUpdate = true,
+ function (params)
+ local core = require 'core.code-action'
+ local uri = params.textDocument.uri
+ local range = params.range
+ local diagnostics = params.context.diagnostics
+ if not files.exists(uri) then
+ return nil
+ end
- if not results or #results == 0 then
- return nil
- end
+ local start, finish = converter.unpackRange(uri, range)
+ local results = core(uri, start, finish, diagnostics)
+
+ if not results or #results == 0 then
+ return nil
+ end
- for _, res in ipairs(results) do
- if res.edit then
- for turi, changes in pairs(res.edit.changes) do
- for _, change in ipairs(changes) do
- change.range = converter.packRange(turi, change.start, change.finish)
- change.start = nil
- change.finish = nil
+ for _, res in ipairs(results) do
+ if res.edit then
+ for turi, changes in pairs(res.edit.changes) do
+ for _, change in ipairs(changes) do
+ change.range = converter.packRange(turi, change.start, change.finish)
+ change.start = nil
+ change.finish = nil
+ end
end
end
end
- end
- return results
-end)
+ return results
+ end
+}
-proto.on('workspace/executeCommand', function (params)
- local command = params.command:gsub(':.+', '')
- if command == 'lua.removeSpace' then
- local core = require 'core.command.removeSpace'
- return core(params.arguments[1])
- elseif command == 'lua.solve' then
- local core = require 'core.command.solve'
- return core(params.arguments[1])
- elseif command == 'lua.jsonToLua' then
- local core = require 'core.command.jsonToLua'
- return core(params.arguments[1])
- elseif command == 'lua.setConfig' then
- local core = require 'core.command.setConfig'
- return core(params.arguments[1])
- elseif command == 'lua.autoRequire' then
- local core = require 'core.command.autoRequire'
- return core(params.arguments[1])
+m.register 'workspace/executeCommand' {
+ ---@async
+ function (params)
+ local command = params.command:gsub(':.+', '')
+ if command == 'lua.removeSpace' then
+ local core = require 'core.command.removeSpace'
+ return core(params.arguments[1])
+ elseif command == 'lua.solve' then
+ local core = require 'core.command.solve'
+ return core(params.arguments[1])
+ elseif command == 'lua.jsonToLua' then
+ local core = require 'core.command.jsonToLua'
+ return core(params.arguments[1])
+ elseif command == 'lua.setConfig' then
+ local core = require 'core.command.setConfig'
+ return core(params.arguments[1])
+ elseif command == 'lua.autoRequire' then
+ local core = require 'core.command.autoRequire'
+ return core(params.arguments[1])
+ end
end
-end)
+}
-proto.on('workspace/symbol', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_WS_SYMBOL, 0.5)
- local core = require 'core.workspace-symbol'
+m.register 'workspace/symbol' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_WS_SYMBOL, 0.5)
+ local core = require 'core.workspace-symbol'
- local symbols = core(params.query)
- if not symbols or #symbols == 0 then
- return nil
- end
+ local symbols = core(params.query)
+ if not symbols or #symbols == 0 then
+ return nil
+ end
- local function convert(symbol)
- symbol.location = converter.location(
- symbol.uri,
- converter.packRange(
+ local function convert(symbol)
+ symbol.location = converter.location(
symbol.uri,
- symbol.range[1],
- symbol.range[2]
+ converter.packRange(
+ symbol.uri,
+ symbol.range[1],
+ symbol.range[2]
+ )
)
- )
- symbol.uri = nil
- end
-
- for _, symbol in ipairs(symbols) do
- convert(symbol)
- end
+ symbol.uri = nil
+ end
- return symbols
-end)
+ for _, symbol in ipairs(symbols) do
+ convert(symbol)
+ end
-proto.on('textDocument/semanticTokens/full', function (params)
- local uri = params.textDocument.uri
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_FULL, 0.5)
- local core = require 'core.semantic-tokens'
- local results = core(uri, 0, math.huge)
- return {
- data = results
- }
-end)
+ return symbols
+ end
+}
-proto.on('textDocument/semanticTokens/range', function (params)
- local uri = params.textDocument.uri
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_RANGE, 0.5)
- local core = require 'core.semantic-tokens'
- local cache = files.getOpenedCache(uri)
- local start, finish
- if cache and not cache['firstSemantic'] then
- cache['firstSemantic'] = true
- start = 0
- finish = math.huge
- else
- start, finish = converter.unpackRange(uri, params.range)
+m.register 'textDocument/semanticTokens/full' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ local uri = params.textDocument.uri
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_FULL, 0.5)
+ local core = require 'core.semantic-tokens'
+ local results = core(uri, 0, math.huge)
+ return {
+ data = results
+ }
end
- local results = core(uri, start, finish)
- return {
- data = results
- }
-end)
+}
-proto.on('textDocument/foldingRange', function (params)
- local core = require 'core.folding'
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local regions = core(uri)
- if not regions then
- return nil
- end
-
- local results = {}
- for _, region in ipairs(regions) do
- local startLine = converter.packPosition(uri, region.start).line
- local endLine = converter.packPosition(uri, region.finish).line
- if not region.hideLastLine then
- endLine = endLine - 1
- end
- if startLine < endLine then
- results[#results+1] = {
- startLine = startLine,
- endLine = endLine,
- kind = region.kind,
- }
+m.register 'textDocument/semanticTokens/range' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ local uri = params.textDocument.uri
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_RANGE, 0.5)
+ local core = require 'core.semantic-tokens'
+ local cache = files.getOpenedCache(uri)
+ local start, finish
+ if cache and not cache['firstSemantic'] then
+ cache['firstSemantic'] = true
+ start = 0
+ finish = math.huge
+ else
+ start, finish = converter.unpackRange(uri, params.range)
end
+ local results = core(uri, start, finish)
+ return {
+ data = results
+ }
end
+}
- return results
-end)
+m.register 'textDocument/foldingRange' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ local core = require 'core.folding'
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local regions = core(uri)
+ if not regions then
+ return nil
+ end
-proto.on('window/workDoneProgress/cancel', function (params)
- progress.cancel(params.token)
-end)
+ local results = {}
+ for _, region in ipairs(regions) do
+ local startLine = converter.packPosition(uri, region.start).line
+ local endLine = converter.packPosition(uri, region.finish).line
+ if not region.hideLastLine then
+ endLine = endLine - 1
+ end
+ if startLine < endLine then
+ results[#results+1] = {
+ startLine = startLine,
+ endLine = endLine,
+ kind = region.kind,
+ }
+ end
+ end
-proto.on('$/didChangeVisibleRanges', function (params)
- local uri = params.uri
- await.close('visible:' .. uri)
- await.setID('visible:' .. uri)
- await.delay()
- files.setVisibles(uri, params.ranges)
-end)
+ return results
+ end
+}
-proto.on('$/status/click', function ()
- -- TODO: translate
- local titleDiagnostic = '进行工作区诊断'
- local result = client.awaitRequestMessage('Info', 'xxx', {
- titleDiagnostic,
- })
- if not result then
- return
+m.register 'window/workDoneProgress/cancel' {
+ function (params)
+ log.debug('close proto(cancel):', params.token)
+ progress.cancel(params.token)
end
- if result == titleDiagnostic then
- local diagnostic = require 'provider.diagnostic'
- diagnostic.diagnosticsAll(true)
+}
+
+m.register '$/didChangeVisibleRanges' {
+ ---@async
+ function (params)
+ local uri = params.uri
+ await.close('visible:' .. uri)
+ await.setID('visible:' .. uri)
+ await.delay()
+ files.setVisibles(uri, params.ranges)
end
-end)
+}
-proto.on('textDocument/onTypeFormatting', function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_FORMATTING, 0.5)
- local ch = params.ch
- local uri = params.textDocument.uri
- if not files.exists(uri) then
- return nil
- end
- local core = require 'core.type-formatting'
- local pos = converter.unpackPosition(uri, params.position)
- local edits = core(uri, pos, ch)
- if not edits or #edits == 0 then
- return nil
- end
- local tab = '\t'
- if params.options.insertSpaces then
- tab = (' '):rep(params.options.tabSize)
- end
- local results = {}
- for i, edit in ipairs(edits) do
- results[i] = {
- range = converter.packRange(uri, edit.start, edit.finish),
- newText = edit.text:gsub('\t', tab),
- }
+m.register '$/status/click' {
+ ---@async
+ function ()
+ -- TODO: translate
+ local titleDiagnostic = '进行工作区诊断'
+ local result = client.awaitRequestMessage('Info', 'xxx', {
+ titleDiagnostic,
+ })
+ if not result then
+ return
+ end
+ if result == titleDiagnostic then
+ local diagnostic = require 'provider.diagnostic'
+ diagnostic.diagnosticsAll(true)
+ end
end
- return results
-end)
+}
-proto.on('$/cancelRequest', function (params)
- proto.close(params.id, define.ErrorCodes.RequestCancelled)
-end)
+m.register 'textDocument/onTypeFormatting' {
+ abortByFileUpdate = true,
+ ---@async
+ function (params)
+ workspace.awaitReady()
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_FORMATTING, 0.5)
+ local ch = params.ch
+ local uri = params.textDocument.uri
+ if not files.exists(uri) then
+ return nil
+ end
+ local core = require 'core.type-formatting'
+ local pos = converter.unpackPosition(uri, params.position)
+ local edits = core(uri, pos, ch)
+ if not edits or #edits == 0 then
+ return nil
+ end
+ local tab = '\t'
+ if params.options.insertSpaces then
+ tab = (' '):rep(params.options.tabSize)
+ end
+ local results = {}
+ for i, edit in ipairs(edits) do
+ results[i] = {
+ range = converter.packRange(uri, edit.start, edit.finish),
+ newText = edit.text:gsub('\t', tab),
+ }
+ end
+ return results
+ end
+}
-proto.on('$/requestHint', function (params)
- local core = require 'core.hint'
- if not config.get 'Lua.hint.enable' then
- return
+m.register '$/cancelRequest' {
+ function (params)
+ proto.close(params.id, define.ErrorCodes.RequestCancelled)
end
- workspace.awaitReady()
- local uri = params.textDocument.uri
- local start, finish = converter.unpackRange(uri, params.range)
- local results = core(uri, start, finish)
- local hintResults = {}
- for i, res in ipairs(results) do
- hintResults[i] = {
- text = res.text,
- pos = converter.packPosition(uri, res.offset),
- kind = res.kind,
- }
+}
+
+m.register '$/requestHint' {
+ ---@async
+ function (params)
+ local core = require 'core.hint'
+ if not config.get 'Lua.hint.enable' then
+ return
+ end
+ workspace.awaitReady()
+ local uri = params.textDocument.uri
+ local start, finish = converter.unpackRange(uri, params.range)
+ local results = core(uri, start, finish)
+ local hintResults = {}
+ for i, res in ipairs(results) do
+ hintResults[i] = {
+ text = res.text,
+ pos = converter.packPosition(uri, res.offset),
+ kind = res.kind,
+ }
+ end
+ return hintResults
end
- return hintResults
-end)
+}
-- Hint
do
+ ---@async
local function updateHint(uri)
if not config.get 'Lua.hint.enable' then
return
@@ -838,6 +960,10 @@ do
if not visibles then
return
end
+ await.close 'updateHint'
+ await.setID 'updateHint'
+ await.delay()
+ workspace.awaitReady()
local edits = {}
local hint = require 'core.hint'
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_HINT, 0.5)
@@ -862,7 +988,7 @@ do
files.watch(function (ev, uri)
if ev == 'update'
or ev == 'updateVisible' then
- await.call(function ()
+ await.call(function () ---@async
updateHint(uri)
end)
end
@@ -884,7 +1010,7 @@ config.watch(function (key, value)
end
end)
-proto.on('$/status/refresh', refreshStatusBar)
+m.register '$/status/refresh' { refreshStatusBar }
files.watch(function (ev, uri)
if not workspace.isReady() then
@@ -893,7 +1019,8 @@ files.watch(function (ev, uri)
if ev == 'update'
or ev == 'remove' then
for id, p in pairs(proto.holdon) do
- if p.params.textDocument and p.params.textDocument.uri == uri then
+ if m.attributes[p.method].abortByFileUpdate then
+ log.debug('close proto(ContentModified):', id, p.method)
proto.close(id, define.ErrorCodes.ContentModified)
end
end
diff --git a/script/pub/pub.lua b/script/pub/pub.lua
index ba57e5cb..fd780477 100644
--- a/script/pub/pub.lua
+++ b/script/pub/pub.lua
@@ -104,6 +104,7 @@ end
---@parma name string
---@param params any
---@return any
+---@async
function m.awaitTask(name, params)
local info = {
id = counter(),
diff --git a/script/service/telemetry.lua b/script/service/telemetry.lua
index 1ab3fdbe..dac72f3f 100644
--- a/script/service/telemetry.lua
+++ b/script/service/telemetry.lua
@@ -111,7 +111,7 @@ function m.updateConfig()
end
m.hasShowedMessage = true
- await.call(function ()
+ await.call(function () ---@async
local enableTitle = lang.script.WINDOW_TELEMETRY_ENABLE
local disableTitle = lang.script.WINDOW_TELEMETRY_DISABLE
local item = proto.awaitRequest('window/showMessageRequest', {
diff --git a/script/utility.lua b/script/utility.lua
index b5eb4095..85c98cb1 100644
--- a/script/utility.lua
+++ b/script/utility.lua
@@ -691,6 +691,7 @@ function m.switch()
return obj
end
+---@param f async fun()
function m.getUpvalue(f, name)
for i = 1, 999 do
local uname, value = getupvalue(f, i)
diff --git a/script/vm/eachDef.lua b/script/vm/eachDef.lua
index 2bfad4bf..ea14ed9f 100644
--- a/script/vm/eachDef.lua
+++ b/script/vm/eachDef.lua
@@ -1,4 +1,4 @@
----@type vm
+---@class vm
local vm = require 'vm.vm'
local searcher = require 'core.searcher'
diff --git a/script/vm/eachRef.lua b/script/vm/eachRef.lua
index 35425818..899c04c6 100644
--- a/script/vm/eachRef.lua
+++ b/script/vm/eachRef.lua
@@ -1,4 +1,4 @@
----@type vm
+---@class vm
local vm = require 'vm.vm'
local searcher = require 'core.searcher'
diff --git a/script/vm/getDocs.lua b/script/vm/getDocs.lua
index 2fb2bda9..3a0765bf 100644
--- a/script/vm/getDocs.lua
+++ b/script/vm/getDocs.lua
@@ -1,6 +1,6 @@
local files = require 'files'
local guide = require 'parser.guide'
----@type vm
+---@class vm
local vm = require 'vm.vm'
local config = require 'config'
local collector = require 'core.collector'
@@ -180,6 +180,140 @@ function vm.isDeprecated(value, deep)
end
end
+local function isAsync(value)
+ if value.type == 'function' then
+ if not value.bindDocs then
+ return false
+ end
+ if value._async ~= nil then
+ return value._async
+ end
+ for _, doc in ipairs(value.bindDocs) do
+ if doc.type == 'doc.async' then
+ value._async = true
+ return true
+ end
+ end
+ value._async = false
+ return false
+ end
+ return value.async == true
+end
+
+function vm.isAsync(value, deep)
+ if isAsync(value) then
+ return true
+ end
+ if deep then
+ local defs = vm.getDefs(value)
+ if #defs == 0 then
+ return false
+ end
+ for _, def in ipairs(defs) do
+ if isAsync(def) then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function isNoDiscard(value)
+ if value.type == 'function' then
+ if not value.bindDocs then
+ return false
+ end
+ if value._nodiscard ~= nil then
+ return value._nodiscard
+ end
+ for _, doc in ipairs(value.bindDocs) do
+ if doc.type == 'doc.nodiscard' then
+ value._nodiscard = true
+ return true
+ end
+ end
+ value._nodiscard = false
+ return false
+ end
+ return false
+end
+
+function vm.isNoDiscard(value, deep)
+ if isNoDiscard(value) then
+ return true
+ end
+ if deep then
+ local defs = vm.getDefs(value)
+ if #defs == 0 then
+ return false
+ end
+ for _, def in ipairs(defs) do
+ if isNoDiscard(def) then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local function isCalledInFunction(param)
+ if not param.ref then
+ return false
+ end
+ local func = guide.getParentFunction(param)
+ for _, ref in ipairs(param.ref) do
+ if ref.type == 'getlocal' then
+ if ref.parent.type == 'call'
+ and guide.getParentFunction(ref) == func then
+ return true
+ end
+ if ref.parent.type == 'callargs'
+ and ref.parent[1] == ref
+ and guide.getParentFunction(ref) == func then
+ if ref.parent.parent.node.special == 'pcall'
+ or ref.parent.parent.node.special == 'xpcall' then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+local function isLinkedCall(node, index)
+ for _, def in ipairs(vm.getDefs(node)) do
+ if def.type == 'function' then
+ local param = def.args and def.args[index]
+ if param then
+ if isCalledInFunction(param) then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function vm.isLinkedCall(node, index)
+ return isLinkedCall(node, index)
+end
+
+function vm.isAsyncCall(call)
+ if vm.isAsync(call.node, true) then
+ return true
+ end
+ if not call.args then
+ return
+ end
+ for i, arg in ipairs(call.args) do
+ if vm.isAsync(arg, true)
+ and isLinkedCall(call.node, i) then
+ return true
+ end
+ end
+ return false
+end
+
local function makeDiagRange(uri, doc, results)
local names
if doc.names then
diff --git a/script/vm/getGlobals.lua b/script/vm/getGlobals.lua
index 6dacda43..92fd1c8e 100644
--- a/script/vm/getGlobals.lua
+++ b/script/vm/getGlobals.lua
@@ -1,6 +1,6 @@
local collector = require 'core.collector'
local guide = require 'parser.guide'
----@type vm
+---@class vm
local vm = require 'vm.vm'
local noder = require 'core.noder'
diff --git a/script/vm/getLinks.lua b/script/vm/getLinks.lua
index d2332504..b245bdaa 100644
--- a/script/vm/getLinks.lua
+++ b/script/vm/getLinks.lua
@@ -1,5 +1,5 @@
local guide = require 'parser.guide'
----@type vm
+---@class vm
local vm = require 'vm.vm'
local files = require 'files'
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
index 6abaaa0e..aa18ea73 100644
--- a/script/vm/vm.lua
+++ b/script/vm/vm.lua
@@ -14,7 +14,7 @@ local weakMT = { __mode = 'kv' }
_ENV = nil
----@type vm
+---@class vm
local m = {}
function m.getArgInfo(source)
diff --git a/script/workspace/require-path.lua b/script/workspace/require-path.lua
index 2ec2918c..e2149bac 100644
--- a/script/workspace/require-path.lua
+++ b/script/workspace/require-path.lua
@@ -27,7 +27,9 @@ local function getOnePath(path, searcher)
return nil
end
-function m.getVisiblePath(path, searchers, strict)
+function m.getVisiblePath(path)
+ local searchers = config.get 'Lua.runtime.path'
+ local strict = config.get 'Lua.runtime.pathStrict'
path = path:gsub('^[/\\]+', '')
local uri = furi.encode(path)
local libraryPath = files.getLibraryPath(uri)
@@ -91,7 +93,9 @@ files.watch(function (ev)
end)
config.watch(function (key, value, oldValue)
- if key == 'Lua.completion.requireSeparator' then
+ if key == 'Lua.completion.requireSeparator'
+ or key == 'Lua.runtime.path'
+ or key == 'Lua.runtime.pathStrict' then
m.flush()
end
end)
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index 240596a4..099196ce 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -74,6 +74,7 @@ local globInteferFace = {
}
--- 创建排除文件匹配器
+---@async
function m.getNativeMatcher()
if not m.path then
return nil
@@ -177,6 +178,7 @@ function m.getLibraryMatchers()
end
--- 文件是否被忽略
+---@async
function m.isIgnored(uri)
local path = m.getRelativePath(uri)
local ignore = m.getNativeMatcher()
@@ -186,6 +188,7 @@ function m.isIgnored(uri)
return ignore(path)
end
+---@async
function m.isValidLuaUri(uri)
if not files.isLua(uri) then
return false
@@ -198,7 +201,7 @@ function m.isValidLuaUri(uri)
end
local function loadFileFactory(root, progressData, isLibrary)
- return function (path)
+ return function (path) ---@async
local uri = furi.encode(path)
if files.isLua(uri) then
if not isLibrary and progressData.preload >= config.get 'Lua.workspace.maxPreload' then
@@ -246,7 +249,7 @@ local function loadFileFactory(root, progressData, isLibrary)
log.info('++++As library of:', root)
files.setLibraryPath(uri, root)
end
- files.setText(uri, text, false, true)
+ files.setText(uri, text, false)
else
files.remove(uri)
end
@@ -279,6 +282,7 @@ local function loadFileFactory(root, progressData, isLibrary)
end
end
+---@async
function m.awaitLoadFile(uri)
local progressBar <close> = progress.create(lang.script.WORKSPACE_LOADING)
local progressData = {
@@ -299,6 +303,7 @@ function m.awaitLoadFile(uri)
end
--- 预读工作区内所有文件
+---@async
function m.awaitPreload()
local diagnostic = require 'provider.diagnostic'
await.close 'preload'
@@ -347,7 +352,7 @@ function m.awaitPreload()
if isLoadingFiles then
return
end
- await.call(function ()
+ await.call(function () ---@async
isLoadingFiles = true
while true do
local loader = table.remove(progressData.loaders)
@@ -391,17 +396,22 @@ function m.findUrisByFilePath(path)
return resultCache[path].results, resultCache[path].posts
end
tracy.ZoneBeginN('findUrisByFilePath #1')
+ local strict = config.get 'Lua.runtime.pathStrict'
local results = {}
local posts = {}
for uri in files.eachFile() do
if not uri:find(lpath, 1, true) then
goto CONTINUE
end
+ local relat = m.getRelativePath(uri)
local pathLen = #path
- local curPath = furi.decode(uri)
+ local curPath = relat
local curLen = #curPath
local seg = curPath:sub(curLen - pathLen, curLen - pathLen)
if seg == '/' or seg == '\\' or seg == '' then
+ if strict and seg ~= '' then
+ goto CONTINUE
+ end
local see = curPath:sub(curLen - pathLen + 1, curLen)
if see == path then
results[#results+1] = uri
@@ -558,6 +568,7 @@ function m.init()
m.reload()
end
+---@async
function m.awaitReload()
m.ready = false
m.hasHitMaxPreload = false
@@ -575,6 +586,7 @@ function m.awaitReload()
end
---等待工作目录加载完成
+---@async
function m.awaitReady()
if m.isReady() then
return
@@ -592,7 +604,7 @@ function m.getLoadProcess()
return m.fileLoaded, m.fileFound
end
-files.watch(function (ev, uri)
+files.watch(function (ev, uri) ---@async
if ev == 'close'
and m.isIgnored(uri)
and not files.isLibrary(uri) then
@@ -610,7 +622,7 @@ config.watch(function (key, value, oldValue)
end
end)
-fw.event(function (changes)
+fw.event(function (changes) ---@async
m.awaitReady()
for _, change in ipairs(changes) do
local path = change.path