summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--script/config/loader.lua3
-rw-r--r--script/core/collector.lua195
-rw-r--r--script/core/command/autoRequire.lua4
-rw-r--r--script/core/completion/completion.lua12
-rw-r--r--script/core/definition.lua2
-rw-r--r--script/core/diagnostics/different-requires.lua2
-rw-r--r--script/core/hover/description.lua2
-rw-r--r--script/core/type-definition.lua2
-rw-r--r--script/plugin.lua4
-rw-r--r--script/vm/compiler.lua4
-rw-r--r--script/workspace/require-path.lua288
-rw-r--r--script/workspace/workspace.lua10
12 files changed, 174 insertions, 354 deletions
diff --git a/script/config/loader.lua b/script/config/loader.lua
index bf662ef8..5cc7139f 100644
--- a/script/config/loader.lua
+++ b/script/config/loader.lua
@@ -42,6 +42,9 @@ end
---@return table?
function m.loadLocalConfig(uri, filename)
+ if not filename then
+ return nil
+ end
local scp = scope.getScope(uri)
local path = workspace.getAbsolutePath(uri, filename)
if not path then
diff --git a/script/core/collector.lua b/script/core/collector.lua
deleted file mode 100644
index 368a04ec..00000000
--- a/script/core/collector.lua
+++ /dev/null
@@ -1,195 +0,0 @@
-local scope = require 'workspace.scope'
-
----@class collector
----@field subscribed table<uri, table<string, any>>
----@field collect table<string, table<uri, any>>
-local mt = {}
-mt.__index = mt
-
---- 订阅一个名字
----@param uri uri
----@param name string
----@param value any
-function mt:subscribe(uri, name, value)
- uri = uri or '<fallback>'
- -- 订阅部分
- local uriSubscribed = self.subscribed[uri]
- if not uriSubscribed then
- uriSubscribed = {}
- self.subscribed[uri] = uriSubscribed
- end
- uriSubscribed[name] = true
- -- 收集部分
- local nameCollect = self.collect[name]
- if not nameCollect then
- nameCollect = {}
- self.collect[name] = nameCollect
- end
- if value == nil then
- value = true
- end
- nameCollect[uri] = value
-end
-
---- 丢弃掉某个 uri 中收集的所有信息
----@param uri uri
-function mt:dropUri(uri)
- uri = uri or '<fallback>'
- local uriSubscribed = self.subscribed[uri]
- if not uriSubscribed then
- return
- end
- self.subscribed[uri] = nil
- for name in pairs(uriSubscribed) do
- self.collect[name][uri] = nil
- if not next(self.collect[name]) then
- self.collect[name] = nil
- end
- end
-end
-
-function mt:dropAll()
- self.subscribed = {}
- self.collect = {}
-end
-
---- 是否包含某个名字的订阅
----@param uri uri
----@param name string
----@return boolean
-function mt:has(uri, name)
- if self:each(uri, name)() then
- return true
- else
- return false
- end
-end
-
-local DUMMY_FUNCTION = function () end
-
----@param scp scope
-local function eachOfFolder(nameCollect, scp)
- local curi, value
-
- ---@return any
- ---@return uri
- local function getNext()
- curi, value = next(nameCollect, curi)
- if not curi then
- return nil, nil
- end
- if scp:isChildUri(curi)
- or scp:isLinkedUri(curi) then
- return value, curi
- end
- return getNext()
- end
-
- return getNext
-end
-
----@param scp scope
-local function eachOfLinked(nameCollect, scp)
- local curi, value
-
- ---@return any
- ---@return uri
- local function getNext()
- curi, value = next(nameCollect, curi)
- if not curi then
- return nil, nil
- end
- if scp:isChildUri(curi)
- and scp:isLinkedUri(curi) then
- return value, curi
- end
-
- local cscp = scope.getFolder(curi)
- or scope.getLinkedScope(curi)
- or scope.fallback
-
- if cscp == scp
- or cscp:isChildUri(scp.uri)
- or cscp:isLinkedUri(scp.uri) then
- return value, curi
- end
-
- return getNext()
- end
-
- return getNext
-end
-
----@param scp scope
-local function eachOfFallback(nameCollect, scp)
- local curi, value
-
- ---@return any
- ---@return uri
- local function getNext()
- curi, value = next(nameCollect, curi)
- if not curi then
- return nil, nil
- end
- if scp:isLinkedUri(curi) then
- return value, curi
- end
-
- local cscp = scope.getFolder(curi)
- or scope.getLinkedScope(curi)
- or scope.fallback
-
- if cscp == scp then
- return value, curi
- end
-
- return getNext()
- end
-
- return getNext
-end
-
---- 迭代某个名字的订阅
----@param uri uri
----@param name string
----@return fun():any, uri
-function mt:each(uri, name)
- uri = uri or '<fallback>'
- local nameCollect = self.collect[name]
- if not nameCollect then
- return DUMMY_FUNCTION
- end
-
- local scp = scope.getFolder(uri)
-
- if scp then
- return eachOfFolder(nameCollect, scp)
- end
-
- scp = scope.getLinkedScope(uri)
-
- if scp then
- return eachOfLinked(nameCollect, scp)
- end
-
- return eachOfFallback(nameCollect, scope.fallback)
-end
-
-local collectors = {}
-
-local function new()
- return setmetatable({
- collect = {},
- subscribed = {},
- }, mt)
-end
-
----@return collector
-return function (name)
- if name then
- collectors[name] = collectors[name] or new()
- return collectors[name]
- else
- return new()
- end
-end
diff --git a/script/core/command/autoRequire.lua b/script/core/command/autoRequire.lua
index 020cacae..32911d92 100644
--- a/script/core/command/autoRequire.lua
+++ b/script/core/command/autoRequire.lua
@@ -71,7 +71,7 @@ local function askAutoRequire(uri, visiblePaths)
local selects = {}
local nameMap = {}
for _, visible in ipairs(visiblePaths) do
- local expect = visible.expect
+ local expect = visible.name
local select = lang.script(expect)
if not nameMap[select] then
nameMap[select] = expect
@@ -146,7 +146,7 @@ return function (data)
return
end
table.sort(visiblePaths, function (a, b)
- return #a.expect < #b.expect
+ return #a.name < #b.name
end)
local result = askAutoRequire(uri, visiblePaths)
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua
index 2b806314..b2f74aa8 100644
--- a/script/core/completion/completion.lua
+++ b/script/core/completion/completion.lua
@@ -916,21 +916,21 @@ local function collectRequireNames(mode, myUri, literal, source, smark, position
local infos = rpath.getVisiblePath(uri, path)
local relative = workspace.getRelativePath(path)
for _, info in ipairs(infos) do
- if matchKey(literal, info.expect) then
- if not collect[info.expect] then
- collect[info.expect] = {
+ if matchKey(literal, info.name) then
+ if not collect[info.name] then
+ collect[info.name] = {
textEdit = {
start = smark and (source.start + #smark) or position,
finish = smark and (source.finish - #smark) or position,
- newText = smark and info.expect or util.viewString(info.expect),
+ newText = smark and info.name or util.viewString(info.name),
},
path = relative,
}
end
if vm.isMetaFile(uri) then
- collect[info.expect][#collect[info.expect]+1] = ('* [[meta]](%s)'):format(uri)
+ collect[info.name][#collect[info.name]+1] = ('* [[meta]](%s)'):format(uri)
else
- collect[info.expect][#collect[info.expect]+1] = ([=[* [%s](%s) %s]=]):format(
+ collect[info.name][#collect[info.name]+1] = ([=[* [%s](%s) %s]=]):format(
relative,
uri,
lang.script('HOVER_USE_LUA_PATH', info.searcher)
diff --git a/script/core/definition.lua b/script/core/definition.lua
index 09a5fcf1..866e8f84 100644
--- a/script/core/definition.lua
+++ b/script/core/definition.lua
@@ -77,7 +77,7 @@ local function checkRequire(source, offset)
return nil
end
if libName == 'require' then
- return rpath.findUrisByRequirePath(guide.getUri(source), literal)
+ return rpath.findUrisByRequireName(guide.getUri(source), literal)
elseif libName == 'dofile'
or libName == 'loadfile' then
return workspace.findUrisByFilePath(literal)
diff --git a/script/core/diagnostics/different-requires.lua b/script/core/diagnostics/different-requires.lua
index de063c9f..22e3e681 100644
--- a/script/core/diagnostics/different-requires.lua
+++ b/script/core/diagnostics/different-requires.lua
@@ -21,7 +21,7 @@ return function (uri, callback)
return
end
local literal = arg1[1]
- local results = rpath.findUrisByRequirePath(uri, literal)
+ local results = rpath.findUrisByRequireName(uri, literal)
if not results or #results ~= 1 then
return
end
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index 0bfe8cc8..2097e0a3 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -11,7 +11,7 @@ local furi = require 'file-uri'
local function collectRequire(mode, literal, uri)
local result, searchers
if mode == 'require' then
- result, searchers = rpath.findUrisByRequirePath(uri, literal)
+ result, searchers = rpath.findUrisByRequireName(uri, literal)
elseif mode == 'dofile'
or mode == 'loadfile' then
result = ws.findUrisByFilePath(literal)
diff --git a/script/core/type-definition.lua b/script/core/type-definition.lua
index 791dfa83..a1c2b29f 100644
--- a/script/core/type-definition.lua
+++ b/script/core/type-definition.lua
@@ -76,7 +76,7 @@ local function checkRequire(source, offset)
return nil
end
if libName == 'require' then
- return rpath.findUrisByRequirePath(guide.getUri(source), literal)
+ return rpath.findUrisByRequireName(guide.getUri(source), literal)
elseif libName == 'dofile'
or libName == 'loadfile' then
return workspace.findUrisByFilePath(literal)
diff --git a/script/plugin.lua b/script/plugin.lua
index bdd02ea8..2a5ee27f 100644
--- a/script/plugin.lua
+++ b/script/plugin.lua
@@ -77,6 +77,10 @@ local function initPlugin(uri)
local interface = {}
scp:set('pluginInterface', interface)
+ if not scp.uri then
+ return
+ end
+
local pluginPath = ws.getAbsolutePath(scp.uri, config.get(scp.uri, 'Lua.runtime.plugin'))
log.info('plugin path:', pluginPath)
if not pluginPath then
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 46814830..fbc358a6 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -45,7 +45,7 @@ local function bindDocs(source)
if not name then
return true
end
- local uri = rpath.findUrisByRequirePath(guide.getUri(source), name)[1]
+ local uri = rpath.findUrisByRequireName(guide.getUri(source), name)[1]
if not uri then
return true
end
@@ -1450,7 +1450,7 @@ local compilerSwitch = util.switch()
if not name or type(name) ~= 'string' then
return
end
- local uri = rpath.findUrisByRequirePath(guide.getUri(func), name)[1]
+ local uri = rpath.findUrisByRequireName(guide.getUri(func), name)[1]
if not uri then
return
end
diff --git a/script/workspace/require-path.lua b/script/workspace/require-path.lua
index 24a0b025..6c550e12 100644
--- a/script/workspace/require-path.lua
+++ b/script/workspace/require-path.lua
@@ -3,24 +3,37 @@ local files = require 'files'
local furi = require 'file-uri'
local workspace = require "workspace"
local config = require 'config'
-local collector = require 'core.collector'
local scope = require 'workspace.scope'
+local util = require 'utility'
---@class require-path
local m = {}
-local function addRequireName(suri, uri, name)
- local separator = config.get(uri, 'Lua.completion.requireSeparator')
- local fsname = name:gsub('%' .. separator, '/')
- local scp = scope.getScope(suri)
- ---@type collector
- local clt = scp:get('requireName') or scp:set('requireName', collector())
- clt:subscribe(uri, fsname, name)
+---@class require-manager
+---@field scp scope
+---@field nameMap table<string, string>
+---@field visibleCache table<string, require-manager.visibleResult[]>
+local mt = {}
+mt.__index = mt
+
+---@alias require-manager.visibleResult { searcher: string, name: string }
+
+---@param scp scope
+---@return require-manager
+local function createRequireManager(scp)
+ return setmetatable({
+ scp = scp,
+ nameMap = {},
+ visibleCache = {},
+ }, mt)
end
--- `aaa/bbb/ccc.lua` 与 `?.lua` 将返回 `aaa.bbb.cccc`
-local function getOnePath(uri, path, searcher)
- local separator = config.get(uri, 'Lua.completion.requireSeparator')
+---@param path string
+---@param searcher string
+---@return string?
+function mt:getRequireNameByPath(path, searcher)
+ local separator = config.get(self.scp.uri, 'Lua.completion.requireSeparator')
local stemPath = path
: gsub('%.[^%.]+$', '')
: gsub('[/\\%.]+', separator)
@@ -41,89 +54,118 @@ local function getOnePath(uri, path, searcher)
return nil
end
-function m.getVisiblePath(suri, path)
- local searchers = config.get(suri, 'Lua.runtime.path')
- local strict = config.get(suri, 'Lua.runtime.pathStrict')
- path = workspace.normalize(path)
+---@param path string
+---@return require-manager.visibleResult[]
+function mt:getRequireResultByPath(path)
local uri = furi.encode(path)
- local scp = scope.getScope(suri)
- if not scp:isChildUri(uri)
- and not scp:isLinkedUri(uri) then
- return {}
- end
- local libUri = files.getLibraryUri(suri, uri)
+ local searchers = config.get(self.scp.uri, 'Lua.runtime.path')
+ local strict = config.get(self.scp.uri, 'Lua.runtime.pathStrict')
+ local libUri = files.getLibraryUri(self.scp.uri, uri)
local libraryPath = libUri and furi.decode(libUri)
- local cache = scp:get('visiblePath') or scp:set('visiblePath', {})
- local result = cache[path]
- if not result then
- result = {}
- cache[path] = result
- for _, searcher in ipairs(searchers) do
- local isAbsolute = searcher:match '^[/\\]'
- or searcher:match '^%a+%:'
- searcher = workspace.normalize(searcher)
- local cutedPath = path
- local currentPath = path
- local head
- local pos = 1
- if not isAbsolute then
- if libraryPath then
- currentPath = currentPath:sub(#libraryPath + 2)
- else
- currentPath = workspace.getRelativePath(uri)
- end
+ local result = {}
+ for _, searcher in ipairs(searchers) do
+ local isAbsolute = searcher:match '^[/\\]'
+ or searcher:match '^%a+%:'
+ searcher = workspace.normalize(searcher)
+ local cutedPath = path
+ local currentPath = path
+ local head
+ local pos = 1
+ if not isAbsolute then
+ if libraryPath then
+ currentPath = currentPath:sub(#libraryPath + 2)
+ else
+ currentPath = workspace.getRelativePath(uri)
end
- repeat
- cutedPath = currentPath:sub(pos)
- head = currentPath:sub(1, pos - 1)
- pos = currentPath:match('[/\\]+()', pos)
- if platform.OS == 'Windows' then
- searcher = searcher :gsub('[/\\]+', '\\')
- else
- searcher = searcher :gsub('[/\\]+', '/')
- end
- local expect = getOnePath(suri, cutedPath, searcher)
- if expect then
- local mySearcher = searcher
- if head then
- mySearcher = head .. searcher
- end
- result[#result+1] = {
- searcher = mySearcher,
- expect = expect,
- }
- addRequireName(suri, uri, expect)
- end
- until not pos or strict
end
+ repeat
+ cutedPath = currentPath:sub(pos)
+ head = currentPath:sub(1, pos - 1)
+ pos = currentPath:match('[/\\]+()', pos)
+ if platform.OS == 'Windows' then
+ searcher = searcher :gsub('[/\\]+', '\\')
+ else
+ searcher = searcher :gsub('[/\\]+', '/')
+ end
+ local name = self:getRequireNameByPath(cutedPath, searcher)
+ if name then
+ local mySearcher = searcher
+ if head then
+ mySearcher = head .. searcher
+ end
+ result[#result+1] = {
+ name = name,
+ searcher = mySearcher,
+ }
+ end
+ until not pos or strict
end
return result
end
---- 查找符合指定require path的所有uri
----@param path string
-function m.findUrisByRequirePath(suri, path)
- if type(path) ~= 'string' then
+---@param name string
+function mt:addName(name)
+ local separator = config.get(self.scp.uri, 'Lua.completion.requireSeparator')
+ local fsname = name:gsub('%' .. separator, '/')
+ self.nameMap[fsname] = name
+end
+
+---@return require-manager.visibleResult[]
+function mt:getVisiblePath(path)
+ local uri = furi.encode(path)
+ if not self.scp:isChildUri(uri)
+ and not self.scp:isLinkedUri(uri) then
return {}
end
- local separator = config.get(suri, 'Lua.completion.requireSeparator')
- local fspath = path:gsub('%' .. separator, '/')
- tracy.ZoneBeginN('findUrisByRequirePath')
- local results = {}
- local searchers = {}
-
- ---@type collector
- local clt = scope.getScope(suri):get('requireName')
- if clt then
- for _, uri in clt:each(suri, fspath) do
- if uri ~= suri then
- local infos = m.getVisiblePath(suri, furi.decode(uri))
- for _, info in ipairs(infos) do
- local fsexpect = info.expect:gsub('%' .. separator, '/')
- if fsexpect == fspath then
- results[#results+1] = uri
- searchers[uri] = info.searcher
+ path = workspace.normalize(path)
+ local result = self.visibleCache[path]
+ if not result then
+ result = self:getRequireResultByPath(path)
+ self.visibleCache[path] = result
+ end
+ return result
+end
+
+--- 查找符合指定require name的所有uri
+---@param suri uri
+---@param name string
+---@return uri[]
+---@return table<uri, string>?
+function mt:findUrisByRequireName(suri, name)
+ if type(name) ~= 'string' then
+ return {}
+ end
+ local searchers = config.get(self.scp.uri, 'Lua.runtime.path')
+ local strict = config.get(self.scp.uri, 'Lua.runtime.pathStrict')
+ local separator = config.get(self.scp.uri, 'Lua.completion.requireSeparator')
+ local path = name:gsub('%' .. separator, '/')
+ local results = {}
+ local searcherMap = {}
+
+ for _, searcher in ipairs(searchers) do
+ local fspath = searcher:gsub('%?', path)
+ if self.scp.uri then
+ local fullPath = workspace.getAbsolutePath(self.scp.uri, fspath)
+ local fullUri = furi.encode(fullPath)
+ if files.exists(fullUri)
+ and fullUri ~= suri then
+ results[#results+1] = fullUri
+ searcherMap[fullUri] = searcher
+ end
+ end
+ if not strict then
+ local tail = '/' .. furi.encode(fspath):gsub('^file:[/]*', '')
+ for uri in files.eachFile(self.scp.uri) do
+ if not searcherMap[uri]
+ and suri ~= uri
+ and util.stringEndWith(uri, tail) then
+ results[#results+1] = uri
+ local parentUri = files.getLibraryUri(self.scp.uri, uri) or self.scp.uri
+ if parentUri == nil or parentUri == '' then
+ parentUri = furi.encode ''
end
+ local relative = uri:sub(#parentUri + 1):sub(1, - #tail)
+ searcherMap[uri] = workspace.normalize(relative .. searcher)
end
end
end
@@ -132,73 +174,42 @@ function m.findUrisByRequirePath(suri, path)
for uri in files.eachDll() do
local opens = files.getDllOpens(uri) or {}
for _, open in ipairs(opens) do
- if open == fspath then
+ if open == path then
results[#results+1] = uri
end
end
end
- tracy.ZoneEnd()
- return results, searchers
-end
-
-local function createVisiblePath(uri)
- for _, scp in ipairs(workspace.folders) do
- m.getVisiblePath(scp.uri, furi.decode(uri))
- end
- m.getVisiblePath(nil, furi.decode(uri))
-end
-
-local function removeVisiblePath(uri)
- local path = furi.decode(uri)
- path = workspace.normalize(path)
- if not path then
- return
- end
- for _, scp in ipairs(workspace.folders) do
- if scp:get('visiblePath') then
- scp:get('visiblePath')[path] = nil
- end
- ---@type collector
- local clt = scp:get('requireName')
- if clt then
- clt:dropUri(uri)
- end
- end
- if scope.fallback:get('visiblePath') then
- scope.fallback:get('visiblePath')[path] = nil
- end
- ---@type collector
- local clt = scope.fallback:get('requireName')
- if clt then
- clt:dropUri(uri)
- end
+ return results, searcherMap
end
-function m.flush(suri)
- local scp = scope.getScope(suri)
- scp:set('visiblePath', {})
- ---@type collector
- local clt = scp:get('requireName')
- if clt then
- clt:dropAll()
- end
- for uri in files.eachFile(suri) do
- m.getVisiblePath(scp.uri, furi.decode(uri))
- end
+---@param uri uri
+---@param path string
+---@return require-manager.visibleResult[]
+function m.getVisiblePath(uri, path)
+ local scp = scope.getScope(uri)
+ ---@type require-manager
+ local mgr = scp:get 'requireManager'
+ or scp:set('requireManager', createRequireManager(scp))
+ return mgr:getVisiblePath(path)
end
-for _, scp in ipairs(scope.folders) do
- m.flush(scp.uri)
+---@param uri uri
+---@param name string
+function m.findUrisByRequireName(uri, name)
+ local scp = scope.getScope(uri)
+ ---@type require-manager
+ local mgr = scp:get 'requireManager'
+ or scp:set('requireManager', createRequireManager(scp))
+ return mgr:findUrisByRequireName(uri, name)
end
-m.flush(nil)
files.watch(function (ev, uri)
- if ev == 'create' then
- createVisiblePath(uri)
- end
- if ev == 'remove' then
- removeVisiblePath(uri)
+ if ev == 'create' or ev == 'delete' then
+ for _, scp in ipairs(workspace.folders) do
+ scp:set('requireManager', nil)
+ end
+ scope.fallback:set('requireManager', nil)
end
end)
@@ -206,7 +217,8 @@ config.watch(function (uri, key, value, oldValue)
if key == 'Lua.completion.requireSeparator'
or key == 'Lua.runtime.path'
or key == 'Lua.runtime.pathStrict' then
- m.flush(uri)
+ local scp = scope.getScope(uri)
+ scp:set('requireManager', nil)
end
end)
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index b719c3e5..c2a5dfc0 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -394,16 +394,12 @@ function m.normalize(path)
return path
end
----@return string?
+---@param folderUri uri
+---@param path string
+---@return string
function m.getAbsolutePath(folderUri, path)
- if not path or path == '' then
- return nil
- end
path = m.normalize(path)
if fs.path(path):is_relative() then
- if not folderUri then
- return nil
- end
local folderPath = furi.decode(folderUri)
path = m.normalize(folderPath .. '/' .. path)
end