summaryrefslogtreecommitdiff
path: root/script
diff options
context:
space:
mode:
authorCppCXY <812125110@qq.com>2022-05-31 20:07:46 +0800
committerCppCXY <812125110@qq.com>2022-05-31 20:07:46 +0800
commitb406413b059843ced4f3dcf4c0c9d041eb78ef05 (patch)
treeb6c9b5a40a025b4fe0011eec9965de9557441aa0 /script
parent5eef995bdc9e9450f1803fb0f54cdea1025d6e10 (diff)
parent496b61d601003619aeb6243300b9b3b0f5be944a (diff)
downloadlua-language-server-b406413b059843ced4f3dcf4c0c9d041eb78ef05.zip
Merge branch 'master' of https://github.com/sumneko/lua-language-server into HEAD
Diffstat (limited to 'script')
-rw-r--r--script/config/config.lua12
-rw-r--r--script/core/code-action.lua4
-rw-r--r--script/core/folding.lua12
-rw-r--r--script/glob/gitignore.lua6
-rw-r--r--script/lclient.lua1
-rw-r--r--script/library.lua3
-rw-r--r--script/proto/define.lua1
-rw-r--r--script/provider/diagnostic.lua146
-rw-r--r--script/provider/formatting.lua3
-rw-r--r--script/provider/provider.lua102
-rw-r--r--script/vm/compiler.lua2
-rw-r--r--script/vm/generic.lua3
-rw-r--r--script/vm/ref.lua20
-rw-r--r--script/workspace/workspace.lua19
14 files changed, 307 insertions, 27 deletions
diff --git a/script/config/config.lua b/script/config/config.lua
index 1dda8c0b..46e80994 100644
--- a/script/config/config.lua
+++ b/script/config/config.lua
@@ -167,6 +167,9 @@ local Template = {
>> util.deepCopy(define.DiagnosticDefaultSeverity),
['Lua.diagnostics.neededFileStatus'] = Type.Hash(Type.String, Type.String)
>> util.deepCopy(define.DiagnosticDefaultNeededFileStatus),
+ ['Lua.diagnostics.disableScheme'] = Type.Hash(Type.String, Type.Boolean, ';') >> {
+ ['git'] = true,
+ },
['Lua.diagnostics.workspaceDelay'] = Type.Integer >> 5,
['Lua.diagnostics.workspaceRate'] = Type.Integer >> 100,
['Lua.diagnostics.libraryFiles'] = Type.String >> 'Opened',
@@ -179,6 +182,11 @@ local Template = {
['Lua.workspace.library'] = Type.Hash(Type.String, Type.Boolean, ';'),
['Lua.workspace.checkThirdParty'] = Type.Boolean >> true,
['Lua.workspace.userThirdParty'] = Type.Array(Type.String),
+ ['Lua.workspace.supportScheme'] = Type.Hash(Type.String, Type.Boolean, ';') >> {
+ ['file'] = true,
+ ['untitled'] = true,
+ ['git'] = true,
+ },
['Lua.completion.enable'] = Type.Boolean >> true,
['Lua.completion.callSnippet'] = Type.String >> 'Disable',
['Lua.completion.keywordSnippet'] = Type.String >> 'Replace',
@@ -214,6 +222,8 @@ local Template = {
>> {},
['Lua.spell.dict'] = Type.Array(Type.String),
['Lua.telemetry.enable'] = Type.Or(Type.Boolean >> false, Type.Nil) >> nil,
+
+ -- VSCode
['files.associations'] = Type.Hash(Type.String, Type.String),
['files.exclude'] = Type.Hash(Type.String, Type.Boolean),
['editor.semanticHighlighting.enabled'] = Type.Or(Type.Boolean, Type.String),
@@ -425,7 +435,7 @@ function m.update(scp, ...)
local news = table.pack(...)
for i = 1, news.n do
- if news[i] then
+ if type(news[i]) == 'table' then
expand(news[i])
end
end
diff --git a/script/core/code-action.lua b/script/core/code-action.lua
index 09b271c6..116fff24 100644
--- a/script/core/code-action.lua
+++ b/script/core/code-action.lua
@@ -333,6 +333,8 @@ local function solveAwaitInSync(uri, diag, results)
end
local row = guide.rowColOf(parentFunction.start)
local pos = guide.positionOf(row, 0)
+ local offset = guide.positionToOffset(state, pos + 1)
+ local space = state.lua:match('[ \t]*', offset)
results[#results+1] = {
title = lang.script.ACTION_MARK_ASYNC,
kind = 'quickfix',
@@ -342,7 +344,7 @@ local function solveAwaitInSync(uri, diag, results)
{
start = pos,
finish = pos,
- newText = '---@async\n',
+ newText = space .. '---@async\n',
}
}
}
diff --git a/script/core/folding.lua b/script/core/folding.lua
index 4f93aed9..0034313a 100644
--- a/script/core/folding.lua
+++ b/script/core/folding.lua
@@ -66,7 +66,8 @@ local care = {
['repeat'] = function (source, text, results)
local start = source.start
local finish = source.keyword[#source.keyword]
- if text:sub(finish - #'until' + 1, finish) ~= 'until' then
+ -- must end with 'until'
+ if #source.keyword ~= 4 then
return
end
local folding = {
@@ -143,6 +144,15 @@ local care = {
}
results[#results+1] = folding
end,
+ ['doc.alias'] = function (source, text, results)
+ local folding = {
+ start = source.start,
+ finish = source.bindGroup[#source.bindGroup].finish,
+ kind = 'comment',
+ hideLastLine = true,
+ }
+ results[#results+1] = folding
+ end
}
---@async
diff --git a/script/glob/gitignore.lua b/script/glob/gitignore.lua
index 4dad2747..a6a3df3e 100644
--- a/script/glob/gitignore.lua
+++ b/script/glob/gitignore.lua
@@ -164,8 +164,9 @@ function mt:getRelativePath(path)
end
---@param callback async fun(path: string)
+---@param hook? async fun(ev: string, ...)
---@async
-function mt:scan(path, callback)
+function mt:scan(path, callback, hook)
local files = {}
if type(callback) ~= 'function' then
callback = nil
@@ -203,6 +204,9 @@ function mt:scan(path, callback)
break
end
list[#list] = nil
+ if hook then
+ hook('scan', current)
+ end
if not self:simpleMatch(current) then
check(current)
end
diff --git a/script/lclient.lua b/script/lclient.lua
index ad1fff3d..ce47a816 100644
--- a/script/lclient.lua
+++ b/script/lclient.lua
@@ -208,6 +208,7 @@ function mt:registerFakers()
'textDocument/publishDiagnostics',
'workspace/configuration',
'workspace/semanticTokens/refresh',
+ 'workspace/diagnostic/refresh',
'window/workDoneProgress/create',
'window/showMessage',
'window/logMessage',
diff --git a/script/library.lua b/script/library.lua
index 66c4d364..c7b9ea9a 100644
--- a/script/library.lua
+++ b/script/library.lua
@@ -477,6 +477,9 @@ local function check3rd(uri)
if hasAsked then
return
end
+ if not ws.isReady(uri) then
+ return
+ end
if not config.get(uri, 'Lua.workspace.checkThirdParty') then
return
end
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 13ae05e2..52006992 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -262,6 +262,7 @@ m.TokenTypes = {
["number"] = 19,
["regexp"] = 20,
["operator"] = 21,
+ ["decorator"] = 22,
}
m.BuiltIn = {
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index 15b08d49..076a613e 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -14,6 +14,9 @@ local loading = require 'workspace.loading'
local scope = require 'workspace.scope'
local time = require 'bee.time'
local ltable = require 'linked-table'
+local furi = require 'file-uri'
+local json = require 'json'
+local fw = require 'filewatch'
---@class diagnosticProvider
local m = {}
@@ -154,6 +157,18 @@ function m.clearCache(uri)
m.cache[uri] = false
end
+function m.clearCacheExcept(uris)
+ local excepts = {}
+ for _, uri in ipairs(uris) do
+ excepts[uri] = true
+ end
+ for uri in pairs(m.cache) do
+ if not excepts[uri] then
+ m.cache[uri] = false
+ end
+ end
+end
+
function m.clearAll()
for luri in pairs(m.cache) do
m.clear(luri)
@@ -193,30 +208,45 @@ local function copyDiagsWithoutSyntax(diags)
end
---@async
-function m.doDiagnostic(uri, isScopeDiag)
+---@param uri uri
+---@return boolean
+local function isValid(uri)
if not config.get(uri, 'Lua.diagnostics.enable') then
- return
+ return false
end
if files.isLibrary(uri, true) then
local status = config.get(uri, 'Lua.diagnostics.libraryFiles')
if status == 'Disable' then
- return
+ return false
elseif status == 'Opened' then
if not files.isOpen(uri) then
- return
+ return false
end
end
end
if ws.isIgnored(uri) then
local status = config.get(uri, 'Lua.diagnostics.ignoredFiles')
if status == 'Disable' then
- return
+ return false
elseif status == 'Opened' then
if not files.isOpen(uri) then
- return
+ return false
end
end
end
+ local scheme = furi.split(uri)
+ local disableScheme = config.get(uri, 'Lua.diagnostics.disableScheme')
+ if disableScheme[scheme] then
+ return false
+ end
+ return true
+end
+
+---@async
+function m.doDiagnostic(uri, isScopeDiag)
+ if not isValid(uri) then
+ return
+ end
await.delay()
@@ -287,6 +317,42 @@ function m.doDiagnostic(uri, isScopeDiag)
pushResult()
end
+---@async
+---@return table|nil result
+---@return boolean? unchanged
+function m.pullDiagnostic(uri, isScopeDiag)
+ if not isValid(uri) then
+ return nil, util.equal(m.cache[uri], nil)
+ end
+
+ await.delay()
+
+ local state = files.getState(uri)
+ if not state then
+ return nil, util.equal(m.cache[uri], nil)
+ end
+
+ local prog <close> = progress.create(uri, lang.script.WINDOW_DIAGNOSING, 0.5)
+ prog:setMessage(ws.getRelativePath(uri))
+
+ local syntax = m.syntaxErrors(uri, state)
+ local diags = {}
+
+ xpcall(core, log.error, uri, isScopeDiag, function (result)
+ diags[#diags+1] = buildDiagnostic(uri, result)
+ end)
+
+ local full = mergeDiags(syntax, diags)
+
+ if util.equal(m.cache[uri], full) then
+ return full, true
+ end
+
+ m.cache[uri] = full
+
+ return full
+end
+
function m.refresh(uri)
if not ws.isReady(uri) then
return
@@ -359,7 +425,7 @@ local function askForDisable(uri)
end
---@async
-function m.awaitDiagnosticsScope(suri)
+function m.awaitDiagnosticsScope(suri, callback)
local scp = scope.getScope(suri)
while loading.count() > 0 do
await.sleep(1.0)
@@ -393,7 +459,7 @@ function m.awaitDiagnosticsScope(suri)
i = i + 1
bar:setMessage(('%d/%d'):format(i, #uris))
bar:setPercentage(i / #uris * 100)
- xpcall(m.doDiagnostic, log.error, uri, true)
+ callback(uri)
await.delay()
if cancelled then
log.info('Break workspace diagnostics')
@@ -416,13 +482,65 @@ function m.diagnosticsScope(uri, force)
local id = 'diagnosticsScope:' .. scp:getName()
await.close(id)
await.call(function () ---@async
- m.awaitDiagnosticsScope(uri)
+ m.awaitDiagnosticsScope(uri, function (fileUri)
+ xpcall(m.doDiagnostic, log.error, fileUri, true)
+ end)
end, id)
end
+---@async
+function m.pullDiagnosticScope(callback)
+ local processing = 0
+
+ for _, scp in ipairs(scope.folders) do
+ if ws.isReady(scp.uri)
+ and config.get(scp.uri, 'Lua.diagnostics.enable') then
+ local id = 'diagnosticsScope:' .. scp:getName()
+ await.close(id)
+ await.call(function () ---@async
+ processing = processing + 1
+ local _ <close> = util.defer(function ()
+ processing = processing - 1
+ end)
+
+ local delay = config.get(scp.uri, 'Lua.diagnostics.workspaceDelay') / 1000
+ if delay < 0 then
+ return
+ end
+ print(delay)
+ await.sleep(math.max(delay, 0.2))
+ print('start')
+
+ m.awaitDiagnosticsScope(scp.uri, function (fileUri)
+ local suc, result, unchanged = xpcall(m.pullDiagnostic, log.error, fileUri, true)
+ if suc then
+ callback {
+ uri = fileUri,
+ result = result,
+ unchanged = unchanged,
+ version = files.getVersion(fileUri),
+ }
+ end
+ end)
+ end, id)
+ end
+ end
+
+ -- sleep for ever
+ while true do
+ await.sleep(1.0)
+ end
+end
+
+function m.refreshClient()
+ log.debug('Refresh client diagnostics')
+ proto.request('workspace/diagnostic/refresh', json.null)
+end
+
ws.watch(function (ev, uri)
if ev == 'reload' then
m.diagnosticsScope(uri)
+ m.refreshClient()
end
end)
@@ -449,6 +567,16 @@ config.watch(function (uri, key, value, oldValue)
if key:find 'Lua.diagnostics' then
if value ~= oldValue then
m.diagnosticsScope(uri)
+ m.refreshClient()
+ end
+ end
+end)
+
+fw.event(function (ev, path)
+ if util.stringEndWith(path, '.editorconfig') then
+ for _, scp in ipairs(ws.folders) do
+ m.diagnosticsScope(scp.uri)
+ m.refreshClient()
end
end
end)
diff --git a/script/provider/formatting.lua b/script/provider/formatting.lua
index 2c8a222e..da31ef65 100644
--- a/script/provider/formatting.lua
+++ b/script/provider/formatting.lua
@@ -30,9 +30,6 @@ fw.event(function(ev, path)
end
end
end
- for _, scp in ipairs(ws.folders) do
- diagnostics.diagnosticsScope(scp.uri)
- end
end
end)
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index 08b6ca93..af78cd26 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -236,9 +236,10 @@ m.register 'workspace/didRenameFiles' {
m.register 'textDocument/didOpen' {
function (params)
- local doc = params.textDocument
- local scheme = furi.split(doc.uri)
- if scheme ~= 'file' then
+ local doc = params.textDocument
+ local scheme = furi.split(doc.uri)
+ local supports = config.get(doc.uri, 'Lua.workspace.supportScheme')
+ if not supports[scheme] then
return
end
local uri = files.getRealUri(doc.uri)
@@ -265,9 +266,10 @@ m.register 'textDocument/didClose' {
m.register 'textDocument/didChange' {
function (params)
- local doc = params.textDocument
- local scheme = furi.split(doc.uri)
- if scheme ~= 'file' then
+ local doc = params.textDocument
+ local scheme = furi.split(doc.uri)
+ local supports = config.get(doc.uri, 'Lua.workspace.supportScheme')
+ if not supports[scheme] then
return
end
local changes = params.contentChanges
@@ -1210,6 +1212,94 @@ m.register 'inlayHint/resolve' {
end
}
+m.register 'textDocument/diagnostic' {
+ preview = true,
+ capability = {
+ diagnosticProvider = {
+ identifier = 'identifier',
+ interFileDependencies = true,
+ workspaceDiagnostics = false,
+ }
+ },
+ ---@async
+ function (params)
+ local uri = files.getRealUri(params.textDocument.uri)
+ workspace.awaitReady(uri)
+ local core = require 'provider.diagnostic'
+ -- TODO: do some trick
+ core.refresh(uri)
+
+ return {
+ kind = 'unchanged',
+ resultId = uri,
+ }
+
+ --if not params.previousResultId then
+ -- core.clearCache(uri)
+ --end
+ --local results, unchanged = core.pullDiagnostic(uri, false)
+ --if unchanged then
+ -- return {
+ -- kind = 'unchanged',
+ -- resultId = uri,
+ -- }
+ --else
+ -- return {
+ -- kind = 'full',
+ -- resultId = uri,
+ -- items = results or {},
+ -- }
+ --end
+ end
+}
+
+m.register 'workspace/diagnostic' {
+ --preview = true,
+ --capability = {
+ -- diagnosticProvider = {
+ -- workspaceDiagnostics = false,
+ -- }
+ --},
+ ---@async
+ function (params)
+ local core = require 'provider.diagnostic'
+ local excepts = {}
+ for _, id in ipairs(params.previousResultIds) do
+ excepts[#excepts+1] = id.value
+ end
+ core.clearCacheExcept(excepts)
+ local function convertItem(result)
+ if result.unchanged then
+ return {
+ kind = 'unchanged',
+ resultId = result.uri,
+ uri = result.uri,
+ version = result.version,
+ }
+ else
+ return {
+ kind = 'full',
+ resultId = result.uri,
+ items = result.result or {},
+ uri = result.uri,
+ version = result.version,
+ }
+ end
+ end
+ core.pullDiagnosticScope(function (result)
+ proto.notify('$/progress', {
+ token = params.partialResultToken,
+ value = {
+ items = {
+ convertItem(result)
+ }
+ }
+ })
+ end)
+ return { items = {} }
+ end
+}
+
local function refreshStatusBar()
local valid = config.get(nil, 'Lua.window.statusBar')
for _, scp in ipairs(workspace.folders) do
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 6c95b5bd..cd2b602d 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -533,7 +533,7 @@ end
-- 该函数有副作用,会给source绑定node!
local function bindDocs(source)
local isParam = source.parent.type == 'funcargs'
- or source.parent.type == 'in'
+ or (source.parent.type == 'in' and source.finish <= source.parent.keys.finish)
local docs = source.bindDocs
for i = #docs, 1, -1 do
local doc = docs[i]
diff --git a/script/vm/generic.lua b/script/vm/generic.lua
index 6462028e..fafda518 100644
--- a/script/vm/generic.lua
+++ b/script/vm/generic.lua
@@ -15,6 +15,9 @@ mt.type = 'generic'
---@param resolved? table<string, vm.node>
---@return parser.object | vm.node
local function cloneObject(source, resolved)
+ if not source then
+ return nil
+ end
if not resolved then
return source
end
diff --git a/script/vm/ref.lua b/script/vm/ref.lua
index 545c294a..fbb9d015 100644
--- a/script/vm/ref.lua
+++ b/script/vm/ref.lua
@@ -279,10 +279,22 @@ local function searchByDef(source, pushResult)
defMap[source] = true
return defMap
end
- local defs = vm.getDefs(source)
- for _, def in ipairs(defs) do
- pushResult(def)
- defMap[def] = true
+ if source.type == 'field'
+ or source.type == 'method' then
+ source = source.parent
+ end
+ defMap[source] = true
+ if guide.isSet(source) then
+ local defs = vm.getDefs(source)
+ for _, def in ipairs(defs) do
+ pushResult(def)
+ end
+ else
+ local defs = vm.getDefs(source)
+ for _, def in ipairs(defs) do
+ pushResult(def)
+ defMap[def] = true
+ end
end
return defMap
end
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index 33f8784d..33252b9d 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -13,6 +13,7 @@ local fw = require 'filewatch'
local scope = require 'workspace.scope'
local loading = require 'workspace.loading'
local inspect = require 'inspect'
+local lang = require 'language'
---@class workspace
local m = {}
@@ -46,6 +47,11 @@ end
--- 初始化工作区
function m.create(uri)
log.info('Workspace create: ', uri)
+ if uri == furi.encode '/'
+ or uri == furi.encode(os.getenv 'HOME') then
+ client.showMessage('Error', lang.script('WORKSPACE_NOT_ALLOWED', furi.decode(uri)))
+ return
+ end
local path = m.normalize(furi.decode(uri))
fw.watch(path)
local scp = scope.createFolder(uri)
@@ -283,22 +289,34 @@ function m.awaitPreload(scp)
if scp.uri then
log.info('Scan files at:', scp:getName())
+ local count = 0
---@async
native:scan(furi.decode(scp.uri), function (path)
local uri = files.getRealUri(furi.encode(path))
scp:get('cachedUris')[uri] = true
ld:loadFile(uri)
+ end, function () ---@async
+ count = count + 1
+ if count == 100000 then
+ client.showMessage('Warning', lang.script('WORKSPACE_SCAN_TOO_MUCH', count, furi.decode(scp.uri)))
+ end
end)
end
for _, libMatcher in ipairs(librarys) do
log.info('Scan library at:', libMatcher.uri)
+ local count = 0
scp:addLink(libMatcher.uri)
---@async
libMatcher.matcher:scan(furi.decode(libMatcher.uri), function (path)
local uri = files.getRealUri(furi.encode(path))
scp:get('cachedUris')[uri] = true
ld:loadFile(uri, libMatcher.uri)
+ end, function () ---@async
+ count = count + 1
+ if count == 100000 then
+ client.showMessage('Warning', lang.script('WORKSPACE_SCAN_TOO_MUCH', count, furi.decode(libMatcher.uri)))
+ end
end)
scp:gc(fw.watch(furi.decode(libMatcher.uri)))
end
@@ -353,6 +371,7 @@ function m.normalize(path)
if platform.OS == 'Windows' then
path = path:gsub('[/\\]+', '\\')
:gsub('[/\\]+$', '')
+ :gsub('^(%a:)$', '%1\\')
else
path = path:gsub('[/\\]+', '/')
:gsub('[/\\]+$', '')