summaryrefslogtreecommitdiff
path: root/script/core/searcher.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/core/searcher.lua')
-rw-r--r--script/core/searcher.lua1350
1 files changed, 0 insertions, 1350 deletions
diff --git a/script/core/searcher.lua b/script/core/searcher.lua
deleted file mode 100644
index eb70c349..00000000
--- a/script/core/searcher.lua
+++ /dev/null
@@ -1,1350 +0,0 @@
-local noder = require 'core.noder'
-local guide = require 'parser.guide'
-local files = require 'files'
-local generic = require 'core.generic'
-local rpath = require 'workspace.require-path'
-local vm = require 'vm.vm'
-local collector = require 'core.collector' 'searcher'
-local util = require 'utility'
-
-local TRACE = TRACE
-local FOOTPRINT = FOOTPRINT
-local TEST = TEST
-local log = log
-local select = select
-local tostring = tostring
-local next = next
-local error = error
-local type = type
-local setmetatable = setmetatable
-local ipairs = ipairs
-local tconcat = table.concat
-local ssub = string.sub
-local sfind = string.find
-local sformat = string.format
-
-local getUri = guide.getUri
-
-local getState = files.getState
-
-local getNoders = noder.getNoders
-local getID = noder.getID
-local getLastID = noder.getLastID
-local removeID = noder.removeID
-local getNodersByUri = noder.getNodersByUri
-local getFirstID = noder.getFirstID
-local getHeadID = noder.getHeadID
-local eachForward = noder.eachForward
-local getUriAndID = noder.getUriAndID
-local eachBackward = noder.eachBackward
-local eachExtend = noder.eachExtend
-local eachSource = noder.eachSource
-local compileAllNodes = noder.compileAllNodes
-local hasCall = noder.hasCall
-
-local SPLIT_CHAR = noder.SPLIT_CHAR
-local RETURN_INDEX = noder.RETURN_INDEX
-local TABLE_KEY = noder.TABLE_KEY
-local STRING_CHAR = noder.STRING_CHAR
-local STRING_FIELD = noder.STRING_FIELD
-local WEAK_TABLE_KEY = noder.WEAK_TABLE_KEY
-local ANY_FIELD = noder.ANY_FIELD
-local WEAK_ANY_FIELD = noder.WEAK_ANY_FIELD
-
-_ENV = nil
-
-local ignoredSources = {
- ['int:'] = true,
- ['num:'] = true,
- ['str:'] = true,
- ['bool:'] = true,
- ['nil:'] = true,
-}
-
-local ignoredIDs = {
- ['dn:unknown'] = true,
- ['dn:nil'] = true,
- ['dn:any'] = true,
- ['dn:boolean'] = true,
- ['dn:table'] = true,
- ['dn:number'] = true,
- ['dn:integer'] = true,
- ['dn:userdata'] = true,
- ['dn:lightuserdata'] = true,
- ['dn:function'] = true,
- ['dn:thread'] = true,
-}
-
----@class searcher
-local m = {}
-
----@alias guide.searchmode '"ref"'|'"def"'|'"field"'|'"allref"'|'"alldef"'|'"allfield"'
-
-local pushDefResultsMap = util.switch()
- : case 'local'
- : case 'setlocal'
- : case 'setglobal'
- : call(function (source, status)
- if source.type ~= 'local' then
- source = source.node
- end
- if source[1] == 'self'
- and source.parent.type == 'funcargs' then
- local func = source.parent.parent
- if status.source.start < func.start
- or status.source.start > func.finish then
- return false
- end
- end
- return true
- end)
- : case 'label'
- : case 'setfield'
- : case 'setmethod'
- : case 'setindex'
- : case 'tableindex'
- : case 'tablefield'
- : case 'tableexp'
- : case 'function'
- : case 'table'
- : case 'doc.class.name'
- : case 'doc.alias.name'
- : case 'doc.field.name'
- : case 'doc.type.enum'
- : case 'doc.resume'
- : case 'doc.param'
- : case 'doc.type.array'
- : case 'doc.type.table'
- : case 'doc.type.ltable'
- : case 'doc.type.field'
- : case 'doc.type.function'
- : call(function (source, status)
- return true
- end)
- : case 'doc.type.name'
- : call(function (source, status)
- return source.typeGeneric ~= nil
- end)
- : case 'call'
- : call(function (source, status)
- if source.node.special == 'rawset' then
- return true
- end
- end)
- : getMap()
-
-local pushRefResultsMap = util.switch()
- : case 'local'
- : case 'setlocal'
- : case 'getlocal'
- : case 'setglobal'
- : case 'getglobal'
- : case 'label'
- : case 'goto'
- : case 'setfield'
- : case 'getfield'
- : case 'setmethod'
- : case 'getmethod'
- : case 'setindex'
- : case 'getindex'
- : case 'tableindex'
- : case 'tablefield'
- : case 'tableexp'
- : case 'function'
- : case 'table'
- : case 'string'
- : case 'boolean'
- : case 'number'
- : case 'integer'
- : case 'nil'
- : case 'doc.class.name'
- : case 'doc.type.name'
- : case 'doc.alias.name'
- : case 'doc.extends.name'
- : case 'doc.field.name'
- : case 'doc.type.enum'
- : case 'doc.resume'
- : case 'doc.type.array'
- : case 'doc.type.table'
- : case 'doc.type.ltable'
- : case 'doc.type.field'
- : case 'doc.type.function'
- : call(function (source, status)
- return true
- end)
- : case 'call'
- : call(function (source, status)
- if source.node.special == 'rawset'
- or source.node.special == 'rawget' then
- return true
- end
- end)
- : getMap()
-
----添加结果
----@param status guide.status
----@param mode guide.searchmode
----@param source parser.guide.object
----@param force boolean
-local function pushResult(status, mode, source, force)
- if not source then
- return false
- end
- local results = status.results
- local mark = status.rmark
- if mark[source] then
- return true
- end
- mark[source] = true
- if force then
- results[#results+1] = source
- return true
- end
-
- if mode == 'def'
- or mode == 'alldef' then
- local f = pushDefResultsMap[source.type]
- if f and f(source, status) then
- results[#results+1] = source
- return true
- end
- elseif mode == 'ref'
- or mode == 'field'
- or mode == 'allfield'
- or mode == 'allref' then
- local f = pushRefResultsMap[source.type]
- if f and f(source, status) then
- results[#results+1] = source
- return true
- end
- end
-
- --local parent = source.parent
- --if parent.type == 'return' then
- -- if source ~= status.source then
- -- results[#results+1] = source
- -- return true
- -- end
- --end
-
- return false
-end
-
----@param obj parser.guide.object
----@return parser.guide.object?
-function m.getObjectValue(obj)
- while obj.type == 'paren' do
- obj = obj.exp
- if not obj then
- return nil
- end
- end
- if obj.type == 'boolean'
- or obj.type == 'number'
- or obj.type == 'integer'
- or obj.type == 'string' then
- return obj
- end
- if obj.value then
- return obj.value
- end
- if obj.type == 'field'
- or obj.type == 'method' then
- return obj.parent and obj.parent.value
- end
- if obj.type == 'call' then
- if obj.node.special == 'rawset' then
- return obj.args and obj.args[3]
- else
- return obj
- end
- end
- if obj.type == 'select' then
- return obj
- end
- return nil
-end
-
-local strs = {}
-local function footprint(status, ...)
- if TRACE then
- log.debug(...)
- end
- if FOOTPRINT then
- local n = select('#', ...)
- for i = 1, n do
- strs[i] = tostring(select(i, ...))
- end
- status.footprint[#status.footprint+1] = tconcat(strs, '\t', 1, n)
- end
-end
-
-local function checkCache(status, uri, expect, mode)
- local cache = status.cache
- local fileCache = cache[uri]
- local results = fileCache[expect]
- if results then
- for i = 1, #results do
- local res = results[i]
- pushResult(status, mode, res, true)
- end
- return true
- end
- return false, function ()
- fileCache[expect] = status.results
- if mode == 'def'
- or mode == 'alldef' then
- return
- end
- for id in next, status.ids do
- fileCache[id] = status.results
- end
- end
-end
-
-local function stop(status, msg)
- if TEST then
- if FOOTPRINT then
- log.debug(status.mode)
- log.debug(tconcat(status.footprint, '\n'))
- end
- error(msg)
- else
- log.warn(msg)
- if FOOTPRINT then
- FOOTPRINT = false
- log.error(status.mode)
- log.debug(tconcat(status.footprint, '\n'))
- end
- return
- end
-end
-
-local function isCallID(field)
- if not field then
- return false
- end
- if ssub(field, 1, 2) == RETURN_INDEX then
- return true
- end
- return false
-end
-
-local genercCache = {
- mark = {},
- genericCallArgs = {},
- closureCache = {},
-}
-
-local function flushGeneric()
- --清除来自泛型的临时对象
- for _, closure in next, genercCache.closureCache do
- if closure then
- local noders = getNoders(closure)
- removeID(noders, getID(closure))
- local values = closure.values
- for i = 1, #values do
- local value = values[i]
- removeID(noders, getID(value))
- end
- end
- end
- genercCache.mark = {}
- genercCache.closureCache = {}
- genercCache.genericCallArgs = {}
-end
-
-files.watch(function (ev)
- if ev == 'version' then
- flushGeneric()
- end
-end)
-
-local nodersMapMT = {__index = function (self, uri)
- local noders = getNodersByUri(uri)
- self[uri] = noders or false
- return noders
-end}
-
-local uriMapMT = {__index = function (self, uri)
- local t = {}
- self[uri] = t
- return t
-end}
-
-function m.searchRefsByID(status, suri, expect, mode)
- local state = getState(suri)
- if not state then
- return
- end
- local searchStep
-
- status.id = expect
-
- local callStack = status.callStack
- local ids = status.ids
- local dontCross = 0
- ---@type table<uri, noders>
- local nodersMap = setmetatable({}, nodersMapMT)
- local frejectMap = setmetatable({}, uriMapMT)
- local brejectMap = setmetatable({}, uriMapMT)
- local slockMap = setmetatable({}, uriMapMT)
- local elockMap = setmetatable({}, uriMapMT)
- local ecallMap = setmetatable({}, uriMapMT)
- local slockGlobalMap = slockMap['@global']
-
- compileAllNodes(state.ast)
-
- local function lockExpanding(elock, ecall, id, field)
- if not field then
- field = ''
- end
- local call = callStack[#callStack]
- local locked = elock[id]
- local called = ecall[id]
- if locked and called == call then
- if #locked <= #field then
- if ssub(field, -#locked) == locked then
- footprint(status, 'elocked:', id, locked, field)
- return false
- end
- end
- end
- elock[id] = field
- ecall[id] = call
- return true
- end
-
- local function releaseExpanding(elock, ecall, id, field)
- elock[id] = nil
- ecall[id] = nil
- end
-
- local function search(uri, id, field)
- local firstID = getFirstID(id)
- if ignoredIDs[firstID] and (field or firstID ~= id) then
- return
- end
-
- footprint(status, 'search:', id, field)
- searchStep(uri, id, field)
- footprint(status, 'pop:', id, field)
- end
-
- local function splitID(uri, id, field)
- if field then
- return
- end
- local leftID = ''
- local rightID
-
- while true do
- local firstID = getHeadID(rightID or id)
- if not firstID or firstID == id then
- break
- end
- leftID = leftID .. firstID
- if leftID == id then
- break
- end
- rightID = ssub(id, #leftID + 1)
- search(uri, leftID, rightID)
- local isCall = isCallID(firstID)
- if isCall then
- break
- end
- end
- end
-
- local function searchID(uri, id, field, sourceUri)
- if not id then
- return
- end
- if not nodersMap[uri] then
- return
- end
- if field then
- id = id .. field
- end
- ids[id] = true
- if slockMap[uri][id] then
- footprint(status, 'slocked:', id)
- return
- end
- slockMap[uri][id] = true
- if sourceUri and uri ~= sourceUri then
- footprint(status, 'cross uri:', uri)
- compileAllNodes(getState(uri).ast)
- end
- search(uri, id, nil)
- if sourceUri and uri ~= sourceUri then
- footprint(status, 'back uri:', sourceUri)
- end
- end
-
- ---@return parser.guide.object?
- local function findLastCall()
- for i = #callStack, 1, -1 do
- local call = callStack[i]
- if call then
- -- 标记此处的call失效,等待在堆栈平衡时弹出
- callStack[i] = false
- return call
- end
- end
- return nil
- end
-
- local genericCallArgs = genercCache.genericCallArgs
- local closureCache = genercCache.closureCache
- local function checkGeneric(uri, source, field)
- if not source.isGeneric then
- return
- end
- if not isCallID(field) then
- return
- end
- local call = findLastCall()
- if not call then
- return
- end
- local id = genercCache.mark[call]
- if id == false then
- return
- end
- if id then
- searchID(uri, id, field)
- return
- end
-
- local args = call.args
- if args then
- for i = 1, #args do
- local arg = args[i]
- genericCallArgs[arg] = true
- end
- end
-
- local cacheID = uri .. getID(source) .. getID(call)
- local closure = closureCache[cacheID]
- if closure == false then
- genercCache.mark[call] = false
- return
- end
- if not closure then
- closure = generic.createClosure(source, call)
- closureCache[cacheID] = closure or false
- if not closure then
- genercCache.mark[call] = false
- return
- end
- end
- id = getID(closure)
- genercCache.mark[call] = id
- searchID(uri, id, field)
- end
-
- local function checkENV(uri, source, field)
- if not field then
- return
- end
- if source.special ~= '_G' then
- return
- end
- local newID = 'g:' .. ssub(field, 2)
- searchID(uri, newID)
- end
-
- ---@param ward '"forward"'|'"backward"'
- ---@param info node.info
- local function pushThenCheckReject(uri, ward, info)
- local reject = info.reject
- local checkReject
- local pushReject
- if ward == 'forward' then
- checkReject = brejectMap[uri]
- pushReject = frejectMap[uri]
- else
- checkReject = frejectMap[uri]
- pushReject = brejectMap[uri]
- end
- pushReject[reject] = (pushReject[reject] or 0) + 1
- if checkReject[reject] and checkReject[reject] > 0 then
- return false
- end
- return true
- end
-
- ---@param ward '"forward"'|'"backward"'
- ---@param info node.info
- local function popReject(uri, ward, info)
- local reject = info.reject
- local popTags
- if ward == 'forward' then
- popTags = frejectMap[uri]
- else
- popTags = brejectMap[uri]
- end
- popTags[reject] = popTags[reject] - 1
- end
-
- ---@type table<node.filter, integer>
- local filters = {}
-
- ---@param id string
- ---@param info node.info
- local function pushInfoFilter(id, field, info)
- local filter = info.filter
- local filterValid = info.filterValid
- if filterValid and not filterValid(id, field) then
- return
- end
- filters[filter] = (filters[filter] or 0) + 1
- end
-
- ---@param id string
- ---@param info node.info
- local function releaseInfoFilter(id, field, info)
- local filter = info.filter
- local filterValid = info.filterValid
- if filterValid and not filterValid(id, field) then
- return
- end
- if filters[filter] <= 1 then
- filters[filter] = nil
- else
- filters[filter] = filters[filter] - 1
- end
- end
-
- ---@param id string
- ---@param info node.info
- local function checkInfoFilter(id, field, info)
- for filter in next, filters do
- if not filter(id, field, mode) then
- return false
- end
- end
- return true
- end
-
- local function checkForward(uri, id, field)
- for forwardID, info in eachForward(nodersMap[uri], id) do
- local targetUri, targetID
-
- --#region checkBeforeForward
- if info then
- if info.filter then
- pushInfoFilter(forwardID, field, info)
- end
- if info.reject then
- if not pushThenCheckReject(uri, 'forward', info) then
- goto RELEASE_THEN_CONTINUE
- end
- end
- end
- if not checkInfoFilter(forwardID, field, info) then
- goto RELEASE_THEN_CONTINUE
- end
- --#endregion
-
- targetUri, targetID = getUriAndID(forwardID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or forwardID, field)
- end
-
- ::RELEASE_THEN_CONTINUE::
- --#region releaseAfterForward
- if info then
- if info.reject then
- popReject(uri, 'forward', info)
- end
- if info.filter then
- releaseInfoFilter(id, field, info)
- end
- end
- --#endregion
- end
- end
-
- local function checkBackward(uri, id, field)
- if ignoredIDs[id]
- or id == 'dn:string' then
- return
- end
- if mode ~= 'ref'
- and mode ~= 'allfield'
- and mode ~= 'allref'
- and not field then
- return
- end
- for backwardID, info in eachBackward(nodersMap[uri], id) do
- local targetUri, targetID
- if info and info.deep and mode ~= 'allref' and mode ~= 'allfield' then
- goto CONTINUE
- end
-
- --#region checkBeforeBackward
- if info then
- if info.dontCross then
- dontCross = dontCross + 1
- end
- if info.filter then
- pushInfoFilter(backwardID, field, info)
- end
- if info.reject then
- if not pushThenCheckReject(uri, 'backward', info) then
- goto RELEASE_THEN_CONTINUE
- end
- end
- end
- if not checkInfoFilter(backwardID, field, info) then
- goto RELEASE_THEN_CONTINUE
- end
- --#endregion
-
- targetUri, targetID = getUriAndID(backwardID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or backwardID, field)
- end
-
- ::RELEASE_THEN_CONTINUE::
- --#region releaseAfterBackward
- if info then
- if info.reject then
- popReject(uri, 'backward', info)
- end
- if info.filter then
- releaseInfoFilter(backwardID, field, info)
- end
- if info.dontCross then
- dontCross = dontCross - 1
- end
- end
- --#endregion
-
- ::CONTINUE::
- end
- end
-
- local function checkExtend(uri, id, field)
- if not field
- and mode ~= 'field'
- and mode ~= 'allfield' then
- return
- end
- if field then
- local results = status.results
- for i = #results, 1, -1 do
- local res = results[i]
- if res.type == 'setfield'
- or res.type == 'setmethod'
- or res.type == 'setindex' then
- local resField = noder.getFieldID(getID(res))
- if field == resField then
- return
- end
- end
- if res.type == 'doc.field.name' then
- local resField = STRING_FIELD .. res[1]
- if field == resField then
- return
- end
- end
- end
- end
- for extendID in eachExtend(nodersMap[uri], id) do
- local targetUri, targetID
-
- targetUri, targetID = getUriAndID(extendID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or extendID, field)
- end
- end
- end
-
- local function searchSpecial(uri, id, field)
- -- Special rule: ('').XX -> stringlib.XX
- if id == 'str:'
- or id == 'dn:string' then
- if field or mode == 'field' or mode == 'allfield' then
- searchID(uri, 'dn:stringlib', field)
- else
- searchID(uri, 'dn:string', field)
- end
- end
- end
-
- local function checkRequire(uri, requireName, field)
- if not requireName then
- return
- end
- local uris = rpath.findUrisByRequirePath(suri, requireName)
- footprint(status, 'require:', requireName)
- for i = 1, #uris do
- local ruri = uris[i]
- if uri ~= ruri then
- searchID(ruri, 'mainreturn', field, uri)
- break
- end
- end
- end
-
- local function searchGlobal(uri, id, field)
- if dontCross ~= 0 then
- return
- end
- if ssub(id, 1, 2) ~= 'g:' then
- return
- end
- footprint(status, 'searchGlobal:', id, field)
- local crossed = {}
- if mode == 'def'
- or mode == 'alldef'
- or field
- or hasCall(field) then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- elseif mode == 'field'
- or mode == 'allfield' then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- for _, guri in collector:each(suri, 'field:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- else
- for _, guri in collector:each(suri, id) do
- if crossed[guri] then
- goto CONTINUE
- end
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- end
- end
-
- local function searchClass(uri, id, field)
- if dontCross ~= 0 then
- return
- end
- if ssub(id, 1, 3) ~= 'dn:' then
- return
- end
- footprint(status, 'searchClass:', id, field)
- local crossed = {}
- if mode == 'def'
- or mode == 'alldef'
- or mode == 'field'
- or ignoredIDs[id]
- or id == 'dn:string'
- or hasCall(field) then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- else
- for _, guri in collector:each(suri, id) do
- if crossed[guri] then
- goto CONTINUE
- end
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- end
- end
-
- local function checkMainReturn(uri, id, field)
- if id ~= 'mainreturn' then
- return
- end
- local calls = vm.getLinksTo(uri)
- for i = 1, #calls do
- local call = calls[i]
- local curi = getUri(call)
- local cid = getID(call)
- if curi ~= uri then
- searchID(curi, cid, field, uri)
- end
- end
- end
-
- local function searchNode(uri, id, field)
- local noders = nodersMap[uri]
- local call = noders.call[id]
- callStack[#callStack+1] = call
-
- if field == nil and not ignoredSources[id] then
- for source in eachSource(noders, id) do
- local force = genericCallArgs[source]
- pushResult(status, mode, source, force)
- end
- end
-
- local requireName = noders.require[id]
- if requireName then
- checkRequire(uri, requireName, field)
- end
-
- local elock = elockMap[uri]
- local ecall = ecallMap[uri]
-
- if lockExpanding(elock, ecall, id, field) then
- if noders.forward[id] then
- checkForward(uri, id, field)
- end
- if noders.backward[id] then
- checkBackward(uri, id, field)
- end
- if noders.extend[id] then
- checkExtend(uri, id, field)
- end
- releaseExpanding(elock, ecall, id, field)
- end
-
- local source = noders.source[id]
- if source then
- checkGeneric(uri, source, field)
- checkENV(uri, source, field)
- end
-
- if mode == 'allref' or mode == 'alldef' then
- checkMainReturn(uri, id, field)
- end
-
- if call then
- callStack[#callStack] = nil
- end
-
- return false
- end
-
- local function searchAnyField(uri, id, field)
- if mode == 'ref' or mode == 'allref' then
- return
- end
- local lastID = getLastID(id)
- if not lastID then
- return
- end
- local originField = ssub(id, #lastID + 1)
- if originField == TABLE_KEY
- or originField == WEAK_TABLE_KEY then
- return
- end
- local anyFieldID = lastID .. ANY_FIELD
- searchNode(uri, anyFieldID, field)
- end
-
- local function searchWeak(uri, id, field)
- local lastID = getLastID(id)
- if not lastID then
- return
- end
- local originField = ssub(id, #lastID + 1)
- if originField == WEAK_TABLE_KEY then
- local newID = lastID .. TABLE_KEY
- searchNode(uri, newID, field)
- end
- if originField == WEAK_ANY_FIELD then
- local newID = lastID .. ANY_FIELD
- searchNode(uri, newID, field)
- end
- end
-
- local stepCount = 0
- local stepMaxCount = 1e4
- if mode == 'allref' or mode == 'alldef' then
- stepMaxCount = 1e5
- end
-
- function searchStep(uri, id, field)
- stepCount = stepCount + 1
- if stepCount > stepMaxCount then
- stop(status, 'too deep!')
- return
- end
- searchSpecial(uri, id, field)
- searchNode(uri, id, field)
- if field and nodersMap[uri].skip[id] then
- return
- end
-
- local fullID = id .. (field or '')
- if not slockGlobalMap[fullID] then
- slockGlobalMap[fullID] = true
- searchGlobal(uri, id, field)
- searchClass(uri, id, field)
- slockGlobalMap[fullID] = nil
- end
-
- splitID(uri, id, field)
- searchAnyField(uri, id, field)
- searchWeak(uri, id, field)
- end
-
- search(suri, expect, nil)
- flushGeneric()
-end
-
-local function prepareSearch(source)
- if not source then
- return
- end
- if source.type == 'field'
- or source.type == 'method' then
- source = source.parent
- end
- if not source then
- return
- end
- local uri = getUri(source)
- local id = getID(source)
- -- return function
- if source.type == 'function' and source.parent.type == 'return' then
- local func = guide.getParentFunction(source)
- if func.type == 'function' then
- for index, rtn in ipairs(source.parent) do
- if rtn == source then
- id = sformat('%s%s%s'
- , getID(func)
- , RETURN_INDEX
- , index
- )
- break
- end
- end
- end
- end
- return uri, id
-end
-
-local fieldNextTypeMap = util.switch()
- : case 'getmethod'
- : case 'setmethod'
- : case 'getfield'
- : case 'setfield'
- : case 'getindex'
- : case 'setindex'
- : call(pushResult)
- : getMap()
-
-local function getField(status, source, mode)
- if source.type == 'table' then
- for i = 1, #source do
- local field = source[i]
- pushResult(status, mode, field)
- end
- return
- end
- if source.type == 'doc.type.ltable' then
- local fields = source.fields
- for i = 1, #fields do
- local field = fields[i]
- pushResult(status, mode, field)
- end
- end
- if source.type == 'doc.class.name' then
- local class = source.parent
- local fields = class.fields
- for i = 1, #fields do
- local field = fields[i]
- pushResult(status, mode, field.field)
- end
- return
- end
- local field = source.next
- if field then
- local ftype = field.type
- local pushResultOrNil = fieldNextTypeMap[ftype]
- if pushResultOrNil then
- pushResultOrNil(status, mode, field)
- end
- return
- end
-end
-
-local function searchAllGlobalByUri(status, mode, uri, fullID)
- local ast = files.getState(uri)
- if not ast then
- return
- end
- local root = ast.ast
- --compileAllNodes(root)
- local noders = getNoders(root)
- if fullID then
- for source in eachSource(noders, fullID) do
- pushResult(status, mode, source)
- end
- else
- for id in next, noders.source do
- if ssub(id, 1, 2) == 'g:'
- and not sfind(id, SPLIT_CHAR) then
- for source in eachSource(noders, id) do
- pushResult(status, mode, source)
- end
- end
- end
- end
-end
-
-local function searchAllGlobals(status, mode)
- for uri in files.eachFile() do
- searchAllGlobalByUri(status, mode, uri)
- end
-end
-
----查找全局变量
----@param uri uri
----@param mode guide.searchmode
----@param name string
----@return parser.guide.object[]
-function m.findGlobals(uri, mode, name)
- local status = m.status(nil, nil, mode)
-
- if name then
- local fullID
- if type(name) == 'string' then
- fullID = sformat('%s%s%s', 'g:', STRING_CHAR, name)
- else
- fullID = sformat('%s%s%s', 'g:', '', name)
- end
- searchAllGlobalByUri(status, mode, uri, fullID)
- else
- searchAllGlobalByUri(status, mode, uri)
- end
-
- return status.results
-end
-
----搜索对象的引用
----@param status guide.status
----@param source parser.guide.object
----@param mode guide.searchmode
-function m.searchRefs(status, source, mode)
- local uri, id = prepareSearch(source)
- if not id then
- return
- end
-
- local cached, makeCache = checkCache(status, uri, id, mode)
-
- if cached then
- return
- end
-
- if TRACE then
- log.debug('searchRefs:', id)
- end
- m.searchRefsByID(status, uri, id, mode)
- if makeCache then
- makeCache()
- end
-end
-
----搜索对象的field
----@param status guide.status
----@param source parser.guide.object
----@param mode guide.searchmode
----@param field string
-function m.searchFields(status, source, mode, field)
- local uri, id = prepareSearch(source)
- if not id then
- return
- end
- if TRACE then
- log.debug('searchFields:', id, field)
- end
- if field == '*' then
- if source.special == '_G' then
- local cached, makeCache = checkCache(status, uri, '*', mode)
- if cached then
- return
- end
- searchAllGlobals(status, mode)
- if makeCache then
- makeCache()
- end
- else
- local cached, makeCache = checkCache(status, uri, id .. '*', mode)
- if cached then
- return
- end
- local fieldMode = 'field'
- if mode == 'allref' then
- fieldMode = 'allfield'
- end
- local newStatus = m.status(source, field, fieldMode)
- m.searchRefsByID(newStatus, uri, id, fieldMode)
- local results = newStatus.results
- for i = 1, #results do
- local def = results[i]
- getField(status, def, mode)
- end
- if makeCache then
- makeCache()
- end
- end
- else
- if source.special == '_G' then
- local fullID
- if type(field) == 'string' then
- fullID = sformat('%s%s%s', 'g:', STRING_CHAR, field)
- else
- fullID = sformat('%s%s%s', 'g:', '', field)
- end
- local cahced, makeCache = checkCache(status, uri, fullID, mode)
- if cahced then
- return
- end
- m.searchRefsByID(status, uri, fullID, mode)
- if makeCache then
- makeCache()
- end
- else
- local fullID
- if type(field) == 'string' then
- fullID = sformat('%s%s%s', id, STRING_FIELD, field)
- else
- fullID = sformat('%s%s%s', id, SPLIT_CHAR, field)
- end
- local cahced, makeCache = checkCache(status, uri, fullID, mode)
- if cahced then
- return
- end
- m.searchRefsByID(status, uri, fullID, mode)
- if makeCache then
- makeCache()
- end
- end
- end
-end
-
----@class guide.status
----搜索结果
----@field results parser.guide.object[]
----@field rmark table
----@field id string
----@field source parser.guide.object
----@field field string
-
----创建搜索状态
----@param mode guide.searchmode
----@return guide.status
-function m.status(source, field, mode)
- local status = {
- callStack = {},
- results = {},
- rmark = {},
- footprint = {},
- ids = {},
- mode = mode,
- source = source,
- field = field,
- cache = setmetatable(vm.getCache('searcher:' .. mode), uriMapMT),
- }
- return status
-end
-
---- 请求对象的引用
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestReference(obj, field)
- local status = m.status(obj, field, 'ref')
-
- if field then
- m.searchFields(status, obj, 'ref', field)
- else
- m.searchRefs(status, obj, 'ref')
- end
-
- return status.results
-end
-
---- 请求对象的全部引用(深度搜索)
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestAllReference(obj, field)
- local status = m.status(obj, field, 'allref')
-
- if field then
- m.searchFields(status, obj, 'allref', field)
- else
- m.searchRefs(status, obj, 'allref')
- end
-
- return status.results
-end
-
---- 请求对象的定义
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestDefinition(obj, field)
- local status = m.status(obj, field, 'def')
-
- if field then
- m.searchFields(status, obj, 'def', field)
- else
- m.searchRefs(status, obj, 'def')
- end
-
- return status.results
-end
-
---- 请求对象的全部定义(深度搜索)
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestAllDefinition(obj, field)
- local status = m.status(obj, field, 'alldef')
-
- if field then
- m.searchFields(status, obj, 'alldef', field)
- else
- m.searchRefs(status, obj, 'alldef')
- end
-
- return status.results
-end
-
---m.requestReference = m.requestAllReference
---m.requestDefinition = m.requestAllDefinition
-
-return m