summaryrefslogtreecommitdiff
path: root/script/workspace/require-path.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/workspace/require-path.lua')
-rw-r--r--script/workspace/require-path.lua288
1 files changed, 150 insertions, 138 deletions
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)