Skip to content

Commit f69167e

Browse files
committed
add option Lua.hint.awaitPropagate to propagate ---@async
This option is disabled by default. When disabled, it has no impact on performance. When enabled, due to its upward propagation characteristics, it may render some await-in-sync diagnostics ineffective, but not all of them.
1 parent 8d3a4c8 commit f69167e

File tree

7 files changed

+97
-17
lines changed

7 files changed

+97
-17
lines changed

locale/en-us/setting.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable =
256256
'Disable hints of array index.'
257257
config.hint.await =
258258
'If the called function is marked `---@async`, prompt `await` at the call.'
259+
config.hint.awaitPropagate =
260+
'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z
261+
it will be automatically marked as `---@async`.'
259262
config.hint.semicolon =
260263
'If there is no semicolon at the end of the statement, display a virtual semicolon.'
261264
config.hint.semicolon.All =

locale/ja-jp/setting.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable = -- TODO: need translate!
256256
'Disable hints of array index.'
257257
config.hint.await = -- TODO: need translate!
258258
'If the called function is marked `---@async`, prompt `await` at the call.'
259+
config.hint.awaitPropagate = -- TODO: need translate!
260+
'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z
261+
it will be automatically marked as `---@async`.'
259262
config.hint.semicolon = -- TODO: need translate!
260263
'If there is no semicolon at the end of the statement, display a virtual semicolon.'
261264
config.hint.semicolon.All = -- TODO: need translate!

locale/pt-br/setting.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ config.hint.arrayIndex.Disable = -- TODO: need translate!
256256
'Disable hints of array index.'
257257
config.hint.await = -- TODO: need translate!
258258
'If the called function is marked `---@async`, prompt `await` at the call.'
259+
config.hint.awaitPropagate = -- TODO: need translate!
260+
'Enable the propagation of `await`. When a function calls a function marked `---@async`,\z
261+
it will be automatically marked as `---@async`.'
259262
config.hint.semicolon = -- TODO: need translate!
260263
'If there is no semicolon at the end of the statement, display a virtual semicolon.'
261264
config.hint.semicolon.All = -- TODO: need translate!

locale/zh-cn/setting.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ config.hint.arrayIndex.Disable =
255255
'禁用数组索引提示。'
256256
config.hint.await =
257257
'如果调用的函数被标记为了 `---@async` ,则在调用处提示 `await` 。'
258+
config.hint.awaitPropagate =
259+
'启用 `await` 的传播, 当一个函数调用了一个`---@async`标记的函数时,会自动标记为`---@async`。'
258260
config.hint.semicolon =
259261
'若语句尾部没有分号,则显示虚拟分号。'
260262
config.hint.semicolon.All =

locale/zh-tw/setting.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ config.hint.arrayIndex.Disable =
255255
'停用陣列索引提示。'
256256
config.hint.await =
257257
'如果呼叫的函數被標記為了 `---@async`,則在呼叫處提示 `await`。'
258+
config.hint.awaitPropagate =
259+
'啟用 `await` 的傳播,當一個函數呼叫了一個 `---@async` 標記的函數時,會自動標記為 `---@async`。'
258260
config.hint.semicolon =
259261
'若陳述式尾部沒有分號,則顯示虛擬分號。'
260262
config.hint.semicolon.All =

