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)
186187end
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
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)
325349end
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
487512end
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