diff --git a/changelog.md b/changelog.md index d1d0d1088..46d629548 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## Unreleased * `FIX` Incorrect infer for function array annotation on tables [#2367](https://github.com/LuaLS/lua-language-server/issues/2367) +* `NEW` Setting: `Lua.hint.awaitPropagate`: When enabled, --@async propagates to the caller. ## 3.13.4 `2024-12-13` diff --git a/locale/en-us/setting.lua b/locale/en-us/setting.lua index da103ac18..3dfdaceb8 100644 --- a/locale/en-us/setting.lua +++ b/locale/en-us/setting.lua @@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable = 'Disable hints of array index.' config.hint.await = 'If the called function is marked `---@async`, prompt `await` at the call.' +config.hint.awaitPropagate = +'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z +it will be automatically marked as `---@async`.' config.hint.semicolon = 'If there is no semicolon at the end of the statement, display a virtual semicolon.' config.hint.semicolon.All = diff --git a/locale/ja-jp/setting.lua b/locale/ja-jp/setting.lua index fa5b8f233..afa05b641 100644 --- a/locale/ja-jp/setting.lua +++ b/locale/ja-jp/setting.lua @@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable = -- TODO: need translate! 'Disable hints of array index.' config.hint.await = -- TODO: need translate! 'If the called function is marked `---@async`, prompt `await` at the call.' +config.hint.awaitPropagate = -- TODO: need translate! +'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z +it will be automatically marked as `---@async`.' config.hint.semicolon = -- TODO: need translate! 'If there is no semicolon at the end of the statement, display a virtual semicolon.' config.hint.semicolon.All = -- TODO: need translate! diff --git a/locale/pt-br/setting.lua b/locale/pt-br/setting.lua index b58fa7ceb..cce2e96c5 100644 --- a/locale/pt-br/setting.lua +++ b/locale/pt-br/setting.lua @@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable = -- TODO: need translate! 'Disable hints of array index.' config.hint.await = -- TODO: need translate! 'If the called function is marked `---@async`, prompt `await` at the call.' +config.hint.awaitPropagate = -- TODO: need translate! +'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z +it will be automatically marked as `---@async`.' config.hint.semicolon = -- TODO: need translate! 'If there is no semicolon at the end of the statement, display a virtual semicolon.' config.hint.semicolon.All = -- TODO: need translate! diff --git a/locale/zh-cn/setting.lua b/locale/zh-cn/setting.lua index 8dcc4889c..a55f8d42e 100644 --- a/locale/zh-cn/setting.lua +++ b/locale/zh-cn/setting.lua @@ -255,6 +255,8 @@ config.hint.arrayIndex.Disable = '禁用数组索引提示。' config.hint.await = '如果调用的函数被标记为了 `---@async` ,则在调用处提示 `await` 。' +config.hint.awaitPropagate = +'启用 `await` 的传播, 当一个函数调用了一个`---@async`标记的函数时,会自动标记为`---@async`。' config.hint.semicolon = '若语句尾部没有分号,则显示虚拟分号。' config.hint.semicolon.All = diff --git a/locale/zh-tw/setting.lua b/locale/zh-tw/setting.lua index 4747452b0..dfb716db8 100644 --- a/locale/zh-tw/setting.lua +++ b/locale/zh-tw/setting.lua @@ -255,6 +255,8 @@ config.hint.arrayIndex.Disable = '停用陣列索引提示。' config.hint.await = '如果呼叫的函數被標記為了 `---@async`,則在呼叫處提示 `await`。' +config.hint.awaitPropagate = +'啟用 `await` 的傳播,當一個函數呼叫了一個 `---@async` 標記的函數時,會自動標記為 `---@async`。' config.hint.semicolon = '若陳述式尾部沒有分號,則顯示虛擬分號。' config.hint.semicolon.All = diff --git a/script/config/template.lua b/script/config/template.lua index 62512ce31..74b7c4b8e 100644 --- a/script/config/template.lua +++ b/script/config/template.lua @@ -369,6 +369,7 @@ local template = { 'Disable', }, ['Lua.hint.await'] = Type.Boolean >> true, + ['Lua.hint.awaitPropagate'] = Type.Boolean >> false, ['Lua.hint.arrayIndex'] = Type.String >> 'Auto' << { 'Enable', 'Auto', diff --git a/script/vm/doc.lua b/script/vm/doc.lua index c936d2f3f..ae6c629b7 100644 --- a/script/vm/doc.lua +++ b/script/vm/doc.lua @@ -1,8 +1,9 @@ -local files = require 'files' -local guide = require 'parser.guide' +local files = require 'files' +local await = require 'await' +local guide = require 'parser.guide' ---@class vm -local vm = require 'vm.vm' -local config = require 'config' +local vm = require 'vm.vm' +local config = require 'config' ---@class parser.object ---@field package _castTargetHead? parser.object | vm.global | false @@ -186,20 +187,53 @@ function vm.getDeprecated(value, deep) end ---@param value parser.object +---@param propagate boolean +---@param deepLevel integer? ---@return boolean -local function isAsync(value) +local function isAsync(value, propagate, deepLevel) if value.type == 'function' then - if not value.bindDocs then - return false - end - if value._async ~= nil then + if value._async ~= nil then --already calculated, directly return return value._async end - for _, doc in ipairs(value.bindDocs) do - if doc.type == 'doc.async' then - value._async = true + local asyncCache + if propagate then + asyncCache = vm.getCache 'async.propagate' + local result = asyncCache[value] + if result ~= nil then + return result + end + end + if value.bindDocs then --try parse the annotation + for _, doc in ipairs(value.bindDocs) do + if doc.type == 'doc.async' then + value._async = true + return true + end + end + end + if propagate then -- if enable async propagation, try check calling functions + if deepLevel and deepLevel > 50 then + return false + end + local isAsyncCall = vm.isAsyncCall + local callingAsync = guide.eachSourceType(value, 'call', function (source) + local parent = guide.getParentFunction(source) + if parent ~= value then + return nil + end + local nextLevel = (deepLevel or 1) + 1 + local ok = isAsyncCall(source, nextLevel) + if ok then --if any calling function is async, directly return + return ok + end + --if not, try check the next calling function + return nil + end) + if callingAsync then + asyncCache[value] = true return true end + asyncCache[value] = false end value._async = false return false @@ -212,9 +246,12 @@ end ---@param value parser.object ---@param deep boolean? +---@param deepLevel integer? ---@return boolean -function vm.isAsync(value, deep) - if isAsync(value) then +function vm.isAsync(value, deep, deepLevel) + local uri = guide.getUri(value) + local propagate = config.get(uri, 'Lua.hint.awaitPropagate') + if isAsync(value, propagate, deepLevel) then return true end if deep then @@ -223,7 +260,7 @@ function vm.isAsync(value, deep) return false end for _, def in ipairs(defs) do - if isAsync(def) then + if isAsync(def, propagate, deepLevel) then return true end end @@ -325,16 +362,17 @@ function vm.isLinkedCall(node, index) end ---@param call parser.object +---@param deepLevel integer? ---@return boolean -function vm.isAsyncCall(call) - if vm.isAsync(call.node, true) then +function vm.isAsyncCall(call, deepLevel) + if vm.isAsync(call.node, true, deepLevel) then return true end if not call.args then return false end for i, arg in ipairs(call.args) do - if vm.isAsync(arg, true) + if vm.isAsync(arg, true, deepLevel) and isLinkedCall(call.node, i) then return true end