script/config/template.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ local template = {
369369
'Disable',
370370
},
371371
['Lua.hint.await'] = Type.Boolean >> true,
372+
['Lua.hint.awaitPropagate'] = Type.Boolean >> false,
372373
['Lua.hint.arrayIndex'] = Type.String >> 'Auto' << {
373374
'Enable',
374375
'Auto',

script/vm/doc.lua

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
local files = require 'files'
2-
local guide = require 'parser.guide'
1+
local files = require 'files'
2+
local await = require 'await'
3+
local guide = require 'parser.guide'
34
---@class vm
4-
local vm = require 'vm.vm'
5-
local config = require 'config'
5+
local vm = require 'vm.vm'
6+
local config = require 'config'
67

78
---@class parser.object
89
---@field package _castTargetHead? parser.object | vm.global | false
@@ -186,17 +187,37 @@ function vm.getDeprecated(value, deep)
186187
end
187188

188189
---@param value parser.object
190+
---@param propagate boolean
191+
---@param deepLevel integer?
189192
---@return boolean
190-
local function isAsync(value)
193+
local function isAsync(value, propagate, deepLevel)
191194
if value.type == 'function' then
192-
if not value.bindDocs then
193-
return false
194-
end
195-
if value._async ~= nil then
195+
if value._async ~= nil then --already calculated, directly return
196196
return value._async
197197
end
198-
for _, doc in ipairs(value.bindDocs) do
199-
if doc.type == 'doc.async' then
198+
if value.bindDocs then --try parse the annotation
199+
for _, doc in ipairs(value.bindDocs) do
200+
if doc.type == 'doc.async' then
201+
value._async = true
202+
return true
203+
end
204+
end
205+
end
206+
if propagate then -- if enable async propagation, try check calling functions
207+
if deepLevel and deepLevel > 50 then
208+
return false
209+
end
210+
local isAsyncCall = vm.isAsyncCall
211+
local callingAsync = guide.eachSourceType(value, 'call', function (source)
212+
local nextLevel = (deepLevel or 1) + 1
213+
local ok = isAsyncCall(source, nextLevel)
214+
if ok then --if any calling function is async, directly return
215+
return ok
216+
end
217+
--if not, try check the next calling function
218+
return nil
219+
end)
220+
if callingAsync then
200221
value._async = true
201222
return true
202223
end
@@ -212,9 +233,12 @@ end
212233

213234
---@param value parser.object
214235
---@param deep boolean?
236+
---@param deepLevel integer?
215237
---@return boolean
216-
function vm.isAsync(value, deep)
217-
if isAsync(value) then
238+
function vm.isAsync(value, deep, deepLevel)
239+
local uri = guide.getUri(value)
240+
local propagate = config.get(uri, 'Lua.hint.awaitPropagate')
241+
if isAsync(value, propagate, deepLevel) then
218242
return true
219243
end
220244
if deep then
@@ -223,7 +247,7 @@ function vm.isAsync(value, deep)
223247
return false
224248
end
225249
for _, def in ipairs(defs) do
226-
if isAsync(def) then
250+
if isAsync(def, propagate, deepLevel) then
227251
return true
228252
end
229253
end
@@ -325,16 +349,17 @@ function vm.isLinkedCall(node, index)
325349
end
326350

327351
---@param call parser.object
352+
---@param deepLevel integer?
328353
---@return boolean
329-
function vm.isAsyncCall(call)
330-
if vm.isAsync(call.node, true) then
354+
function vm.isAsyncCall(call, deepLevel)
355+
if vm.isAsync(call.node, true, deepLevel) then
331356
return true
332357
end
333358
if not call.args then
334359
return false
335360
end
336361
for i, arg in ipairs(call.args) do
337-
if vm.isAsync(arg, true)
362+
if vm.isAsync(arg, true, deepLevel)
338363
and isLinkedCall(call.node, i) then
339364
return true
340365
end
@@ -485,3 +510,44 @@ function vm.docHasAttr(doc, key)
485510
end
486511
return false
487512
end
513+
514+
---@async
515+
local function clearAsyncPropagate(uri)
516+
local propagate = config.get(uri, 'Lua.hint.awaitPropagate')
517+
if not propagate then
518+
return
519+
end
520+
local state = files.getState(uri)
521+
if not state then
522+
return
523+
end
524+
local marked = {}
525+
local list = {}
526+
guide.eachSourceType(state.ast, 'function', function (source)
527+
marked[source] = true
528+
list[#list+1] = source
529+
end)
530+
local pairs = pairs
531+
local remove = table.remove
532+
while #list > 0 do
533+
local source = remove(list)
534+
local refs = vm.getRefs(source)
535+
for _, ref in pairs(refs) do
536+
while ref and ref.type ~= 'function' do
537+
ref = ref.parent
538+
end
539+
if ref and not marked[ref] then
540+
ref._async = nil
541+
marked[ref] = true
542+
list[#list+1] = ref
543+
end
544+
end
545+
await.delay()
546+
end
547+
end
548+
549+
files.watch(function (ev, uri) ---@async
550+
if ev == 'compile' then
551+
clearAsyncPropagate(uri)
552+
end
553+
end)

0 commit comments

Comments
 (0)