summaryrefslogtreecommitdiff
path: root/script/core
diff options
context:
space:
mode:
Diffstat (limited to 'script/core')
-rw-r--r--script/core/completion/completion.lua1
-rw-r--r--script/core/diagnostics/deprecated.lua1
-rw-r--r--script/core/diagnostics/duplicate-doc-field.lua1
-rw-r--r--script/core/diagnostics/duplicate-index.lua2
-rw-r--r--script/core/diagnostics/undefined-field.lua1
-rw-r--r--script/core/diagnostics/undefined-global.lua1
-rw-r--r--script/core/generic.lua282
-rw-r--r--script/core/hover/description.lua1
-rw-r--r--script/core/infer.lua1
-rw-r--r--script/core/noder.lua1910
-rw-r--r--script/core/rename.lua3
-rw-r--r--script/core/searcher.lua1350
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