diff options
Diffstat (limited to 'script/core')
-rw-r--r-- | script/core/completion/completion.lua | 1 | ||||
-rw-r--r-- | script/core/diagnostics/deprecated.lua | 1 | ||||
-rw-r--r-- | script/core/diagnostics/duplicate-doc-field.lua | 1 | ||||
-rw-r--r-- | script/core/diagnostics/duplicate-index.lua | 2 | ||||
-rw-r--r-- | script/core/diagnostics/undefined-field.lua | 1 | ||||
-rw-r--r-- | script/core/diagnostics/undefined-global.lua | 1 | ||||
-rw-r--r-- | script/core/generic.lua | 282 | ||||
-rw-r--r-- | script/core/hover/description.lua | 1 | ||||
-rw-r--r-- | script/core/infer.lua | 1 | ||||
-rw-r--r-- | script/core/noder.lua | 1910 | ||||
-rw-r--r-- | script/core/rename.lua | 3 | ||||
-rw-r--r-- | script/core/searcher.lua | 1350 |
12 files changed, 0 insertions, 3554 deletions
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua index a8000c91..4c78972f 100644 --- a/script/core/completion/completion.lua +++ b/script/core/completion/completion.lua @@ -19,7 +19,6 @@ local lang = require 'language' 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 postfix = require 'core.completion.postfix' diff --git a/script/core/diagnostics/deprecated.lua b/script/core/diagnostics/deprecated.lua index 1c248646..28f67b15 100644 --- a/script/core/diagnostics/deprecated.lua +++ b/script/core/diagnostics/deprecated.lua @@ -5,7 +5,6 @@ local guide = require 'parser.guide' local config = require 'config' local define = require 'proto.define' local await = require 'await' -local noder = require 'core.noder' local types = {'getglobal', 'getfield', 'getindex', 'getmethod'} ---@async diff --git a/script/core/diagnostics/duplicate-doc-field.lua b/script/core/diagnostics/duplicate-doc-field.lua index ab3084c9..5394665c 100644 --- a/script/core/diagnostics/duplicate-doc-field.lua +++ b/script/core/diagnostics/duplicate-doc-field.lua @@ -1,6 +1,5 @@ local files = require 'files' local lang = require 'language' -local noder = require 'core.noder' return function (uri, callback) local state = files.getState(uri) diff --git a/script/core/diagnostics/duplicate-index.lua b/script/core/diagnostics/duplicate-index.lua index d1141901..bf46996e 100644 --- a/script/core/diagnostics/duplicate-index.lua +++ b/script/core/diagnostics/duplicate-index.lua @@ -2,8 +2,6 @@ local files = require 'files' local guide = require 'parser.guide' local lang = require 'language' local define = require 'proto.define' -local vm = require 'vm' -local noder = require 'core.noder' return function (uri, callback) local ast = files.getState(uri) diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua index 2e64f0cc..b456ef9a 100644 --- a/script/core/diagnostics/undefined-field.lua +++ b/script/core/diagnostics/undefined-field.lua @@ -2,7 +2,6 @@ local files = require 'files' local vm = require 'vm' local lang = require 'language' local guide = require 'parser.guide' -local noder = require 'core.noder' local await = require 'await' local SkipCheckClass = { diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua index b570ca65..cf31ec4e 100644 --- a/script/core/diagnostics/undefined-global.lua +++ b/script/core/diagnostics/undefined-global.lua @@ -3,7 +3,6 @@ local vm = require 'vm' local lang = require 'language' local config = require 'config' local guide = require 'parser.guide' -local noder = require 'core.noder' local collector = require 'core.collector' 'searcher' local await = require 'await' diff --git a/script/core/generic.lua b/script/core/generic.lua deleted file mode 100644 index 2eb454ff..00000000 --- a/script/core/generic.lua +++ /dev/null @@ -1,282 +0,0 @@ -local guide = require 'parser.guide' -local noder = require "core.noder" - ----@class generic.value: parser.guide.object ----@field type string ----@field closure generic.closure ----@field proto parser.guide.object ----@field parent parser.guide.object - ----@class generic.closure: parser.guide.object ----@field type string ----@field proto parser.guide.object ----@field upvalues table<string, generic.value[]> ----@field params generic.value[] ----@field returns generic.value[] - -local m = {} - ----@param closure generic.closure ----@param proto parser.guide.object -local function instantValue(closure, proto) - ---@type generic.value - local value = { - type = 'generic.value', - closure = closure, - proto = proto, - parent = proto.parent, - } - closure.values[#closure.values+1] = value - return value -end - ----递归实例化对象 ----@param proto parser.guide.object ----@return generic.value -local function createValue(closure, proto, callback, road) - road = road or {} - if proto.type == 'doc.type' then - local types = {} - local hasGeneric - for i, tp in ipairs(proto.types) do - local genericValue = createValue(closure, tp, callback, road) - if genericValue then - hasGeneric = true - types[i] = genericValue - else - types[i] = tp - end - end - if not hasGeneric then - return nil - end - local value = instantValue(closure, proto) - value.types = types - noder.compileNode(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.name' then - if not proto.typeGeneric then - return nil - end - local key = proto[1] - local value = instantValue(closure, proto) - if callback then - callback(road, key, proto) - end - noder.compileNode(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.function' then - local hasGeneric - local args = {} - local returns = {} - for i, arg in ipairs(proto.args) do - local value = createValue(closure, arg, callback, road) - if value then - hasGeneric = true - end - args[i] = value or arg - end - for i, rtn in ipairs(proto.returns) do - local value = createValue(closure, rtn, callback, road) - if value then - hasGeneric = true - end - returns[i] = value or rtn - end - if not hasGeneric then - return nil - end - local value = instantValue(closure, proto) - value.args = args - value.returns = returns - value.isGeneric = true - noder.pushSource(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.array' then - road[#road+1] = noder.WEAK_ANY_FIELD - local node = createValue(closure, proto.node, callback, road) - road[#road] = nil - if not node then - return nil - end - local value = instantValue(closure, proto) - value.node = node - noder.compileNode(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.table' then - road[#road+1] = noder.WEAK_TABLE_KEY - local tkey = createValue(closure, proto.tkey, callback, road) - road[#road] = nil - - road[#road+1] = noder.WEAK_ANY_FIELD - local tvalue = createValue(closure, proto.tvalue, callback, road) - road[#road] = nil - - if not tkey and not tvalue then - return nil - end - local value = instantValue(closure, proto) - value.tkey = tkey or proto.tkey - value.tvalue = tvalue or proto.tvalue - noder.compileNode(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.ltable' then - local fields = {} - for i, field in ipairs(proto.fields) do - fields[i] = createValue(closure, field, callback, road) or field - end - if #fields == 0 then - return nil - end - local value = instantValue(closure, proto) - value.fields = fields - noder.compileNode(noder.getNoders(proto), value) - return value - end - if proto.type == 'doc.type.field' then - local name = proto.name[1] - if type(name) == 'string' then - road[#road+1] = ('%s%s'):format( - noder.STRING_FIELD, - name - ) - else - road[#road+1] = ('%s%s'):format( - noder.SPLIT_CHAR, - name - ) - end - local typeUnit = createValue(closure, proto.extends, callback, road) - road[#road] = nil - if not typeUnit then - return nil - end - local value = instantValue(closure, proto) - value.name = proto.name - value.extends = typeUnit - noder.compileNode(noder.getNoders(proto), value) - return value - end -end - -local function buildValue(road, key, proto, param, upvalues) - local paramID - if proto.literal then - local str = param.type == 'string' and param[1] - if not str then - return - end - paramID = 'dn:' .. str - else - paramID = noder.getID(param) - end - if not paramID then - return - end - local myUri = guide.getUri(param) - local myHead = noder.URI_CHAR .. myUri .. noder.URI_CHAR - paramID = myHead .. paramID - if not upvalues[key] then - upvalues[key] = {} - end - upvalues[key][#upvalues[key]+1] = paramID .. table.concat(road) -end - --- 为所有的 param 与 return 创建副本 ----@param closure generic.closure -local function buildValues(closure) - local protoFunction = closure.proto - local upvalues = closure.upvalues - local params = closure.call.args - local args = protoFunction.args - local paramMap = {} - if params then - for i, param in ipairs(params) do - local arg = args and args[i] - if arg then - if arg.type == 'local' then - paramMap[arg[1]] = param - elseif arg.type == 'doc.type.arg' then - paramMap[arg.name[1]] = param - end - end - end - end - - if protoFunction.type == 'function' then - for _, doc in ipairs(protoFunction.bindDocs) do - if doc.type == 'doc.param' then - local name = doc.param[1] - local extends = doc.extends - if name and extends then - local param = paramMap[name] - closure.params[name] = param and createValue(closure, extends, function (road, key, proto) - buildValue(road, key, proto, param, upvalues) - end) or extends - end - end - end - for _, doc in ipairs(protoFunction.bindDocs) do - if doc.type == 'doc.return' then - for _, rtn in ipairs(doc.returns) do - closure.returns[rtn.returnIndex] = createValue(closure, rtn) or rtn - end - end - end - end - if protoFunction.type == 'doc.type.function' then - for index, arg in ipairs(protoFunction.args) do - local name = arg.name[1] - local extends = arg.extends - local param = paramMap[name] - closure.params[name] = param and createValue(closure, extends, function (road, key, proto) - buildValue(road, key, proto, param, upvalues) - end) or extends - end - for index, rtn in ipairs(protoFunction.returns) do - closure.returns[index] = createValue(closure, rtn) or rtn - end - end -end - ----创建一个闭包 ----@param proto parser.guide.object|generic.value # 原型函数|泛型值 ----@return generic.closure -function m.createClosure(proto, call) - local protoFunction, parentClosure - if proto.type == 'function' then - protoFunction = proto - elseif proto.type == 'doc.type.function' then - protoFunction = proto - elseif proto.type == 'generic.value' then - protoFunction = proto.proto - parentClosure = proto.closure - end - ---@type generic.closure - local closure = { - type = 'generic.closure', - parent = protoFunction.parent, - proto = protoFunction, - call = call, - upvalues = parentClosure and parentClosure.upvalues or {}, - params = {}, - returns = {}, - values = {}, - } - buildValues(closure) - - if #closure.returns == 0 then - return nil - end - - noder.compileNode(noder.getNoders(proto), closure) - - return closure -end - -return m diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua index 43d15c39..d57139b3 100644 --- a/script/core/hover/description.lua +++ b/script/core/hover/description.lua @@ -6,7 +6,6 @@ local config = require 'config' local lang = require 'language' local util = require 'utility' local guide = require 'parser.guide' -local noder = require 'core.noder' local rpath = require 'workspace.require-path' local function collectRequire(mode, literal, uri) diff --git a/script/core/infer.lua b/script/core/infer.lua index 3c62712e..691424a2 100644 --- a/script/core/infer.lua +++ b/script/core/infer.lua @@ -1,6 +1,5 @@ local searcher = require 'core.searcher' local config = require 'config' -local noder = require 'core.noder' local util = require 'utility' local vm = require "vm.vm" local guide = require "parser.guide" diff --git a/script/core/noder.lua b/script/core/noder.lua deleted file mode 100644 index 0a04a744..00000000 --- a/script/core/noder.lua +++ /dev/null @@ -1,1910 +0,0 @@ -local util = require 'utility' -local guide = require 'parser.guide' -local collector = require 'core.collector' 'searcher' -local files = require 'files' -local config = require 'config' - -local tostring = tostring -local error = error -local ipairs = ipairs -local type = type -local next = next -local log = log -local ssub = string.sub -local sformat = string.format -local sgsub = string.gsub -local smatch = string.match -local sfind = string.find - -_ENV = nil - -local SPLIT_CHAR = '\x1F' -local LAST_REGEX = SPLIT_CHAR .. '[^' .. SPLIT_CHAR .. ']*$' -local FIRST_REGEX = '^[^' .. SPLIT_CHAR .. ']*' -local HEAD_REGEX = '^' .. SPLIT_CHAR .. '?[^' .. SPLIT_CHAR .. ']*' -local STRING_CHAR = '.' -local ANY_FIELD_CHAR = '*' -local INDEX_CHAR = '[' -local RETURN_INDEX = SPLIT_CHAR .. '#' -local PARAM_INDEX = SPLIT_CHAR .. '&' -local EVENT_ENUM = SPLIT_CHAR .. '>' -local TABLE_KEY = SPLIT_CHAR .. '<' -local WEAK_TABLE_KEY = SPLIT_CHAR .. '<<' -local STRING_FIELD = SPLIT_CHAR .. STRING_CHAR -local INDEX_FIELD = SPLIT_CHAR .. INDEX_CHAR -local ANY_FIELD = SPLIT_CHAR .. ANY_FIELD_CHAR -local WEAK_ANY_FIELD = SPLIT_CHAR .. ANY_FIELD_CHAR .. ANY_FIELD_CHAR -local URI_CHAR = '@' -local URI_REGEX = URI_CHAR .. '([^' .. URI_CHAR .. ']*)' .. URI_CHAR .. '(.*)' - -local INFO_DEEP = { - deep = true, -} -local INFO_REJECT_SET = { - reject = 'set', -} -local INFO_DEEP_AND_REJECT_SET = { - reject = 'set', - deep = true, -} -local INFO_META_INDEX = { - filter = function (id, field) - if field then - return true - end - return ssub(id, 1, 2) ~= 'f:' - end, - filterValid = function (id, field) - return not field - end -} -local INFO_CLASS_TO_EXNTENDS = { - filter = function (_, field, mode) - return field ~= nil - or mode == 'field' - or mode == 'allfield' - end, - filterValid = function (_, field) - return not field - end, - reject = 'set', -} -local INFO_DEEP_AND_DONT_CROSS = { - deep = true, - dontCross = true, -} - ----@alias node.id string ----@alias node.filter fun(id: string, field?: string):boolean - ----@class noders --- 使用该ID的单元 ----@field source table<node.id, parser.guide.object> --- 使用该ID的单元 ----@field sources table<node.id, parser.guide.object[]> --- 前进的关联ID ----@field forward table<node.id, node.id> --- 第一个前进关联的info ----@field finfo? table<node.id, node.info> --- 前进的关联ID与info ----@field forwards table<node.id, node.id[]|table<node.id, node.info>> --- 后退的关联ID ----@field backward table<node.id, node.id> --- 第一个后退关联的info ----@field binfo? table<node.id, node.info> --- 后退的关联ID与info ----@field backwards table<node.id, node.id[]|table<node.id, node.info>> --- 第一个继承 ----@field extend table<node.id, node.id> --- 其他继承 ----@field extends table<node.id, node.id[]> --- 函数调用参数信息(用于泛型) ----@field call table<node.id, parser.guide.object> ----@field require table<node.id, string> ----@field skip table<node.id, boolean> - ----@class node.info ----@field reject? string ----@field deep? boolean ----@field filter? node.filter ----@field filterValid? node.filter ----@field dontCross? boolean - ----如果对象是 arg self, 则认为 id 是 method 的 node ----@param source parser.guide.object ----@return nil -local function getMethodNode(source) - if source.type ~= 'local' or source[1] ~= 'self' then - return nil - end - if source._mnode ~= nil then - return source._mnode or nil - end - source._mnode = false - local func = guide.getParentFunction(source) - if not func then - return - end - if func.isGeneric then - return - end - if source.parent.type ~= 'funcargs' then - return - end - local setmethod = func.parent - if setmethod and ( setmethod.type == 'setmethod' - or setmethod.type == 'setfield' - or setmethod.type == 'setindex') then - source._mnode = setmethod.node - return setmethod.node - end -end - -local function getFieldEventName(field) - if field._eventName then - return field._eventName or nil - end - field._eventName = false - local fieldType = field.extends - if not fieldType then - return nil - end - local docFunc = fieldType.types[1] - if not docFunc or docFunc.type ~= 'doc.type.function' then - return nil - end - local firstArg = docFunc.args and docFunc.args[1] - if not firstArg then - return nil - end - local secondArg - if firstArg.name[1] == 'self' then - firstArg = docFunc.args[2] - if not firstArg then - return nil - end - secondArg = docFunc.args[3] - else - secondArg = docFunc.args[2] - end - if not secondArg then - return - end - local firstType = firstArg.extends - if not firstType then - return nil - end - local firstEnum = firstType.types[1] - if not firstEnum then - return nil - end - local secondType = secondArg.extends - if not secondType then - return nil - end - local secondTypeUnit = secondType.types[1] - if not secondTypeUnit or secondTypeUnit.type ~= 'doc.type.function' then - return nil - end - local eventName = firstEnum[1]:match [[^['"](.+)['"]$]] - field._eventName = eventName - return eventName -end - -local getKey, getID -local getKeyMap = util.switch() - : case 'local' - : call(function (source) - if source.parent.type == 'funcargs' then - return 'p:' .. source.start, nil - end - return 'l:' .. source.start, nil - end) - : case 'setlocal' - : case 'getlocal' - : call(function (source) - return getKey(source.node) - end) - : case 'setglobal' - : case 'getglobal' - : call(function (source) - local node = source.node - if node.tag == '_ENV' then - return STRING_CHAR .. (source[1] or ''), nil - else - return STRING_CHAR .. (source[1] or ''), node - end - end) - : case 'getfield' - : case 'setfield' - : call(function (source) - return STRING_CHAR .. (source.field and source.field[1] or ''), source.node - end) - : case 'tablefield' - : call(function (source) - local t = source.parent - local parent = t.parent - local node - if parent.value == t then - node = parent - else - node = t - end - return STRING_CHAR .. (source.field and source.field[1] or ''), node - end) - : case 'getmethod' - : case 'setmethod' - : call(function (source) - return STRING_CHAR .. (source.method and source.method[1] or ''), source.node - end) - : case 'setindex' - : case 'getindex' - : call(function (source) - local index = source.index - if not index then - return INDEX_CHAR, source.node - end - if index.type == 'string' then - return STRING_CHAR .. (index[1] or ''), source.node - elseif index.type == 'boolean' - or index.type == 'integer' - or index.type == 'number' then - return tostring(index[1] or ''), source.node - else - return INDEX_CHAR, source.node - end - end) - : case 'tableindex' - : call(function (source) - local t = source.parent - local parent = t.parent - local node - if parent.value == t then - node = parent - else - node = t - end - local index = source.index - if not index then - return ANY_FIELD_CHAR, node - end - if index.type == 'string' then - return STRING_CHAR .. (index[1] or ''), node - elseif index.type == 'boolean' - or index.type == 'integer' - or index.type == 'number' then - return tostring(index[1] or ''), node - elseif index.type ~= 'function' - and index.type ~= 'table' then - return ANY_FIELD_CHAR, node - end - end) - : case 'tableexp' - : call(function (source) - local t = source.parent - local parent = t.parent - local node - if parent.value == t then - node = parent - else - node = t - end - return tostring(source.tindex), node - end) - : case 'table' - : call(function (source) - return 't:' .. source.start, nil - end) - : case 'label' - : call(function (source) - return 'l:' .. source.start, nil - end) - : case 'goto' - : call(function (source) - if source.node then - return 'l:' .. source.node.start, nil - end - return nil, nil - end) - : case 'function' - : call(function (source) - return 'f:' .. source.start, nil - end) - : case 'string' - : call(function (source) - return 'str:', nil - end) - : case 'integer' - : call(function (source) - return 'int:', nil - end) - : case 'number' - : call(function (source) - return 'num:', nil - end) - : case 'boolean' - : call(function (source) - return 'bool:', nil - end) - : case 'nil' - : call(function (source) - return 'nil:', nil - end) - : case '...' - : call(function (source) - return 'va:' .. source.start, nil - end) - : case 'varargs' - : call(function (source) - if source.node then - return 'va:' .. source.node.start, nil - end - end) - : case 'select' - : call(function (source) - return sformat('s:%d%s%d', source.start, RETURN_INDEX, source.sindex) - end) - : case 'call' - : call(function (source) - local node = source.node - if node.special == 'rawget' - or node.special == 'rawset' then - if not source.args then - return nil, nil - end - local tbl, key = source.args[1], source.args[2] - if not tbl or not key then - return nil, nil - end - if key.type == 'string' then - return STRING_CHAR .. (key[1] or ''), tbl - else - return '', tbl - end - end - return 'c:' .. source.finish, nil - end) - : case 'doc.class.name' - : case 'doc.alias.name' - : case 'doc.extends.name' - : call(function (source) - local name = source[1] - return 'dn:' .. name, nil - end) - : case 'doc.type.name' - : call(function (source) - local name = source[1] - if source.typeGeneric then - local first = source.typeGeneric[name][1] - if first then - return 'dg:' .. first.start, nil - end - else - return 'dn:' .. name, nil - end - end) - : case 'doc.see.name' - : call(function (source) - local name = source[1] - return 'dsn:' .. name, nil - end) - : case 'doc.class' - : call(function (source) - return 'dc:' .. source.start - end) - : case 'doc.type' - : call(function (source) - return 'dt:' .. source.start - end) - : case 'doc.param' - : call(function (source) - return 'dp:' .. source.start - end) - : case 'doc.vararg' - : call(function (source) - return 'dv:' .. source.start - end) - : case 'doc.field.name' - : call(function (source) - return 'dfn:' .. source.start - end) - : case 'doc.type.enum' - : case 'doc.resume' - : call(function (source) - return 'de:' .. source.start - end) - : case 'doc.type.table' - : call(function (source) - return 'dtable:' .. source.start - end) - : case 'doc.type.ltable' - : call(function (source) - return 'dltable:' .. source.start - end) - : case 'doc.type.field' - : call(function (source) - return 'dfield:' .. source.start - end) - : case 'doc.type.array' - : call(function (source) - return 'darray:' .. source.finish - end) - : case 'doc.type.function' - : call(function (source) - return 'dfun:' .. source.start, nil - end) - : case 'doc.see.field' - : call(function (source) - return STRING_CHAR .. (source[1]), source.parent.name - end) - : case 'generic.closure' - : call(function (source) - return 'gc:' .. source.call.start, nil - end) - : case 'generic.value' - : call(function (source) - local tail = '' - if guide.getUri(source.closure.call) ~= guide.getUri(source.proto) then - tail = URI_CHAR .. guide.getUri(source.closure.call) - end - return sformat('gv:%s|%s%s' - , source.closure.call.start - , getKey(source.proto) - , tail - ) - end) - : getMap() - ----获取语法树单元的key ----@param source parser.guide.object ----@return string? key ----@return parser.guide.object? node -function getKey(source) - local f = getKeyMap[source.type] - if f then - return f(source) - end - return nil -end - -local function getLocalValueID(source) - if source.type ~= 'local' then - return nil - end - local value = source.value - if not value then - return nil - end - local id = getID(value) - if not id then - return nil - end - local ct = id:sub(1, 2) - if ct == 'g:' - or ct == 'p:' - or ct == 'l:' then - return id - end - return nil -end - -local function getNodeKey(source) - if source.type == 'getlocal' - or source.type == 'setlocal' then - source = source.node - end - local methodNode = getMethodNode(source) - if methodNode then - return getNodeKey(methodNode) - end - if config.get(guide.getUri(source), 'Lua.IntelliSense.traceFieldInject') then - local localValueID = getLocalValueID(source) - if localValueID then - return localValueID - end - end - local key, node = getKey(source) - if key and guide.isGlobal(source) then - return 'g:' .. key, nil - end - return key, node -end - ----获取语法树单元的字符串ID ----@param source parser.guide.object ----@return string? id -function getID(source) - if not source then - return nil - end - if source._id ~= nil then - return source._id or nil - end - if source.type == 'field' - or source.type == 'method' then - source._id = false - return nil - end - local current = source - while current.type == 'paren' do - current = current.exp - if not current then - source._id = false - return nil - end - end - local id, node = getNodeKey(current) - if not id then - source._id = false - return nil - end - if node then - local pid = getID(node) - if not pid then - source._id = false - return nil - end - id = pid .. SPLIT_CHAR .. id - end - source._id = id - return id -end - ----添加关联的前进ID ----@param noders noders ----@param id node.id ----@param forwardID node.id ----@param info? node.info -local function pushForward(noders, id, forwardID, info) - if not id - or not forwardID - or forwardID == '' - or id == forwardID then - return - end - if not noders.forward[id] then - noders.forward[id] = forwardID - noders.finfo[id] = info - return - end - if noders.forward[id] == forwardID then - return - end - local forwards = noders.forwards[id] - if not forwards then - forwards = {} - noders.forwards[id] = forwards - end - if forwards[forwardID] ~= nil then - return - end - forwards[forwardID] = info or false - forwards[#forwards+1] = forwardID -end - ----添加关联的后退ID ----@param noders noders ----@param id node.id ----@param backwardID node.id ----@param info? node.info -local function pushBackward(noders, id, backwardID, info) - if not id - or not backwardID - or backwardID == '' - or id == backwardID then - return - end - if not noders.backward[id] then - noders.backward[id] = backwardID - noders.binfo[id] = info - return - end - if noders.backward[id] == backwardID then - return - end - local backwards = noders.backwards[id] - if not backwards then - backwards = {} - noders.backwards[id] = backwards - end - if backwards[backwardID] ~= nil then - return - end - backwards[backwardID] = info or false - backwards[#backwards+1] = backwardID -end - ----添加继承的关联ID ----@param noders noders ----@param id node.id ----@param extendID node.id -local function pushExtend(noders, id, extendID) - if not id - or not extendID - or extendID == '' - or id == extendID then - return - end - if not noders.extend[id] then - noders.extend[id] = extendID - return - end - if noders.extend[id] == extendID then - return - end - local extends = noders.extends[id] - if not extends then - extends = {} - noders.extends[id] = extends - end - if extends[extendID] ~= nil then - return - end - extends[extendID] = false - extends[#extends+1] = extendID -end - ----@class noder -local m = {} - -m.SPLIT_CHAR = SPLIT_CHAR -m.STRING_CHAR = STRING_CHAR -m.STRING_FIELD = STRING_FIELD -m.RETURN_INDEX = RETURN_INDEX -m.PARAM_INDEX = PARAM_INDEX -m.TABLE_KEY = TABLE_KEY -m.ANY_FIELD = ANY_FIELD -m.URI_CHAR = URI_CHAR -m.INDEX_FIELD = INDEX_FIELD -m.WEAK_TABLE_KEY = WEAK_TABLE_KEY -m.WEAK_ANY_FIELD = WEAK_ANY_FIELD - ---- 寻找doc的主体 ----@param obj parser.guide.object ----@return parser.guide.object -local function getDocStateWithoutCrossFunction(obj) - for _ = 1, 1000 do - local parent = obj.parent - if not parent then - return obj - end - if parent.type == 'doc' then - return obj - end - if parent.type == 'doc.type.function' then - return nil - end - obj = parent - end - error('guide.getDocState overstack') -end - -local dontPushSourceMap = util.arrayToHash { - 'str:', 'nil:', 'num:', 'int:', 'bool:' -} - ----添加关联单元 ----@param noders noders ----@param source parser.guide.object -function m.pushSource(noders, source, id) - id = id or getID(source) - if not id then - return - end - if dontPushSourceMap[id] then - return - end - if not noders.source[id] then - noders.source[id] = source - return - end - local sources = noders.sources[id] - if not sources then - sources = {} - noders.sources[id] = sources - end - sources[#sources+1] = source -end - -local DUMMY_FUNCTION = function () end - ----遍历关联单元 ----@param noders noders ----@param id node.id ----@return fun():parser.guide.object -function m.eachSource(noders, id) - local source = noders.source[id] - if not source then - return DUMMY_FUNCTION - end - local index - local sources = noders.sources[id] - return function () - if not index then - index = 0 - return source - end - if not sources then - return nil - end - index = index + 1 - return sources[index] - end -end - ----遍历forward ----@param noders noders ----@param id node.id ----@return fun():string, node.info -function m.eachForward(noders, id) - local forward = noders.forward[id] - if not forward then - return DUMMY_FUNCTION - end - local index - local forwards = noders.forwards[id] - return function () - if not index then - index = 0 - return forward, noders.finfo[id] - end - if not forwards then - return nil - end - index = index + 1 - local id = forwards[index] - local tag = forwards[id] - return id, tag - end -end - ----遍历backward ----@param noders noders ----@param id node.id ----@return fun():string, node.info -function m.eachBackward(noders, id) - local backward = noders.backward[id] - if not backward then - return DUMMY_FUNCTION - end - local index - local backwards = noders.backwards[id] - return function () - if not index then - index = 0 - return backward, noders.binfo[id] - end - if not backwards then - return nil - end - index = index + 1 - local id = backwards[index] - local tag = backwards[id] - return id, tag - end -end - ----遍历extend ----@param noders noders ----@param id node.id ----@return fun():string, node.info -function m.eachExtend(noders, id) - local extend = noders.extend[id] - if not extend then - return DUMMY_FUNCTION - end - local index - local extends = noders.extends[id] - return function () - if not index then - index = 0 - return extend - end - if not extends then - return nil - end - index = index + 1 - local id = extends[index] - return id - end -end - -local function bindValue(noders, source, id) - local value = source.value - if not value then - return - end - local valueID = getID(value) - if not valueID then - return - end - - local bindDocs = source.bindDocs - if source.type == 'getlocal' - or source.type == 'setlocal' then - if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceLocalSet') then - return - end - bindDocs = source.node.bindDocs - end - if bindDocs and value.type ~= 'table' then - for _, doc in ipairs(bindDocs) do - if doc.type == 'doc.class' - or doc.type == 'doc.type' then - return - end - end - end - -- x = y : x -> y - pushForward(noders, id, valueID, INFO_REJECT_SET) - if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceBeSetted') - and source.type ~= 'local' then - return - end - -- 参数/call禁止反向查找赋值 - local valueType = smatch(valueID, '^(.-:).') - if not valueType then - return - end - pushBackward(noders, valueID, id, INFO_DEEP_AND_REJECT_SET) -end - -local function compileCallParam(noders, call, sourceID) - if not sourceID then - return - end - if not call.args then - return - end - local node = call.node - local fixIndex = 0 - if call.node.special == 'pcall' then - fixIndex = 1 - node = call.args[1] - elseif call.node.special == 'xpcall' then - fixIndex = 2 - node = call.args[1] - end - local nodeID = getID(node) - if not nodeID then - return - end - local methodIndex = 0 - if node.type == 'getmethod' then - fixIndex = fixIndex + 1 - methodIndex = 1 - end - local eventNodeID - for firstIndex, callArg in ipairs(call.args) do - firstIndex = firstIndex - fixIndex - if firstIndex == 1 and callArg.type == 'string' then - if callArg[1] then - eventNodeID = sformat('%s%s%s' - , nodeID - , EVENT_ENUM - , callArg[1] - ) - end - end - if firstIndex > 0 and callArg.type == 'function' then - if callArg.args then - for secondIndex, funcParam in ipairs(callArg.args) do - local paramID = sformat('%s%s%s%s%s' - , nodeID - , PARAM_INDEX - , firstIndex + methodIndex - , PARAM_INDEX - , secondIndex - ) - pushForward(noders, getID(funcParam), paramID) - if eventNodeID then - local eventParamID = sformat('%s%s%s%s%s' - , eventNodeID - , PARAM_INDEX - , firstIndex + methodIndex - , PARAM_INDEX - , secondIndex - ) - pushForward(noders, getID(funcParam), eventParamID) - end - end - end - end - if callArg.type == 'table' then - local paramID = sformat('%s%s%s' - , nodeID - , PARAM_INDEX - , firstIndex + methodIndex - ) - pushForward(noders, getID(callArg), paramID) - end - end -end - -local function compileCallReturn(noders, call, sourceID, returnIndex) - if not sourceID then - return - end - local node = call.node - local nodeID = getID(node) - if not nodeID then - return - end - local callID = getID(call) - if not callID then - return - end - -- 将setmetatable映射到 param1 以及 param2.__index 上 - if node.special == 'setmetatable' then - local tblID = getID(call.args and call.args[1]) - local metaID = getID(call.args and call.args[2]) - local indexID - if metaID then - indexID = sformat('%s%s%s' - , metaID - , STRING_FIELD - , '__index' - ) - end - pushForward(noders, sourceID, tblID) - pushForward(noders, sourceID, indexID, INFO_META_INDEX) - pushBackward(noders, tblID, sourceID) - --pushBackward(noders, indexID, callID) - return - end - if node.special == 'require' then - local arg1 = call.args and call.args[1] - if arg1 and arg1.type == 'string' then - noders.require[sourceID] = arg1[1] - end - pushBackward(noders, callID, sourceID, INFO_DEEP) - return - end - if node.special == 'pcall' - or node.special == 'xpcall' then - local index = returnIndex - 1 - if index <= 0 then - return - end - local funcID = call.args and getID(call.args[1]) - if not funcID then - return - end - local pfuncXID = sformat('%s%s%s' - , funcID - , RETURN_INDEX - , index - ) - pushForward(noders, sourceID, pfuncXID) - pushBackward(noders, pfuncXID, sourceID, INFO_DEEP) - return - end - local funcXID = sformat('%s%s%s' - , nodeID - , RETURN_INDEX - , returnIndex - ) - noders.call[sourceID] = call - pushForward(noders, sourceID, funcXID) - pushBackward(noders, funcXID, sourceID, INFO_DEEP) -end - -local specialMap = util.arrayToHash { - 'require', 'dofile', 'loadfile', - 'rawset', 'rawget', 'setmetatable', -} - -local compileNodeMap -compileNodeMap = util.switch() - : case 'string' - : call(function (noders, id, source) - pushForward(noders, id, 'str:') - end) - : case 'boolean' - : call(function (noders, id, source) - pushForward(noders, id, 'dn:boolean') - end) - : case 'number' - : call(function (noders, id, source) - pushForward(noders, id, 'dn:number') - end) - : case 'integer' - : call(function (noders, id, source) - pushForward(noders, id, 'dn:integer') - end) - : case 'nil' - : call(function (noders, id, source) - pushForward(noders, id, 'dn:nil') - end) - : case 'doc.type' - : call(function (noders, id, source) - if source.bindSources then - for _, src in ipairs(source.bindSources) do - if src.parent.type ~= 'funcargs' - and not src.dummy then - pushForward(noders, getID(src), id) - end - end - end - for _, typeUnit in ipairs(source.types) do - local unitID = getID(typeUnit) - pushForward(noders, id, unitID) - if source.bindSources then - for _, src in ipairs(source.bindSources) do - if src.parent.type ~= 'funcargs' - and not src.dummy then - pushBackward(noders, unitID, getID(src)) - end - end - end - end - end) - : case 'doc.type.table' - : call(function (noders, id, source) - if source.node then - pushForward(noders, id, getID(source.node), INFO_CLASS_TO_EXNTENDS) - end - if source.tkey then - local keyID = id .. TABLE_KEY - pushForward(noders, keyID, getID(source.tkey)) - end - if source.tvalue then - local valueID = id .. ANY_FIELD - pushForward(noders, valueID, getID(source.tvalue)) - end - end) - : case 'doc.type.ltable' - : call(function (noders, id, source) - local firstField = source.fields[1] - if not firstField then - return - end - local keyID = id .. WEAK_TABLE_KEY - local valueID = id .. WEAK_ANY_FIELD - pushForward(noders, keyID, 'dn:string') - pushForward(noders, valueID, getID(firstField.extends)) - for _, field in ipairs(source.fields) do - local fname = field.name[1] - local extendsID - if type(fname) == 'string' then - extendsID = sformat('%s%s%s' - , id - , STRING_FIELD - , fname - ) - else - extendsID = sformat('%s%s%s' - , id - , SPLIT_CHAR - , fname - ) - end - pushForward(noders, extendsID, getID(field)) - pushForward(noders, extendsID, getID(field.extends)) - end - end) - : case 'doc.type.array' - : call(function (noders, id, source) - if source.node then - local nodeID = id .. ANY_FIELD - pushForward(noders, nodeID, getID(source.node)) - end - local keyID = id .. TABLE_KEY - pushForward(noders, keyID, 'dn:integer') - end) - : case 'doc.alias' - : call(function (noders, id, source) - pushForward(noders, getID(source.alias), getID(source.extends)) - end) - : case 'doc.class' - : call(function (noders, id, source) - pushForward(noders, id, getID(source.class)) - pushForward(noders, getID(source.class), id) - if source.extends then - for _, ext in ipairs(source.extends) do - pushExtend(noders, id, getID(ext)) - end - end - if source.bindSources then - for _, src in ipairs(source.bindSources) do - if src.parent.type ~= 'funcargs' - and src.type ~= 'setmethod' - and not src.dummy then - pushForward(noders, getID(src), id) - pushForward(noders, id, getID(src)) - end - end - end - for _, field in ipairs(source.fields) do - local key = field.field[1] - if key then - local keyID - if type(key) == 'string' then - keyID = sformat('%s%s%s' - , id - , STRING_FIELD - , key - ) - local eventName = getFieldEventName(field) - if eventName then - keyID = sformat('%s%s%s' - , keyID - , EVENT_ENUM - , eventName - ) - end - else - keyID = sformat('%s%s%s' - , id - , SPLIT_CHAR - , key - ) - end - pushForward(noders, keyID, getID(field.field)) - pushForward(noders, getID(field.field), keyID) - pushForward(noders, keyID, getID(field.extends)) - end - end - end) - : case 'doc.module' - : call(function (noders, id, source) - if not source.module then - return - end - for _, src in ipairs(source.bindSources) do - if guide.isSet(src) then - local sourceID = getID(src) - if sourceID then - noders.require[sourceID] = source.module - end - end - end - end) - : case 'doc.param' - : call(function (noders, id, source) - pushForward(noders, id, getID(source.extends)) - for _, src in ipairs(source.bindSources) do - if src.type == 'local' and src.parent.type == 'in' then - pushForward(noders, getID(src), id) - end - end - if source.bindSources then - for _, src in ipairs(source.bindSources) do - if src.type == 'function' and src.args then - for _, arg in ipairs(src.args) do - if arg[1] == source.param[1] then - pushForward(noders, getID(arg), id) - end - end - end - end - end - end) - : case 'doc.vararg' - : call(function (noders, id, source) - pushForward(noders, getID(source), getID(source.vararg)) - end) - : case 'doc.see' - : call(function (noders, id, source) - local nameID = getID(source.name) - local classID = sgsub(nameID, '^dsn:', 'dn:') - pushForward(noders, nameID, classID) - if source.field then - local fieldID = getID(source.field) - local fieldClassID = sgsub(fieldID, '^dsn:', 'dn:') - pushForward(noders, fieldID, fieldClassID) - end - end) - : case 'call' - : call(function (noders, id, source) - if source.parent.type ~= 'select' then - compileCallReturn(noders, source, id, 1) - end - compileCallParam(noders, source, id) - end) - : case 'select' - : call(function (noders, id, source) - if source.vararg.type == 'call' then - local call = source.vararg - compileCallReturn(noders, call, id, source.sindex) - end - if source.vararg.type == 'varargs' then - pushForward(noders, id, getID(source.vararg)) - end - end) - : case 'doc.type.function' - : call(function (noders, id, source) - if source.args then - for index, param in ipairs(source.args) do - local indexID = sformat('%s%s%s' - , id - , PARAM_INDEX - , index - ) - pushForward(noders, indexID, getID(param.extends)) - end - end - if source.returns then - for index, rtn in ipairs(source.returns) do - local returnID = sformat('%s%s%s' - , id - , RETURN_INDEX - , index - ) - pushForward(noders, returnID, getID(rtn)) - end - end - -- @type fun(x: T):T 的情况 - local docType = getDocStateWithoutCrossFunction(source) - if docType and docType.type == 'doc.type' then - guide.eachSourceType(source, 'doc.type.name', function (typeName) - if typeName.typeGeneric then - source.isGeneric = true - return false - end - end) - end - end) - : case 'doc.type.name' - : call(function (noders, id, source) - local uri = guide.getUri(source) - collector:subscribe(uri, id, noders) - end) - : case 'doc.class.name' - : case 'doc.alias.name' - : call(function (noders, id, source) - local uri = guide.getUri(source) - collector:subscribe(uri, id, noders) - - local defID = 'def:' .. id - collector:subscribe(uri, defID, noders) - - local defAnyID = 'def:dn:' - collector:subscribe(uri, defAnyID, noders) - end) - : case 'function' - : call(function (noders, id, source) - local hasDocReturn - -- 检查 luadoc - if source.bindDocs then - for _, doc in ipairs(source.bindDocs) do - if doc.type == 'doc.return' then - hasDocReturn = true - end - if doc.type == 'doc.vararg' then - if source.args then - for _, param in ipairs(source.args) do - if param.type == '...' then - pushForward(noders, getID(param), getID(doc)) - end - end - end - end - if doc.type == 'doc.generic' then - source.isGeneric = true - end - if doc.type == 'doc.overload' then - pushForward(noders, id, getID(doc.overload)) - end - end - end - if source.args then - local parent = source.parent - local parentID = guide.isSet(parent) and getID(parent) - for i, arg in ipairs(source.args) do - if arg[1] == 'self' then - goto CONTINUE - end - local indexID = sformat('%s%s%s' - , id - , PARAM_INDEX - , i - ) - pushForward(noders, indexID, getID(arg)) - if arg.type ~= 'local' then - for j = i + 1, i + 10 do - pushForward(noders, sformat('%s%s%s' - , id - , PARAM_INDEX - , j - ), getID(arg)) - end - end - ::CONTINUE:: - end - end - -- 检查实体返回值 - if source.returns and not hasDocReturn then - for _, rtn in ipairs(source.returns) do - for index, rtnObj in ipairs(rtn) do - local returnID = sformat('%s%s%s' - , id - , RETURN_INDEX - , index - ) - pushForward(noders, returnID, getID(rtnObj)) - if config.get(guide.getUri(source), 'Lua.IntelliSense.traceReturn') then - pushBackward(noders, getID(rtnObj), returnID, INFO_DEEP_AND_DONT_CROSS) - end - end - end - end - end) - : case 'table' - : call(function (noders, id, source) - local firstField = source[1] - if firstField then - if firstField.type == 'varargs' then - local keyID = id .. TABLE_KEY - local valueID = id .. ANY_FIELD - source.array = firstField - pushForward(noders, keyID, 'dn:integer') - pushForward(noders, valueID, getID(firstField)) - else - local keyID = id .. WEAK_TABLE_KEY - local valueID = id .. WEAK_ANY_FIELD - if firstField.type == 'tablefield' then - pushForward(noders, keyID, 'dn:string') - pushForward(noders, valueID, getID(firstField.value)) - elseif firstField.type == 'tableindex' then - pushForward(noders, keyID, getID(firstField.index)) - pushForward(noders, valueID, getID(firstField.value)) - elseif firstField.type == 'tableexp' then - pushForward(noders, keyID, 'dn:integer') - pushForward(noders, valueID, getID(firstField)) - end - end - end - local parent = source.parent - if guide.isSet(parent) then - pushForward(noders, id, getID(parent)) - end - end) - : case 'loop' - : call(function (noders, id, source) - local loc = source.loc - if loc then - pushForward(noders, getID(loc), 'dn:integer') - end - end) - : case 'in' - : call(function (noders, id, source) - local keys = source.keys - ---@type parser.guide.object[] - local exps = source.exps - if not keys or not exps then - return - end - local node = exps[1] - local param1 = exps[2] - local param2 = exps[3] - if node.type == 'call' then - if not param1 then - param1 = { - type = 'select', - dummy = true, - sindex = 2, - start = node.start, - finish = node.finish, - vararg = node, - parent = source, - } - compileCallReturn(noders, node, getID(param1), 2) - if not param2 then - param2 = { - type = 'select', - dummy = true, - sindex = 3, - start = node.start, - finish = node.finish, - vararg = node, - parent = source, - } - compileCallReturn(noders, node, getID(param2), 3) - end - end - end - local call = { - type = 'call', - dummy = true, - start = source.keyword[3], - finish = exps[#exps].finish, - node = node, - args = { param1, param2 }, - parent = source, - } - for i = 1, #keys do - compileCallReturn(noders, call, getID(keys[i]), i) - end - end) - : case 'main' - : call(function (noders, id, source) - if source.returns then - for _, rtn in ipairs(source.returns) do - local rtnObj = rtn[1] - if rtnObj then - pushForward(noders, 'mainreturn', getID(rtnObj), INFO_REJECT_SET) - pushBackward(noders, getID(rtnObj), 'mainreturn', INFO_DEEP_AND_REJECT_SET) - end - end - end - end) - : case 'doc.return' - : call(function (noders, id, source) - if not source.bindSources then - return - end - for _, rtn in ipairs(source.returns) do - for _, src in ipairs(source.bindSources) do - if src.type == 'function' then - local fullID = sformat('%s%s%s' - , getID(src) - , RETURN_INDEX - , rtn.returnIndex - ) - pushForward(noders, fullID, getID(rtn)) - for _, typeUnit in ipairs(rtn.types) do - pushBackward(noders, getID(typeUnit), fullID, INFO_DEEP_AND_DONT_CROSS) - end - end - end - end - end) - : case 'generic.closure' - : call(function (noders, id, source) - for i, rtn in ipairs(source.returns) do - local closureID = sformat('%s%s%s' - , id - , RETURN_INDEX - , i - ) - local returnID = getID(rtn) - pushForward(noders, closureID, returnID) - end - end) - : case 'generic.value' - : call(function (noders, id, source) - local proto = source.proto - local closure = source.closure - local upvalues = closure.upvalues - if proto.type == 'doc.type.name' then - local key = proto[1] - if upvalues[key] then - for _, paramID in ipairs(upvalues[key]) do - pushForward(noders, id, paramID) - end - end - end - local f = compileNodeMap[proto.type] - if f then - f(noders, id, source) - end - end) - : getMap() - ----@param noders noders ----@param source parser.guide.object ----@return parser.guide.object[] -function m.compileNode(noders, source) - if source._noded then - return - end - source._noded = true - m.pushSource(noders, source) - local id = getID(source) - bindValue(noders, source, id) - - if id and specialMap[source.special] then - noders.skip[id] = true - end - - local f = compileNodeMap[source.type] - if f then - f(noders, id, source) - end - - if id and ssub(id, 1, 2) == 'g:' then - local uri = guide.getUri(source) - collector:subscribe(uri, id, noders) - if guide.isSet(source) - -- local t = Global --> t: g:.Global - and source.type ~= 'local' - and source.type ~= 'setlocal' then - - local defID = 'def:' .. id - collector:subscribe(uri, defID, noders) - - local fieldID = m.getLastID(id) - if fieldID then - local defNodeID = 'field:' .. fieldID - collector:subscribe(uri, defNodeID, noders) - end - - if guide.isGlobal(source) then - local defAnyID = 'def:g:' - collector:subscribe(uri, defAnyID, noders) - end - end - end -end - ----根据ID来获取第一个节点的ID ----@param id string ----@return string -function m.getFirstID(id) - local firstID, count = smatch(id, FIRST_REGEX) - if count == 0 then - return nil - end - if firstID == '' then - return nil - end - return firstID -end - ----根据ID来获取第一个节点的ID或field ----@param id string ----@return string -function m.getHeadID(id) - local headID, count = smatch(id, HEAD_REGEX) - if count == 0 then - return nil - end - if headID == '' then - return nil - end - return headID -end - ----根据ID来获取上个节点的ID ----@param id string ----@return string -function m.getLastID(id) - local lastID, count = sgsub(id, LAST_REGEX, '') - if count == 0 then - return nil - end - if lastID == '' then - return nil - end - return lastID -end - -function m.getFieldID(id) - local fieldID = smatch(id, LAST_REGEX) - return fieldID -end - ----获取ID的长度 ----@param id string ----@return integer -function m.getIDLength(id) - if not id then - return 0 - end - local _, count = sgsub(id, SPLIT_CHAR, SPLIT_CHAR) - return count + 1 -end - ----测试id是否包含field,如果遇到函数调用则中断 ----@param id string ----@return boolean -function m.hasField(id) - local firstID = m.getFirstID(id) - if firstID == id or not firstID then - return false - end - local nextChar = ssub(id, #firstID + 1, #firstID + 1) - if nextChar ~= SPLIT_CHAR then - return false - end - local next2Char = ssub(id, #firstID + 2, #firstID + 2) - if next2Char == RETURN_INDEX - or next2Char == PARAM_INDEX then - return false - end - return true -end - ----把形如 `@file:\\\XXXXX@gv:1|1`拆分成uri与id ----@param id string ----@return uri? string ----@return string id -function m.getUriAndID(id) - local uri, newID = smatch(id, URI_REGEX) - return uri, newID -end - ----是否是普通的field,例如数字或字符串,而不是函数返回值等 ----@param field any -function m.isCommonField(field) - if not field then - return false - end - if ssub(field, 1, #RETURN_INDEX) == RETURN_INDEX then - return false - end - return true -end - ----是否是普通的field,例如数字或字符串,而不是函数返回值等 -function m.hasCall(field) - if not field then - return false - end - if sfind(field, RETURN_INDEX, 1, true) then - return true - end - return false -end - -function m.isGlobalID(id) - return ssub(id, 1, 2) == 'g:' - or ssub(id, 1, 3) == 'dn:' -end - ----获取source的ID ----@param source parser.guide.object ----@return string -function m.getID(source) - return getID(source) -end - ----获取source的key ----@param source parser.guide.object ----@return string -function m.getKey(source) - return getKey(source) -end - ----清除临时id(用于泛型的临时对象) ----@param noders noders ----@param id string -function m.removeID(noders, id) - if not id then - return - end - for _, t in next, noders do - t[id] = nil - end -end - ----寻找doc的主体 ----@param doc parser.guide.object -function m.getDocState(doc) - return getDocStateWithoutCrossFunction(doc) -end - ----@param noders noders ----@return fun():node.id -function m.eachID(noders) - return next, noders.source -end - -m.getFieldEventName = getFieldEventName - ----获取对象的noders ----@param source parser.guide.object ----@return noders -function m.getNoders(source) - local root = guide.getRoot(source) - if not root._noders then - ---@type noders - root._noders = { - source = {}, - sources = {}, - forward = {}, - finfo = {}, - forwards = {}, - backward = {}, - binfo = {}, - backwards = {}, - extend = {}, - extends = {}, - call = {}, - require = {}, - skip = {}, - } - end - return root._noders -end - ----获取对象的noders ----@param uri uri ----@return noders -function m.getNodersByUri(uri) - local state = files.getState(uri) - if not state then - return nil - end - return m.getNoders(state.ast) -end - ----编译整个文件的node ----@param source parser.guide.object ----@return table -function m.compileAllNodes(source) - local root = guide.getRoot(source) - local noders = m.getNoders(source) - if root._initedNoders then - return noders - end - root._initedNoders = true - if not root._compiledGlobals then - collector:dropUri(guide.getUri(root)) - end - root._compiledGlobals = true - --log.debug('compileNodes:', guide.getUri(root)) - guide.eachSource(root, function (src) - m.compileNode(noders, src) - end) - --log.debug('compileNodes finish:', guide.getUri(root)) - return noders -end - -local partNodersMap = util.switch() - : case 'local' - : call(function (noders, source) - local refs = source.ref - if refs then - for i = 1, #refs do - local ref = refs[i] - m.compilePartNodes(noders, ref) - end - end - end) - : case 'setlocal' - : case 'getlocal' - : call(function (noders, source) - m.compilePartNodes(noders, source.node) - - local nxt = source.next - if nxt then - m.compilePartNodes(noders, nxt) - end - - local parent = source.parent - if parent.value == source then - m.compilePartNodes(noders, parent) - end - - if parent.type == 'call' then - local node = parent.node - if node.special == 'rawset' - or node.special == 'rawget' then - m.compilePartNodes(noders, parent) - end - end - end) - : case 'setfield' - : case 'getfield' - : case 'setmethod' - : case 'getmethod' - : call(function (noders, source) - local node = source.node - m.compilePartNodes(noders, node) - - local nxt = source.next - if nxt then - m.compilePartNodes(noders, nxt) - end - - local parent = source.parent - if parent.value == source then - m.compilePartNodes(noders, parent) - end - end) - : case 'setglobal' - : case 'getglobal' - : call(function (noders, source) - local nxt = source.next - if nxt then - m.compilePartNodes(noders, nxt) - end - - local parent = source.parent - if parent.value == source then - m.compilePartNodes(noders, parent) - end - - if parent.type == 'call' then - local node = parent.node - if node.special == 'rawset' - or node.special == 'rawget' then - m.compilePartNodes(noders, parent) - end - end - end) - : case 'label' - : call(function (noders, source) - local refs = source.ref - if not refs then - return - end - for i = 1, #refs do - local ref = refs[i] - m.compilePartNodes(noders, ref) - end - end) - : case 'goto' - : call(function (noders, source) - m.compilePartNodes(noders, source.node) - end) - : case 'table' - : call(function (noders, source) - for i = 1, #source do - local field = source[i] - m.compilePartNodes(noders, field) - end - end) - : case 'tablefield' - : case 'tableindex' - : call(function (noders, source) - m.compilePartNodes(noders, source.parent) - end) - : getMap() - ----编译Class的node ----@param noders noders ----@param source parser.guide.object ----@return table -function m.compilePartNodes(noders, source) - if not source then - return - end - if source._noded then - return - end - m.compileNode(noders, source) - local f = partNodersMap[source.type] - if f then - f(noders, source) - end -end - ----编译全局变量的node ----@param root parser.guide.object ----@return table -function m.compileGlobalNodes(root) - if root._initedNoders then - return - end - if not root._compiledGlobals then - collector:dropUri(guide.getUri(root)) - end - root._compiledGlobals = true - local noders = m.getNoders(root) - local env = guide.getENV(root) - - m.compilePartNodes(noders, env) - - local docs = root.docs - guide.eachSourceTypes(docs, { - 'doc.class.name', - 'doc.alias.name', - 'doc.type.name', - }, function (doc) - m.compileNode(noders, doc) - end) -end - -for uri in files.eachFile() do - local state = files.getState(uri) - if state then - m.compileGlobalNodes(state.ast) - end -end - -files.watch(function (ev, uri) - if ev == 'update' then - local state = files.getState(uri) - if state then - m.compileGlobalNodes(state.ast) - end - end - if ev == 'remove' then - collector:dropUri(uri) - end -end) - -return m diff --git a/script/core/rename.lua b/script/core/rename.lua index 69021197..c8e011b9 100644 --- a/script/core/rename.lua +++ b/script/core/rename.lua @@ -1,11 +1,8 @@ local files = require 'files' local vm = require 'vm' -local proto = require 'proto' -local define = require 'proto.define' local util = require 'utility' local findSource = require 'core.find-source' local guide = require 'parser.guide' -local noder = require 'core.noder' local Forcing 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 |