diff options
Diffstat (limited to 'script/workspace/workspace.lua')
-rw-r--r-- | script/workspace/workspace.lua | 452 |
1 files changed, 186 insertions, 266 deletions
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua index ea2c4737..12971ae5 100644 --- a/script/workspace/workspace.lua +++ b/script/workspace/workspace.lua @@ -6,44 +6,34 @@ local config = require 'config' local glob = require 'glob' local platform = require 'bee.platform' local await = require 'await' -local proto = require 'proto.proto' -local lang = require 'language' local library = require 'library' -local progress = require 'progress' -local define = require "proto.define" local client = require 'client' local plugin = require 'plugin' local util = require 'utility' local fw = require 'filewatch' +local scope = require 'workspace.scope' +local loading = require 'workspace.loading' +---@class workspace local m = {} m.type = 'workspace' -m.nativeVersion = -1 -m.libraryVersion = -1 -m.nativeMatcher = nil -m.fileLoaded = 0 -m.fileFound = 0 -m.waitingReady = {} -m.requireCache = {} -m.cache = {} -m.watchers = {} -m.matchOption = {} ---- 初始化工作区 -function m.initPath(uri) - log.info('Workspace inited: ', uri) - if not uri then - return - end - m.uri = uri - m.path = m.normalize(furi.decode(uri)) - plugin.workspace = m.path +function m.initRoot(uri) + m.rootUri = uri + log.info('Workspace init root: ', uri) + local logPath = fs.path(LOGPATH) / (uri:gsub('[/:]+', '_') .. '.log') client.logMessage('Log', 'Log path: ' .. furi.encode(logPath:string())) log.info('Log path: ', logPath) log.init(ROOT, logPath) +end - fw.watch(m.path) +--- 初始化工作区 +function m.create(uri) + log.info('Workspace create: ', uri) + local path = m.normalize(furi.decode(uri)) + fw.watch(path) + scope.createFolder(uri) end local globInteferFace = { @@ -75,25 +65,23 @@ local globInteferFace = { --- 创建排除文件匹配器 ---@async -function m.getNativeMatcher() - if not m.path then - return nil - end - if m.nativeMatcher then - return m.nativeMatcher +---@param scp scope +function m.getNativeMatcher(scp) + if scp:get 'nativeMatcher' then + return scp:get 'nativeMatcher' end local pattern = {} - -- config.get 'files.exclude' - for path, ignore in pairs(config.get 'files.exclude') do + -- config.get(nil, 'files.exclude' + for path, ignore in pairs(config.get(scp.uri, 'files.exclude')) do if ignore then log.info('Ignore by exclude:', path) pattern[#pattern+1] = path end end - -- config.get 'workspace.useGitIgnore' - if config.get 'Lua.workspace.useGitIgnore' then - local buf = pub.awaitTask('loadFile', furi.encode(m.path .. '/.gitignore')) + -- config.get(nil, 'workspace.useGitIgnore' + if config.get(scp.uri, 'Lua.workspace.useGitIgnore') then + local buf = pub.awaitTask('loadFile', m.rootUri .. '/.gitignore') if buf then for line in buf:gmatch '[^\r\n]+' do if line:sub(1, 1) ~= '#' then @@ -102,7 +90,7 @@ function m.getNativeMatcher() end end end - buf = pub.awaitTask('loadFile', furi.encode(m.path .. '/.git/info/exclude')) + buf = pub.awaitTask('loadFile', m.rootUri .. '/.git/info/exclude') if buf then for line in buf:gmatch '[^\r\n]+' do if line:sub(1, 1) ~= '#' then @@ -112,9 +100,9 @@ function m.getNativeMatcher() end end end - -- config.get 'workspace.ignoreSubmodules' - if config.get 'Lua.workspace.ignoreSubmodules' then - local buf = pub.awaitTask('loadFile', furi.encode(m.path .. '/.gitmodules')) + -- config.get(nil, 'workspace.ignoreSubmodules' + if config.get(scp.uri, 'Lua.workspace.ignoreSubmodules') then + local buf = pub.awaitTask('loadFile', m.rootUri .. '/.gitmodules') if buf then for path in buf:gmatch('path = ([^\r\n]+)') do log.info('Ignore by .gitmodules:', path) @@ -122,66 +110,75 @@ function m.getNativeMatcher() end end end - -- config.get 'workspace.library' - for path in pairs(config.get 'Lua.workspace.library') do + -- config.get(nil, 'workspace.library' + for path in pairs(config.get(scp.uri, 'Lua.workspace.library')) do path = m.getAbsolutePath(path) if path then log.info('Ignore by library:', path) pattern[#pattern+1] = path end end - -- config.get 'workspace.ignoreDir' - for _, path in ipairs(config.get 'Lua.workspace.ignoreDir') do + -- config.get(nil, 'workspace.ignoreDir' + for _, path in ipairs(config.get(scp.uri, 'Lua.workspace.ignoreDir')) do log.info('Ignore directory:', path) pattern[#pattern+1] = path end - m.nativeMatcher = glob.gitignore(pattern, m.matchOption, globInteferFace) - m.nativeMatcher:setOption('root', m.path) + local matcher = glob.gitignore(pattern, { + root = furi.decode(scp.uri), + ignoreCase = platform.OS == 'Windows', + }, globInteferFace) - m.nativeVersion = config.get 'version' - return m.nativeMatcher + scp:set('nativeMatcher', matcher) + return matcher end --- 创建代码库筛选器 -function m.getLibraryMatchers() - if m.libraryMatchers then - return m.libraryMatchers +---@param scp scope +function m.getLibraryMatchers(scp) + if scp:get 'nativeMatcher' then + return scp:get 'nativeMatcher' end local librarys = {} - for path in pairs(config.get 'Lua.workspace.library') do + for path in pairs(config.get(scp.uri, 'Lua.workspace.library')) do path = m.getAbsolutePath(path) if path then librarys[m.normalize(path)] = true end end + -- TODO if library.metaPath then librarys[m.normalize(library.metaPath)] = true end - m.libraryMatchers = {} + + local matchers = {} for path in pairs(librarys) do if fs.exists(fs.path(path)) then local nPath = fs.absolute(fs.path(path)):string() - local matcher = glob.gitignore(true, m.matchOption, globInteferFace) - matcher:setOption('root', path) - log.debug('getLibraryMatchers', path, nPath) - m.libraryMatchers[#m.libraryMatchers+1] = { - path = nPath, + local matcher = glob.gitignore(true, { + root = path, + ignoreCase = platform.OS == 'Windows', + }, globInteferFace) + matchers[#matchers+1] = { + uri = furi.encode(nPath), matcher = matcher } end end - m.libraryVersion = config.get 'version' - return m.libraryMatchers + scp:set('nativeMatcher', matchers) + + return matchers end --- 文件是否被忽略 ---@async +---@param uri uri function m.isIgnored(uri) - local path = m.getRelativePath(uri) - local ignore = m.getNativeMatcher() + local scp = m.getScope(uri) + local path = m.getRelativePath(uri) + local ignore = m.getNativeMatcher(scp) if not ignore then return false end @@ -200,187 +197,63 @@ function m.isValidLuaUri(uri) return true end -local function loadFileFactory(root, progressData, isLibrary) - return function (path) ---@async - local uri = furi.encode(path) - if files.isLua(uri) then - if not isLibrary and progressData.preload >= config.get 'Lua.workspace.maxPreload' then - if not m.hasHitMaxPreload then - m.hasHitMaxPreload = true - proto.request('window/showMessageRequest', { - type = define.MessageType.Info, - message = lang.script('MWS_MAX_PRELOAD', config.get 'Lua.workspace.maxPreload'), - actions = { - { - title = lang.script.WINDOW_INCREASE_UPPER_LIMIT, - }, - { - title = lang.script.WINDOW_CLOSE, - } - } - }, function (item) - if not item then - return - end - if item.title == lang.script.WINDOW_INCREASE_UPPER_LIMIT then - client.setConfig { - { - key = 'Lua.workspace.maxPreload', - action = 'set', - value = config.get 'Lua.workspace.maxPreload' - + math.max(1000, config.get 'Lua.workspace.maxPreload'), - } - } - end - end) - end - return - end - if not isLibrary then - progressData.preload = progressData.preload + 1 - end - progressData.max = progressData.max + 1 - progressData:update() - pub.task('loadFile', uri, function (text) - local loader = function () - if text then - log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #text / 1024.0)) - if isLibrary then - log.info('++++As library of:', root) - files.setLibraryPath(uri, root) - end - files.setText(uri, text, false) - else - files.remove(uri) - end - progressData.read = progressData.read + 1 - progressData:update() - end - if progressData.loaders then - progressData.loaders[#progressData.loaders+1] = loader - else - loader() - end - end) - end - if files.isDll(uri) then - progressData.max = progressData.max + 1 - progressData:update() - pub.task('loadFile', uri, function (content) - if content then - log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #content / 1024.0)) - if isLibrary then - log.info('++++As library of:', root) - end - files.saveDll(uri, content) - end - progressData.read = progressData.read + 1 - progressData:update() - end) - end - await.delay() - end -end - ---@async function m.awaitLoadFile(uri) - local progressBar <close> = progress.create(lang.script.WORKSPACE_LOADING) - local progressData = { - max = 0, - read = 0, - preload = 0, - update = function (self) - progressBar:setMessage(('%d/%d'):format(self.read, self.max)) - progressBar:setPercentage(self.read / self.max * 100) - end - } - local nativeLoader = loadFileFactory(m.path, progressData) - local native = m.getNativeMatcher() - if native then - log.info('Scan files at:', m.path) - native:scan(furi.decode(uri), nativeLoader) - end + local scp = m.getScope(uri) + local ld <close> = loading.create(scp) + local native = m.getNativeMatcher(scp) + log.info('Scan files at:', uri) + ---@async + native:scan(furi.decode(uri), function (path) + ld:scanFile(furi.encode(path)) + end) + ld:loadAll() end --- 预读工作区内所有文件 ---@async -function m.awaitPreload() - local diagnostic = require 'provider.diagnostic' +---@param scp scope +function m.awaitPreload(scp) await.close 'preload' await.setID 'preload' await.sleep(0.1) - diagnostic.pause() - m.libraryMatchers = nil - m.nativeMatcher = nil - m.fileLoaded = 0 - m.fileFound = 0 - m.cache = {} - for i, watchers in ipairs(m.watchers) do - watchers() - m.watchers[i] = nil - end - local progressBar <close> = progress.create(lang.script.WORKSPACE_LOADING) - local progressData = { - max = 0, - read = 0, - preload = 0, - loaders = {}, - update = function (self) - progressBar:setMessage(('%d/%d'):format(self.read, self.max)) - progressBar:setPercentage(self.read / self.max * 100) - m.fileLoaded = self.read - m.fileFound = self.max - end - } - log.info('Preload start.') - local nativeLoader = loadFileFactory(m.path, progressData) - local native = m.getNativeMatcher() - local librarys = m.getLibraryMatchers() - if native then - log.info('Scan files at:', m.path) - native:scan(m.path, nativeLoader) - end - for _, library in ipairs(librarys) do - local libraryLoader = loadFileFactory(library.path, progressData, true) - log.info('Scan library at:', library.path) - library.matcher:scan(library.path, libraryLoader) - m.watchers[#m.watchers+1] = fw.watch(library.path) - end - - local isLoadingFiles = false - local function loadSomeFiles() - if isLoadingFiles then - return + + local watchers = scp:get 'watchers' + if watchers then + for _, dispose in ipairs(watchers) do + dispose() end - await.call(function () ---@async - isLoadingFiles = true - while true do - local loader = table.remove(progressData.loaders) - if not loader then - break - end - loader() - await.delay() - end - isLoadingFiles = false + end + watchers = {} + scp:set('watchers', watchers) + + local ld <close> = loading.create(scp) + + log.info('Preload start:', scp.uri) + + local native = m.getNativeMatcher(scp) + local librarys = m.getLibraryMatchers(scp) + + do + log.info('Scan files at:', m.rootUri) + ---@async + native:scan(furi.decode(scp.uri), function (path) + ld:scanFile(furi.encode(path)) end) end - log.info(('Found %d files.'):format(progressData.max)) - while true do - loadSomeFiles() - log.info(('Loaded %d/%d files'):format(progressData.read, progressData.max)) - progressData:update() - if progressData.read >= progressData.max then - break - end - await.sleep(0.1) + for _, libMatcher in ipairs(librarys) do + log.info('Scan library at:', libMatcher.uri) + ---@async + libMatcher.matcher:scan(furi.decode(libMatcher.uri), function (path) + ld:scanFile(furi.encode(path), libMatcher.uri) + end) + watchers[#watchers+1] = fw.watch(furi.decode(libMatcher.uri)) end - progressBar:remove() + log.info(('Found %d files.'):format(ld.max)) + ld:loadAll() log.info('Preload finish.') - - diagnostic.start() end --- 查找符合指定file path的所有uri @@ -396,7 +269,7 @@ function m.findUrisByFilePath(path) return resultCache[path].results, resultCache[path].posts end tracy.ZoneBeginN('findUrisByFilePath #1') - local strict = config.get 'Lua.runtime.pathStrict' + local strict = config.get(nil, 'Lua.runtime.pathStrict') local results = {} local posts = {} for uri in files.eachFile() do @@ -455,7 +328,7 @@ function m.findUrisByRequirePath(path) local input = path:gsub('%.', '/') :gsub('%%', '%%%%') - for _, luapath in ipairs(config.get 'Lua.runtime.path') do + for _, luapath in ipairs(config.get(nil, 'Lua.runtime.path')) do local part = m.normalize(luapath:gsub('%?', input)) local uris, posts = m.findUrisByFilePath(part) for _, uri in ipairs(uris) do @@ -499,16 +372,22 @@ function m.normalize(path) end ---@return string -function m.getAbsolutePath(path) +function m.getAbsolutePath(folderUriOrPath, path) if not path or path == '' then return nil end path = m.normalize(path) if fs.path(path):is_relative() then - if not m.path then + if not folderUriOrPath then return nil end - path = m.normalize(m.path .. '/' .. path) + local folderPath + if folderUriOrPath:sub(1, 5) == 'file:' then + folderPath = furi.decode(folderUriOrPath) + else + folderPath = folderUriOrPath + end + path = m.normalize(folderPath .. '/' .. path) end return path end @@ -516,17 +395,20 @@ end ---@param uriOrPath uri|string ---@return string function m.getRelativePath(uriOrPath) - local path + local path, uri if uriOrPath:sub(1, 5) == 'file:' then path = furi.decode(uriOrPath) + uri = uriOrPath else path = uriOrPath + uri = furi.encode(uriOrPath) end - if not m.path then + local scp = m.getScope(uri) + if not scp.uri then local relative = m.normalize(path) return relative:gsub('^[/\\]+', '') end - local _, pos = m.normalize(path):find(m.path, 1, true) + local _, pos = m.normalize(path):find(furi.decode(scp.uri), 1, true) if pos then return m.normalize(path:sub(pos + 1)):gsub('^[/\\]+', '') else @@ -534,14 +416,6 @@ function m.getRelativePath(uriOrPath) end end -function m.isWorkspaceUri(uri) - if not m.uri then - return false - end - local ruri = m.uri - return uri:sub(1, #ruri) == ruri -end - --- 获取工作区等级的缓存 function m.getCache(name) if not m.cache[name] then @@ -554,14 +428,18 @@ function m.flushCache() m.cache = {} end -function m.reload() +---@param scp scope +function m.reload(scp) if not m.inited then return end if TEST then return end - await.call(m.awaitReload) + ---@async + await.call(function () + m.awaitReload(scp) + end) end function m.init() @@ -569,39 +447,80 @@ function m.init() return end m.inited = true - m.reload() + if m.rootUri then + for _, folder in ipairs(scope.folders) do + m.reload(folder) + end + else + m.reload(scope.fallback) + end +end + +---@param scp scope +function m.removeFiles(scp) + local cachedUris = scp:get 'cachedUris' + if not cachedUris then + return + end + scp:set('cachedUris', nil) + for _, uri in ipairs(cachedUris) do + files.delRef(uri) + end +end + +---@param scp scope +function m.resetFiles(scp) + local cachedUris = scp:get 'cachedUris' + if not cachedUris then + return + end + for _, uri in ipairs(cachedUris) do + files.resetText(uri) + end end ---@async -function m.awaitReload() - m.ready = false - m.hasHitMaxPreload = false - files.flushAllLibrary() - files.removeAllClosed() - files.flushCache() - plugin.init() - m.awaitPreload() - m.ready = true - local waiting = m.waitingReady - m.waitingReady = {} - for _, waker in ipairs(waiting) do - waker() +---@param scp scope +function m.awaitReload(scp) + m.removeFiles(scp) + plugin.init(scp) + m.awaitPreload(scp) + scp:set('ready', true) + local waiting = scp:get('waitingReady') + if waiting then + scp:set('waitingReady', nil) + for _, waker in ipairs(waiting) do + waker() + end end end +---@param uri uri +---@return scope +function m.getScope(uri) + return scope.getFolder(uri) + or scope.getLinkedScope(uri) + or scope.fallback +end + ---等待工作目录加载完成 ---@async -function m.awaitReady() - if m.isReady() then +function m.awaitReady(uri) + if m.isReady(uri) then return end + local scp = m.getScope(uri) + local waitingReady = scp:get('waitingReady') + or scp:set('waitingReady', {}) await.wait(function (waker) - m.waitingReady[#m.waitingReady+1] = waker + waitingReady[#waitingReady+1] = waker end) end -function m.isReady() - return m.ready == true +---@param uri uri +function m.isReady(uri) + local scp = m.getScope(uri) + return scp:get('ready') == true end function m.getLoadProcess() @@ -627,12 +546,13 @@ config.watch(function (key, value, oldValue) end) fw.event(function (changes) ---@async + -- TODO m.awaitReady() for _, change in ipairs(changes) do local path = change.path local uri = furi.encode(path) - if not m.isWorkspaceUri(uri) - and not files.isLibrary(uri) then + local scp = m.getScope(uri) + if scp.type == 'fallback' then goto CONTINUE end if change.type == 'create' then @@ -657,7 +577,7 @@ fw.event(function (changes) ---@async -- 排除类文件发生更改需要重新扫描 if filename == '.gitignore' or filename == '.gitmodules' then - m.reload() + m.reload(scp) break end end |