summaryrefslogtreecommitdiff
path: root/script/core/noder.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/core/noder.lua')
-rw-r--r--script/core/noder.lua1910
1 files changed, 0 insertions, 1910 deletions
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