diff options
Diffstat (limited to 'script/workspace/require-path.lua')
-rw-r--r-- | script/workspace/require-path.lua | 288 |
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) |