dotfiles from arch
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
local workspace = require 'workspace.workspace'
|
||||
|
||||
return workspace
|
||||
@@ -0,0 +1,228 @@
|
||||
local progress = require 'progress'
|
||||
local lang = require 'language'
|
||||
local await = require 'await'
|
||||
local files = require 'files'
|
||||
local config = require 'config.config'
|
||||
local client = require 'client'
|
||||
local furi = require 'file-uri'
|
||||
local pub = require 'pub'
|
||||
|
||||
---@class workspace.loading
|
||||
---@field scp scope
|
||||
---@field _bar progress
|
||||
---@field _stash function[]
|
||||
---@field _refs uri[]
|
||||
---@field _cache table<uri, boolean>
|
||||
---@field _sets function[]
|
||||
---@field _removed boolean
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
|
||||
mt._loadLock = false
|
||||
mt.read = 0
|
||||
mt.max = 0
|
||||
mt.preload = 0
|
||||
|
||||
function mt:__close()
|
||||
self:remove()
|
||||
end
|
||||
|
||||
function mt:update()
|
||||
self._bar:setMessage(('%d/%d'):format(self.read, self.max))
|
||||
self._bar:setPercentage(self.read / self.max * 100.0)
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
function mt:checkMaxPreload(uri)
|
||||
local max = config.get(uri, 'Lua.workspace.maxPreload')
|
||||
if self.preload <= max then
|
||||
return true
|
||||
end
|
||||
if self.scp:get 'hasHintedMaxPreload' then
|
||||
return false
|
||||
end
|
||||
self.scp:set('hasHintedMaxPreload', true)
|
||||
client.requestMessage('Info'
|
||||
, lang.script('MWS_MAX_PRELOAD', max)
|
||||
, {
|
||||
lang.script('WINDOW_INCREASE_UPPER_LIMIT'),
|
||||
}
|
||||
, function (_, index)
|
||||
if index == 1 then
|
||||
client.setConfig {
|
||||
{
|
||||
key = 'Lua.workspace.maxPreload',
|
||||
uri = self.scp.uri,
|
||||
action = 'set',
|
||||
value = max + math.max(1000, max),
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
)
|
||||
return false
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@param libraryUri? uri
|
||||
---@async
|
||||
function mt:loadFile(uri, libraryUri)
|
||||
if files.isLua(uri) then
|
||||
if not libraryUri then
|
||||
self.preload = self.preload + 1
|
||||
if not self:checkMaxPreload(uri) then
|
||||
return
|
||||
end
|
||||
end
|
||||
self.max = self.max + 1
|
||||
self:update()
|
||||
---@async
|
||||
self._stash[#self._stash+1] = function ()
|
||||
if files.getFile(uri) then
|
||||
self.read = self.read + 1
|
||||
self:update()
|
||||
if not self._cache[uri] then
|
||||
files.addRef(uri)
|
||||
end
|
||||
self._cache[uri] = true
|
||||
log.info(('Skip loaded file: %s'):format(uri))
|
||||
else
|
||||
local content = pub.awaitTask('loadFile', furi.decode(uri))
|
||||
self.read = self.read + 1
|
||||
self:update()
|
||||
if not content then
|
||||
return
|
||||
end
|
||||
if files.getFile(uri) then
|
||||
log.info(('Skip loaded file: %s'):format(uri))
|
||||
return
|
||||
end
|
||||
log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #content / 1024.0))
|
||||
--await.wait(function (waker)
|
||||
-- self._sets[#self._sets+1] = waker
|
||||
--end)
|
||||
files.setText(uri, content, false)
|
||||
files.compileState(uri)
|
||||
if not self._cache[uri] then
|
||||
files.addRef(uri)
|
||||
end
|
||||
self._cache[uri] = true
|
||||
end
|
||||
if libraryUri then
|
||||
log.info('++++As library of:', libraryUri)
|
||||
end
|
||||
end
|
||||
elseif files.isDll(uri) then
|
||||
self.max = self.max + 1
|
||||
self:update()
|
||||
---@async
|
||||
self._stash[#self._stash+1] = function ()
|
||||
if files.getFile(uri) then
|
||||
self.read = self.read + 1
|
||||
self:update()
|
||||
if not self._cache[uri] then
|
||||
files.addRef(uri)
|
||||
end
|
||||
self._cache[uri] = true
|
||||
log.info(('Skip loaded file: %s'):format(uri))
|
||||
else
|
||||
local content = pub.awaitTask('loadFile', furi.decode(uri))
|
||||
self.read = self.read + 1
|
||||
self:update()
|
||||
if not content then
|
||||
return
|
||||
end
|
||||
if files.getFile(uri) then
|
||||
log.info(('Skip loaded file: %s'):format(uri))
|
||||
return
|
||||
end
|
||||
log.info(('Preload dll at: %s , size = %.3f KB'):format(uri, #content / 1024.0))
|
||||
--await.wait(function (waker)
|
||||
-- self._sets[#self._sets+1] = waker
|
||||
--end)
|
||||
files.saveDll(uri, content)
|
||||
if not self._cache[uri] then
|
||||
files.addRef(uri)
|
||||
end
|
||||
self._cache[uri] = true
|
||||
end
|
||||
if libraryUri then
|
||||
log.info('++++As library of:', libraryUri)
|
||||
end
|
||||
end
|
||||
end
|
||||
await.delay()
|
||||
end
|
||||
|
||||
---@async
|
||||
function mt:loadAll(fileName)
|
||||
local startClock = os.clock()
|
||||
log.info('Load files from disk:', fileName)
|
||||
while self.read < self.max do
|
||||
self:update()
|
||||
local loader = table.remove(self._stash)
|
||||
if loader then
|
||||
await.call(loader)
|
||||
await.delay()
|
||||
else
|
||||
await.sleep(0.1)
|
||||
end
|
||||
end
|
||||
local loadedClock = os.clock()
|
||||
log.info(('Loaded files takes [%.3f] sec: %s'):format(loadedClock - startClock, fileName))
|
||||
self._bar:remove()
|
||||
self._bar = progress.create(self.scp.uri, lang.script('WORKSPACE_LOADING', self.scp.uri), 0)
|
||||
for i, set in ipairs(self._sets) do
|
||||
await.delay()
|
||||
set()
|
||||
self.read = i
|
||||
self:update()
|
||||
end
|
||||
log.info(('Compile files takes [%.3f] sec: %s'):format(os.clock() - loadedClock, fileName))
|
||||
log.info('Loaded finish:', fileName)
|
||||
end
|
||||
|
||||
function mt:remove()
|
||||
if self._removed then
|
||||
return
|
||||
end
|
||||
self._removed = true
|
||||
self._bar:remove()
|
||||
end
|
||||
|
||||
function mt:isRemoved()
|
||||
return self._removed == true
|
||||
end
|
||||
|
||||
---@class workspace.loading.manager
|
||||
local m = {}
|
||||
|
||||
---@type table<workspace.loading, boolean>
|
||||
m._loadings = setmetatable({}, { __mode = 'k' })
|
||||
|
||||
---@return workspace.loading
|
||||
function m.create(scp)
|
||||
local loading = setmetatable({
|
||||
scp = scp,
|
||||
_bar = progress.create(scp.uri, lang.script('WORKSPACE_LOADING', scp.uri), 0.5),
|
||||
_stash = {},
|
||||
_cache = {},
|
||||
_sets = {},
|
||||
}, mt)
|
||||
m._loadings[loading] = true
|
||||
return loading
|
||||
end
|
||||
|
||||
function m.count()
|
||||
local num = 0
|
||||
for ld in pairs(m._loadings) do
|
||||
if ld:isRemoved() then
|
||||
m._loadings[ld] = nil
|
||||
else
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,315 @@
|
||||
local platform = require 'bee.platform'
|
||||
local files = require 'files'
|
||||
local furi = require 'file-uri'
|
||||
local workspace = require "workspace"
|
||||
local config = require 'config'
|
||||
local scope = require 'workspace.scope'
|
||||
local util = require 'utility'
|
||||
local plugin = require 'plugin'
|
||||
|
||||
---@class require-path
|
||||
local m = {}
|
||||
|
||||
---@class require-manager
|
||||
---@field scp scope
|
||||
---@field nameMap table<string, string>
|
||||
---@field visibleCache table<string, require-manager.visibleResult[]>
|
||||
---@field requireCache table<string, table>
|
||||
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 = {},
|
||||
requireCache = {},
|
||||
}, mt)
|
||||
end
|
||||
|
||||
--- `aaa/bbb/ccc.lua` 与 `?.lua` 将返回 `aaa.bbb.cccc`
|
||||
---@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)
|
||||
local stemSearcher = searcher
|
||||
: gsub('%.[^%.]+$', '')
|
||||
: gsub('[/\\]+', separator)
|
||||
local start = stemSearcher:match '()%?' or 1
|
||||
if stemPath:sub(1, start - 1) ~= stemSearcher:sub(1, start - 1) then
|
||||
return nil
|
||||
end
|
||||
for pos = #stemPath, start, -1 do
|
||||
local word = stemPath:sub(start, pos)
|
||||
local newSearcher = stemSearcher:gsub('%?', (word:gsub('%%', '%%%%')))
|
||||
if newSearcher == stemPath then
|
||||
return word
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param path string
|
||||
---@return require-manager.visibleResult[]
|
||||
function mt:getRequireResultByPath(path)
|
||||
local vm = require 'vm'
|
||||
local uri = furi.encode(path)
|
||||
local result = {}
|
||||
if vm.isMetaFile(uri) then
|
||||
local metaName = vm.getMetaName(uri)
|
||||
if metaName then
|
||||
if vm.isMetaFileRequireable(uri) then
|
||||
result[#result+1] = {
|
||||
name = metaName,
|
||||
searcher = '[[meta]]',
|
||||
}
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
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)
|
||||
for _, searcher in ipairs(searchers) do
|
||||
local isAbsolute = searcher:match '^[/\\]'
|
||||
or searcher:match '^%a+%:'
|
||||
searcher = files.normalize(searcher)
|
||||
if searcher:sub(1, 1) == '.' then
|
||||
strict = true
|
||||
end
|
||||
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
|
||||
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
|
||||
|
||||
---@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
|
||||
path = files.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 name string
|
||||
---@return uri[]
|
||||
---@return table<uri, string>?
|
||||
function mt:searchUrisByRequireName(name)
|
||||
local vm = require 'vm'
|
||||
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 = {}
|
||||
local excludes = {}
|
||||
|
||||
local pluginSuccess, pluginResults = plugin.dispatch('ResolveRequire', self.scp.uri, name)
|
||||
if pluginSuccess and pluginResults ~= nil then
|
||||
return pluginResults
|
||||
end
|
||||
|
||||
for uri in files.eachFile(self.scp.uri) do
|
||||
if vm.isMetaFileRequireable(uri) then
|
||||
local metaName = vm.getMetaName(uri)
|
||||
if metaName == name then
|
||||
results[#results+1] = uri
|
||||
return results
|
||||
end
|
||||
if metaName then
|
||||
excludes[uri] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, searcher in ipairs(searchers) do
|
||||
local fspath = searcher:gsub('%?', (path:gsub('%%', '%%%%')))
|
||||
fspath = files.normalize(fspath)
|
||||
local tail = '/' .. furi.encode(fspath):gsub('^file:[/]*', '')
|
||||
for uri in files.eachFile(self.scp.uri) do
|
||||
if not searcherMap[uri]
|
||||
and not excludes[uri]
|
||||
and util.stringEndWith(uri, tail)
|
||||
and (not vm.isMetaFile(uri) or vm.isMetaFileRequireable(uri)) then
|
||||
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)
|
||||
if not strict
|
||||
or relative == '/'
|
||||
or relative == '' then
|
||||
results[#results+1] = uri
|
||||
searcherMap[uri] = files.normalize(relative .. searcher)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for uri in files.eachDll() do
|
||||
local opens = files.getDllOpens(uri) or {}
|
||||
for _, open in ipairs(opens) do
|
||||
if open == path then
|
||||
results[#results+1] = uri
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return results, searcherMap
|
||||
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 cache = self.requireCache[name]
|
||||
if not cache then
|
||||
local results, searcherMap = self:searchUrisByRequireName(name)
|
||||
cache = {
|
||||
results = results,
|
||||
searcherMap = searcherMap,
|
||||
}
|
||||
self.requireCache[name] = cache
|
||||
end
|
||||
local results = {}
|
||||
local searcherMap = {}
|
||||
for _, uri in ipairs(cache.results) do
|
||||
if uri ~= suri then
|
||||
results[#results+1] = uri
|
||||
searcherMap[uri] = cache.searcherMap and cache.searcherMap[uri]
|
||||
end
|
||||
end
|
||||
return results, searcherMap
|
||||
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
|
||||
|
||||
---@param uri uri
|
||||
---@param name string
|
||||
---@return uri[]
|
||||
---@return table<uri, 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
|
||||
|
||||
---@param suri uri
|
||||
---@param uri uri
|
||||
---@param name string
|
||||
---@return boolean
|
||||
function m.isMatchedUri(suri, uri, name)
|
||||
local searchers = config.get(suri, 'Lua.runtime.path')
|
||||
local strict = config.get(suri, 'Lua.runtime.pathStrict')
|
||||
local separator = config.get(suri, 'Lua.completion.requireSeparator')
|
||||
local path = name:gsub('%' .. separator, '/')
|
||||
|
||||
for _, searcher in ipairs(searchers) do
|
||||
local fspath = searcher:gsub('%?', (path:gsub('%%', '%%%%')))
|
||||
fspath = files.normalize(fspath)
|
||||
local tail = '/' .. furi.encode(fspath):gsub('^file:[/]*', '')
|
||||
if util.stringEndWith(uri, tail) then
|
||||
local parentUri = files.getLibraryUri(suri, uri) or uri
|
||||
if parentUri == nil or parentUri == '' then
|
||||
parentUri = furi.encode '/'
|
||||
end
|
||||
local relative = uri:sub(#parentUri + 1):sub(1, - #tail)
|
||||
if not strict
|
||||
or relative == '/'
|
||||
or relative == '' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
files.watch(function (_ev, _uri)
|
||||
for _, scp in ipairs(workspace.folders) do
|
||||
scp:set('requireManager', nil)
|
||||
end
|
||||
scope.fallback:set('requireManager', nil)
|
||||
end)
|
||||
|
||||
config.watch(function (uri, key, _value, _oldValue)
|
||||
if key == 'Lua.completion.requireSeparator'
|
||||
or key == 'Lua.runtime.path'
|
||||
or key == 'Lua.runtime.pathStrict' then
|
||||
local scp = scope.getScope(uri)
|
||||
scp:set('requireManager', nil)
|
||||
end
|
||||
end)
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,254 @@
|
||||
local gc = require 'gc'
|
||||
|
||||
---@class scope.manager
|
||||
local m = {}
|
||||
|
||||
---@alias scope.type '"override"'|'"folder"'|'"fallback"'
|
||||
|
||||
---@class scope
|
||||
---@field type scope.type
|
||||
---@field uri? uri
|
||||
---@field folderName? string
|
||||
---@field _links table<uri, boolean>
|
||||
---@field _data table<string, any>
|
||||
---@field _gc gc
|
||||
---@field _removed? true
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
|
||||
function mt:__tostring()
|
||||
if self.uri then
|
||||
return ('{scope|%s|%s}'):format(self.type, self.uri)
|
||||
else
|
||||
return ('{scope|%s}'):format(self.type)
|
||||
end
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
function mt:addLink(uri)
|
||||
self._links[uri] = true
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
function mt:removeLink(uri)
|
||||
self._links[uri] = nil
|
||||
end
|
||||
|
||||
function mt:removeAllLinks()
|
||||
self._links = {}
|
||||
end
|
||||
|
||||
---@return fun(): uri
|
||||
---@return table<uri, true>
|
||||
function mt:eachLink()
|
||||
return next, self._links
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return boolean
|
||||
function mt:isChildUri(uri)
|
||||
if not uri then
|
||||
return false
|
||||
end
|
||||
if not self.uri then
|
||||
return false
|
||||
end
|
||||
if self.uri == uri then
|
||||
return true
|
||||
end
|
||||
if uri:sub(1, #self.uri) ~= self.uri then
|
||||
return false
|
||||
end
|
||||
if uri:sub(#self.uri, #self.uri) == '/'
|
||||
or uri:sub(#self.uri + 1, #self.uri + 1) == '/' then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return boolean
|
||||
function mt:isLinkedUri(uri)
|
||||
if not uri then
|
||||
return false
|
||||
end
|
||||
for linkUri in pairs(self._links) do
|
||||
if uri == linkUri then
|
||||
return true
|
||||
end
|
||||
if uri:sub(1, #linkUri) ~= linkUri then
|
||||
goto CONTINUE
|
||||
end
|
||||
if uri:sub(#linkUri, #linkUri) == '/'
|
||||
or uri:sub(#linkUri + 1, #linkUri + 1) == '/' then
|
||||
return true
|
||||
end
|
||||
::CONTINUE::
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return boolean
|
||||
function mt:isVisible(uri)
|
||||
return self:isChildUri(uri)
|
||||
or self:isLinkedUri(uri)
|
||||
or self == m.getScope(uri)
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return uri?
|
||||
function mt:getLinkedUri(uri)
|
||||
if not uri then
|
||||
return nil
|
||||
end
|
||||
for linkUri in pairs(self._links) do
|
||||
if uri:sub(1, #linkUri) == linkUri then
|
||||
return linkUri
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return uri?
|
||||
function mt:getRootUri(uri)
|
||||
if self:isChildUri(uri) then
|
||||
return self.uri
|
||||
end
|
||||
return self:getLinkedUri(uri)
|
||||
end
|
||||
|
||||
---@param k string
|
||||
---@param v any
|
||||
function mt:set(k, v)
|
||||
self._data[k] = v
|
||||
return v
|
||||
end
|
||||
|
||||
function mt:get(k)
|
||||
return self._data[k]
|
||||
end
|
||||
|
||||
---@return string
|
||||
function mt:getName()
|
||||
return self.uri or ('<' .. self.type .. '>')
|
||||
end
|
||||
|
||||
---@return string?
|
||||
function mt:getFolderName()
|
||||
return self.folderName
|
||||
end
|
||||
|
||||
function mt:gc(obj)
|
||||
self._gc:add(obj)
|
||||
end
|
||||
|
||||
function mt:flushGC()
|
||||
self._gc:remove()
|
||||
if self._removed then
|
||||
return
|
||||
end
|
||||
self._gc = gc()
|
||||
end
|
||||
|
||||
function mt:remove()
|
||||
if self._removed then
|
||||
return
|
||||
end
|
||||
self._removed = true
|
||||
for i, scp in ipairs(m.folders) do
|
||||
if scp == self then
|
||||
table.remove(m.folders, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
self:flushGC()
|
||||
end
|
||||
|
||||
function mt:isRemoved()
|
||||
return self._removed == true
|
||||
end
|
||||
|
||||
---@param scopeType scope.type
|
||||
---@return scope
|
||||
local function createScope(scopeType)
|
||||
local scope = setmetatable({
|
||||
type = scopeType,
|
||||
_links = {},
|
||||
_data = {},
|
||||
_gc = gc(),
|
||||
}, mt)
|
||||
|
||||
return scope
|
||||
end
|
||||
|
||||
function m.reset()
|
||||
---@type scope[]
|
||||
m.folders = {}
|
||||
m.override = createScope 'override'
|
||||
m.fallback = createScope 'fallback'
|
||||
end
|
||||
|
||||
m.reset()
|
||||
|
||||
---@param uri uri
|
||||
---@param folderName? string
|
||||
---@return scope
|
||||
function m.createFolder(uri, folderName)
|
||||
local scope = createScope 'folder'
|
||||
scope.uri = uri
|
||||
scope.folderName = folderName
|
||||
|
||||
local inserted = false
|
||||
for i, otherScope in ipairs(m.folders) do
|
||||
if #uri > #otherScope.uri then
|
||||
table.insert(m.folders, i, scope)
|
||||
inserted = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not inserted then
|
||||
table.insert(m.folders, scope)
|
||||
end
|
||||
|
||||
return scope
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return scope?
|
||||
function m.getFolder(uri)
|
||||
for _, scope in ipairs(m.folders) do
|
||||
if scope:isChildUri(uri) then
|
||||
return scope
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return scope?
|
||||
function m.getLinkedScope(uri)
|
||||
if m.override and m.override:isLinkedUri(uri) then
|
||||
return m.override
|
||||
end
|
||||
for _, scope in ipairs(m.folders) do
|
||||
if scope:isLinkedUri(uri) then
|
||||
return scope
|
||||
end
|
||||
end
|
||||
if m.fallback:isLinkedUri(uri) then
|
||||
return m.fallback
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param uri? uri
|
||||
---@return scope
|
||||
function m.getScope(uri)
|
||||
return uri and (m.getFolder(uri)
|
||||
or m.getLinkedScope(uri))
|
||||
or m.fallback
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,615 @@
|
||||
local pub = require 'pub'
|
||||
local fs = require 'bee.filesystem'
|
||||
local furi = require 'file-uri'
|
||||
local files = require 'files'
|
||||
local config = require 'config'
|
||||
local glob = require 'glob'
|
||||
local platform = require 'bee.platform'
|
||||
local await = require 'await'
|
||||
local client = require 'client'
|
||||
local util = require 'utility'
|
||||
local fw = require 'filewatch'
|
||||
local scope = require 'workspace.scope'
|
||||
local loading = require 'workspace.loading'
|
||||
local inspect = require 'inspect'
|
||||
local lang = require 'language'
|
||||
|
||||
---@class workspace
|
||||
local m = {}
|
||||
m.type = 'workspace'
|
||||
m.watchList = {}
|
||||
|
||||
--- 注册事件
|
||||
---@param callback async fun(ev: string, uri: uri)
|
||||
function m.watch(callback)
|
||||
m.watchList[#m.watchList+1] = callback
|
||||
end
|
||||
|
||||
function m.onWatch(ev, uri)
|
||||
for _, callback in ipairs(m.watchList) do
|
||||
await.call(function ()
|
||||
callback(ev, uri)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
--- 初始化工作区
|
||||
function m.create(uri, folderName)
|
||||
log.info('Workspace create: ', uri)
|
||||
local scp = scope.createFolder(uri, folderName)
|
||||
m.folders[#m.folders+1] = scp
|
||||
if uri == furi.encode '/'
|
||||
or uri == furi.encode(os.getenv 'HOME' or '') then
|
||||
if not FORCE_ACCEPT_WORKSPACE then
|
||||
client.showMessage('Error', lang.script('WORKSPACE_NOT_ALLOWED', furi.decode(uri)))
|
||||
scp:set('bad root', true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function m.remove(uri)
|
||||
log.info('Workspace remove: ', uri)
|
||||
for i, scp in ipairs(m.folders) do
|
||||
if scp.uri == uri then
|
||||
scp:remove()
|
||||
table.remove(m.folders, i)
|
||||
scp:set('ready', false)
|
||||
scp:set('nativeMatcher', nil)
|
||||
scp:set('libraryMatcher', nil)
|
||||
scp:removeAllLinks()
|
||||
m.flushFiles(scp)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function m.reset()
|
||||
---@type scope[]
|
||||
m.folders = {}
|
||||
m.rootUri = nil
|
||||
end
|
||||
m.reset()
|
||||
|
||||
function m.getRootUri(uri)
|
||||
local scp = scope.getScope(uri)
|
||||
return scp.uri
|
||||
end
|
||||
|
||||
local globInteferFace = {
|
||||
type = function (path, data)
|
||||
if data[path] then
|
||||
return data[path]
|
||||
end
|
||||
local result
|
||||
pcall(function ()
|
||||
if fs.is_directory(fs.path(path)) then
|
||||
result = 'directory'
|
||||
data[path] = 'directory'
|
||||
else
|
||||
result = 'file'
|
||||
data[path] = 'file'
|
||||
end
|
||||
end)
|
||||
return result
|
||||
end,
|
||||
list = function (path, data)
|
||||
if data[path] == 'file' then
|
||||
return nil
|
||||
end
|
||||
local fullPath = fs.path(path)
|
||||
if not fs.is_directory(fullPath) then
|
||||
data[path] = 'file'
|
||||
return nil
|
||||
end
|
||||
data[path] = true
|
||||
local paths = {}
|
||||
pcall(function ()
|
||||
for fullpath, status in fs.pairs(fullPath) do
|
||||
local pathString = fullpath:string()
|
||||
paths[#paths+1] = pathString
|
||||
local st = status:type()
|
||||
if st == 'directory'
|
||||
or st == 'symlink'
|
||||
or st == 'junction' then
|
||||
data[pathString] = 'directory'
|
||||
else
|
||||
data[pathString] = 'file'
|
||||
end
|
||||
end
|
||||
end)
|
||||
return paths
|
||||
end
|
||||
}
|
||||
|
||||
--- 创建排除文件匹配器
|
||||
---@param scp scope
|
||||
function m.getNativeMatcher(scp)
|
||||
if scp:get 'nativeMatcher' then
|
||||
return scp:get 'nativeMatcher'
|
||||
end
|
||||
|
||||
local pattern = {}
|
||||
for path, ignore in pairs(config.get(scp.uri, 'files.exclude')) do
|
||||
if ignore then
|
||||
log.debug('Ignore by exclude:', path)
|
||||
pattern[#pattern+1] = path
|
||||
end
|
||||
end
|
||||
if scp.uri and config.get(scp.uri, 'Lua.workspace.useGitIgnore') then
|
||||
local buf = util.loadFile(furi.decode(scp.uri) .. '/.gitignore')
|
||||
if buf then
|
||||
for line in buf:gmatch '[^\r\n]+' do
|
||||
if line:sub(1, 1) ~= '#' then
|
||||
log.debug('Ignore by .gitignore:', line)
|
||||
pattern[#pattern+1] = line
|
||||
end
|
||||
end
|
||||
end
|
||||
buf = util.loadFile(furi.decode(scp.uri).. '/.git/info/exclude')
|
||||
if buf then
|
||||
for line in buf:gmatch '[^\r\n]+' do
|
||||
if line:sub(1, 1) ~= '#' then
|
||||
log.debug('Ignore by .git/info/exclude:', line)
|
||||
pattern[#pattern+1] = line
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if scp.uri and config.get(scp.uri, 'Lua.workspace.ignoreSubmodules') then
|
||||
local buf = util.loadFile(furi.decode(scp.uri) .. '/.gitmodules')
|
||||
if buf then
|
||||
for path in buf:gmatch('path = ([^\r\n]+)') do
|
||||
log.debug('Ignore by .gitmodules:', path)
|
||||
pattern[#pattern+1] = path
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, path in ipairs(config.get(scp.uri, 'Lua.workspace.library')) do
|
||||
path = m.getAbsolutePath(scp.uri, path)
|
||||
if path then
|
||||
log.debug('Ignore by library:', path)
|
||||
debug[#pattern+1] = path
|
||||
end
|
||||
end
|
||||
for _, path in ipairs(config.get(scp.uri, 'Lua.workspace.ignoreDir')) do
|
||||
log.debug('Ignore directory:', path)
|
||||
pattern[#pattern+1] = path
|
||||
end
|
||||
|
||||
local matcher = glob.gitignore(pattern, {
|
||||
root = scp.uri and furi.decode(scp.uri),
|
||||
ignoreCase = platform.os == 'windows',
|
||||
}, globInteferFace)
|
||||
|
||||
scp:set('nativeMatcher', matcher)
|
||||
return matcher
|
||||
end
|
||||
|
||||
--- 创建代码库筛选器
|
||||
---@param scp scope
|
||||
function m.getLibraryMatchers(scp)
|
||||
if scp:get 'libraryMatcher' then
|
||||
return scp:get 'libraryMatcher'
|
||||
end
|
||||
log.debug('Build library matchers:', scp)
|
||||
|
||||
local pattern = {}
|
||||
for path, ignore in pairs(config.get(scp.uri, 'files.exclude')) do
|
||||
if ignore then
|
||||
log.debug('Ignore by exclude:', path)
|
||||
pattern[#pattern+1] = path
|
||||
end
|
||||
end
|
||||
for _, path in ipairs(config.get(scp.uri, 'Lua.workspace.ignoreDir')) do
|
||||
log.debug('Ignore directory:', path)
|
||||
pattern[#pattern+1] = path
|
||||
end
|
||||
|
||||
local librarys = {}
|
||||
for _, path in ipairs(config.get(scp.uri, 'Lua.workspace.library')) do
|
||||
path = m.getAbsolutePath(scp.uri, path)
|
||||
if path then
|
||||
librarys[files.normalize(path)] = true
|
||||
end
|
||||
end
|
||||
local metaPaths = scp:get 'metaPaths'
|
||||
log.debug('meta path:', inspect(metaPaths))
|
||||
if metaPaths then
|
||||
for _, metaPath in ipairs(metaPaths) do
|
||||
librarys[files.normalize(metaPath)] = true
|
||||
end
|
||||
end
|
||||
|
||||
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(pattern, {
|
||||
root = path,
|
||||
ignoreCase = platform.os == 'windows',
|
||||
}, globInteferFace)
|
||||
matchers[#matchers+1] = {
|
||||
uri = furi.encode(nPath),
|
||||
matcher = matcher
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
scp:set('libraryMatcher', matchers)
|
||||
--log.debug('library matcher:', inspect(matchers))
|
||||
|
||||
return matchers
|
||||
end
|
||||
|
||||
--- 文件是否被忽略
|
||||
---@param uri uri
|
||||
function m.isIgnored(uri)
|
||||
local scp = scope.getScope(uri)
|
||||
local path = m.getRelativePath(uri)
|
||||
local ignore = m.getNativeMatcher(scp)
|
||||
if not ignore then
|
||||
return false
|
||||
end
|
||||
return ignore(path)
|
||||
end
|
||||
|
||||
---@async
|
||||
function m.isValidLuaUri(uri)
|
||||
if not files.isLua(uri) then
|
||||
return false
|
||||
end
|
||||
if m.isIgnored(uri)
|
||||
and not files.isLibrary(uri) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@async
|
||||
function m.awaitLoadFile(uri)
|
||||
m.awaitReady(uri)
|
||||
local scp = scope.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)
|
||||
local uri = files.getRealUri(furi.encode(path))
|
||||
scp:get('cachedUris')[uri] = true
|
||||
ld:loadFile(uri)
|
||||
end)
|
||||
ld:loadAll(uri)
|
||||
end
|
||||
|
||||
function m.removeFile(uri)
|
||||
for _, scp in ipairs(m.folders) do
|
||||
if scp:isChildUri(uri)
|
||||
or scp:isLinkedUri(uri) then
|
||||
local cachedUris = scp:get 'cachedUris'
|
||||
if cachedUris and cachedUris[uri] then
|
||||
cachedUris[uri] = nil
|
||||
files.delRef(uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 预读工作区内所有文件
|
||||
---@async
|
||||
---@param scp scope
|
||||
function m.awaitPreload(scp)
|
||||
await.close('preload:' .. scp:getName())
|
||||
await.setID('preload:' .. scp:getName())
|
||||
await.sleep(0.1)
|
||||
|
||||
scp:flushGC()
|
||||
|
||||
if scp:isRemoved() then
|
||||
return
|
||||
end
|
||||
|
||||
local ld <close> = loading.create(scp)
|
||||
scp:set('loading', ld)
|
||||
|
||||
log.info('Preload start:', scp:getName())
|
||||
|
||||
local native = m.getNativeMatcher(scp)
|
||||
local librarys = m.getLibraryMatchers(scp)
|
||||
|
||||
if scp.uri and not scp:get('bad root') then
|
||||
log.info('Scan files at:', scp:getName())
|
||||
scp:gc(fw.watch(files.normalize(furi.decode(scp.uri)), true, function (path)
|
||||
local rpath = m.getRelativePath(path)
|
||||
if native(rpath) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end))
|
||||
local count = 0
|
||||
---@async
|
||||
native:scan(furi.decode(scp.uri), function (path)
|
||||
local uri = files.getRealUri(furi.encode(path))
|
||||
scp:get('cachedUris')[uri] = true
|
||||
ld:loadFile(uri)
|
||||
end, function (_) ---@async
|
||||
count = count + 1
|
||||
if count == 100000 then
|
||||
client.showMessage('Warning', lang.script('WORKSPACE_SCAN_TOO_MUCH', count, furi.decode(scp.uri)))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
for _, libMatcher in ipairs(librarys) do
|
||||
log.info('Scan library at:', libMatcher.uri)
|
||||
local count = 0
|
||||
scp:gc(fw.watch(furi.decode(libMatcher.uri), true, function (path)
|
||||
local rpath = m.getRelativePath(path)
|
||||
if libMatcher.matcher(rpath) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end))
|
||||
scp:addLink(libMatcher.uri)
|
||||
---@async
|
||||
libMatcher.matcher:scan(furi.decode(libMatcher.uri), function (path)
|
||||
local uri = files.getRealUri(furi.encode(path))
|
||||
scp:get('cachedUris')[uri] = true
|
||||
ld:loadFile(uri, libMatcher.uri)
|
||||
end, function () ---@async
|
||||
count = count + 1
|
||||
if count == 100000 then
|
||||
client.showMessage('Warning', lang.script('WORKSPACE_SCAN_TOO_MUCH', count, furi.decode(libMatcher.uri)))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- must wait for other scopes to add library
|
||||
await.sleep(0.1)
|
||||
|
||||
log.info(('Found %d files at:'):format(ld.max), scp:getName())
|
||||
ld:loadAll(scp:getName())
|
||||
log.info('Preload finish at:', scp:getName())
|
||||
end
|
||||
|
||||
--- 查找符合指定file path的所有uri
|
||||
---@param path string
|
||||
function m.findUrisByFilePath(path)
|
||||
if type(path) ~= 'string' then
|
||||
return {}
|
||||
end
|
||||
local myUri = furi.encode(path)
|
||||
local vm = require 'vm'
|
||||
local resultCache = vm.getCache 'findUrisByFilePath.result'
|
||||
if resultCache[path] then
|
||||
return resultCache[path]
|
||||
end
|
||||
local results = {}
|
||||
for uri in files.eachFile() do
|
||||
if uri == myUri then
|
||||
results[#results+1] = uri
|
||||
end
|
||||
end
|
||||
resultCache[path] = results
|
||||
return results
|
||||
end
|
||||
|
||||
|
||||
---@param folderUri? uri
|
||||
---@param path string
|
||||
---@return string?
|
||||
function m.getAbsolutePath(folderUri, path)
|
||||
path = files.normalize(path)
|
||||
if fs.path(path):is_relative() then
|
||||
if not folderUri then
|
||||
return nil
|
||||
end
|
||||
local folderPath = furi.decode(folderUri)
|
||||
path = files.normalize(folderPath .. '/' .. path)
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
---@param uriOrPath uri|string
|
||||
---@return string
|
||||
---@return boolean suc
|
||||
function m.getRelativePath(uriOrPath)
|
||||
local path, uri
|
||||
if uriOrPath:sub(1, 5) == 'file:' then
|
||||
path = furi.decode(uriOrPath)
|
||||
uri = uriOrPath
|
||||
else
|
||||
path = uriOrPath
|
||||
uri = furi.encode(uriOrPath)
|
||||
end
|
||||
local scp = scope.getScope(uri)
|
||||
if not scp.uri then
|
||||
local relative = files.normalize(path)
|
||||
return relative:gsub('^[/\\]+', ''), false
|
||||
end
|
||||
local _, pos = files.normalize(path):find(furi.decode(scp.uri), 1, true)
|
||||
if pos then
|
||||
return files.normalize(path:sub(pos + 1)):gsub('^[/\\]+', ''), true
|
||||
else
|
||||
return files.normalize(path):gsub('^[/\\]+', ''), false
|
||||
end
|
||||
end
|
||||
|
||||
---@param scp scope
|
||||
function m.reload(scp)
|
||||
---@async
|
||||
await.call(function ()
|
||||
m.awaitReload(scp)
|
||||
end)
|
||||
end
|
||||
|
||||
function m.init()
|
||||
if m.rootUri then
|
||||
for _, folder in ipairs(scope.folders) do
|
||||
m.reload(folder)
|
||||
end
|
||||
end
|
||||
m.reload(scope.fallback)
|
||||
end
|
||||
|
||||
---@param scp scope
|
||||
function m.flushFiles(scp)
|
||||
local cachedUris = scp:get 'cachedUris'
|
||||
scp:set('cachedUris', {})
|
||||
if not cachedUris then
|
||||
return
|
||||
end
|
||||
for uri in pairs(cachedUris) do
|
||||
files.delRef(uri)
|
||||
end
|
||||
end
|
||||
|
||||
---@param scp scope
|
||||
function m.resetFiles(scp)
|
||||
local cachedUris = scp:get 'cachedUris'
|
||||
if cachedUris then
|
||||
for uri in pairs(cachedUris) do
|
||||
files.resetText(uri)
|
||||
end
|
||||
end
|
||||
for uri in pairs(files.openMap) do
|
||||
if scope.getScope(uri) == scp then
|
||||
files.resetText(uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@async
|
||||
---@param scp scope
|
||||
function m.awaitReload(scp)
|
||||
await.unique('workspace reload:' .. scp:getName())
|
||||
await.sleep(0.1)
|
||||
scp:set('ready', false)
|
||||
scp:set('nativeMatcher', nil)
|
||||
scp:set('libraryMatcher', nil)
|
||||
scp:removeAllLinks()
|
||||
m.flushFiles(scp)
|
||||
m.onWatch('startReload', scp.uri)
|
||||
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
|
||||
|
||||
m.onWatch('reload', scp.uri)
|
||||
end
|
||||
|
||||
---@return scope
|
||||
function m.getFirstScope()
|
||||
return m.folders[1] or scope.fallback
|
||||
end
|
||||
|
||||
---等待工作目录加载完成
|
||||
---@async
|
||||
function m.awaitReady(uri)
|
||||
if m.isReady(uri) then
|
||||
return
|
||||
end
|
||||
local scp = scope.getScope(uri)
|
||||
local waitingReady = scp:get('waitingReady')
|
||||
or scp:set('waitingReady', {})
|
||||
await.wait(function (waker)
|
||||
waitingReady[#waitingReady+1] = waker
|
||||
end)
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
---@return boolean
|
||||
function m.isReady(uri)
|
||||
local scp = scope.getScope(uri)
|
||||
return scp:get('ready') == true
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function m.isAllReady()
|
||||
local scp = scope.fallback
|
||||
if not scp:get 'ready' then
|
||||
return false
|
||||
end
|
||||
for _, folder in ipairs(scope.folders) do
|
||||
if not folder:get 'ready' then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function m.getLoadingProcess(uri)
|
||||
local scp = scope.getScope(uri)
|
||||
---@type workspace.loading
|
||||
local ld = scp:get 'loading'
|
||||
if ld then
|
||||
return ld.read, ld.max
|
||||
else
|
||||
return 0, 0
|
||||
end
|
||||
end
|
||||
|
||||
config.watch(function (uri, key, value, oldValue)
|
||||
if key:find '^Lua.runtime'
|
||||
or key:find '^Lua.workspace'
|
||||
or key:find '^Lua.type'
|
||||
or key:find '^files' then
|
||||
if value ~= oldValue then
|
||||
local scp = scope.getScope(uri)
|
||||
m.reload(scp)
|
||||
m.resetFiles(scp)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
fw.event(function (ev, path) ---@async
|
||||
local uri = furi.encode(path)
|
||||
|
||||
if ev == 'create' then
|
||||
log.debug('FileChangeType.Created', uri)
|
||||
m.awaitLoadFile(uri)
|
||||
elseif ev == 'delete' then
|
||||
log.debug('FileChangeType.Deleted', uri)
|
||||
files.remove(uri)
|
||||
m.removeFile(uri)
|
||||
local childs = files.getChildFiles(uri)
|
||||
for _, curi in ipairs(childs) do
|
||||
log.debug('FileChangeType.Deleted.Child', curi)
|
||||
files.remove(curi)
|
||||
m.removeFile(uri)
|
||||
end
|
||||
elseif ev == 'change' then
|
||||
if m.isValidLuaUri(uri) then
|
||||
-- 如果文件处于关闭状态,则立即更新;否则等待didChange协议来更新
|
||||
if not files.isOpen(uri) then
|
||||
files.setText(uri, pub.awaitTask('loadFile', furi.decode(uri)), false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local filename = fs.path(path):filename():string()
|
||||
-- 排除类文件发生更改需要重新扫描
|
||||
if filename == '.gitignore'
|
||||
or filename == '.gitmodules' then
|
||||
local scp = scope.getScope(uri)
|
||||
if scp.type ~= 'fallback' then
|
||||
m.reload(scp)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return m
|
||||
Reference in New Issue
Block a user