summaryrefslogtreecommitdiff
path: root/script
diff options
context:
space:
mode:
Diffstat (limited to 'script')
-rw-r--r--script/client.lua120
-rw-r--r--script/config/config.lua175
-rw-r--r--script/config/loader.lua55
-rw-r--r--script/core/code-action.lua14
-rw-r--r--script/core/collector.lua52
-rw-r--r--script/core/command/autoRequire.lua7
-rw-r--r--script/core/completion/completion.lua147
-rw-r--r--script/core/completion/postfix.lua2
-rw-r--r--script/core/definition.lua2
-rw-r--r--script/core/diagnostics/circle-doc-class.lua2
-rw-r--r--script/core/diagnostics/deprecated.lua6
-rw-r--r--script/core/diagnostics/different-requires.lua2
-rw-r--r--script/core/diagnostics/duplicate-doc-class.lua2
-rw-r--r--script/core/diagnostics/init.lua6
-rw-r--r--script/core/diagnostics/lowercase-global.lua2
-rw-r--r--script/core/diagnostics/type-check.lua16
-rw-r--r--script/core/diagnostics/undefined-doc-class.lua2
-rw-r--r--script/core/diagnostics/undefined-global.lua4
-rw-r--r--script/core/hint.lua8
-rw-r--r--script/core/hover/description.lua14
-rw-r--r--script/core/hover/label.lua2
-rw-r--r--script/core/hover/return.lua3
-rw-r--r--script/core/hover/table.lua3
-rw-r--r--script/core/infer.lua17
-rw-r--r--script/core/noder.lua8
-rw-r--r--script/core/searcher.lua15
-rw-r--r--script/core/semantic-tokens.lua45
-rw-r--r--script/core/type-definition.lua2
-rw-r--r--script/file-uri.lua6
-rw-r--r--script/files.lua117
-rw-r--r--script/library.lua88
-rw-r--r--script/plugin.lua14
-rw-r--r--script/progress.lua9
-rw-r--r--script/provider/capability.lua10
-rw-r--r--script/provider/completion.lua27
-rw-r--r--script/provider/diagnostic.lua106
-rw-r--r--script/provider/init.lua1
-rw-r--r--script/provider/provider.lua176
-rw-r--r--script/provider/semantic-tokens.lua12
-rw-r--r--script/service/service.lua11
-rw-r--r--script/service/telemetry.lua33
-rw-r--r--script/utility.lua8
-rw-r--r--script/vm/getDocs.lua8
-rw-r--r--script/vm/getGlobals.lua6
-rw-r--r--script/vm/getLinks.lua2
-rw-r--r--script/workspace/loading.lua170
-rw-r--r--script/workspace/require-path.lua20
-rw-r--r--script/workspace/scope.lua126
-rw-r--r--script/workspace/workspace.lua528
49 files changed, 1276 insertions, 935 deletions
diff --git a/script/client.lua b/script/client.lua
index c7e12fbf..d223715d 100644
--- a/script/client.lua
+++ b/script/client.lua
@@ -6,6 +6,7 @@ local define = require 'proto.define'
local config = require 'config'
local converter = require 'proto.converter'
local json = require 'json-beautify'
+local await = require 'await'
local m = {}
@@ -108,10 +109,8 @@ end
---@param type message.type
---@param message string
---@param titles string[]
----@return string action
----@return integer index
----@async
-function m.awaitRequestMessage(type, message, titles)
+---@param callback fun(action: string, index: integer)
+function m.requestMessage(type, message, titles, callback)
proto.notify('window/logMessage', {
type = define.MessageType[type] or 3,
message = message,
@@ -124,15 +123,29 @@ function m.awaitRequestMessage(type, message, titles)
}
map[title] = i
end
- local item = proto.awaitRequest('window/showMessageRequest', {
+ proto.request('window/showMessageRequest', {
type = define.MessageType[type] or 3,
message = message,
actions = actions,
- })
- if not item then
- return nil
- end
- return item.title, map[item.title]
+ }, function (item)
+ if item then
+ callback(item.title, map[item.title])
+ else
+ callback(nil, nil)
+ end
+ end)
+end
+
+---@param type message.type
+---@param message string
+---@param titles string[]
+---@return string action
+---@return integer index
+---@async
+function m.awaitRequestMessage(type, message, titles)
+ return await.wait(function (waker)
+ m.requestMessage(type, message, titles, waker)
+ end)
end
---@param type message.type
@@ -187,35 +200,49 @@ end
---@field uri? uri
---@param cfg table
+---@param uri uri
---@param changes config.change[]
-local function applyConfig(cfg, changes)
+---@return boolean
+local function applyConfig(cfg, uri, changes)
+ local ws = require 'workspace'
+ local scp = ws.getScope(uri)
+ local ok = false
for _, change in ipairs(changes) do
- cfg[change.key] = config.getRaw(change.key)
+ if scp:isChildUri(change.uri)
+ or scp:isLinkedUri(change.uri) then
+ cfg[change.key] = config.getRaw(change.uri, change.key)
+ ok = true
+ end
end
+ return ok
end
-local function tryModifySpecifiedConfig(finalChanges)
+local function tryModifySpecifiedConfig(uri, finalChanges)
if #finalChanges == 0 then
return false
end
local workspace = require 'workspace'
local loader = require 'config.loader'
- if loader.lastLocalType ~= 'json' then
+ local scp = workspace.getScope(uri)
+ if scp:get('lastLocalType') ~= 'json' then
+ return false
+ end
+ local suc = applyConfig(scp:get('lastLocalConfig'), uri, finalChanges)
+ if not suc then
return false
end
- applyConfig(loader.lastLocalConfig, finalChanges)
- local path = workspace.getAbsolutePath(CONFIGPATH)
- util.saveFile(path, json.beautify(loader.lastLocalConfig, { indent = ' ' }))
+ local path = workspace.getAbsolutePath(uri, CONFIGPATH)
+ util.saveFile(path, json.beautify(scp:get('lastLocalConfig'), { indent = ' ' }))
return true
end
-local function tryModifyRC(finalChanges, create)
+local function tryModifyRC(uri, finalChanges, create)
if #finalChanges == 0 then
return false
end
local workspace = require 'workspace'
local loader = require 'config.loader'
- local path = workspace.getAbsolutePath '.luarc.json'
+ local path = workspace.getAbsolutePath(uri, '.luarc.json')
if not path then
return false
end
@@ -223,24 +250,40 @@ local function tryModifyRC(finalChanges, create)
if not buf and not create then
return false
end
- local rc = loader.lastRCConfig or {
+ local scp = workspace.getScope(uri)
+ local rc = scp:get('lastRCConfig') or {
['$schema'] = lang.id == 'zh-cn' and [[https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema-zh-cn.json]] or [[https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json]]
}
- applyConfig(rc, finalChanges)
+ local suc = applyConfig(rc, uri, finalChanges)
+ if not suc then
+ return false
+ end
util.saveFile(path, json.beautify(rc, { indent = ' ' }))
return true
end
-local function tryModifyClient(finalChanges)
+local function tryModifyClient(uri, finalChanges)
if #finalChanges == 0 then
return false
end
if not m.getOption 'changeConfiguration' then
return false
end
+ local ws = require 'workspace'
+ local scp = ws.getScope(uri)
+ local scpChanges = {}
+ for _, change in ipairs(finalChanges) do
+ if change.uri
+ and (scp:isChildUri(change.uri) or scp:isLinkedUri(change.uri)) then
+ scpChanges[#scpChanges+1] = change
+ end
+ end
+ if #scpChanges == 0 then
+ return false
+ end
proto.notify('$/command', {
command = 'lua.config',
- data = finalChanges,
+ data = scpChanges,
})
return true
end
@@ -274,22 +317,21 @@ function m.setConfig(changes, onlyMemory)
local finalChanges = {}
for _, change in ipairs(changes) do
if change.action == 'add' then
- local suc = config.add(change.key, change.value)
+ local suc = config.add(change.uri, change.key, change.value)
if suc then
finalChanges[#finalChanges+1] = change
end
elseif change.action == 'set' then
- local suc = config.set(change.key, change.value)
+ local suc = config.set(change.uri, change.key, change.value)
if suc then
finalChanges[#finalChanges+1] = change
end
elseif change.action == 'prop' then
- local suc = config.prop(change.key, change.prop, change.value)
+ local suc = config.prop(change.uri, change.key, change.prop, change.value)
if suc then
finalChanges[#finalChanges+1] = change
end
end
- change.uri = m.info.rootUri
end
if onlyMemory then
return
@@ -298,17 +340,25 @@ function m.setConfig(changes, onlyMemory)
return
end
xpcall(function ()
- tryModifyClientGlobal(finalChanges)
- if tryModifySpecifiedConfig(finalChanges) then
- return
- end
- if tryModifyRC(finalChanges) then
+ local ws = require 'workspace'
+ if #ws.folders == 0 then
+ tryModifyClient(finalChanges)
return
end
- if tryModifyClient(finalChanges) then
- return
+ tryModifyClientGlobal(finalChanges)
+ for _, scp in ipairs(ws.folders) do
+ if tryModifySpecifiedConfig(scp.uri, finalChanges) then
+ goto CONTINUE
+ end
+ if tryModifyRC(scp.uri, finalChanges, false) then
+ goto CONTINUE
+ end
+ if tryModifyClient(scp.uri, finalChanges) then
+ goto CONTINUE
+ end
+ tryModifyRC(scp.uri, finalChanges, true)
+ ::CONTINUE::
end
- tryModifyRC(finalChanges, true)
end, log.error)
end
diff --git a/script/config/config.lua b/script/config/config.lua
index 0f74828d..81ab4b81 100644
--- a/script/config/config.lua
+++ b/script/config/config.lua
@@ -1,6 +1,7 @@
local util = require 'utility'
local define = require 'proto.define'
local timer = require 'timer'
+local scope = require 'workspace.scope'
---@alias config.source '"client"'|'"path"'|'"local"'
@@ -214,42 +215,85 @@ local Template = {
['editor.acceptSuggestionOnEnter'] = Type.String >> 'on',
}
-local config = {}
-local rawConfig = {}
-
---@class config.api
local m = {}
m.watchList = {}
-local function update(key, value, raw)
- local oldValue = config[key]
- config[key] = value
- rawConfig[key] = raw
- m.event(key, value, oldValue)
+m.NULL = {}
+
+---@param scp scope
+---@param key string
+---@param nowValue any
+---@param rawValue any
+local function update(scp, key, nowValue, rawValue)
+ local now = scp:get 'config.now'
+ local raw = scp:get 'config.raw'
+
+ local oldValue = now[key]
+
+ now[key] = nowValue
+ raw[key] = rawValue
+
+ m.event(scp.uri, key, nowValue, oldValue)
+end
+
+---@param uri uri
+---@param key? string
+---@return scope
+local function getScope(uri, key)
+ local raw = scope.override:get 'config.raw'
+ if raw then
+ if not key or raw[key] ~= nil then
+ return scope.override
+ end
+ end
+ if uri then
+ ---@type scope
+ local scp = scope.getFolder(uri) or scope.getLinkedScope(uri)
+ if scp then
+ if not key
+ or (scp:get 'config.raw' and scp:get 'config.raw' [key] ~= nil) then
+ return scp
+ end
+ end
+ end
+ return scope.fallback
end
-function m.set(key, value)
+---@param scp scope
+---@param key string
+---@param value any
+function m.setByScope(scp, key, value)
local unit = Template[key]
if not unit then
return false
end
- if util.equal(rawConfig[key], value) then
+ local raw = scp:get 'config.raw'
+ if util.equal(raw[key], value) then
return false
end
if unit:checker(value) then
- update(key, unit:loader(value), value)
+ update(scp, key, unit:loader(value), value)
else
- update(key, unit.default, unit.default)
+ update(scp, key, unit.default, unit.default)
end
return true
end
-function m.add(key, value)
+---@param uri uri
+---@param key string
+---@param value any
+function m.set(uri, key, value)
+ local scp = getScope(uri)
+ return m.setByScope(scp, key, value)
+end
+
+function m.add(uri, key, value)
local unit = Template[key]
if not unit then
return false
end
- local list = rawConfig[key]
+ local list = m.getRaw(uri, key)
if type(list) ~= 'table' then
return false
end
@@ -261,18 +305,15 @@ function m.add(key, value)
copyed[i] = v
end
copyed[#copyed+1] = value
- if unit:checker(copyed) then
- update(key, unit:loader(copyed), copyed)
- end
- return true
+ return m.set(uri, key, copyed)
end
-function m.prop(key, prop, value)
+function m.prop(uri, key, prop, value)
local unit = Template[key]
if not unit then
return false
end
- local map = rawConfig[key]
+ local map = m.getRaw(uri, key)
if type(map) ~= 'table' then
return false
end
@@ -284,43 +325,49 @@ function m.prop(key, prop, value)
copyed[k] = v
end
copyed[prop] = value
- if unit:checker(copyed) then
- update(key, unit:loader(copyed), copyed)
- end
- return true
+ return m.set(uri, key, copyed)
end
-function m.get(key)
- return config[key]
-end
-
-function m.getRaw(key)
- return rawConfig[key]
+---@param uri uri
+---@param key string
+---@return any
+function m.get(uri, key)
+ local scp = getScope(uri, key)
+ local value = scp:get 'config.now' [key]
+ if value == nil then
+ value = Template[key].default
+ end
+ if value == m.NULL then
+ value = nil
+ end
+ return value
end
-function m.dump()
- local dump = {}
-
- local function expand(parent, key, value)
- local left, right = key:match '([^%.]+)%.(.+)'
- if left then
- if not parent[left] then
- parent[left] = {}
- end
- expand(parent[left], right, value)
- else
- parent[key] = value
- end
+---@param uri uri
+---@param key string
+---@return any
+function m.getRaw(uri, key)
+ local scp = getScope(uri, key)
+ local value = scp:get 'config.raw' [key]
+ if value == nil then
+ value = Template[key].default
end
-
- for key, value in pairs(config) do
- expand(dump, key, value)
+ if value == m.NULL then
+ value = nil
end
-
- return dump
+ return value
end
-function m.update(new, null)
+---@param scp scope
+---@param new table
+---@param null any
+function m.update(scp, new, null)
+ local oldConfig = scp:get 'config.now'
+ local newConfig = {}
+
+ scp:set('config.now', newConfig)
+ scp:set('config.raw', {})
+
local function expand(t, left)
for key, value in pairs(t) do
local fullKey = key
@@ -328,12 +375,12 @@ function m.update(new, null)
fullKey = left .. '.' .. key
end
if value == null then
- value = nil
+ value = m.NULL
end
if Template[fullKey] then
- m.set(fullKey, value)
+ m.setByScope(scp, fullKey, value)
elseif Template['Lua.' .. fullKey] then
- m.set('Lua.' .. fullKey, value)
+ m.setByScope(scp, 'Lua.' .. fullKey, value)
elseif type(value) == 'table' then
expand(value, fullKey)
end
@@ -341,14 +388,21 @@ function m.update(new, null)
end
expand(new)
+
+ -- compare then fire event
+ if oldConfig then
+ for key, oldValue in pairs(oldConfig) do
+ m.event(scp.uri, key, oldValue, newConfig[key])
+ end
+ end
end
----@param callback fun(key: string, value: any, oldValue: any)
+---@param callback fun(uri: uri, key: string, value: any, oldValue: any)
function m.watch(callback)
m.watchList[#m.watchList+1] = callback
end
-function m.event(key, value, oldValue)
+function m.event(uri, key, value, oldValue)
if not m.delay then
m.delay = {}
timer.wait(0, function ()
@@ -356,26 +410,19 @@ function m.event(key, value, oldValue)
m.delay = nil
for _, info in ipairs(delay) do
for _, callback in ipairs(m.watchList) do
- callback(info.key, info.value, info.oldValue)
+ callback(info.uri, info.key, info.value, info.oldValue)
end
end
end)
end
m.delay[#m.delay+1] = {
+ uri = uri,
key = key,
value = value,
oldValue = oldValue,
}
end
-function m.init()
- if m.inited then
- return
- end
- m.inited = true
- for key, unit in pairs(Template) do
- m.set(key, unit.default)
- end
-end
+m.update(scope.fallback, {})
return m
diff --git a/script/config/loader.lua b/script/config/loader.lua
index daa370cd..b2cf6fd3 100644
--- a/script/config/loader.lua
+++ b/script/config/loader.lua
@@ -1,5 +1,3 @@
-local fs = require 'bee.filesystem'
-local fsu = require 'fs-utility'
local json = require 'json'
local proto = require 'proto'
local lang = require 'language'
@@ -14,40 +12,43 @@ local function errorMessage(msg)
log.error(msg)
end
+---@class config.loader
local m = {}
-function m.loadRCConfig(filename)
- local path = workspace.getAbsolutePath(filename)
+function m.loadRCConfig(uri, filename)
+ local scp = workspace.getScope(uri)
+ local path = workspace.getAbsolutePath(uri, filename)
if not path then
- m.lastRCConfig = nil
+ scp:set('lastRCConfig', nil)
return nil
end
local buf = util.loadFile(path)
if not buf then
- m.lastRCConfig = nil
+ scp:set('lastRCConfig', nil)
return nil
end
local suc, res = pcall(json.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return m.lastRCConfig
+ return scp:get('lastRCConfig')
end
- m.lastRCConfig = res
+ scp:set('lastRCConfig', res)
return res
end
-function m.loadLocalConfig(filename)
- local path = workspace.getAbsolutePath(filename)
+function m.loadLocalConfig(uri, filename)
+ local scp = workspace.getScope(uri)
+ local path = workspace.getAbsolutePath(uri, filename)
if not path then
- m.lastLocalConfig = nil
- m.lastLocalType = nil
+ scp:set('lastLocalConfig', nil)
+ scp:set('lastLocalType', nil)
return nil
end
local buf = util.loadFile(path)
if not buf then
errorMessage(lang.script('CONFIG_LOAD_FAILED', path))
- m.lastLocalConfig = nil
- m.lastLocalType = nil
+ scp:set('lastLocalConfig', nil)
+ scp:set('lastLocalType', nil)
return nil
end
local firstChar = buf:match '%S'
@@ -55,10 +56,10 @@ function m.loadLocalConfig(filename)
local suc, res = pcall(json.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return m.lastLocalConfig
+ return scp:get('lastLocalConfig')
end
- m.lastLocalConfig = res
- m.lastLocalType = 'json'
+ scp:set('lastLocalConfig', res)
+ scp:set('lastLocalType', 'json')
return res
else
local suc, res = pcall(function ()
@@ -66,36 +67,38 @@ function m.loadLocalConfig(filename)
end)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
- return m.lastLocalConfig
+ scp:set('lastLocalConfig', res)
end
- m.lastLocalConfig = res
- m.lastLocalType = 'lua'
+ scp:set('lastLocalConfig', res)
+ scp:set('lastLocalType', 'lua')
return res
end
end
---@async
-function m.loadClientConfig()
+---@param uri? uri
+---@return table
+function m.loadClientConfig(uri)
local configs = proto.awaitRequest('workspace/configuration', {
items = {
{
- scopeUri = workspace.uri,
+ scopeUri = uri,
section = 'Lua',
},
{
- scopeUri = workspace.uri,
+ scopeUri = uri,
section = 'files.associations',
},
{
- scopeUri = workspace.uri,
+ scopeUri = uri,
section = 'files.exclude',
},
{
- scopeUri = workspace.uri,
+ scopeUri = uri,
section = 'editor.semanticHighlighting.enabled',
},
{
- scopeUri = workspace.uri,
+ scopeUri = uri,
section = 'editor.acceptSuggestionOnEnter',
},
},
diff --git a/script/core/code-action.lua b/script/core/code-action.lua
index ad048c48..b2a6fac9 100644
--- a/script/core/code-action.lua
+++ b/script/core/code-action.lua
@@ -49,7 +49,7 @@ local function disableDiagnostic(uri, code, start, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_DISABLE_DIAG,
- command = 'lua.setConfig:' .. sp:get_id(),
+ command = 'lua.setConfig',
arguments = {
{
key = 'Lua.diagnostics.disable',
@@ -86,7 +86,7 @@ local function markGlobal(uri, name, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_MARK_GLOBAL,
- command = 'lua.setConfig:' .. sp:get_id(),
+ command = 'lua.setConfig',
arguments = {
{
key = 'Lua.diagnostics.globals',
@@ -105,7 +105,7 @@ local function changeVersion(uri, version, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_RUNTIME_VERSION,
- command = 'lua.setConfig:' .. sp:get_id(),
+ command = 'lua.setConfig',
arguments = {
{
key = 'Lua.runtime.version',
@@ -223,7 +223,7 @@ local function solveSyntaxUnicodeName(uri, err, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_UNICODE_NAME,
- command = 'lua.setConfig:' .. sp:get_id(),
+ command = 'lua.setConfig',
arguments = {
{
key = 'Lua.runtime.unicodeName',
@@ -280,7 +280,7 @@ local function solveAmbiguity1(uri, diag, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_ADD_BRACKETS,
- command = 'lua.solve:' .. sp:get_id(),
+ command = 'lua.solve',
arguments = {
{
name = 'ambiguity-1',
@@ -298,7 +298,7 @@ local function solveTrailingSpace(uri, diag, results)
kind = 'quickfix',
command = {
title = lang.script.COMMAND_REMOVE_SPACE,
- command = 'lua.removeSpace:' .. sp:get_id(),
+ command = 'lua.removeSpace',
arguments = {
{
uri = uri,
@@ -570,7 +570,7 @@ local function checkJsonToLua(results, uri, start, finish)
kind = 'refactor.rewrite',
command = {
title = lang.script.COMMAND_JSON_TO_LUA,
- command = 'lua.jsonToLua:' .. sp:get_id(),
+ command = 'lua.jsonToLua',
arguments = {
{
uri = uri,
diff --git a/script/core/collector.lua b/script/core/collector.lua
index 3293c9fe..2f29fc37 100644
--- a/script/core/collector.lua
+++ b/script/core/collector.lua
@@ -1,3 +1,6 @@
+local scope = require 'workspace.scope'
+local ws = require 'workspace'
+
local collect = {}
local subscribed = {}
@@ -58,17 +61,56 @@ end
local DUMMY_FUNCTION = function () end
--- 迭代某个名字的订阅
+---@param uri uri
---@param name string
-function m.each(name)
+function m.each(uri, name)
local nameCollect = collect[name]
if not nameCollect then
return DUMMY_FUNCTION
end
- local uri, value
- return function ()
- uri, value = next(nameCollect, uri)
- return value, uri
+ ---@type scope
+ local scp = scope.getFolder(uri)
+ or scope.getLinkedScope(uri)
+ or scope.fallback
+
+ local curi, value
+ local function getNext()
+ curi, value = next(nameCollect, curi)
+ if not curi then
+ return nil, nil
+ end
+ if not scp:isChildUri(curi)
+ and not scp:isLinkedUri(curi) then
+ return getNext()
+ end
+
+ return value, curi
end
+ return getNext
end
+--- 迭代某个名字的引用订阅
+---@param uri uri
+---@param name string
+function m.eachRef(uri, name)
+ local nameCollect = collect[name]
+ if not nameCollect then
+ return DUMMY_FUNCTION
+ end
+ ---@type scope
+ if scope.getFolder(uri) then
+ return m.each(uri, name)
+ end
+
+ local curi, value
+ local function getNext()
+ curi, value = next(nameCollect, curi)
+ if not curi then
+ return nil, nil
+ end
+
+ return value, curi
+ end
+ return getNext
+end
return m
diff --git a/script/core/command/autoRequire.lua b/script/core/command/autoRequire.lua
index 30bd13a1..c0deecfc 100644
--- a/script/core/command/autoRequire.lua
+++ b/script/core/command/autoRequire.lua
@@ -64,7 +64,7 @@ local function findInsertRow(uri)
end
---@async
-local function askAutoRequire(visiblePaths)
+local function askAutoRequire(uri, visiblePaths)
local selects = {}
local nameMap = {}
for _, visible in ipairs(visiblePaths) do
@@ -91,6 +91,7 @@ local function askAutoRequire(visiblePaths)
key = 'Lua.completion.autoRequire',
action = 'set',
value = false,
+ uri = uri,
}
}
return
@@ -137,7 +138,7 @@ return function (data)
end
local path = furi.decode(target)
- local visiblePaths = rpath.getVisiblePath(path)
+ local visiblePaths = rpath.getVisiblePath(uri, path)
if not visiblePaths or #visiblePaths == 0 then
return
end
@@ -145,7 +146,7 @@ return function (data)
return #a.expect < #b.expect
end)
- local result = askAutoRequire(visiblePaths)
+ local result = askAutoRequire(uri, visiblePaths)
if not result then
return
end
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua
index 425c8026..28e9d108 100644
--- a/script/core/completion/completion.lua
+++ b/script/core/completion/completion.lua
@@ -180,7 +180,7 @@ local function buildDetail(source)
end
local function getSnip(source)
- local context = config.get 'Lua.completion.displayContext'
+ local context = config.get(guide.getUri(source), 'Lua.completion.displayContext')
if context <= 0 then
return nil
end
@@ -222,7 +222,7 @@ local function buildDesc(source)
end
local function buildFunction(results, source, value, oop, data)
- local snipType = config.get 'Lua.completion.callSnippet'
+ local snipType = config.get(guide.getUri(source), 'Lua.completion.callSnippet')
if snipType == 'Disable' or snipType == 'Both' then
results[#results+1] = data
end
@@ -324,7 +324,7 @@ local function checkLocal(state, word, position, results)
end
local function checkModule(state, word, position, results)
- if not config.get 'Lua.completion.autoRequire' then
+ if not config.get(state.uri, 'Lua.completion.autoRequire') then
return
end
local locals = guide.getVisibleLocals(state.ast, position)
@@ -337,7 +337,7 @@ local function checkModule(state, word, position, results)
local stemName = fileName:gsub('%..+', '')
if not locals[stemName]
and not vm.hasGlobalSets(stemName)
- and not config.get 'Lua.diagnostics.globals'[stemName]
+ and not config.get(state.uri, 'Lua.diagnostics.globals')[stemName]
and stemName:match '^[%a_][%w_]*$'
and matchKey(word, stemName) then
local targetState = files.getState(uri)
@@ -367,7 +367,7 @@ local function checkModule(state, word, position, results)
commitCharacters = {'.'},
command = {
title = 'autoRequire',
- command = 'lua.autoRequire:' .. sp:get_id(),
+ command = 'lua.autoRequire',
arguments = {
{
uri = guide.getUri(state.ast),
@@ -448,8 +448,8 @@ local function checkFieldFromFieldToIndex(state, name, src, parent, word, startP
}
end
else
- if config.get 'Lua.runtime.version' == 'lua 5.1'
- or config.get 'Lua.runtime.version' == 'luaJIT' then
+ if config.get(state.uri, 'Lua.runtime.version') == 'lua 5.1'
+ or config.get(state.uri, 'Lua.runtime.version') == 'luaJIT' then
textEdit.newText = '_G' .. textEdit.newText
else
textEdit.newText = '_ENV' .. textEdit.newText
@@ -536,7 +536,7 @@ local function checkFieldOfRefs(refs, state, word, startPos, position, parent, o
goto CONTINUE
end
local funcLabel
- if config.get 'Lua.completion.showParams' then
+ if config.get(state.uri, 'Lua.completion.showParams') then
local value = searcher.getObjectValue(src) or src
if value.type == 'function'
or value.type == 'doc.type.function' then
@@ -584,14 +584,14 @@ end
---@async
local function checkGlobal(state, word, startPos, position, parent, oop, results)
local locals = guide.getVisibleLocals(state.ast, position)
- local globals = vm.getGlobalSets '*'
+ local globals = vm.getGlobalSets(state.uri, '*')
checkFieldOfRefs(globals, state, word, startPos, position, parent, oop, results, locals, 'global')
end
---@async
local function checkField(state, word, start, position, parent, oop, results)
if parent.tag == '_ENV' or parent.special == '_G' then
- local globals = vm.getGlobalSets '*'
+ local globals = vm.getGlobalSets(state.uri, '*')
checkFieldOfRefs(globals, state, word, start, position, parent, oop, results)
else
local refs = vm.getRefs(parent, '*')
@@ -630,7 +630,7 @@ end
local function checkCommon(state, word, position, results)
local myUri = state.uri
- local showWord = config.get 'Lua.completion.showWord'
+ local showWord = config.get(state.uri, 'Lua.completion.showWord')
if showWord == 'Disable' then
return
end
@@ -645,7 +645,7 @@ local function checkCommon(state, word, position, results)
for _, data in ipairs(keyWordMap) do
used[data[1]] = true
end
- if config.get 'Lua.completion.workspaceWord' and #word >= 2 then
+ if config.get(state.uri, 'Lua.completion.workspaceWord') and #word >= 2 then
results.complete = true
local myHead = word:sub(1, 2)
for uri in files.eachFile() do
@@ -720,7 +720,7 @@ end
local function checkKeyWord(state, start, position, word, hasSpace, afterLocal, results)
local text = state.lua
- local snipType = config.get 'Lua.completion.keywordSnippet'
+ local snipType = config.get(state.uri, 'Lua.completion.keywordSnippet')
local symbol = lookBackward.findSymbol(text, guide.positionToOffset(state, start))
local isExp = symbol == '(' or symbol == ',' or symbol == '='
local info = {
@@ -887,7 +887,7 @@ local function collectRequireNames(mode, myUri, literal, source, smark, position
goto CONTINUE
end
local path = workspace.getRelativePath(uri)
- local infos = rpath.getVisiblePath(path)
+ local infos = rpath.getVisiblePath(uri, path)
for _, info in ipairs(infos) do
if matchKey(literal, info.expect) then
if not collect[info.expect] then
@@ -1628,7 +1628,7 @@ local function tryluaDocBySource(state, position, source, results)
if source.type == 'doc.extends.name' then
if source.parent.type == 'doc.class' then
local used = {}
- for _, doc in ipairs(vm.getDocDefines '*') do
+ for _, doc in ipairs(vm.getDocDefines(state.uri, '*')) do
if doc.type == 'doc.class.name'
and doc.parent ~= source.parent
and not used[doc[1]]
@@ -1649,7 +1649,7 @@ local function tryluaDocBySource(state, position, source, results)
return true
elseif source.type == 'doc.type.name' then
local used = {}
- for _, doc in ipairs(vm.getDocDefines '*') do
+ for _, doc in ipairs(vm.getDocDefines(state.uri, '*')) do
if (doc.type == 'doc.class.name' or doc.type == 'doc.alias.name')
and doc.parent ~= source.parent
and not used[doc[1]]
@@ -1727,7 +1727,7 @@ end
local function tryluaDocByErr(state, position, err, docState, results)
if err.type == 'LUADOC_MISS_CLASS_EXTENDS_NAME' then
- for _, doc in ipairs(vm.getDocDefines '*') do
+ for _, doc in ipairs(vm.getDocDefines(state.uri, '*')) do
if doc.type == 'doc.class.name'
and doc.parent ~= docState then
results[#results+1] = {
@@ -1737,7 +1737,7 @@ local function tryluaDocByErr(state, position, err, docState, results)
end
end
elseif err.type == 'LUADOC_MISS_TYPE_NAME' then
- for _, doc in ipairs(vm.getDocDefines '*') do
+ for _, doc in ipairs(vm.getDocDefines(state.uri, '*')) do
if (doc.type == 'doc.class.name' or doc.type == 'doc.alias.name') then
results[#results+1] = {
label = doc[1],
@@ -1940,89 +1940,6 @@ local function tryComment(state, position, results)
checkCommon(state, word, position, results)
end
-local function makeCache(uri, position, results)
- local cache = workspace.getCache 'completion'
- if not uri then
- cache.results = nil
- return
- end
- local text = files.getText(uri)
- local state = files.getState(uri)
- local word = lookBackward.findWord(text, guide.positionToOffset(state, position))
- if not word or #word < 2 then
- cache.results = nil
- return
- end
- cache.results = results
- cache.position= position
- cache.word = word:lower()
- cache.length = #word
- cache.uri = uri
-end
-
-local function isValidCache(word, result)
- if result.kind == define.CompletionItemKind.Text then
- return false
- end
- local match = result.match or result.label
- if matchKey(word, match) then
- return true
- end
- if result.textEdit then
- match = result.textEdit.newText:match '[%w_]+'
- if match and matchKey(word, match) then
- return true
- end
- end
- return false
-end
-
-local function getCache(uri, position)
- do return nil end
- local cache = workspace.getCache 'completion'
- if not cache.results then
- return nil
- end
- if cache.uri ~= uri then
- return nil
- end
- local text = files.getText(uri)
- local state = files.getState(uri)
- local word = lookBackward.findWord(text, guide.positionToOffset(state, position))
- if not word then
- return nil
- end
- if word:sub(1, #cache.word):lower() ~= cache.word then
- return nil
- end
-
- local ext = #word - cache.length
- cache.length = #word
- local results = cache.results
- for i = #results, 1, -1 do
- local result = results[i]
- if isValidCache(word, result) then
- if result.textEdit then
- result.textEdit.finish = result.textEdit.finish + ext
- end
- else
- results[i] = results[#results]
- results[#results] = nil
- end
- end
-
- if results.enableCommon then
- checkCommon(state, word, position, results)
- end
-
- return cache.results
-end
-
-local function clearCache()
- local cache = workspace.getCache 'completion'
- cache.results = nil
-end
-
---@async
local function tryCompletions(state, position, triggerCharacter, results)
local text = state.lua
@@ -2052,56 +1969,30 @@ end
---@async
local function completion(uri, position, triggerCharacter)
- tracy.ZoneBeginN 'completion cache'
- local results = getCache(uri, position)
- tracy.ZoneEnd()
- if results then
- return results
- end
- await.delay()
- tracy.ZoneBeginN 'completion #1'
local state = files.getState(uri)
if not state then
return nil
end
- results = {}
clearStack()
- tracy.ZoneEnd()
+ local results = {}
tracy.ZoneBeginN 'completion #2'
tryCompletions(state, position, triggerCharacter, results)
tracy.ZoneEnd()
if #results == 0 then
- clearCache()
return nil
end
- --tracy.ZoneBeginN 'completion #3'
- --makeCache(uri, position, results)
- --tracy.ZoneEnd()
return results
end
---@async
local function resolve(id)
local item = resolveStack(id)
- local cache = workspace.getCache 'completion'
- if item and cache.results then
- for _, res in ipairs(cache.results) do
- if res and res.id == id then
- for k, v in pairs(item) do
- res[k] = v
- end
- res.id = nil
- break
- end
- end
- end
return item
end
return {
completion = completion,
resolve = resolve,
- clearCache = clearCache,
}
diff --git a/script/core/completion/postfix.lua b/script/core/completion/postfix.lua
index 0395e2a5..2ac24933 100644
--- a/script/core/completion/postfix.lua
+++ b/script/core/completion/postfix.lua
@@ -278,7 +278,7 @@ return function (state, position, results)
offset = newOffset - 1
end
local symbol = text:sub(offset, offset)
- if symbol == config.get 'Lua.completion.postfix' then
+ if symbol == config.get(state.uri, 'Lua.completion.postfix') then
local wordPosition = guide.offsetToPosition(state, offset - 1)
checkPostFix(state, word or '', wordPosition, position, symbol, results)
return symbol ~= '.' and symbol ~= ':'
diff --git a/script/core/definition.lua b/script/core/definition.lua
index eadae30f..6c6b3df3 100644
--- a/script/core/definition.lua
+++ b/script/core/definition.lua
@@ -75,7 +75,7 @@ local function checkRequire(source, offset)
return nil
end
if libName == 'require' then
- return rpath.findUrisByRequirePath(literal)
+ return rpath.findUrisByRequirePath(guide.getUri(source), literal)
elseif libName == 'dofile'
or libName == 'loadfile' then
return workspace.findUrisByFilePath(literal)
diff --git a/script/core/diagnostics/circle-doc-class.lua b/script/core/diagnostics/circle-doc-class.lua
index ae6d4d3b..61dc46b4 100644
--- a/script/core/diagnostics/circle-doc-class.lua
+++ b/script/core/diagnostics/circle-doc-class.lua
@@ -40,7 +40,7 @@ return function (uri, callback)
end
if not mark[newName] then
mark[newName] = true
- local docs = vm.getDocDefines(newName)
+ local docs = vm.getDocDefines(uri, newName)
for _, otherDoc in ipairs(docs) do
if otherDoc.type == 'doc.class.name' then
list[#list+1] = otherDoc.parent
diff --git a/script/core/diagnostics/deprecated.lua b/script/core/diagnostics/deprecated.lua
index e9a1fef7..1c248646 100644
--- a/script/core/diagnostics/deprecated.lua
+++ b/script/core/diagnostics/deprecated.lua
@@ -23,10 +23,10 @@ return function (uri, callback)
if not key then
return
end
- if config.get 'Lua.diagnostics.globals'[key] then
+ if config.get(uri, 'Lua.diagnostics.globals')[key] then
return
end
- if config.get 'Lua.runtime.special'[key] then
+ if config.get(uri, 'Lua.runtime.special')[key] then
return
end
end
@@ -83,7 +83,7 @@ return function (uri, callback)
end
table.sort(versions)
if #versions > 0 then
- message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_VERSION', table.concat(versions, '/'), config.get 'Lua.runtime.version'))
+ message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_VERSION', table.concat(versions, '/'), config.get(uri, 'Lua.runtime.version')))
end
end
cache[id] = {
diff --git a/script/core/diagnostics/different-requires.lua b/script/core/diagnostics/different-requires.lua
index 3a49ceef..de063c9f 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(literal)
+ local results = rpath.findUrisByRequirePath(uri, literal)
if not results or #results ~= 1 then
return
end
diff --git a/script/core/diagnostics/duplicate-doc-class.lua b/script/core/diagnostics/duplicate-doc-class.lua
index 97e2089a..5114a54f 100644
--- a/script/core/diagnostics/duplicate-doc-class.lua
+++ b/script/core/diagnostics/duplicate-doc-class.lua
@@ -19,7 +19,7 @@ return function (uri, callback)
if doc.type == 'doc.alias' then
local name = guide.getKeyName(doc)
if not cache[name] then
- local docs = vm.getDocDefines(name)
+ local docs = vm.getDocDefines(uri, name)
cache[name] = {}
for _, otherDoc in ipairs(docs) do
if otherDoc.type == 'doc.class.name'
diff --git a/script/core/diagnostics/init.lua b/script/core/diagnostics/init.lua
index 4950900b..4e2a4976 100644
--- a/script/core/diagnostics/init.lua
+++ b/script/core/diagnostics/init.lua
@@ -20,13 +20,13 @@ table.sort(diagList, function (a, b)
end)
local function check(uri, name, results)
- if config.get 'Lua.diagnostics.disable'[name] then
+ if config.get(uri, 'Lua.diagnostics.disable')[name] then
return
end
- local level = config.get 'Lua.diagnostics.severity'[name]
+ local level = config.get(uri, 'Lua.diagnostics.severity')[name]
or define.DiagnosticDefaultSeverity[name]
- local neededFileStatus = config.get 'Lua.diagnostics.neededFileStatus'[name]
+ local neededFileStatus = config.get(uri, 'Lua.diagnostics.neededFileStatus')[name]
or define.DiagnosticDefaultNeededFileStatus[name]
if neededFileStatus == 'None' then
diff --git a/script/core/diagnostics/lowercase-global.lua b/script/core/diagnostics/lowercase-global.lua
index 299ac110..d7032c13 100644
--- a/script/core/diagnostics/lowercase-global.lua
+++ b/script/core/diagnostics/lowercase-global.lua
@@ -24,7 +24,7 @@ return function (uri, callback)
end
local definedGlobal = {}
- for name in pairs(config.get 'Lua.diagnostics.globals') do
+ for name in pairs(config.get(uri, 'Lua.diagnostics.globals')) do
definedGlobal[name] = true
end
diff --git a/script/core/diagnostics/type-check.lua b/script/core/diagnostics/type-check.lua
index 8728b169..5f37312e 100644
--- a/script/core/diagnostics/type-check.lua
+++ b/script/core/diagnostics/type-check.lua
@@ -36,11 +36,11 @@ local function isTable(name)
return false
end
-local function isUserDefineClass(name)
+local function isUserDefineClass(uri, name)
if vm.isBuiltinType(name) then
return false
else
- local defs = vm.getDocDefines(name)
+ local defs = vm.getDocDefines(uri, name)
for _, v in ipairs(defs) do
if v.type == 'doc.class.name' then
return true
@@ -116,10 +116,10 @@ end
-- end
-- end
-local function addFatherClass(infers)
+local function addFatherClass(uri, infers)
for k in pairs(infers) do
if type(k) == 'string' then
- local docDefs = vm.getDocDefines(k)
+ local docDefs = vm.getDocDefines(uri, k)
for _, doc in ipairs(docDefs) do
if doc.parent
and doc.parent.type == 'doc.class'
@@ -264,7 +264,7 @@ local function getInfoFromDefs(defs)
return paramsTypes
end
-local function getArgsInfo(callArgs)
+local function getArgsInfo(uri, callArgs)
local callArgsType = {}
for _, arg in ipairs(callArgs) do
-- local defs = vm.getDefs(arg)
@@ -276,7 +276,7 @@ local function getArgsInfo(callArgs)
end
local hasAny = infers['any']
---处理继承
- addFatherClass(infers)
+ addFatherClass(uri, infers)
if not hasAny then
infers['any'] = nil
infers['unknown'] = nil
@@ -285,7 +285,7 @@ local function getArgsInfo(callArgs)
if not infers['table'] then
for k in pairs(infers) do
if not vm.isBuiltinType(k)
- and isUserDefineClass(k) then
+ and isUserDefineClass(uri, k) then
infers['table'] = true
break
end
@@ -424,7 +424,7 @@ return function (uri, callback)
end
await.delay()
local callArgs = source.args
- local suc, callArgsType = getArgsInfo(callArgs)
+ local suc, callArgsType = getArgsInfo(uri, callArgs)
if not suc then
return
end
diff --git a/script/core/diagnostics/undefined-doc-class.lua b/script/core/diagnostics/undefined-doc-class.lua
index e7133ab9..5f3902a2 100644
--- a/script/core/diagnostics/undefined-doc-class.lua
+++ b/script/core/diagnostics/undefined-doc-class.lua
@@ -25,7 +25,7 @@ return function (uri, callback)
end
for _, ext in ipairs(doc.extends) do
local name = ext[1]
- local docs = vm.getDocDefines(name)
+ local docs = vm.getDocDefines(uri, name)
if cache[name] == nil then
cache[name] = false
for _, otherDoc in ipairs(docs) do
diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua
index 48c8a226..45fc390b 100644
--- a/script/core/diagnostics/undefined-global.lua
+++ b/script/core/diagnostics/undefined-global.lua
@@ -27,10 +27,10 @@ return function (uri, callback)
if not key then
return
end
- if config.get 'Lua.diagnostics.globals'[key] then
+ if config.get(uri, 'Lua.diagnostics.globals')[key] then
return
end
- if config.get 'Lua.runtime.special'[key] then
+ if config.get(uri, 'Lua.runtime.special')[key] then
return
end
local node = src.node
diff --git a/script/core/hint.lua b/script/core/hint.lua
index 06bbf421..eebadb05 100644
--- a/script/core/hint.lua
+++ b/script/core/hint.lua
@@ -32,11 +32,11 @@ local function typeHint(uri, results, start, finish)
return
end
if source.parent.type == 'funcargs' then
- if not config.get 'Lua.hint.paramType' then
+ if not config.get(uri, 'Lua.hint.paramType') then
return
end
else
- if not config.get 'Lua.hint.setType' then
+ if not config.get(uri, 'Lua.hint.setType') then
return
end
end
@@ -99,7 +99,7 @@ end
---@async
local function paramName(uri, results, start, finish)
- local paramConfig = config.get 'Lua.hint.paramName'
+ local paramConfig = config.get(uri, 'Lua.hint.paramName')
if not paramConfig or paramConfig == 'Disable' then
return
end
@@ -162,7 +162,7 @@ end
---@async
local function awaitHint(uri, results, start, finish)
- local awaitConfig = config.get 'Lua.hint.await'
+ local awaitConfig = config.get(uri, 'Lua.hint.await')
if not awaitConfig then
return
end
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index df9fb6e9..c46b239d 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -11,11 +11,11 @@ local guide = require 'parser.guide'
local noder = require 'core.noder'
local rpath = require 'workspace.require-path'
-local function collectRequire(mode, literal)
- local rootPath = ws.path or ''
+local function collectRequire(mode, literal, uri)
+ local rootPath = ws.getRootUri(uri) or ''
local result, searchers
if mode == 'require' then
- result, searchers = rpath.findUrisByRequirePath(literal)
+ result, searchers = rpath.findUrisByRequirePath(uri, literal)
elseif mode == 'dofile'
or mode == 'loadfile' then
result = ws.findUrisByFilePath(literal)
@@ -57,7 +57,7 @@ local function asStringInRequire(source, literal)
if libName == 'require'
or libName == 'dofile'
or libName == 'loadfile' then
- return collectRequire(libName, literal)
+ return collectRequire(libName, literal, guide.getUri(source))
end
end
end
@@ -65,11 +65,11 @@ end
local function asStringView(source, literal)
-- 内部包含转义符?
local rawLen = source.finish - source.start - 2 * #source[2]
- if config.get 'Lua.hover.viewString'
+ if config.get(guide.getUri(source), 'Lua.hover.viewString')
and (source[2] == '"' or source[2] == "'")
and rawLen > #literal then
local view = literal
- local max = config.get 'Lua.hover.viewStringMax'
+ local max = config.get(guide.getUri(source), 'Lua.hover.viewStringMax')
if #view > max then
view = view:sub(1, max) .. '...'
end
@@ -157,7 +157,7 @@ local function tryDocModule(source)
if not source.module then
return
end
- return collectRequire('require', source.module)
+ return collectRequire('require', source.module, guide.getUri(source))
end
local function buildEnumChunk(docType, name)
diff --git a/script/core/hover/label.lua b/script/core/hover/label.lua
index 78756ea8..a39f0eac 100644
--- a/script/core/hover/label.lua
+++ b/script/core/hover/label.lua
@@ -157,7 +157,7 @@ local function formatNumber(n)
end
local function asNumber(source)
- if not config.get 'Lua.hover.viewNumber' then
+ if not config.get(guide.getUri(source), 'Lua.hover.viewNumber') then
return nil
end
local num = source[1]
diff --git a/script/core/hover/return.lua b/script/core/hover/return.lua
index 49f9536a..681e9747 100644
--- a/script/core/hover/return.lua
+++ b/script/core/hover/return.lua
@@ -1,4 +1,5 @@
local infer = require 'core.infer'
+local guide = require 'parser.guide'
local function getReturnDualByDoc(source)
local docs = source.bindDocs
@@ -67,7 +68,7 @@ local function asFunction(source)
end
end
if next(infers) or rtn[1] then
- local tp = infer.viewInfers(infers)
+ local tp = infer.viewInfers(guide.getUri(source), infers)
if rtn[1].name then
line[#line+1] = ('%s%s: %s'):format(
rtn[1].name[1],
diff --git a/script/core/hover/table.lua b/script/core/hover/table.lua
index 285d5c02..bd2f81ad 100644
--- a/script/core/hover/table.lua
+++ b/script/core/hover/table.lua
@@ -3,6 +3,7 @@ local util = require 'utility'
local config = require 'config'
local infer = require 'core.infer'
local await = require 'await'
+local guide = require 'parser.guide'
local function formatKey(key)
if type(key) == 'string' then
@@ -136,7 +137,7 @@ end
---@async
return function (source)
- local maxFields = config.get 'Lua.hover.previewFields'
+ local maxFields = config.get(guide.getUri(source), 'Lua.hover.previewFields')
if maxFields <= 0 then
return 'table'
end
diff --git a/script/core/infer.lua b/script/core/infer.lua
index d204fff1..982b8ef3 100644
--- a/script/core/infer.lua
+++ b/script/core/infer.lua
@@ -3,6 +3,7 @@ local config = require 'config'
local noder = require 'core.noder'
local util = require 'utility'
local vm = require "vm.vm"
+local guide = require "parser.guide"
local CLASS = {'CLASS'}
local TABLE = {'TABLE'}
@@ -232,8 +233,8 @@ local function bindClassOrType(source)
return false
end
-local function cleanInfers(infers)
- local version = config.get 'Lua.runtime.version'
+local function cleanInfers(uri, infers)
+ local version = config.get(uri, 'Lua.runtime.version')
local enableInteger = version == 'Lua 5.3' or version == 'Lua 5.4'
infers['unknown'] = nil
if infers['number'] then
@@ -269,7 +270,7 @@ end
---合并对象的推断类型
---@param infers string[]
---@return string
-function m.viewInfers(infers)
+function m.viewInfers(uri, infers)
if infers[CACHE] then
return infers[CACHE]
end
@@ -298,7 +299,7 @@ function m.viewInfers(infers)
return sa < sb
end
end)
- local limit = config.get 'Lua.hover.enumsLimit'
+ local limit = config.get(uri, 'Lua.hover.enumsLimit')
if limit < 0 then
limit = 0
end
@@ -510,7 +511,7 @@ function m.searchInfers(source, field, mark)
end
end
end
- cleanInfers(infers)
+ cleanInfers(guide.getUri(source), infers)
return infers
end
@@ -606,7 +607,7 @@ function m.searchAndViewInfers(source, field, mark)
return 'any'
end
local infers = m.searchInfers(source, field, mark)
- local view = m.viewInfers(infers)
+ local view = m.viewInfers(guide.getUri(source), infers)
if type(view) == 'boolean' then
log.error('Why view is boolean?', util.dump(infers))
return 'any'
@@ -630,8 +631,8 @@ function m.getClass(source)
end
end
end
- cleanInfers(infers)
- local view = m.viewInfers(infers)
+ cleanInfers(guide.getUri(source), infers)
+ local view = m.viewInfers(guide.getUri(source), infers)
if view == 'any' then
return nil
end
diff --git a/script/core/noder.lua b/script/core/noder.lua
index dc846cf3..6681fa7c 100644
--- a/script/core/noder.lua
+++ b/script/core/noder.lua
@@ -497,7 +497,7 @@ local function getNodeKey(source)
if methodNode then
return getNodeKey(methodNode)
end
- if config.get 'Lua.IntelliSense.traceFieldInject' then
+ if config.get(guide.getUri(source), 'Lua.IntelliSense.traceFieldInject') then
local localValueID = getLocalValueID(source)
if localValueID then
return localValueID
@@ -822,7 +822,7 @@ local function bindValue(noders, source, id)
local bindDocs = source.bindDocs
if source.type == 'getlocal'
or source.type == 'setlocal' then
- if not config.get 'Lua.IntelliSense.traceLocalSet' then
+ if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceLocalSet') then
return
end
bindDocs = source.node.bindDocs
@@ -837,7 +837,7 @@ local function bindValue(noders, source, id)
end
-- x = y : x -> y
pushForward(noders, id, valueID, INFO_REJECT_SET)
- if not config.get 'Lua.IntelliSense.traceBeSetted'
+ if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceBeSetted')
and source.type ~= 'local' then
return
end
@@ -1329,7 +1329,7 @@ compileNodeMap = util.switch()
, index
)
pushForward(noders, returnID, getID(rtnObj))
- if config.get 'Lua.IntelliSense.traceReturn' then
+ if config.get(guide.getUri(source), 'Lua.IntelliSense.traceReturn') then
pushBackward(noders, getID(rtnObj), returnID, INFO_DEEP_AND_DONT_CROSS)
end
end
diff --git a/script/core/searcher.lua b/script/core/searcher.lua
index 72dda55f..558dc96b 100644
--- a/script/core/searcher.lua
+++ b/script/core/searcher.lua
@@ -27,6 +27,7 @@ local getUri = guide.getUri
local getRoot = guide.getRoot
local ceach = collector.each
+local ceachref = collector.eachRef
local getState = files.getState
@@ -796,7 +797,7 @@ function m.searchRefsByID(status, suri, expect, mode)
if not requireName then
return
end
- local uris = rpath.findUrisByRequirePath(requireName)
+ local uris = rpath.findUrisByRequirePath(suri, requireName)
footprint(status, 'require:', requireName)
for i = 1, #uris do
local ruri = uris[i]
@@ -820,7 +821,7 @@ function m.searchRefsByID(status, suri, expect, mode)
or mode == 'alldef'
or field
or hasCall(field) then
- for _, guri in ceach('def:' .. id) do
+ for _, guri in ceach(suri, 'def:' .. id) do
if uri == guri then
goto CONTINUE
end
@@ -829,14 +830,14 @@ function m.searchRefsByID(status, suri, expect, mode)
end
elseif mode == 'field'
or mode == 'allfield' then
- for _, guri in ceach('def:' .. id) do
+ for _, guri in ceach(suri, 'def:' .. id) do
if uri == guri then
goto CONTINUE
end
searchID(guri, id, field, uri)
::CONTINUE::
end
- for _, guri in ceach('field:' .. id) do
+ for _, guri in ceach(suri, 'field:' .. id) do
if uri == guri then
goto CONTINUE
end
@@ -844,7 +845,7 @@ function m.searchRefsByID(status, suri, expect, mode)
::CONTINUE::
end
else
- for _, guri in ceach(id) do
+ for _, guri in ceachref(suri, id) do
if crossed[guri] then
goto CONTINUE
end
@@ -872,7 +873,7 @@ function m.searchRefsByID(status, suri, expect, mode)
or ignoredIDs[id]
or id == 'dn:string'
or hasCall(field) then
- for _, guri in ceach('def:' .. id) do
+ for _, guri in ceach(suri, 'def:' .. id) do
if uri == guri then
goto CONTINUE
end
@@ -880,7 +881,7 @@ function m.searchRefsByID(status, suri, expect, mode)
::CONTINUE::
end
else
- for _, guri in ceach(id) do
+ for _, guri in ceachref(suri, id) do
if crossed[guri] then
goto CONTINUE
end
diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua
index 8389cbb4..12848d2b 100644
--- a/script/core/semantic-tokens.lua
+++ b/script/core/semantic-tokens.lua
@@ -9,10 +9,8 @@ local converter = require 'proto.converter'
local infer = require 'core.infer'
local config = require 'config'
-local isEnhanced = config.get 'Lua.color.mode' == 'SemanticEnhanced'
-
local Care = {}
-Care['getglobal'] = function (source, results)
+Care['getglobal'] = function (source, options, results)
local isLib = vm.isGlobalLibraryName(source[1])
local isFunc = false
local value = source.value
@@ -21,7 +19,7 @@ Care['getglobal'] = function (source, results)
isFunc = true
elseif source.parent.type == 'call' then
isFunc = true
- elseif isEnhanced then
+ elseif options.isEnhanced then
isFunc = infer.hasType(source, 'function')
end
@@ -36,7 +34,7 @@ Care['getglobal'] = function (source, results)
}
end
Care['setglobal'] = Care['getglobal']
-Care['getmethod'] = function (source, results)
+Care['getmethod'] = function (source, options, results)
local method = source.method
if method and method.type == 'method' then
results[#results+1] = {
@@ -48,7 +46,7 @@ Care['getmethod'] = function (source, results)
end
end
Care['setmethod'] = Care['getmethod']
-Care['field'] = function (source, results)
+Care['field'] = function (source, options, results)
local modifiers = 0
if source.parent and source.parent.type == 'tablefield' then
modifiers = define.TokenModifiers.declaration
@@ -65,7 +63,7 @@ Care['field'] = function (source, results)
return
end
end
- if isEnhanced and infer.hasType(source, 'function') then
+ if options.isEnhanced and infer.hasType(source, 'function') then
results[#results+1] = {
start = source.start,
finish = source.finish,
@@ -90,7 +88,7 @@ Care['field'] = function (source, results)
modifieres = modifiers,
}
end
-Care['getlocal'] = function (source, results)
+Care['getlocal'] = function (source, options, results)
local loc = source.node
-- 1. 值为函数的局部变量 | Local variable whose value is a function
if loc.refs then
@@ -127,7 +125,7 @@ Care['getlocal'] = function (source, results)
return
end
-- 5. References to other functions
- if isEnhanced and infer.hasType(loc, 'function') then
+ if options.isEnhanced and infer.hasType(loc, 'function') then
results[#results+1] = {
start = source.start,
finish = source.finish,
@@ -137,7 +135,7 @@ Care['getlocal'] = function (source, results)
return
end
-- 6. Class declaration
- if isEnhanced then
+ if options.isEnhanced then
-- search all defs
for _, def in ipairs(vm.getDefs(source)) do
if def.bindDocs then
@@ -214,7 +212,7 @@ Care['getlocal'] = function (source, results)
}
end
Care['setlocal'] = Care['getlocal']
-Care['local'] = function (source, results) -- Local declaration, i.e. "local x", "local y = z", or "local function() end"
+Care['local'] = function (source, options, results) -- Local declaration, i.e. "local x", "local y = z", or "local function() end"
if source[1] == '_ENV'
or source[1] == 'self' then
return
@@ -231,7 +229,7 @@ Care['local'] = function (source, results) -- Local declaration, i.e. "local x",
if source.value then
local isFunction = false
- if isEnhanced then
+ if options.isEnhanced then
isFunction = source.value.type == 'function' or infer.hasType(source.value, 'function')
else
isFunction = source.value.type == 'function'
@@ -299,21 +297,21 @@ Care['local'] = function (source, results) -- Local declaration, i.e. "local x",
modifieres = modifiers,
}
end
-Care['doc.return.name'] = function (source, results)
+Care['doc.return.name'] = function (source, options, results)
results[#results+1] = {
start = source.start,
finish = source.finish,
type = define.TokenTypes.parameter,
}
end
-Care['doc.tailcomment'] = function (source, results)
+Care['doc.tailcomment'] = function (source, options, results)
results[#results+1] = {
start = source.start,
finish = source.finish,
type = define.TokenTypes.comment,
}
end
-Care['doc.type.name'] = function (source, results)
+Care['doc.type.name'] = function (source, options, results)
if source.typeGeneric then
results[#results+1] = {
start = source.start,
@@ -323,14 +321,14 @@ Care['doc.type.name'] = function (source, results)
end
end
-Care['nonstandardSymbol.comment'] = function (source, results)
+Care['nonstandardSymbol.comment'] = function (source, options, results)
results[#results+1] = {
start = source.start,
finish = source.finish,
type = define.TokenTypes.comment,
}
end
-Care['nonstandardSymbol.continue'] = function (source, results)
+Care['nonstandardSymbol.continue'] = function (source, options, results)
results[#results+1] = {
start = source.start,
finish = source.finish,
@@ -367,27 +365,24 @@ local function buildTokens(uri, results)
return tokens
end
-config.watch(function (key, value)
- if key == 'Lua.color.mode' then
- isEnhanced = value == 'SemanticEnhanced'
- end
-end)
-
---@async
return function (uri, start, finish)
local state = files.getState(uri)
- local text = files.getText(uri)
if not state then
return nil
end
+ local options = {
+ isEnhanced = config.get(uri, 'Lua.color.mode') == 'SemanticEnhanced',
+ }
+
local results = {}
guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
local method = Care[source.type]
if not method then
return
end
- method(source, results)
+ method(source, options, results)
await.delay()
end)
diff --git a/script/core/type-definition.lua b/script/core/type-definition.lua
index e9cf3e47..1f021fb3 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(literal)
+ return rpath.findUrisByRequirePath(guide.getUri(source), literal)
elseif libName == 'dofile'
or libName == 'loadfile' then
return workspace.findUrisByFilePath(literal)
diff --git a/script/file-uri.lua b/script/file-uri.lua
index d849eac1..7f3e36a8 100644
--- a/script/file-uri.lua
+++ b/script/file-uri.lua
@@ -24,6 +24,9 @@ local m = {}
---@param path string
---@return uri uri
function m.encode(path)
+ if not path then
+ return nil
+ end
local authority = ''
if platform.OS == 'Windows' then
path = path:gsub('\\', '/')
@@ -67,6 +70,9 @@ end
---@param uri uri
---@return string path
function m.decode(uri)
+ if not uri then
+ return nil
+ end
local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)')
if not scheme then
return ''
diff --git a/script/files.lua b/script/files.lua
index 074a262e..d18fade3 100644
--- a/script/files.lua
+++ b/script/files.lua
@@ -64,6 +64,7 @@ function m.open(uri)
cache = {},
}
m.onWatch('open', uri)
+ m.addRef(uri)
end
--- 关闭文件
@@ -75,6 +76,7 @@ function m.close(uri)
file.trusted = false
end
m.onWatch('close', uri)
+ m.delRef(uri)
end
--- 是否打开
@@ -93,8 +95,12 @@ function m.getOpenedCache(uri)
end
--- 标记为库文件
-function m.setLibraryPath(uri, libraryPath)
- m.libraryMap[uri] = libraryPath
+---@param scp scope
+---@param uri uri
+---@param libraryUri uri
+function m.setLibraryUri(scp, uri, libraryUri)
+ scp:get 'libraryMap' [uri] = libraryUri
+ scp:addLink(uri)
end
--- 是否是库文件
@@ -107,8 +113,9 @@ function m.getLibraryPath(uri)
return m.libraryMap[uri]
end
-function m.flushAllLibrary()
- m.libraryMap = {}
+---@param scp scope
+function m.flushAllLibrary(scp)
+ scp:set('libraryMap', {})
end
--- 是否存在
@@ -174,7 +181,7 @@ function m.setText(uri, text, isTrust, version)
return
end
if not isTrust then
- local encoding = config.get 'Lua.runtime.fileEncoding'
+ local encoding = config.get(uri, 'Lua.runtime.fileEncoding')
text = encoder.decode(encoding, text)
end
file.version = version
@@ -344,6 +351,28 @@ function m.getChildFiles(uri)
return results
end
+function m.addRef(uri)
+ local file = m.fileMap[uri]
+ if not file then
+ return nil
+ end
+ file._ref = (file._ref or 0) + 1
+ return function ()
+ m.delRef(uri)
+ end
+end
+
+function m.delRef(uri)
+ local file = m.fileMap[uri]
+ if not file then
+ return
+ end
+ file._ref = (file._ref or 0) - 1
+ if file._ref <= 0 then
+ m.remove(uri)
+ end
+end
+
--- 移除文件
---@param uri uri
function m.remove(uri)
@@ -355,7 +384,6 @@ function m.remove(uri)
m.fileMap[uri] = nil
m.astMap[uri] = nil
m._pairsCache = nil
- m.flushFileCache(uri)
m.fileCount = m.fileCount - 1
m.globalVersion = m.globalVersion + 1
@@ -365,43 +393,6 @@ function m.remove(uri)
m.onWatch('remove', originUri)
end
---- 移除所有文件
-function m.removeAll()
- local ws = require 'workspace.workspace'
- m.globalVersion = m.globalVersion + 1
- await.close('files.version')
- m.onWatch('version')
- m._pairsCache = nil
- for uri in pairs(m.fileMap) do
- if not m.libraryMap[uri] then
- m.fileCount = m.fileCount - 1
- m.fileMap[uri] = nil
- m.astMap[uri] = nil
- m.onWatch('remove', uri)
- end
- end
- ws.flushCache()
- --m.notifyCache = {}
-end
-
---- 移除所有关闭的文件
-function m.removeAllClosed()
- m.globalVersion = m.globalVersion + 1
- await.close('files.version')
- m.onWatch('version')
- m._pairsCache = nil
- for uri in pairs(m.fileMap) do
- if not m.openMap[uri]
- and not m.libraryMap[uri] then
- m.fileCount = m.fileCount - 1
- m.fileMap[uri] = nil
- m.astMap[uri] = nil
- m.onWatch('remove', uri)
- end
- end
- --m.notifyCache = {}
-end
-
--- 获取一个包含所有文件uri的数组
---@return uri[]
function m.getAllUris()
@@ -452,7 +443,7 @@ function m.compileState(uri, text)
local client = require 'client'
if not m.isOpen(uri)
and not m.isLibrary(uri)
- and #text >= config.get 'Lua.workspace.preloadFileSize' * 1000 then
+ and #text >= config.get(uri, 'Lua.workspace.preloadFileSize') * 1000 then
if not m.notifyCache['preloadFileSize'] then
m.notifyCache['preloadFileSize'] = {}
m.notifyCache['skipLargeFileCount'] = 0
@@ -462,7 +453,7 @@ function m.compileState(uri, text)
m.notifyCache['skipLargeFileCount'] = m.notifyCache['skipLargeFileCount'] + 1
local message = lang.script('WORKSPACE_SKIP_LARGE_FILE'
, ws.getRelativePath(uri)
- , config.get 'Lua.workspace.preloadFileSize'
+ , config.get(uri, 'Lua.workspace.preloadFileSize')
, #text / 1000
)
if m.notifyCache['skipLargeFileCount'] <= 1 then
@@ -478,11 +469,11 @@ function m.compileState(uri, text)
local clock = os.clock()
local state, err = parser.compile(text
, 'Lua'
- , config.get 'Lua.runtime.version'
+ , config.get(uri, 'Lua.runtime.version')
, {
- special = config.get 'Lua.runtime.special',
- unicodeName = config.get 'Lua.runtime.unicodeName',
- nonstandardSymbol = config.get 'Lua.runtime.nonstandardSymbol',
+ special = config.get(uri, 'Lua.runtime.special'),
+ unicodeName = config.get(uri, 'Lua.runtime.unicodeName'),
+ nonstandardSymbol = config.get(uri, 'Lua.runtime.nonstandardSymbol'),
}
)
local passed = os.clock() - clock
@@ -633,13 +624,9 @@ function m.getCache(uri)
end
--- 获取文件关联
-function m.getAssoc()
- if m.assocVersion == config.get 'version' then
- return m.assocMatcher
- end
- m.assocVersion = config.get 'version'
+function m.getAssoc(uri)
local patt = {}
- for k, v in pairs(config.get 'files.associations') do
+ for k, v in pairs(config.get(uri, 'files.associations')) do
if v == 'lua' then
patt[#patt+1] = k
end
@@ -659,7 +646,7 @@ function m.isLua(uri)
if ext == 'lua' then
return true
end
- local matcher = m.getAssoc()
+ local matcher = m.getAssoc(uri)
local path = furi.decode(uri)
return matcher(path)
end
@@ -752,24 +739,6 @@ function m.onWatch(ev, uri)
end
end
-function m.flushCache()
- for uri, file in pairs(m.fileMap) do
- file.cacheActiveTime = math.huge
- m.astMap[uri] = nil
- file.cache = {}
- end
-end
-
-function m.flushFileCache(uri)
- local file = m.fileMap[uri]
- if not file then
- return
- end
- file.cacheActiveTime = math.huge
- m.astMap[uri] = nil
- file.cache = {}
-end
-
function m.init()
--TODO 可以清空文件缓存,之后看要不要启用吧
--timer.loop(10, function ()
diff --git a/script/library.lua b/script/library.lua
index daf91a38..3fa202b6 100644
--- a/script/library.lua
+++ b/script/library.lua
@@ -11,11 +11,14 @@ local files = require 'files'
local await = require 'await'
local timer = require 'timer'
local encoder = require 'encoder'
+local ws = require 'workspace.workspace'
local m = {}
-local function getDocFormater()
- local version = config.get 'Lua.runtime.version'
+m.metaPaths = {}
+
+local function getDocFormater(uri)
+ local version = config.get(uri, 'Lua.runtime.version')
if client.isVSCode() then
if version == 'Lua 5.1' then
return 'HOVER_NATIVE_DOCUMENT_LUA51'
@@ -43,8 +46,8 @@ local function getDocFormater()
end
end
-local function convertLink(text)
- local fmt = getDocFormater()
+local function convertLink(uri, text)
+ local fmt = getDocFormater(uri)
return text:gsub('%$([%.%w]+)', function (name)
local lastDot = ''
if name:sub(-1) == '.' then
@@ -82,7 +85,7 @@ local function createViewDocument(name)
return ('[%s](%s)'):format(lang.script.HOVER_VIEW_DOCUMENTS, lang.script(fmt, 'pdf-' .. name))
end
-local function compileSingleMetaDoc(script, metaLang, status)
+local function compileSingleMetaDoc(uri, script, metaLang, status)
if not script then
return nil
end
@@ -99,11 +102,11 @@ local function compileSingleMetaDoc(script, metaLang, status)
middleBuf[#middleBuf+1] = ('PUSH [===[%s]===]'):format(script:sub(last))
local middleScript = table.concat(middleBuf, '\n')
local version, jit
- if config.get 'Lua.runtime.version' == 'LuaJIT' then
+ if config.get(uri, 'Lua.runtime.version') == 'LuaJIT' then
version = 5.1
jit = true
else
- version = tonumber(config.get 'Lua.runtime.version':sub(-3))
+ version = tonumber(config.get(uri, 'Lua.runtime.version'):sub(-3))
jit = false
end
@@ -122,7 +125,7 @@ local function compileSingleMetaDoc(script, metaLang, status)
compileBuf[#compileBuf+1] = '---\n'
for line in util.eachLine(des) do
compileBuf[#compileBuf+1] = '---'
- compileBuf[#compileBuf+1] = convertLink(line)
+ compileBuf[#compileBuf+1] = convertLink(uri, line)
compileBuf[#compileBuf+1] = '\n'
end
local viewDocument = createViewDocument(name)
@@ -138,7 +141,7 @@ local function compileSingleMetaDoc(script, metaLang, status)
if not des then
des = ('Miss locale <%s>'):format(name)
end
- compileBuf[#compileBuf+1] = convertLink(des)
+ compileBuf[#compileBuf+1] = convertLink(uri, des)
compileBuf[#compileBuf+1] = '\n'
end,
ALIVE = function (str)
@@ -197,14 +200,12 @@ local function loadMetaLocale(langID, result)
return result
end
-local function initBuiltIn()
- if not m.inited then
- return
- end
+local function initBuiltIn(uri)
+ local scp = ws.getScope(uri)
local langID = lang.id
- local version = config.get 'Lua.runtime.version'
- local encoding = config.get 'Lua.runtime.fileEncoding'
- local metaPath = fs.path(METAPATH) / config.get 'Lua.runtime.meta':gsub('%$%{(.-)%}', {
+ local version = config.get(uri, 'Lua.runtime.version')
+ local encoding = config.get(uri, 'Lua.runtime.fileEncoding')
+ local metaPath = fs.path(METAPATH) / config.get(uri, 'Lua.runtime.meta'):gsub('%$%{(.-)%}', {
version = version,
language = langID,
encoding = encoding,
@@ -214,13 +215,11 @@ local function initBuiltIn()
if langID ~= 'en-US' then
loadMetaLocale(langID, metaLang)
end
- --log.debug('metaLang:', util.dump(metaLang))
- if m.metaPath == metaPath:string() then
+ if scp:get('metaPath') == metaPath:string() then
return
end
- m.metaPath = metaPath:string()
- m.metaPaths = {}
+ scp:set('metaPath', metaPath:string())
local suc = xpcall(function ()
if not fs.exists(metaPath) then
fs.create_directories(metaPath)
@@ -232,18 +231,18 @@ local function initBuiltIn()
local out = fsu.dummyFS()
local templateDir = ROOT / 'meta' / 'template'
for libName, status in pairs(define.BuiltIn) do
- status = config.get 'Lua.runtime.builtin'[libName] or status
+ status = config.get(uri, 'Lua.runtime.builtin')[libName] or status
if status == 'disable' then
goto CONTINUE
end
libName = libName .. '.lua'
local libPath = templateDir / libName
- local metaDoc = compileSingleMetaDoc(fsu.loadFile(libPath), metaLang, status)
+ local metaDoc = compileSingleMetaDoc(uri, fsu.loadFile(libPath), metaLang, status)
if metaDoc then
- local outPath = metaPath / libName
metaDoc = encoder.encode(encoding, metaDoc, 'auto')
out:saveFile(libName, metaDoc)
- m.metaPaths[#m.metaPaths+1] = outPath:string()
+ local outputPath = metaPath / libName
+ m.metaPaths[outputPath:string()] = true
end
::CONTINUE::
end
@@ -310,17 +309,17 @@ local function load3rdConfigInDir(dir, configs, inner)
end
end
-local function load3rdConfig()
+local function load3rdConfig(uri)
local configs = {}
load3rdConfigInDir(innerThirdDir, configs, true)
- local thirdDirs = config.get 'Lua.workspace.userThirdParty'
+ local thirdDirs = config.get(uri, 'Lua.workspace.userThirdParty')
for _, thirdDir in ipairs(thirdDirs) do
load3rdConfigInDir(fs.path(thirdDir), configs)
end
return configs
end
-local function apply3rd(cfg, onlyMemory)
+local function apply3rd(uri, cfg, onlyMemory)
local changes = {}
if cfg.configs then
for _, change in ipairs(cfg.configs) do
@@ -333,6 +332,7 @@ local function apply3rd(cfg, onlyMemory)
key = 'Lua.runtime.plugin',
action = 'set',
value = ('%s/plugin.lua'):format(cfg.dirname),
+ uri = uri,
}
end
@@ -340,6 +340,7 @@ local function apply3rd(cfg, onlyMemory)
key = 'Lua.workspace.library',
action = 'add',
value = ('%s/library'):format(cfg.dirname),
+ uri = uri,
}
client.setConfig(changes, onlyMemory)
@@ -347,7 +348,7 @@ end
local hasAsked
---@async
-local function askFor3rd(cfg)
+local function askFor3rd(uri, cfg)
hasAsked = true
local yes1 = lang.script.WINDOW_APPLY_WHIT_SETTING
local yes2 = lang.script.WINDOW_APPLY_WHITOUT_SETTING
@@ -360,21 +361,23 @@ local function askFor3rd(cfg)
return nil
end
if result == yes1 then
- apply3rd(cfg, false)
+ apply3rd(uri, cfg, false)
client.setConfig({
{
key = 'Lua.workspace.checkThirdParty',
action = 'set',
value = false,
+ uri = uri,
},
}, false)
elseif result == yes2 then
- apply3rd(cfg, true)
+ apply3rd(uri, cfg, true)
client.setConfig({
{
key = 'Lua.workspace.checkThirdParty',
action = 'set',
value = false,
+ uri = uri,
},
}, true)
end
@@ -397,7 +400,7 @@ local function wholeMatch(a, b)
return true
end
-local function check3rdByWords(text, configs)
+local function check3rdByWords(uri, text, configs)
if hasAsked then
return
end
@@ -407,7 +410,7 @@ local function check3rdByWords(text, configs)
for _, word in ipairs(cfg.words) do
await.delay()
if wholeMatch(text, word) then
- askFor3rd(cfg)
+ askFor3rd(uri, cfg)
return
end
end
@@ -420,7 +423,6 @@ local function check3rdByFileName(uri, configs)
if hasAsked then
return
end
- local ws = require 'workspace'
local path = ws.getRelativePath(uri)
if not path then
return
@@ -431,7 +433,7 @@ local function check3rdByFileName(uri, configs)
for _, filename in ipairs(cfg.files) do
await.delay()
if wholeMatch(path, filename) then
- askFor3rd(cfg)
+ askFor3rd(uri, cfg)
return
end
end
@@ -455,11 +457,11 @@ local function check3rd(uri)
if hasAsked then
return
end
- if not config.get 'Lua.workspace.checkThirdParty' then
+ if not config.get(uri, 'Lua.workspace.checkThirdParty') then
return
end
if thirdConfigs == nil then
- thirdConfigs = load3rdConfig() or false
+ thirdConfigs = load3rdConfig(uri) or false
end
if not thirdConfigs then
return
@@ -468,16 +470,16 @@ local function check3rd(uri)
if files.isLua(uri) then
local text = files.getText(uri)
if text then
- check3rdByWords(text, thirdConfigs)
+ check3rdByWords(uri, text, thirdConfigs)
end
end
check3rdByFileName(uri, thirdConfigs)
end
end
-config.watch(function (key, value, oldValue)
+config.watch(function (uri, key, value, oldValue)
if key:find '^Lua.runtime' then
- initBuiltIn()
+ initBuiltIn(uri)
end
end)
@@ -493,7 +495,13 @@ function m.init()
return
end
m.inited = true
- initBuiltIn()
+ if #ws.folders == 0 then
+ initBuiltIn(nil)
+ else
+ for _, scp in ipairs(ws.folders) do
+ initBuiltIn(scp.uri)
+ end
+ end
end
return m
diff --git a/script/plugin.lua b/script/plugin.lua
index f56dc9f9..0914c0c0 100644
--- a/script/plugin.lua
+++ b/script/plugin.lua
@@ -43,13 +43,6 @@ function m.isReady()
return m.interface ~= nil
end
-local function resetFiles()
- local files = require 'files'
- for uri in files.eachFile() do
- files.resetText(uri)
- end
-end
-
---@async
local function checkTrustLoad()
local filePath = LOGPATH .. '/trusted'
@@ -75,7 +68,8 @@ local function checkTrustLoad()
return true
end
-function m.init()
+---@param scp scope
+function m.init(scp)
if m.hasInited then
return
end
@@ -84,7 +78,7 @@ function m.init()
local ws = require 'workspace'
m.interface = {}
- local pluginPath = ws.getAbsolutePath(config.get 'Lua.runtime.plugin')
+ local pluginPath = ws.getAbsolutePath(scp.uri, config.get(scp.uri, 'Lua.runtime.plugin'))
log.info('plugin path:', pluginPath)
if not pluginPath then
return
@@ -110,7 +104,7 @@ function m.init()
return
end
- resetFiles()
+ ws.resetFiles(scp)
end)
end
diff --git a/script/progress.lua b/script/progress.lua
index 8ae5e9e0..d5c174ce 100644
--- a/script/progress.lua
+++ b/script/progress.lua
@@ -10,6 +10,7 @@ local m = {}
m.map = {}
---@class progress
+---@field _scp scope
local mt = {}
mt.__index = mt
mt._token = nil
@@ -83,7 +84,7 @@ function mt:_update()
and self._clock + self._delay <= os.clock() then
self._updated = os.clock()
self._dirty = false
- if not config.get 'Lua.window.progressBar' then
+ if not config.get(self._scp.uri, 'Lua.window.progressBar') then
return
end
proto.request('window/workDoneProgress/create', {
@@ -106,7 +107,7 @@ function mt:_update()
if not self._showed then
return
end
- if not config.get 'Lua.window.progressBar' then
+ if not config.get(self._scp.uri, 'Lua.window.progressBar') then
self:remove()
return
end
@@ -143,14 +144,16 @@ function m.update()
end
---创建一个进度条
+---@param scp scope
---@param title string # 标题
---@param delay number # 至少经过这么久之后才会显示出来
-function m.create(title, delay)
+function m.create(scp, title, delay)
local prog = setmetatable({
_token = nextToken(),
_title = title,
_clock = os.clock(),
_delay = delay,
+ _scp = scp,
}, mt)
m.map[prog._token] = prog
diff --git a/script/provider/capability.lua b/script/provider/capability.lua
index b712defc..439978d6 100644
--- a/script/provider/capability.lua
+++ b/script/provider/capability.lua
@@ -81,11 +81,11 @@ function m.getIniter()
},
executeCommandProvider = {
commands = {
- 'lua.removeSpace:' .. sp:get_id(),
- 'lua.solve:' .. sp:get_id(),
- 'lua.jsonToLua:' .. sp:get_id(),
- 'lua.setConfig:' .. sp:get_id(),
- 'lua.autoRequire:' .. sp:get_id(),
+ 'lua.removeSpace',
+ 'lua.solve',
+ 'lua.jsonToLua',
+ 'lua.setConfig',
+ 'lua.autoRequire',
},
},
foldingRangeProvider = true,
diff --git a/script/provider/completion.lua b/script/provider/completion.lua
index ec31858a..ec2ac6c7 100644
--- a/script/provider/completion.lua
+++ b/script/provider/completion.lua
@@ -2,6 +2,7 @@ local proto = require 'proto'
local nonil = require 'without-check-nil'
local client = require 'client'
local config = require 'config'
+local ws = require 'workspace'
local isEnable = false
@@ -13,15 +14,17 @@ local function allWords()
list[#list+1] = c
mark[c] = true
end
- local postfix = config.get 'Lua.completion.postfix'
- if postfix ~= '' and not mark[postfix] then
- list[#list+1] = postfix
- mark[postfix] = true
+ for _, scp in ipairs(ws.folders) do
+ local postfix = config.get(scp.uri, 'Lua.completion.postfix')
+ if postfix ~= '' and not mark[postfix] then
+ list[#list+1] = postfix
+ mark[postfix] = true
+ end
end
return list
end
-local function enable()
+local function enable(uri)
if isEnable then
return
end
@@ -46,7 +49,7 @@ local function enable()
})
end
-local function disable()
+local function disable(uri)
if not isEnable then
return
end
@@ -67,18 +70,18 @@ local function disable()
})
end
-config.watch(function (key, value)
+config.watch(function (uri, key, value)
if key == 'Lua.completion.enable' then
if value == true then
- enable()
+ enable(uri)
else
- disable()
+ disable(uri)
end
end
if key == 'Lua.completion.postfix' then
- if config.get 'Lua.completion.enable' then
- disable()
- enable()
+ if config.get(uri, 'Lua.completion.enable') then
+ disable(uri)
+ enable(uri)
end
end
end)
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index 03b5e179..4a6706bb 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -13,9 +13,9 @@ local converter = require 'proto.converter'
---@class diagnosticProvider
local m = {}
-m._start = false
m.cache = {}
m.sleepRest = 0.0
+m.coroutineUri = setmetatable({}, { __mode = 'k' })
local function concat(t, sep)
if type(t) ~= 'table' then
@@ -29,7 +29,7 @@ local function buildSyntaxError(uri, err)
local message = lang.script('PARSER_'..err.type, err.info)
if err.version then
- local version = err.info and err.info.version or config.get 'Lua.runtime.version'
+ local version = err.info and err.info.version or config.get(uri, 'Lua.runtime.version')
message = message .. ('(%s)'):format(lang.script('DIAG_NEED_VERSION'
, concat(err.version, '/')
, version
@@ -159,7 +159,7 @@ function m.syntaxErrors(uri, ast)
pcall(function ()
for _, err in ipairs(ast.errs) do
- if not config.get 'Lua.diagnostics.disable'[err.type:lower():gsub('_', '-')] then
+ if not config.get(uri, 'Lua.diagnostics.disable')[err.type:lower():gsub('_', '-')] then
results[#results+1] = buildSyntaxError(uri, err)
end
end
@@ -169,11 +169,7 @@ function m.syntaxErrors(uri, ast)
end
function m.diagnostics(uri, diags)
- if not m._start then
- return
- end
-
- if not ws.isReady() then
+ if not ws.isReady(uri) then
return
end
@@ -189,11 +185,11 @@ end
---@async
function m.doDiagnostic(uri)
- if not config.get 'Lua.diagnostics.enable' then
+ if not config.get(uri, 'Lua.diagnostics.enable') then
return
end
if files.isLibrary(uri) then
- local status = config.get 'Lua.diagnostics.libraryFiles'
+ local status = config.get(uri, 'Lua.diagnostics.libraryFiles')
if status == 'Disable' then
return
elseif status == 'Opened' then
@@ -203,7 +199,7 @@ function m.doDiagnostic(uri)
end
end
if ws.isIgnored(uri) then
- local status = config.get 'Lua.diagnostics.ignoredFiles'
+ local status = config.get(uri, 'Lua.diagnostics.ignoredFiles')
if status == 'Disable' then
return
elseif status == 'Opened' then
@@ -215,18 +211,19 @@ function m.doDiagnostic(uri)
await.delay()
- local ast = files.getState(uri)
- if not ast then
+ local state = files.getState(uri)
+ if not state then
m.clear(uri)
return
end
local version = files.getVersion(uri)
+ local scp = ws.getScope(uri)
local prog <close> = progress.create(lang.script.WINDOW_DIAGNOSING, 0.5)
prog:setMessage(ws.getRelativePath(uri))
- local syntax = m.syntaxErrors(uri, ast)
+ local syntax = m.syntaxErrors(uri, state)
local diags = {}
local function pushResult()
tracy.ZoneBeginN 'mergeSyntaxAndDiags'
@@ -252,25 +249,25 @@ function m.doDiagnostic(uri)
end
end
- if await.hasID 'diagnosticsAll' then
- m.checkStepResult = nil
+ if await.hasID('diagnosticsScope:' .. uri) then
+ scp:set('diagStepPush', nil)
else
local clock = os.clock()
- m.checkStepResult = function ()
+ scp:set('diagStepPush', function ()
if os.clock() - clock >= 0.2 then
pushResult()
clock = os.clock()
end
- end
+ end)
end
m.diagnostics(uri, diags)
pushResult()
- m.checkStepResult = nil
+ scp:set('diagStepPush', nil)
end
function m.refresh(uri)
- if not m._start then
+ if not ws.isReady(uri) then
return
end
await.close('diag:' .. uri)
@@ -281,12 +278,12 @@ function m.refresh(uri)
m.clearCache(uri)
xpcall(m.doDiagnostic, log.error, uri)
end
- m.diagnosticsAll()
+ m.diagnosticsScope(uri)
end, 'files.version')
end
---@async
-local function askForDisable()
+local function askForDisable(uri)
if m.dontAskedForDisable then
return
end
@@ -318,6 +315,7 @@ local function askForDisable()
key = 'Lua.diagnostics.workspaceDelay',
action = 'set',
value = delay * 1000,
+ uri = uri,
}
}
elseif item.title == lang.script.WINDOW_DISABLE_DIAGNOSTIC then
@@ -326,25 +324,27 @@ local function askForDisable()
key = 'Lua.diagnostics.workspaceDelay',
action = 'set',
value = -1,
+ uri = uri,
}
}
end
end
-function m.diagnosticsAll(force)
- if not force and not config.get 'Lua.diagnostics.enable' then
- m.clearAll()
+function m.diagnosticsScope(uri, force)
+ if not ws.isReady(uri) then
return
end
- if not m._start then
+ if not force and not config.get(uri, 'Lua.diagnostics.enable') then
+ m.clearAll()
return
end
- local delay = config.get 'Lua.diagnostics.workspaceDelay' / 1000
+ local delay = config.get(uri, 'Lua.diagnostics.workspaceDelay') / 1000
if not force and delay < 0 then
return
end
- await.close 'diagnosticsAll'
+ await.close ('diagnosticsScope:' .. uri)
await.call(function () ---@async
+ m.coroutineUri[coroutine.running()] = uri
await.sleep(delay)
m.diagnosticsAllClock = os.clock()
local clock = os.clock()
@@ -353,7 +353,10 @@ function m.diagnosticsAll(force)
bar:onCancel(function ()
log.debug('Cancel workspace diagnostics')
cancelled = true
- await.call(askForDisable)
+ ---@async
+ await.call(function ()
+ askForDisable(uri)
+ end)
end)
local uris = files.getAllUris()
for i, uri in ipairs(uris) do
@@ -368,31 +371,26 @@ function m.diagnosticsAll(force)
end
bar:remove()
log.debug('全文诊断耗时:', os.clock() - clock)
- end, 'files.version', 'diagnosticsAll')
-end
-
-function m.start()
- m._start = true
- m.diagnosticsAll()
-end
-
-function m.pause()
- m._start = false
- await.close 'diagnosticsAll'
+ end, 'files.version', ('diagnosticsScope:' .. uri))
end
-function m.checkStepResult()
- if await.hasID 'diagnosticsAll' then
+function m.checkStepResult(uri)
+ if await.hasID('diagnosticsScope:' .. uri) then
return
end
+ local scp = ws.getScope(uri)
+ local stepPush = scp:get 'diagStepPush'
+ if stepPush then
+ stepPush()
+ end
end
---@async
-function m.checkWorkspaceDiag()
- if not await.hasID 'diagnosticsAll' then
+function m.checkWorkspaceDiag(uri)
+ if not await.hasID('diagnosticsScope:' .. uri) then
return
end
- local speedRate = config.get 'Lua.diagnostics.workspaceRate'
+ local speedRate = config.get(uri, 'Lua.diagnostics.workspaceRate')
if speedRate <= 0 or speedRate >= 100 then
return
end
@@ -413,6 +411,12 @@ function m.checkWorkspaceDiag()
return false
end
+ws.watch(function (ev, uri)
+ if ev == 'reload' then
+ m.diagnosticsScope(uri)
+ end
+end)
+
files.watch(function (ev, uri) ---@async
if ev == 'remove' then
m.clear(uri)
@@ -435,17 +439,19 @@ end)
await.watch(function (ev, co) ---@async
if ev == 'delay' then
- if m.checkStepResult then
- m.checkStepResult()
+ local uri = m.coroutineUri[co]
+ if not uri then
+ return
end
- return m.checkWorkspaceDiag()
+ m.checkStepResult(uri)
+ return m.checkWorkspaceDiag(uri)
end
end)
-config.watch(function (key, value, oldValue)
+config.watch(function (uri, key, value, oldValue)
if key:find 'Lua.diagnostics' then
if value ~= oldValue then
- m.diagnosticsAll()
+ m.diagnosticsScope(uri)
end
end
end)
diff --git a/script/provider/init.lua b/script/provider/init.lua
index a69fbab3..dfba351a 100644
--- a/script/provider/init.lua
+++ b/script/provider/init.lua
@@ -1 +1,2 @@
+require 'provider.diagnostic'
return require 'provider.provider'
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index cf06090b..77c45778 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -16,30 +16,41 @@ local cfgLoader = require 'config.loader'
local converter = require 'proto.converter'
local filewatch = require 'filewatch'
local json = require 'json'
+local scope = require 'workspace.scope'
---@async
-local function updateConfig()
- local clientConfig = cfgLoader.loadClientConfig()
- if clientConfig then
- log.debug('load config from client')
- config.update(clientConfig, json.null)
+local function updateConfig(uri)
+ local specified = cfgLoader.loadLocalConfig(uri, CONFIGPATH)
+ if specified then
+ log.debug('Load config from specified', CONFIGPATH)
+ log.debug(util.dump(specified))
+ -- watch directory
+ filewatch.watch(workspace.getAbsolutePath(uri, CONFIGPATH):gsub('[^/\\]+$', ''))
+ config.update(scope.override, specified, json.null)
end
- local rc = cfgLoader.loadRCConfig('.luarc.json')
- if rc then
- log.debug('load config from luarc')
- config.update(rc, json.null)
- end
+ for _, folder in ipairs(scope.folders) do
+ local uri = folder.uri
- local cfg = cfgLoader.loadLocalConfig(CONFIGPATH)
- if cfg then
- log.debug('load config from local', CONFIGPATH)
- -- watch directory
- filewatch.watch(workspace.getAbsolutePath(CONFIGPATH):gsub('[^/\\]+$', ''))
- config.update(cfg, json.null)
+ local clientConfig = cfgLoader.loadClientConfig(uri)
+ if clientConfig then
+ log.debug('Load config from client', uri)
+ log.debug(util.dump(clientConfig))
+ config.update(folder, clientConfig, json.null)
+ end
+
+ local rc = cfgLoader.loadRCConfig(uri, '.luarc.json')
+ if rc then
+ log.debug('Load config from luarc.json', uri)
+ log.debug(util.dump(rc))
+ config.update(folder, rc, json.null)
+ end
end
- log.debug('loaded config dump:', util.dump(config.dump()))
+ local global = cfgLoader.loadClientConfig()
+ log.debug('Load config from client', 'fallback')
+ log.debug(util.dump(global))
+ config.update(scope.fallback, global, json.null)
end
---@class provider
@@ -55,13 +66,22 @@ function m.register(method)
end
filewatch.event(function (changes) ---@async
- local configPath = CONFIGPATH and workspace.getAbsolutePath(CONFIGPATH)
- local rcPath = workspace.getAbsolutePath('.luarc.json')
for _, change in ipairs(changes) do
- if change.path == configPath
- or change.path == rcPath then
- updateConfig()
- return
+ if (CONFIGPATH and util.stringEndWith(change.path, CONFIGPATH)) then
+ for _, scp in ipairs(workspace.folders) do
+ local configPath = workspace.getAbsolutePath(scp.uri, CONFIGPATH)
+ if change.path == configPath then
+ updateConfig(scp.uri)
+ end
+ end
+ end
+ if util.stringEndWith(change.path, '.luarc.json') then
+ for _, scp in ipairs(workspace.folders) do
+ local rcPath = workspace.getAbsolutePath(scp.uri, '.luarc.json')
+ if change.path == rcPath then
+ updateConfig(scp.uri)
+ end
+ end
end
end
end)
@@ -69,8 +89,19 @@ end)
m.register 'initialize' {
function (params)
client.init(params)
- config.init()
- workspace.initPath(params.rootUri)
+
+ if params.rootUri then
+ workspace.initRoot(params.rootUri)
+ end
+
+ if params.workspaceFolders then
+ for _, folder in ipairs(params.workspaceFolders) do
+ workspace.create(folder.uri)
+ end
+ elseif params.rootUri then
+ workspace.create(params.rootUri)
+ end
+
return {
capabilities = cap.getIniter(),
serverInfo = {
@@ -189,9 +220,9 @@ m.register 'workspace/didRenameFiles' {
m.register 'textDocument/didOpen' {
---@async
function (params)
- workspace.awaitReady()
local doc = params.textDocument
local uri = files.getRealUri(doc.uri)
+ workspace.awaitReady(uri)
local text = doc.text
files.setText(uri, text, true, doc.version)
files.open(uri)
@@ -213,10 +244,10 @@ m.register 'textDocument/didClose' {
m.register 'textDocument/didChange' {
---@async
function (params)
- workspace.awaitReady()
local doc = params.textDocument
local changes = params.contentChanges
local uri = files.getRealUri(doc.uri)
+ workspace.awaitReady(uri)
--log.debug('changes', util.dump(changes))
local text = files.getOriginText(uri) or ''
local rows = files.getCachedRows(uri)
@@ -230,13 +261,13 @@ m.register 'textDocument/hover' {
abortByFileUpdate = true,
---@async
function (params)
- if not config.get('Lua.hover.enable') then
- return
- end
local doc = params.textDocument
local uri = files.getRealUri(doc.uri)
+ if not config.get(uri, 'Lua.hover.enable') then
+ return
+ end
if not workspace.isReady() then
- local count, max = workspace.getLoadProcess()
+ local count, max = workspace.getLoadingProcess(uri)
return {
contents = {
value = lang.script('HOVER_WS_LOADING', count, max),
@@ -268,13 +299,13 @@ m.register 'textDocument/definition' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_DEFINITION, 0.5)
- local core = require 'core.definition'
local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
if not files.exists(uri) then
return nil
end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_DEFINITION, 0.5)
+ local core = require 'core.definition'
local pos = converter.unpackPosition(uri, params.position)
local result = core(uri, pos)
if not result then
@@ -307,13 +338,13 @@ m.register 'textDocument/typeDefinition' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_DEFINITION, 0.5)
- local core = require 'core.type-definition'
local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
if not files.exists(uri) then
return nil
end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_DEFINITION, 0.5)
+ local core = require 'core.type-definition'
local pos = converter.unpackPosition(uri, params.position)
local result = core(uri, pos)
if not result then
@@ -346,13 +377,13 @@ m.register 'textDocument/references' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_REFERENCE, 0.5)
- local core = require 'core.reference'
local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
if not files.exists(uri) then
return nil
end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_REFERENCE, 0.5)
+ local core = require 'core.reference'
local pos = converter.unpackPosition(uri, params.position)
local result = core(uri, pos)
if not result then
@@ -397,13 +428,13 @@ m.register 'textDocument/rename' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_RENAME, 0.5)
- local core = require 'core.rename'
local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
if not files.exists(uri) then
return nil
end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_RENAME, 0.5)
+ local core = require 'core.rename'
local pos = converter.unpackPosition(uri, params.position)
local result = core.rename(uri, pos, params.newName)
if not result then
@@ -449,7 +480,7 @@ m.register 'textDocument/completion' {
function (params)
local uri = files.getRealUri(params.textDocument.uri)
if not workspace.isReady() then
- local count, max = workspace.getLoadProcess()
+ local count, max = workspace.getLoadingProcess(uri)
return {
{
label = lang.script('HOVER_WS_LOADING', count, max),textEdit = {
@@ -471,7 +502,7 @@ m.register 'textDocument/completion' {
return nil
end
local triggerCharacter = params.context and params.context.triggerCharacter
- if config.get 'editor.acceptSuggestionOnEnter' ~= 'off' then
+ if config.get(uri, 'editor.acceptSuggestionOnEnter') ~= 'off' then
if triggerCharacter == '\n'
or triggerCharacter == '{'
or triggerCharacter == ',' then
@@ -600,15 +631,15 @@ m.register 'textDocument/signatureHelp' {
abortByFileUpdate = true,
---@async
function (params)
- if not config.get 'Lua.signatureHelp.enable' then
+ local uri = files.getRealUri(params.textDocument.uri)
+ if not config.get(uri, 'Lua.signatureHelp.enable') then
return nil
end
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SIGNATURE, 0.5)
- local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
if not files.exists(uri) then
return nil
end
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SIGNATURE, 0.5)
local pos = converter.unpackPosition(uri, params.position)
local core = require 'core.signature'
local results = core(uri, pos)
@@ -646,11 +677,11 @@ m.register 'textDocument/documentSymbol' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
- local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SYMBOL, 0.5)
- local core = require 'core.document-symbol'
local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
+ local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SYMBOL, 0.5)
+ local core = require 'core.document-symbol'
local symbols = core(uri)
if not symbols then
return nil
@@ -749,7 +780,6 @@ m.register 'workspace/symbol' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_WS_SYMBOL, 0.5)
local core = require 'core.workspace-symbol'
@@ -783,7 +813,7 @@ m.register 'textDocument/semanticTokens/full' {
---@async
function (params)
local uri = files.getRealUri(params.textDocument.uri)
- workspace.awaitReady()
+ workspace.awaitReady(uri)
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_FULL, 0.5)
local core = require 'core.semantic-tokens'
local results = core(uri, 0, math.huge)
@@ -798,7 +828,7 @@ m.register 'textDocument/semanticTokens/range' {
---@async
function (params)
local uri = files.getRealUri(params.textDocument.uri)
- workspace.awaitReady()
+ workspace.awaitReady(uri)
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_RANGE, 0.5)
local core = require 'core.semantic-tokens'
local cache = files.getOpenedCache(uri)
@@ -882,7 +912,9 @@ m.register '$/status/click' {
end
if result == titleDiagnostic then
local diagnostic = require 'provider.diagnostic'
- diagnostic.diagnosticsAll(true)
+ for _, scp in ipairs(workspace.folders) do
+ diagnostic.diagnosticsScope(scp.uri, true)
+ end
end
end
}
@@ -891,10 +923,10 @@ m.register 'textDocument/onTypeFormatting' {
abortByFileUpdate = true,
---@async
function (params)
- workspace.awaitReady()
+ local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_TYPE_FORMATTING, 0.5)
local ch = params.ch
- local uri = files.getRealUri(params.textDocument.uri)
if not files.exists(uri) then
return nil
end
@@ -928,12 +960,12 @@ m.register '$/cancelRequest' {
m.register '$/requestHint' {
---@async
function (params)
- local core = require 'core.hint'
- if not config.get 'Lua.hint.enable' then
+ local uri = files.getRealUri(params.textDocument.uri)
+ if not config.get(uri, 'Lua.hint.enable') then
return
end
- workspace.awaitReady()
- local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
+ local core = require 'core.hint'
local start, finish = converter.unpackRange(uri, params.range)
local results = core(uri, start, finish)
local hintResults = {}
@@ -952,13 +984,13 @@ m.register '$/requestHint' {
do
---@async
local function updateHint(uri)
- if not config.get 'Lua.hint.enable' then
+ if not config.get(uri, 'Lua.hint.enable') then
return
end
local id = 'updateHint' .. uri
await.close(id)
await.setID(id)
- workspace.awaitReady()
+ workspace.awaitReady(uri)
local visibles = files.getVisibles(uri)
if not visibles then
return
@@ -966,7 +998,7 @@ do
await.close(id)
await.setID(id)
await.delay()
- workspace.awaitReady()
+ workspace.awaitReady(uri)
local edits = {}
local hint = require 'core.hint'
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_HINT, 0.5)
@@ -999,15 +1031,21 @@ do
end
local function refreshStatusBar()
- local value = config.get 'Lua.window.statusBar'
- if value then
+ local valid = true
+ for _, scp in ipairs(workspace.folders) do
+ if not config.get(scp.uri, 'Lua.window.statusBar') then
+ valid = false
+ break
+ end
+ end
+ if valid then
proto.notify('$/status/show')
else
proto.notify('$/status/hide')
end
end
-config.watch(function (key, value)
+config.watch(function (uri, key, value)
if key == 'Lua.window.statusBar' then
refreshStatusBar()
end
@@ -1016,7 +1054,7 @@ end)
m.register '$/status/refresh' { refreshStatusBar }
files.watch(function (ev, uri)
- if not workspace.isReady() then
+ if not workspace.isReady(uri) then
return
end
if ev == 'update'
diff --git a/script/provider/semantic-tokens.lua b/script/provider/semantic-tokens.lua
index 68db9b7f..f6acb280 100644
--- a/script/provider/semantic-tokens.lua
+++ b/script/provider/semantic-tokens.lua
@@ -20,7 +20,7 @@ local function toArray(map)
end
local dontShowAgain = false
-local function enable()
+local function enable(uri)
if isEnable then
return
end
@@ -47,7 +47,7 @@ local function enable()
},
}
})
- if config.get 'editor.semanticHighlighting.enabled' == 'configuredByTheme' and not dontShowAgain then
+ if config.get(uri, 'editor.semanticHighlighting.enabled') == 'configuredByTheme' and not dontShowAgain then
proto.request('window/showMessageRequest', {
type = define.MessageType.Info,
message = lang.script.WINDOW_CHECK_SEMANTIC,
@@ -80,7 +80,7 @@ local function enable()
end
end
-local function disable()
+local function disable(uri)
if not isEnable then
return
end
@@ -109,16 +109,16 @@ local function refresh()
proto.request('workspace/semanticTokens/refresh', json.null)
end
-config.watch(function (key, value, oldValue)
+config.watch(function (uri, key, value, oldValue)
if key == 'Lua.color.mode' then
if value == 'Semantic' or value == 'SemanticEnhanced' then
if isEnable and value ~= oldValue then
refresh()
else
- enable()
+ enable(uri)
end
else
- disable()
+ disable(uri)
end
end
if key:find '^Lua.runtime'
diff --git a/script/service/service.lua b/script/service/service.lua
index 825ebfba..087ee85e 100644
--- a/script/service/service.lua
+++ b/script/service/service.lua
@@ -11,6 +11,7 @@ local lang = require 'language'
local ws = require 'workspace'
local time = require 'bee.time'
local fw = require 'filewatch'
+local furi = require 'file-uri'
local m = {}
m.type = 'service'
@@ -189,8 +190,14 @@ function m.reportStatus()
else
info.text = '😺Lua'
end
+
+ local roots = {}
+ for i, scp in ipairs(ws.folders) do
+ roots[i] = furi.decode(scp.uri)
+ end
+
info.tooltip = lang.script('WINDOW_LUA_STATUS', {
- ws = ws.path or '',
+ ws = table.concat(roots, ';'),
ast = files.astCount,
max = files.fileCount,
mem = collectgarbage('count') / 1000,
@@ -226,8 +233,6 @@ function m.start()
require 'provider'
m.startTimer()
-
- ws.reload()
end
return m
diff --git a/script/service/telemetry.lua b/script/service/telemetry.lua
index dac72f3f..d20719b0 100644
--- a/script/service/telemetry.lua
+++ b/script/service/telemetry.lua
@@ -72,9 +72,12 @@ local function pushErrorLog(link)
))
end
+local validMap = {}
+local isValid = false
+
timer.wait(5, function ()
timer.loop(300, function ()
- if not config.get 'Lua.telemetry.enable' then
+ if not isValid then
return
end
local suc, link = pcall(net.connect, 'tcp', 'moe-moe.love', 11577)
@@ -93,7 +96,7 @@ timer.wait(5, function ()
end
end)()
timer.loop(1, function ()
- if not config.get 'Lua.telemetry.enable' then
+ if not isValid then
return
end
net.update()
@@ -102,8 +105,26 @@ end)
local m = {}
-function m.updateConfig()
- if config.get 'Lua.telemetry.enable' ~= nil then
+function m.updateConfig(uri, value)
+ validMap[uri or ''] = value
+ isValid = config.get(nil, 'Lua.telemetry.enable')
+ if isValid == false then
+ return
+ end
+ -- one false, all false
+ for _, v in pairs(validMap) do
+ if v == false then
+ isValid = false
+ return
+ end
+ end
+ for _, v in pairs(validMap) do
+ if v == true then
+ isValid = true
+ break
+ end
+ end
+ if isValid ~= nil then
return
end
if m.hasShowedMessage then
@@ -151,9 +172,9 @@ function m.updateConfig()
end)
end
-config.watch(function (key)
+config.watch(function (uri, key, value)
if key == 'Lua.telemetry.enable' then
- m.updateConfig()
+ m.updateConfig(uri, value)
end
end)
diff --git a/script/utility.lua b/script/utility.lua
index f06dd21e..004e8066 100644
--- a/script/utility.lua
+++ b/script/utility.lua
@@ -708,4 +708,12 @@ function m.getUpvalue(f, name)
return nil, false
end
+function m.stringStartWith(str, head)
+ return str:sub(1, #head) == head
+end
+
+function m.stringEndWith(str, tail)
+ return str:sub(-#tail) == tail
+end
+
return m
diff --git a/script/vm/getDocs.lua b/script/vm/getDocs.lua
index 3a0765bf..c635b2a9 100644
--- a/script/vm/getDocs.lua
+++ b/script/vm/getDocs.lua
@@ -10,14 +10,14 @@ local noder = require 'core.noder'
---获取class与alias
---@param name? string
---@return parser.guide.object[]
-function vm.getDocDefines(name)
+function vm.getDocDefines(uri, name)
local cache = vm.getCache 'getDocDefines'
if cache[name] then
return cache[name]
end
local results = {}
if name == '*' then
- for noders in collector.each('def:dn:') do
+ for noders in collector.each(uri, 'def:dn:') do
for id in noder.eachID(noders) do
if id:sub(1, 3) == 'dn:'
and not id:find(noder.SPLIT_CHAR) then
@@ -31,7 +31,7 @@ function vm.getDocDefines(name)
end
else
local id = 'dn:' .. name
- for noders in collector.each('def:' .. id) do
+ for noders in collector.each(uri, 'def:' .. id) do
for source in noder.eachSource(noders, id) do
if source.type == 'doc.class.name'
or source.type == 'doc.alias.name' then
@@ -153,7 +153,7 @@ local function isDeprecated(value)
return true
elseif doc.type == 'doc.version' then
local valids = vm.getValidVersions(doc)
- if not valids[config.get 'Lua.runtime.version'] then
+ if not valids[config.get(guide.getUri(value), 'Lua.runtime.version')] then
value._deprecated = true
return true
end
diff --git a/script/vm/getGlobals.lua b/script/vm/getGlobals.lua
index 92fd1c8e..f6646559 100644
--- a/script/vm/getGlobals.lua
+++ b/script/vm/getGlobals.lua
@@ -14,7 +14,7 @@ function vm.hasGlobalSets(name)
return collector.has(id)
end
-function vm.getGlobalSets(name)
+function vm.getGlobalSets(uri, name)
local cache = vm.getCache 'getGlobalSets'
if cache[name] then
return cache[name]
@@ -22,7 +22,7 @@ function vm.getGlobalSets(name)
local results = {}
cache[name] = results
if name == '*' then
- for noders in collector.each('def:g:') do
+ for noders in collector.each(uri, 'def:g:') do
for id in noder.eachID(noders) do
if id:sub(1, 2) == 'g:'
and not id:find(noder.SPLIT_CHAR) then
@@ -41,7 +41,7 @@ function vm.getGlobalSets(name)
else
id = ('g:%s'):format(noder.STRING_CHAR, name)
end
- for noders in collector.each('def:' .. id) do
+ for noders in collector.each(uri, 'def:' .. id) do
for source in noder.eachSource(noders, id) do
if guide.isSet(source) then
results[#results+1] = source
diff --git a/script/vm/getLinks.lua b/script/vm/getLinks.lua
index 77d869f8..7fe31e5e 100644
--- a/script/vm/getLinks.lua
+++ b/script/vm/getLinks.lua
@@ -20,7 +20,7 @@ local function getFileLinks(uri)
if not args or not args[1] or args[1].type ~= 'string' then
return
end
- local uris = rpath.findUrisByRequirePath(args[1][1])
+ local uris = rpath.findUrisByRequirePath(uri, args[1][1])
for _, u in ipairs(uris) do
if not links[u] then
links[u] = {}
diff --git a/script/workspace/loading.lua b/script/workspace/loading.lua
new file mode 100644
index 00000000..cbcfa567
--- /dev/null
+++ b/script/workspace/loading.lua
@@ -0,0 +1,170 @@
+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 pub = require 'pub.pub'
+
+---@class workspace.loading
+---@field scp scope
+---@field _bar progress
+---@field _stash function[]
+---@field _refs uri[]
+---@field _cache table<uri, boolean>
+local mt = {}
+mt.__index = mt
+
+mt._loadLock = false
+mt.read = 0
+mt.max = 0
+mt.preload = 0
+
+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
+ }
+ , 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 boolean
+---@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()
+ pub.task('loadFile', uri, function (content)
+ self._stash[#self._stash+1] = function ()
+ self.read = self.read + 1
+ self:update()
+ if not content then
+ return
+ end
+ if self._cache[uri] then
+ return
+ end
+ log.info(('Preload file at: %s , size = %.3f KB'):format(uri, #content / 1024.0))
+ self._cache[uri] = true
+ files.setText(uri, content, false)
+ files.addRef(uri)
+ if libraryUri then
+ log.info('++++As library of:', libraryUri)
+ files.setLibraryUri(self.scp, uri, libraryUri)
+ end
+ end
+ end)
+ elseif files.isDll(uri) then
+ self.max = self.max + 1
+ self:update()
+ pub.task('loadFile', uri, function (content)
+ self._stash[#self._stash+1] = function ()
+ self.read = self.read + 1
+ self:update()
+ if not content then
+ return
+ end
+ if self._cache[uri] then
+ return
+ end
+ log.info(('Preload dll at: %s , size = %.3f KB'):format(uri, #content / 1024.0))
+ self._cache[uri] = true
+ files.saveDll(uri, content)
+ files.addRef(uri)
+ if libraryUri then
+ log.info('++++As library of:', libraryUri)
+ end
+ end
+ end)
+ await.delay()
+ end
+end
+
+function mt:loadStashed()
+ self:update()
+ if self._loadLock then
+ return
+ end
+ self._loadLock = true
+ ---@async
+ await.call(function ()
+ while true do
+ local loader = table.remove(self._stash)
+ if not loader then
+ break
+ end
+ loader()
+ await.delay()
+ end
+ self._loadLock = false
+ end)
+end
+
+---@async
+function mt:loadAll()
+ while self.read < self.max do
+ log.info(('Loaded %d/%d files'):format(self.read, self.max))
+ self:loadStashed()
+ await.sleep(0.1)
+ end
+end
+
+function mt:remove()
+ self._bar:remove()
+end
+
+function mt:__close()
+ self:remove()
+end
+
+---@class workspace.loading.manager
+local m = {}
+
+---@return workspace.loading
+function m.create(scp)
+ local loading = setmetatable({
+ scp = scp,
+ _bar = progress.create(lang.script('WORKSPACE_LOADING', scp.uri)),
+ _stash = {},
+ _cache = {},
+ }, mt)
+ scp:set('cachedUris', loading._cache)
+ return loading
+end
+
+return m
diff --git a/script/workspace/require-path.lua b/script/workspace/require-path.lua
index 0fec6cc9..84541808 100644
--- a/script/workspace/require-path.lua
+++ b/script/workspace/require-path.lua
@@ -8,8 +8,8 @@ local m = {}
m.cache = {}
--- `aaa/bbb/ccc.lua` 与 `?.lua` 将返回 `aaa.bbb.cccc`
-local function getOnePath(path, searcher)
- local separator = config.get 'Lua.completion.requireSeparator'
+local function getOnePath(uri, path, searcher)
+ local separator = config.get(uri, 'Lua.completion.requireSeparator')
local stemPath = path
: gsub('%.[^%.]+$', '')
: gsub('[/\\%.]+', separator)
@@ -27,9 +27,9 @@ local function getOnePath(path, searcher)
return nil
end
-function m.getVisiblePath(path)
- local searchers = config.get 'Lua.runtime.path'
- local strict = config.get 'Lua.runtime.pathStrict'
+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)
local uri = furi.encode(path)
local libraryPath = files.getLibraryPath(uri)
@@ -63,7 +63,7 @@ function m.getVisiblePath(path)
else
searcher = searcher :gsub('[/\\]+', '/')
end
- local expect = getOnePath(cutedPath, searcher)
+ local expect = getOnePath(suri, cutedPath, searcher)
if expect then
local mySearcher = searcher
if head then
@@ -82,11 +82,11 @@ end
--- 查找符合指定require path的所有uri
---@param path string
-function m.findUrisByRequirePath(path)
+function m.findUrisByRequirePath(suri, path)
if type(path) ~= 'string' then
return {}
end
- local separator = config.get 'Lua.completion.requireSeparator'
+ local separator = config.get(suri, 'Lua.completion.requireSeparator')
local fspath = path:gsub('%' .. separator, '/')
local vm = require 'vm'
local cache = vm.getCache 'findUrisByRequirePath'
@@ -106,7 +106,7 @@ function m.findUrisByRequirePath(path)
end
for uri in files.eachFile() do
- local infos = m.getVisiblePath(furi.decode(uri))
+ local infos = m.getVisiblePath(suri, furi.decode(uri))
for _, info in ipairs(infos) do
local fsexpect = info.expect:gsub('%' .. separator, '/')
if fsexpect == fspath then
@@ -135,7 +135,7 @@ files.watch(function (ev)
end
end)
-config.watch(function (key, value, oldValue)
+config.watch(function (uri, key, value, oldValue)
if key == 'Lua.completion.requireSeparator'
or key == 'Lua.runtime.path'
or key == 'Lua.runtime.pathStrict' then
diff --git a/script/workspace/scope.lua b/script/workspace/scope.lua
new file mode 100644
index 00000000..fc71e60c
--- /dev/null
+++ b/script/workspace/scope.lua
@@ -0,0 +1,126 @@
+---@alias scope.type '"override"'|'"folder"'|'"fallback"'
+
+---@class scope
+---@field type scope.type
+---@field uri? uri
+---@field _links table<uri, boolean>
+---@field _data table<string, any>
+local mt = {}
+mt.__index = mt
+
+---@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
+
+---@param uri uri
+---@return boolean
+function mt:isChildUri(uri)
+ if not self.uri then
+ return true
+ end
+ return uri:sub(1, #self.uri) == self.uri
+end
+
+---@param uri uri
+---@return boolean
+function mt:isLinkedUri(uri)
+ for linkUri in pairs(self._links) do
+ if uri:sub(1, #linkUri) == linkUri then
+ return true
+ end
+ end
+ return false
+end
+
+---@param k string
+---@param v any
+function mt:set(k, v)
+ self._data[k] = v
+ return v
+end
+
+---@param k string
+---@return any
+function mt:get(k)
+ return self._data[k]
+end
+
+---@param scopeType scope.type
+---@return scope
+local function createScope(scopeType)
+ local scope = setmetatable({
+ type = scopeType,
+ _links = {},
+ _data = {},
+ }, mt)
+
+ return scope
+end
+
+---@class scope.manager
+local m = {}
+
+---@type scope[]
+m.folders = {}
+m.override = createScope 'override'
+m.fallback = createScope 'fallback'
+
+---@param uri uri
+---@return scope
+function m.createFolder(uri)
+ local scope = createScope 'folder'
+ scope.uri = uri
+
+ 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 not uri or 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
+end
+
+return m
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index 49d158c1..b8ddedb5 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -6,44 +6,56 @@ 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 = {}
+---@type scope[]
+m.folders = {}
+m.watchList = {}
+
+--- 注册事件
+---@param callback async fun(ev: string, uri: uri)
+function m.watch(callback)
+ m.watchList[#m.watchList+1] = callback
+end
---- 初始化工作区
-function m.initPath(uri)
- log.info('Workspace inited: ', uri)
- if not uri then
- return
+function m.onWatch(ev, uri)
+ for _, callback in ipairs(m.watchList) do
+ await.call(function ()
+ callback(ev, uri)
+ end)
end
- m.uri = uri
- m.path = m.normalize(furi.decode(uri))
- plugin.workspace = m.path
+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)
+ log.info('Workspace create: ', uri)
+ local path = m.normalize(furi.decode(uri))
+ fw.watch(path)
+ local scp = scope.createFolder(uri)
+ m.folders[#m.folders+1] = scp
+end
- fw.watch(m.path)
+function m.getRootUri(uri)
+ local scp = m.getScope(uri)
+ return scp.uri
end
local globInteferFace = {
@@ -75,25 +87,21 @@ 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
+ 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'))
+ 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 +110,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 +120,8 @@ 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'))
+ 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 +129,73 @@ function m.getNativeMatcher()
end
end
end
- -- config.get 'workspace.library'
- for path in pairs(config.get 'Lua.workspace.library') do
- path = m.getAbsolutePath(path)
+ for path in pairs(config.get(scp.uri, 'Lua.workspace.library')) do
+ path = m.getAbsolutePath(scp.uri, 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
+ 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 'libraryMatcher' then
+ return scp:get 'libraryMatcher'
end
local librarys = {}
- for path in pairs(config.get 'Lua.workspace.library') do
- path = m.getAbsolutePath(path)
+ for path in pairs(config.get(scp.uri, 'Lua.workspace.library')) do
+ path = m.getAbsolutePath(scp.uri, path)
if path then
librarys[m.normalize(path)] = true
end
end
- if library.metaPath then
- librarys[m.normalize(library.metaPath)] = true
+ -- TODO
+ if scp:get 'metaPath' then
+ librarys[m.normalize(scp:get '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('libraryMatcher', 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 +214,77 @@ 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)
+ 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:loadFile(furi.encode(path))
+ end)
+ ld:loadAll()
+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[uri] then
+ cachedUris[uri] = nil
+ files.delRef(uri)
+ end
end
- }
- local nativeLoader = loadFileFactory(m.path, progressData)
- local native = m.getNativeMatcher()
- if native then
- log.info('Scan files at:', furi.decode(uri))
- native:scan(furi.decode(uri), nativeLoader)
end
end
--- 预读工作区内所有文件
---@async
-function m.awaitPreload()
- local diagnostic = require 'provider.diagnostic'
- await.close 'preload'
- await.setID 'preload'
+---@param scp scope
+function m.awaitPreload(scp)
+ await.close('preload:' .. scp.uri)
+ await.setID('preload:' .. scp.uri)
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)
end
+ watchers = {}
+ scp:set('watchers', watchers)
- 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)
+ local ld <close> = loading.create(scp)
+ scp:set('loading', ld)
+
+ 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:loadFile(furi.encode(path))
+ end)
end
- progressBar:remove()
- log.info('Preload finish.')
+ for _, libMatcher in ipairs(librarys) do
+ log.info('Scan library at:', libMatcher.uri)
+ ---@async
+ libMatcher.matcher:scan(furi.decode(libMatcher.uri), function (path)
+ ld:loadFile(furi.encode(path), libMatcher.uri)
+ end)
+ watchers[#watchers+1] = fw.watch(furi.decode(libMatcher.uri))
+ end
- diagnostic.start()
+ log.info(('Found %d files at:'):format(ld.max), scp.uri)
+ ld:loadAll()
+ log.info('Preload finish at:', scp.uri)
end
--- 查找符合指定file path的所有uri
@@ -391,7 +295,7 @@ function m.findUrisByFilePath(path)
end
local myUri = furi.encode(path)
local vm = require 'vm'
- local resultCache = vm.getCache 'findUrisByRequirePath.result'
+ local resultCache = vm.getCache 'findUrisByFilePath.result'
if resultCache[path] then
return resultCache[path]
end
@@ -431,16 +335,17 @@ function m.normalize(path)
end
---@return string
-function m.getAbsolutePath(path)
+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 m.path then
+ if not folderUri then
return nil
end
- path = m.normalize(m.path .. '/' .. path)
+ local folderPath = furi.decode(folderUri)
+ path = m.normalize(folderPath .. '/' .. path)
end
return path
end
@@ -448,17 +353,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
@@ -466,34 +374,18 @@ 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
- m.cache[name] = {}
- end
- return m.cache[name]
-end
-
-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()
@@ -501,43 +393,100 @@ 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.flushFiles(scp)
+ local cachedUris = scp:get 'cachedUris'
+ if not cachedUris then
+ return
+ end
+ scp:set('cachedUris', nil)
+ for uri in pairs(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 pairs(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)
+ scp:set('ready', false)
+ scp:set('nativeMatcher', nil)
+ scp:set('libraryMatcher', nil)
+ m.flushFiles(scp)
+ files.flushAllLibrary(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
+
+ m.onWatch('reload', scp.uri)
+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)
+ if scp.type == 'fallback' then
+ return true
+ end
+ return scp:get('ready') == true
end
-function m.getLoadProcess()
- return m.fileLoaded, m.fileFound
+function m.getLoadingProcess(uri)
+ local scp = m.getScope(uri)
+ ---@type workspace.loading
+ local ld = scp:get 'loading'
+ if ld then
+ return ld.read, ld.max
+ else
+ return 0, 0
+ end
end
files.watch(function (ev, uri) ---@async
@@ -548,35 +497,33 @@ files.watch(function (ev, uri) ---@async
end
end)
-config.watch(function (key, value, oldValue)
+config.watch(function (uri, key, value, oldValue)
if key:find '^Lua.runtime'
or key:find '^Lua.workspace'
or key:find '^files' then
if value ~= oldValue then
- m.reload()
+ m.reload(m.getScope(uri))
end
end
end)
fw.event(function (changes) ---@async
- 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
- goto CONTINUE
- end
+ m.awaitReady(uri)
if change.type == 'create' then
log.debug('FileChangeType.Created', uri)
m.awaitLoadFile(uri)
elseif change.type == '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 change.type == 'change' then
if m.isValidLuaUri(uri) then
@@ -589,7 +536,10 @@ fw.event(function (changes) ---@async
-- 排除类文件发生更改需要重新扫描
if filename == '.gitignore'
or filename == '.gitmodules' then
- m.reload()
+ local scp = m.getScope(uri)
+ if scp.type ~= 'fallback' then
+ m.reload(scp)
+ end
break
end
end