dotfiles from arch
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
local util = require 'utility'
|
||||
local await = require 'await'
|
||||
local progress = require 'progress'
|
||||
local lang = require 'language'
|
||||
|
||||
local m = {}
|
||||
|
||||
---@class meta
|
||||
---@field root string
|
||||
---@field classes meta.class[]
|
||||
|
||||
---@class meta.class
|
||||
---@field name string
|
||||
---@field comment string
|
||||
---@field location string
|
||||
---@field namespace string
|
||||
---@field baseClass string
|
||||
---@field attribute string
|
||||
---@field integerface string[]
|
||||
---@field fields meta.field[]
|
||||
---@field methods meta.method[]
|
||||
|
||||
---@class meta.field
|
||||
---@field name string
|
||||
---@field typeName string
|
||||
---@field comment string
|
||||
---@field location string
|
||||
|
||||
---@class meta.method
|
||||
---@field name string
|
||||
---@field comment string
|
||||
---@field location string
|
||||
---@field isStatic boolean
|
||||
---@field returnTypeName string
|
||||
---@field params {name: string, typeName: string}[]
|
||||
|
||||
---@param ... string
|
||||
---@return string
|
||||
local function mergeString(...)
|
||||
local buf = {}
|
||||
for i = 1, select('#', ...) do
|
||||
local str = select(i, ...)
|
||||
if str ~= '' then
|
||||
buf[#buf+1] = str
|
||||
end
|
||||
end
|
||||
return table.concat(buf, '.')
|
||||
end
|
||||
|
||||
local function addComments(lines, comment)
|
||||
if comment == '' then
|
||||
return
|
||||
end
|
||||
lines[#lines+1] = '--'
|
||||
lines[#lines+1] = '--' .. comment:gsub('[\r\n]+$', ''):gsub('\n', '\n--')
|
||||
lines[#lines+1] = '--'
|
||||
end
|
||||
|
||||
---@param lines string[]
|
||||
---@param name string
|
||||
---@param method meta.method
|
||||
local function addMethod(lines, name, method)
|
||||
if not method.name:match '^[%a_][%w_]*$' then
|
||||
return
|
||||
end
|
||||
addComments(lines, method.comment)
|
||||
lines[#lines+1] = ('---@source %s'):format(method.location:gsub('#', ':'))
|
||||
local params = {}
|
||||
for _, param in ipairs(method.params) do
|
||||
lines[#lines+1] = ('---@param %s %s'):format(param.name, param.typeName)
|
||||
params[#params+1] = param.name
|
||||
end
|
||||
if method.returnTypeName ~= ''
|
||||
and method.returnTypeName ~= 'Void' then
|
||||
lines[#lines+1] = ('---@return %s'):format(method.returnTypeName)
|
||||
end
|
||||
lines[#lines+1] = ('function %s%s%s(%s) end'):format(
|
||||
name,
|
||||
method.isStatic and ':' or '.',
|
||||
method.name,
|
||||
table.concat(params, ', ')
|
||||
)
|
||||
lines[#lines+1] = ''
|
||||
end
|
||||
|
||||
---@param root string
|
||||
---@param class meta.class
|
||||
---@return string
|
||||
local function buildText(root, class)
|
||||
local lines = {}
|
||||
|
||||
addComments(lines, class.comment)
|
||||
lines[#lines+1] = ('---@source %s'):format(class.location:gsub('#', ':'))
|
||||
if class.baseClass == '' then
|
||||
lines[#lines+1] = ('---@class %s'):format(mergeString(class.namespace, class.name))
|
||||
else
|
||||
lines[#lines+1] = ('---@class %s: %s'):format(mergeString(class.namespace, class.name), class.baseClass)
|
||||
end
|
||||
|
||||
for _, field in ipairs(class.fields) do
|
||||
addComments(lines, field.comment)
|
||||
lines[#lines+1] = ('---@source %s'):format(field.location:gsub('#', ':'))
|
||||
lines[#lines+1] = ('---@field %s %s'):format(field.name, field.typeName)
|
||||
end
|
||||
|
||||
lines[#lines+1] = ('---@source %s'):format(class.location:gsub('#', ':'))
|
||||
local name = mergeString(root, class.namespace, class.name)
|
||||
lines[#lines+1] = ('%s = {}'):format(name)
|
||||
lines[#lines+1] = ''
|
||||
|
||||
for _, method in ipairs(class.methods) do
|
||||
addMethod(lines, name, method)
|
||||
end
|
||||
|
||||
return table.concat(lines, '\n')
|
||||
end
|
||||
|
||||
local function buildRootText(api)
|
||||
local lines = {}
|
||||
|
||||
lines[#lines+1] = ('---@class %s'):format(api.root)
|
||||
lines[#lines+1] = ('%s = {}'):format(api.root)
|
||||
lines[#lines+1] = ''
|
||||
return table.concat(lines, '\n')
|
||||
end
|
||||
|
||||
---@async
|
||||
---@param path string
|
||||
---@param api meta
|
||||
function m.build(path, api)
|
||||
|
||||
local files = util.multiTable(2, function ()
|
||||
return { '---@meta' }
|
||||
end)
|
||||
|
||||
files[api.root][#files[api.root]+1] = buildRootText(api)
|
||||
|
||||
local proc <close> = progress.create(nil, lang.script.WINDOW_PROCESSING_BUILD_META, 0.5)
|
||||
for i, class in ipairs(api.classes) do
|
||||
local space = class.namespace ~= '' and class.namespace or api.root
|
||||
proc:setMessage(space)
|
||||
proc:setPercentage(i / #api.classes * 100)
|
||||
local text = buildText(api.root, class)
|
||||
files[space][#files[space]+1] = text
|
||||
await.delay()
|
||||
end
|
||||
|
||||
for space, texts in pairs(files) do
|
||||
util.saveFile(path .. '/' .. space .. '.lua', table.concat(texts, '\n\n'))
|
||||
end
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,84 @@
|
||||
local nonil = require 'without-check-nil'
|
||||
local client = require 'client'
|
||||
local completion = require 'provider.completion'
|
||||
|
||||
require 'provider.semantic-tokens'
|
||||
require 'provider.formatting'
|
||||
require 'provider.inlay-hint'
|
||||
require 'provider.code-lens'
|
||||
|
||||
local m = {}
|
||||
|
||||
m.fillings = {}
|
||||
m.resolvedMap = {}
|
||||
|
||||
local function mergeFillings(provider)
|
||||
for _, filling in ipairs(m.fillings) do
|
||||
for k, v in pairs(filling) do
|
||||
if type(v) == 'table' then
|
||||
if not provider[k] then
|
||||
provider[k] = {}
|
||||
end
|
||||
for kk, vv in pairs(v) do
|
||||
provider[k][kk] = vv
|
||||
end
|
||||
else
|
||||
provider[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function resolve(t)
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == 'table' then
|
||||
resolve(v)
|
||||
end
|
||||
if type(v) == 'string' then
|
||||
t[k] = v:gsub('%{(.-)%}', function (key)
|
||||
return m.resolvedMap[key] or ''
|
||||
end)
|
||||
end
|
||||
if type(v) == 'function' then
|
||||
t[k] = v()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function m.getProvider()
|
||||
local provider = {
|
||||
offsetEncoding = client.getOffsetEncoding(),
|
||||
-- 文本同步方式
|
||||
textDocumentSync = {
|
||||
-- 打开关闭文本时通知
|
||||
openClose = true,
|
||||
-- 文本增量更新
|
||||
change = 2,
|
||||
},
|
||||
}
|
||||
|
||||
nonil.enable()
|
||||
if not client.info.capabilities.textDocument.completion.dynamicRegistration
|
||||
or not client.info.capabilities.workspace.configuration then
|
||||
provider.completionProvider = {
|
||||
resolveProvider = true,
|
||||
triggerCharacters = completion.allWords(),
|
||||
}
|
||||
end
|
||||
nonil.disable()
|
||||
|
||||
mergeFillings(provider)
|
||||
resolve(provider)
|
||||
|
||||
return provider
|
||||
end
|
||||
|
||||
function m.filling(t)
|
||||
m.fillings[#m.fillings+1] = t
|
||||
end
|
||||
|
||||
function m.resolve(key, value)
|
||||
m.resolvedMap[key] = value
|
||||
end
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,29 @@
|
||||
local proto = require 'proto'
|
||||
local client = require 'client'
|
||||
local json = require 'json'
|
||||
local config = require 'config'
|
||||
|
||||
local function refresh()
|
||||
if not client.isReady() then
|
||||
return
|
||||
end
|
||||
if not client.getAbility 'workspace.codeLens.refreshSupport' then
|
||||
return
|
||||
end
|
||||
log.debug('Refresh codeLens.')
|
||||
proto.request('workspace/codeLens/refresh', json.null)
|
||||
end
|
||||
|
||||
config.watch(function (_uri, key, _value, _oldValue)
|
||||
if key == '' then
|
||||
refresh()
|
||||
end
|
||||
if key:find '^Lua.runtime'
|
||||
or key:find '^Lua.workspace'
|
||||
or key:find '^Lua.codeLens'
|
||||
or key:find '^files' then
|
||||
refresh()
|
||||
end
|
||||
end)
|
||||
|
||||
return {}
|
||||
@@ -0,0 +1,104 @@
|
||||
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
|
||||
|
||||
local function allWords()
|
||||
local str = '\t\n.:(\'"[,#*@|=-{ +?'
|
||||
local mark = {}
|
||||
local list = {}
|
||||
for c in str:gmatch '.' do
|
||||
list[#list+1] = c
|
||||
mark[c] = true
|
||||
end
|
||||
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
|
||||
local separator = config.get(scp.uri, 'Lua.completion.requireSeparator')
|
||||
if not mark[separator] then
|
||||
list[#list+1] = separator
|
||||
mark[separator] = true
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function enable(_uri)
|
||||
if isEnable then
|
||||
return
|
||||
end
|
||||
nonil.enable()
|
||||
if not client.info.capabilities.textDocument.completion.dynamicRegistration then
|
||||
nonil.disable()
|
||||
return
|
||||
end
|
||||
nonil.disable()
|
||||
isEnable = true
|
||||
log.info('Enable completion.')
|
||||
proto.request('client/registerCapability', {
|
||||
registrations = {
|
||||
{
|
||||
id = 'completion',
|
||||
method = 'textDocument/completion',
|
||||
registerOptions = {
|
||||
resolveProvider = true,
|
||||
triggerCharacters = allWords(),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
local function disable(_uri)
|
||||
if not isEnable then
|
||||
return
|
||||
end
|
||||
nonil.enable()
|
||||
if not client.info.capabilities.textDocument.completion.dynamicRegistration then
|
||||
nonil.disable()
|
||||
return
|
||||
end
|
||||
nonil.disable()
|
||||
isEnable = false
|
||||
log.info('Disable completion.')
|
||||
proto.request('client/unregisterCapability', {
|
||||
unregisterations = {
|
||||
{
|
||||
id = 'completion',
|
||||
method = 'textDocument/completion',
|
||||
},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
config.watch(function (uri, key, value)
|
||||
if key == '' then
|
||||
key = 'Lua.completion.enable'
|
||||
value = config.get(uri, key)
|
||||
end
|
||||
if key == 'Lua.completion.enable' then
|
||||
if value == true then
|
||||
enable(uri)
|
||||
else
|
||||
disable(uri)
|
||||
end
|
||||
end
|
||||
if key == 'Lua.completion.postfix' then
|
||||
if config.get(uri, 'Lua.completion.enable') then
|
||||
disable(uri)
|
||||
enable(uri)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return {
|
||||
enable = enable,
|
||||
disable = disable,
|
||||
allWords = allWords,
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
local await = require 'await'
|
||||
local proto = require 'proto.proto'
|
||||
local define = require 'proto.define'
|
||||
local lang = require 'language'
|
||||
local files = require 'files'
|
||||
local config = require 'config'
|
||||
local core = require 'core.diagnostics'
|
||||
local util = require 'utility'
|
||||
local ws = require 'workspace'
|
||||
local progress = require "progress"
|
||||
local client = require 'client'
|
||||
local converter = require 'proto.converter'
|
||||
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'
|
||||
local vm = require 'vm.vm'
|
||||
|
||||
---@class diagnosticProvider
|
||||
local m = {}
|
||||
m.cache = {}
|
||||
m.sleepRest = 0.0
|
||||
m.scopeDiagCount = 0
|
||||
m.pauseCount = 0
|
||||
|
||||
local function concat(t, sep)
|
||||
if type(t) ~= 'table' then
|
||||
return t
|
||||
end
|
||||
return table.concat(t, sep)
|
||||
end
|
||||
|
||||
local function buildSyntaxError(uri, err)
|
||||
local state = files.getState(uri)
|
||||
local text = files.getText(uri)
|
||||
if not text or not state then
|
||||
return
|
||||
end
|
||||
local message = lang.script('PARSER_' .. err.type, err.info)
|
||||
|
||||
if err.version then
|
||||
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
|
||||
))
|
||||
end
|
||||
|
||||
local related = err.info and err.info.related
|
||||
local relatedInformation
|
||||
if related then
|
||||
relatedInformation = {}
|
||||
for _, rel in ipairs(related) do
|
||||
local rmessage
|
||||
if rel.message then
|
||||
rmessage = lang.script('PARSER_' .. rel.message)
|
||||
else
|
||||
rmessage = text:sub(rel.start, rel.finish)
|
||||
end
|
||||
local relUri = rel.uri or uri
|
||||
local relState = files.getState(relUri)
|
||||
if relState then
|
||||
relatedInformation[#relatedInformation+1] = {
|
||||
message = rmessage,
|
||||
location = converter.location(relUri, converter.packRange(relState, rel.start, rel.finish)),
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
code = err.type:lower():gsub('_', '-'),
|
||||
range = converter.packRange(state, err.start, err.finish),
|
||||
severity = define.DiagnosticSeverity[err.level],
|
||||
source = lang.script.DIAG_SYNTAX_CHECK,
|
||||
message = message,
|
||||
data = 'syntax',
|
||||
|
||||
relatedInformation = relatedInformation,
|
||||
}
|
||||
end
|
||||
|
||||
local function buildDiagnostic(uri, diag)
|
||||
local state = files.getState(uri)
|
||||
if not state then
|
||||
return
|
||||
end
|
||||
|
||||
local relatedInformation
|
||||
if diag.related then
|
||||
relatedInformation = {}
|
||||
for _, rel in ipairs(diag.related) do
|
||||
local rtext = files.getText(rel.uri)
|
||||
if not rtext then
|
||||
goto CONTINUE
|
||||
end
|
||||
local relState = files.getState(rel.uri)
|
||||
if not relState then
|
||||
goto CONTINUE
|
||||
end
|
||||
relatedInformation[#relatedInformation+1] = {
|
||||
message = rel.message or rtext:sub(rel.start, rel.finish),
|
||||
location = converter.location(rel.uri, converter.packRange(relState, rel.start, rel.finish))
|
||||
}
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
range = converter.packRange(state, diag.start, diag.finish),
|
||||
source = lang.script.DIAG_DIAGNOSTICS,
|
||||
severity = diag.level,
|
||||
message = diag.message,
|
||||
code = diag.code,
|
||||
tags = diag.tags,
|
||||
data = diag.data,
|
||||
|
||||
relatedInformation = relatedInformation,
|
||||
}
|
||||
end
|
||||
|
||||
local function mergeDiags(a, b, c)
|
||||
if not a and not b and not c then
|
||||
return nil
|
||||
end
|
||||
local t = {}
|
||||
|
||||
local function merge(diags)
|
||||
if not diags then
|
||||
return
|
||||
end
|
||||
for i = 1, #diags do
|
||||
local diag = diags[i]
|
||||
local severity = diag.severity
|
||||
if severity == define.DiagnosticSeverity.Hint
|
||||
or severity == define.DiagnosticSeverity.Information then
|
||||
if #t > 10000 then
|
||||
goto CONTINUE
|
||||
end
|
||||
end
|
||||
t[#t+1] = diag
|
||||
::CONTINUE::
|
||||
end
|
||||
end
|
||||
|
||||
merge(a)
|
||||
merge(b)
|
||||
merge(c)
|
||||
|
||||
if #t == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- enable `push`, disable `clear`
|
||||
function m.clear(uri, force)
|
||||
await.close('diag:' .. uri)
|
||||
if m.cache[uri] == nil and not force then
|
||||
return
|
||||
end
|
||||
m.cache[uri] = nil
|
||||
proto.notify('textDocument/publishDiagnostics', {
|
||||
uri = uri,
|
||||
diagnostics = {},
|
||||
})
|
||||
log.info('clearDiagnostics', uri)
|
||||
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
|
||||
|
||||
---@param uri? uri
|
||||
---@param force? boolean
|
||||
function m.clearAll(uri, force)
|
||||
local scp
|
||||
if uri then
|
||||
scp = scope.getScope(uri)
|
||||
end
|
||||
if force then
|
||||
for luri in files.eachFile() do
|
||||
if not scp or scope.getScope(luri) == scp then
|
||||
m.clear(luri, force)
|
||||
end
|
||||
end
|
||||
else
|
||||
for luri in pairs(m.cache) do
|
||||
if not scp or scope.getScope(luri) == scp then
|
||||
m.clear(luri)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function m.syntaxErrors(uri, ast)
|
||||
if #ast.errs == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local results = {}
|
||||
|
||||
pcall(function ()
|
||||
local disables = util.arrayToHash(config.get(uri, 'Lua.diagnostics.disable'))
|
||||
for _, err in ipairs(ast.errs) do
|
||||
local id = err.type:lower():gsub('_', '-')
|
||||
if not disables[id]
|
||||
and not vm.isDiagDisabledAt(uri, err.start, id, true) then
|
||||
results[#results+1] = buildSyntaxError(uri, err)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
local function copyDiagsWithoutSyntax(diags)
|
||||
if not diags then
|
||||
return nil
|
||||
end
|
||||
local copyed = {}
|
||||
for _, diag in ipairs(diags) do
|
||||
if diag.data ~= 'syntax' then
|
||||
copyed[#copyed+1] = diag
|
||||
end
|
||||
end
|
||||
return copyed
|
||||
end
|
||||
|
||||
---@async
|
||||
---@param uri uri
|
||||
---@return boolean
|
||||
local function isValid(uri)
|
||||
if not config.get(uri, 'Lua.diagnostics.enable') then
|
||||
return false
|
||||
end
|
||||
if not ws.isReady(uri) then
|
||||
return false
|
||||
end
|
||||
if files.isLibrary(uri, true) then
|
||||
local status = config.get(uri, 'Lua.diagnostics.libraryFiles')
|
||||
if status == 'Disable' then
|
||||
return false
|
||||
elseif status == 'Opened' then
|
||||
if not files.isOpen(uri) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
if ws.isIgnored(uri) then
|
||||
local status = config.get(uri, 'Lua.diagnostics.ignoredFiles')
|
||||
if status == 'Disable' then
|
||||
return false
|
||||
elseif status == 'Opened' then
|
||||
if not files.isOpen(uri) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
local scheme = furi.split(uri)
|
||||
local disableScheme = config.get(uri, 'Lua.diagnostics.disableScheme')
|
||||
if util.arrayHas(disableScheme, scheme) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@async
|
||||
function m.doDiagnostic(uri, isScopeDiag, ignoreFileState)
|
||||
if not isValid(uri) then
|
||||
return
|
||||
end
|
||||
|
||||
await.delay()
|
||||
|
||||
local state = files.getState(uri)
|
||||
if not state then
|
||||
m.clear(uri)
|
||||
return
|
||||
end
|
||||
|
||||
local version = files.getVersion(uri)
|
||||
|
||||
local prog <close> = progress.create(uri, lang.script.WINDOW_DIAGNOSING, 0.5)
|
||||
prog:setMessage(ws.getRelativePath(uri))
|
||||
|
||||
--log.debug('Diagnostic file:', uri)
|
||||
|
||||
local syntax = m.syntaxErrors(uri, state)
|
||||
|
||||
local diags = {}
|
||||
local lastDiag = copyDiagsWithoutSyntax(m.cache[uri])
|
||||
local function pushResult()
|
||||
tracy.ZoneBeginN 'mergeSyntaxAndDiags'
|
||||
local _ <close> = tracy.ZoneEnd
|
||||
local full = mergeDiags(syntax, lastDiag, diags)
|
||||
--log.debug(('Pushed [%d] results'):format(full and #full or 0))
|
||||
if not full then
|
||||
m.clear(uri)
|
||||
return
|
||||
end
|
||||
|
||||
if util.equal(m.cache[uri], full) then
|
||||
return
|
||||
end
|
||||
m.cache[uri] = full
|
||||
|
||||
if not files.exists(uri) then
|
||||
m.clear(uri)
|
||||
return
|
||||
end
|
||||
|
||||
proto.notify('textDocument/publishDiagnostics', {
|
||||
uri = uri,
|
||||
version = version,
|
||||
diagnostics = full,
|
||||
})
|
||||
log.debug('publishDiagnostics', uri, #full)
|
||||
end
|
||||
|
||||
pushResult()
|
||||
|
||||
local lastPushClock = time.time()
|
||||
---@async
|
||||
xpcall(core, log.error, uri, isScopeDiag, function (result)
|
||||
diags[#diags+1] = buildDiagnostic(uri, result)
|
||||
|
||||
if not isScopeDiag and time.time() - lastPushClock >= 500 then
|
||||
lastPushClock = time.time()
|
||||
pushResult()
|
||||
end
|
||||
end, function (checkedName)
|
||||
if not lastDiag then
|
||||
return
|
||||
end
|
||||
for i, diag in ipairs(lastDiag) do
|
||||
if diag.code == checkedName then
|
||||
lastDiag[i] = lastDiag[#lastDiag]
|
||||
lastDiag[#lastDiag] = nil
|
||||
end
|
||||
end
|
||||
end, ignoreFileState)
|
||||
|
||||
lastDiag = nil
|
||||
pushResult()
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
function m.resendDiagnostic(uri)
|
||||
local full = m.cache[uri]
|
||||
if not full then
|
||||
return
|
||||
end
|
||||
|
||||
if not files.exists(uri) then
|
||||
m.clear(uri)
|
||||
return
|
||||
end
|
||||
|
||||
local version = files.getVersion(uri)
|
||||
|
||||
proto.notify('textDocument/publishDiagnostics', {
|
||||
uri = uri,
|
||||
version = version,
|
||||
diagnostics = full,
|
||||
})
|
||||
log.debug('publishDiagnostics', uri, #full)
|
||||
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
|
||||
|
||||
---@param uri uri
|
||||
function m.stopScopeDiag(uri)
|
||||
local scp = scope.getScope(uri)
|
||||
local scopeID = 'diagnosticsScope:' .. scp:getName()
|
||||
await.close(scopeID)
|
||||
end
|
||||
|
||||
---@param event string
|
||||
---@param uri uri
|
||||
function m.refreshScopeDiag(event, uri)
|
||||
if not ws.isReady(uri) then
|
||||
return
|
||||
end
|
||||
|
||||
local eventConfig = config.get(uri, 'Lua.diagnostics.workspaceEvent')
|
||||
|
||||
if eventConfig ~= event then
|
||||
return
|
||||
end
|
||||
|
||||
---@async
|
||||
await.call(function ()
|
||||
local delay = config.get(uri, 'Lua.diagnostics.workspaceDelay') / 1000
|
||||
if delay < 0 then
|
||||
return
|
||||
end
|
||||
await.sleep(math.max(delay, 0.2))
|
||||
m.diagnosticsScope(uri)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param uri uri
|
||||
function m.refresh(uri)
|
||||
if not ws.isReady(uri) then
|
||||
return
|
||||
end
|
||||
|
||||
await.close('diag:' .. uri)
|
||||
---@async
|
||||
await.call(function ()
|
||||
await.setID('diag:' .. uri)
|
||||
repeat
|
||||
await.sleep(0.1)
|
||||
until not m.isPaused()
|
||||
xpcall(m.doDiagnostic, log.error, uri)
|
||||
end)
|
||||
end
|
||||
|
||||
---@async
|
||||
local function askForDisable(uri)
|
||||
if m.dontAskedForDisable then
|
||||
return
|
||||
end
|
||||
local delay = 30
|
||||
local delayTitle = lang.script('WINDOW_DELAY_WS_DIAGNOSTIC', delay)
|
||||
local item = proto.awaitRequest('window/showMessageRequest', {
|
||||
type = define.MessageType.Info,
|
||||
message = lang.script.WINDOW_SETTING_WS_DIAGNOSTIC,
|
||||
actions = {
|
||||
{
|
||||
title = lang.script.WINDOW_DONT_SHOW_AGAIN,
|
||||
},
|
||||
{
|
||||
title = delayTitle,
|
||||
},
|
||||
{
|
||||
title = lang.script.WINDOW_DISABLE_DIAGNOSTIC,
|
||||
},
|
||||
}
|
||||
})
|
||||
if not item then
|
||||
return
|
||||
end
|
||||
if item.title == lang.script.WINDOW_DONT_SHOW_AGAIN then
|
||||
m.dontAskedForDisable = true
|
||||
elseif item.title == delayTitle then
|
||||
client.setConfig {
|
||||
{
|
||||
key = 'Lua.diagnostics.workspaceDelay',
|
||||
action = 'set',
|
||||
value = delay * 1000,
|
||||
uri = uri,
|
||||
}
|
||||
}
|
||||
elseif item.title == lang.script.WINDOW_DISABLE_DIAGNOSTIC then
|
||||
client.setConfig {
|
||||
{
|
||||
key = 'Lua.diagnostics.workspaceDelay',
|
||||
action = 'set',
|
||||
value = -1,
|
||||
uri = uri,
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function clearMemory(finished)
|
||||
if m.scopeDiagCount > 0 then
|
||||
return
|
||||
end
|
||||
vm.clearNodeCache()
|
||||
if finished then
|
||||
collectgarbage()
|
||||
collectgarbage()
|
||||
end
|
||||
end
|
||||
|
||||
---@async
|
||||
function m.awaitDiagnosticsScope(suri, callback)
|
||||
local scp = scope.getScope(suri)
|
||||
if scp.type == 'fallback' then
|
||||
return
|
||||
end
|
||||
while loading.count() > 0 do
|
||||
await.sleep(1.0)
|
||||
end
|
||||
local finished
|
||||
m.scopeDiagCount = m.scopeDiagCount + 1
|
||||
local scopeDiag <close> = util.defer(function ()
|
||||
m.scopeDiagCount = m.scopeDiagCount - 1
|
||||
clearMemory(finished)
|
||||
end)
|
||||
local clock = os.clock()
|
||||
local bar <close> = progress.create(suri, lang.script.WORKSPACE_DIAGNOSTIC, 1)
|
||||
local cancelled
|
||||
bar:onCancel(function ()
|
||||
log.info('Cancel workspace diagnostics')
|
||||
cancelled = true
|
||||
---@async
|
||||
await.call(function ()
|
||||
askForDisable(suri)
|
||||
end)
|
||||
end)
|
||||
local uris = files.getAllUris(suri)
|
||||
local sortedUris = ltable()
|
||||
for _, uri in ipairs(uris) do
|
||||
if files.isOpen(uri) then
|
||||
sortedUris:pushHead(uri)
|
||||
else
|
||||
sortedUris:pushTail(uri)
|
||||
end
|
||||
end
|
||||
log.info(('Diagnostics scope [%s], files count:[%d]'):format(scp:getName(), #uris))
|
||||
local i = 0
|
||||
for uri in sortedUris:pairs() do
|
||||
while loading.count() > 0 do
|
||||
await.sleep(1.0)
|
||||
end
|
||||
i = i + 1
|
||||
bar:setMessage(('%d/%d'):format(i, #uris))
|
||||
bar:setPercentage(i / #uris * 100)
|
||||
callback(uri)
|
||||
await.delay()
|
||||
if cancelled then
|
||||
log.info('Break workspace diagnostics')
|
||||
break
|
||||
end
|
||||
end
|
||||
bar:remove()
|
||||
log.info(('Diagnostics scope [%s] finished, takes [%.3f] sec.'):format(scp:getName(), os.clock() - clock))
|
||||
finished = true
|
||||
end
|
||||
|
||||
function m.diagnosticsScope(uri, force, ignoreFileOpenState)
|
||||
if not ws.isReady(uri) then
|
||||
return
|
||||
end
|
||||
if not force and not config.get(uri, 'Lua.diagnostics.enable') then
|
||||
m.clearAll(uri)
|
||||
return
|
||||
end
|
||||
if not force and config.get(uri, 'Lua.diagnostics.workspaceDelay') < 0 then
|
||||
return
|
||||
end
|
||||
local scp = scope.getScope(uri)
|
||||
local id = 'diagnosticsScope:' .. scp:getName()
|
||||
await.close(id)
|
||||
await.call(function () ---@async
|
||||
await.sleep(0.0)
|
||||
m.awaitDiagnosticsScope(uri, function (fileUri)
|
||||
xpcall(m.doDiagnostic, log.error, fileUri, true, ignoreFileOpenState)
|
||||
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()
|
||||
if not client.isReady() then
|
||||
return
|
||||
end
|
||||
if not client.getAbility 'workspace.diagnostics.refreshSupport' then
|
||||
return
|
||||
end
|
||||
log.debug('Refresh client diagnostics')
|
||||
proto.request('workspace/diagnostic/refresh', json.null)
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function m.isPaused()
|
||||
return m.pauseCount > 0
|
||||
end
|
||||
|
||||
function m.pause()
|
||||
m.pauseCount = m.pauseCount + 1
|
||||
end
|
||||
|
||||
function m.resume()
|
||||
m.pauseCount = m.pauseCount - 1
|
||||
end
|
||||
|
||||
ws.watch(function (ev, uri)
|
||||
if ev == 'reload' then
|
||||
m.diagnosticsScope(uri)
|
||||
m.refreshClient()
|
||||
end
|
||||
end)
|
||||
|
||||
files.watch(function (ev, uri) ---@async
|
||||
if ev == 'remove' then
|
||||
m.clear(uri)
|
||||
m.stopScopeDiag(uri)
|
||||
m.refresh(uri)
|
||||
m.refreshScopeDiag('OnSave', uri)
|
||||
elseif ev == 'create' then
|
||||
m.stopScopeDiag(uri)
|
||||
m.refresh(uri)
|
||||
m.refreshScopeDiag('OnSave', uri)
|
||||
elseif ev == 'update' then
|
||||
m.stopScopeDiag(uri)
|
||||
m.refresh(uri)
|
||||
m.refreshScopeDiag('OnChange', uri)
|
||||
elseif ev == 'open' then
|
||||
if ws.isReady(uri) then
|
||||
m.resendDiagnostic(uri)
|
||||
xpcall(m.doDiagnostic, log.error, uri)
|
||||
end
|
||||
elseif ev == 'close' then
|
||||
if files.isLibrary(uri, true)
|
||||
or ws.isIgnored(uri) then
|
||||
m.clear(uri)
|
||||
end
|
||||
elseif ev == 'save' then
|
||||
m.refreshScopeDiag('OnSave', uri)
|
||||
end
|
||||
end)
|
||||
|
||||
config.watch(function (uri, key, value, oldValue)
|
||||
if util.stringStartWith(key, 'Lua.diagnostics')
|
||||
or util.stringStartWith(key, 'Lua.spell')
|
||||
or util.stringStartWith(key, 'Lua.doc') 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)
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,101 @@
|
||||
local suc, codeFormat = pcall(require, 'code_format')
|
||||
if not suc then
|
||||
return
|
||||
end
|
||||
|
||||
local ws = require 'workspace'
|
||||
local furi = require 'file-uri'
|
||||
local fs = require 'bee.filesystem'
|
||||
local fw = require 'filewatch'
|
||||
local util = require 'utility'
|
||||
local config = require 'config'
|
||||
|
||||
local loadedUris = {}
|
||||
|
||||
local updateType = {
|
||||
Created = 1,
|
||||
Changed = 2,
|
||||
Deleted = 3,
|
||||
}
|
||||
|
||||
fw.event(function(_ev, path)
|
||||
if util.stringEndWith(path, '.editorconfig') then
|
||||
for uri, fsPath in pairs(loadedUris) do
|
||||
loadedUris[uri] = nil
|
||||
if fsPath ~= true then
|
||||
local status, err = codeFormat.update_config(updateType.Deleted, uri, fsPath:string())
|
||||
if not status and err then
|
||||
log.error(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local m = {}
|
||||
|
||||
m.loadedDefaultConfig = false
|
||||
|
||||
---@param uri uri
|
||||
function m.updateConfig(uri)
|
||||
if not m.loadedDefaultConfig then
|
||||
m.loadedDefaultConfig = true
|
||||
codeFormat.set_default_config(config.get(uri, 'Lua.format.defaultConfig'))
|
||||
m.updateNonStandardSymbols(config.get(nil, 'Lua.runtime.nonstandardSymbol'))
|
||||
end
|
||||
|
||||
local currentUri = uri
|
||||
while true do
|
||||
currentUri = currentUri:match('^(.+)/[^/]*$')
|
||||
if not currentUri or loadedUris[currentUri] then
|
||||
return
|
||||
end
|
||||
loadedUris[currentUri] = true
|
||||
|
||||
local currentPath = furi.decode(currentUri)
|
||||
local editorConfigFSPath = fs.path(currentPath) / '.editorconfig'
|
||||
if fs.exists(editorConfigFSPath) then
|
||||
loadedUris[currentUri] = editorConfigFSPath
|
||||
local status, err = codeFormat.update_config(updateType.Created, currentUri, editorConfigFSPath:string())
|
||||
if not status and err then
|
||||
log.error(err)
|
||||
end
|
||||
end
|
||||
|
||||
if not ws.rootUri then
|
||||
return
|
||||
end
|
||||
|
||||
for _, scp in ipairs(ws.folders) do
|
||||
if scp.uri == currentUri then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param symbols? string[]
|
||||
function m.updateNonStandardSymbols(symbols)
|
||||
if symbols == nil then
|
||||
return
|
||||
end
|
||||
|
||||
for _, symbol in ipairs(symbols) do
|
||||
if symbol == "//" then
|
||||
codeFormat.set_clike_comments_symbol()
|
||||
end
|
||||
end
|
||||
|
||||
codeFormat.set_nonstandard_symbol()
|
||||
end
|
||||
|
||||
config.watch(function(_uri, key, value)
|
||||
if key == "Lua.format.defaultConfig" then
|
||||
codeFormat.set_default_config(value)
|
||||
elseif key == "Lua.runtime.nonstandardSymbol" then
|
||||
m.updateNonStandardSymbols(value)
|
||||
end
|
||||
end)
|
||||
|
||||
return m
|
||||
@@ -0,0 +1,2 @@
|
||||
require 'provider.diagnostic'
|
||||
return require 'provider.provider'
|
||||
@@ -0,0 +1,29 @@
|
||||
local proto = require 'proto'
|
||||
local client = require 'client'
|
||||
local json = require "json"
|
||||
local config = require 'config'
|
||||
|
||||
local function refresh()
|
||||
if not client.isReady() then
|
||||
return
|
||||
end
|
||||
if not client.getAbility 'workspace.inlayHint.refreshSupport' then
|
||||
return
|
||||
end
|
||||
log.debug('Refresh inlay hints.')
|
||||
proto.request('workspace/inlayHint/refresh', json.null)
|
||||
end
|
||||
|
||||
config.watch(function (_uri, key, _value, _oldValue)
|
||||
if key == '' then
|
||||
refresh()
|
||||
end
|
||||
if key:find '^Lua.runtime'
|
||||
or key:find '^Lua.workspace'
|
||||
or key:find '^Lua.hint'
|
||||
or key:find '^files' then
|
||||
refresh()
|
||||
end
|
||||
end)
|
||||
|
||||
return {}
|
||||
@@ -0,0 +1,113 @@
|
||||
local config = require 'config'
|
||||
local ws = require 'workspace'
|
||||
local util = require 'utility'
|
||||
|
||||
local function getConfig(key)
|
||||
local scope = ws.getFirstScope()
|
||||
local uri = scope.uri
|
||||
return config.get(uri, key)
|
||||
end
|
||||
|
||||
-- Enumeration of commonly encountered syntax token types.
|
||||
local SyntaxTokenType = {
|
||||
Other = 0, -- Everything except tokens that are part of comments, string literals and regular expressions.
|
||||
Comment = 1, -- A comment.
|
||||
String = 2, -- A string literal.
|
||||
RegEx = 3 -- A regular expression.
|
||||
}
|
||||
|
||||
-- Describes what to do with the indentation when pressing Enter.
|
||||
local IndentAction = {
|
||||
None = 0, -- Insert new line and copy the previous line's indentation.
|
||||
Indent = 1, -- Insert new line and indent once (relative to the previous line's indentation).
|
||||
IndentOutdent = 2, -- Insert two new lines: the first one indented which will hold the cursor, and the second one at the same indentation level.
|
||||
Outdent = 3 -- Insert new line and outdent once (relative to the previous line's indentation).
|
||||
}
|
||||
|
||||
local languageConfiguration = {
|
||||
id = 'lua',
|
||||
configuration = {
|
||||
autoClosingPairs = {
|
||||
{ open = "{", close = "}" },
|
||||
{ open = "[", close = "]" },
|
||||
{ open = "(", close = ")" },
|
||||
{ open = "'", close = "'", notIn = { SyntaxTokenType.String } },
|
||||
{ open = '"', close = '"', notIn = { SyntaxTokenType.String } },
|
||||
{ open = "[=", close = "=]" },
|
||||
{ open = "[==", close = "==]" },
|
||||
{ open = "[===", close = "===]" },
|
||||
{ open = "[====", close = "====]" },
|
||||
{ open = "[=====", close = "=====]" },
|
||||
},
|
||||
onEnterRules = {
|
||||
{
|
||||
beforeText = [[\)\s*$]],
|
||||
afterText = [[^\s*end\b]],
|
||||
action = {
|
||||
indentAction = IndentAction.IndentOutdent,
|
||||
}
|
||||
},
|
||||
{
|
||||
beforeText = [[\b()\s*$]],
|
||||
afterText = [[^\s*end\b]],
|
||||
action = {
|
||||
indentAction = IndentAction.IndentOutdent,
|
||||
}
|
||||
},
|
||||
{
|
||||
beforeText = [[\b(repeat)\s*$]],
|
||||
afterText = [[^\s*until\b]],
|
||||
action = {
|
||||
indentAction = IndentAction.IndentOutdent,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local completeAnnotation = {
|
||||
configuration = {
|
||||
onEnterRules = {
|
||||
{
|
||||
beforeText = [[^\s*---@]],
|
||||
action = {
|
||||
indentAction = IndentAction.None,
|
||||
appendText = "---@"
|
||||
}
|
||||
},
|
||||
{
|
||||
beforeText = [[^\s*--- @]],
|
||||
action = {
|
||||
indentAction = IndentAction.None,
|
||||
appendText = "--- @"
|
||||
}
|
||||
},
|
||||
{
|
||||
beforeText = [[^\s*--- ]],
|
||||
action = {
|
||||
indentAction = IndentAction.None,
|
||||
appendText = "--- "
|
||||
}
|
||||
},
|
||||
{
|
||||
beforeText = [[^\s*---]],
|
||||
action = {
|
||||
indentAction = IndentAction.None,
|
||||
appendText = "---"
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.make()
|
||||
local result = languageConfiguration
|
||||
if getConfig 'Lua.language.completeAnnotation' then
|
||||
result = util.mergeStruct(result, completeAnnotation)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,163 @@
|
||||
local wssymbol = require("core.workspace-symbol")
|
||||
local guide = require("parser.guide")
|
||||
|
||||
---@class markdown
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
mt.__name = 'markdown'
|
||||
|
||||
mt._splitLine = false
|
||||
|
||||
---Converts `[mySymbol](lua://mySymbol)` into a link that points to the origin of `mySymbol`.
|
||||
---@param txt string
|
||||
local function processSymbolReferences(txt)
|
||||
local function replacer(linkText, symbol)
|
||||
local source ---@type table
|
||||
|
||||
for _, match in ipairs(wssymbol(symbol)) do
|
||||
if match.name == symbol then
|
||||
source = match.source
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not source then
|
||||
log.warn(string.format("Failed to find source of %q symbol in markdown comment", symbol))
|
||||
return
|
||||
end
|
||||
|
||||
local row, _ = guide.rowColOf(source.start)
|
||||
local uri = string.format("%s#%i", guide.getUri(source), row + 1)
|
||||
|
||||
return string.format("[%s](%s)", linkText, uri)
|
||||
end
|
||||
|
||||
return string.gsub(txt, "%[([^]]*)%]%(lua://([^)]+)%)", replacer)
|
||||
end
|
||||
|
||||
function mt:__tostring()
|
||||
return self:string()
|
||||
end
|
||||
|
||||
---@param language string
|
||||
---@param text? string|markdown
|
||||
function mt:add(language, text)
|
||||
if not text then
|
||||
return self
|
||||
end
|
||||
self._cacheResult = nil
|
||||
if type(text) == 'table' then
|
||||
self[#self+1] = {
|
||||
type = 'markdown',
|
||||
markdown = text,
|
||||
}
|
||||
else
|
||||
text = tostring(text)
|
||||
self[#self+1] = {
|
||||
type = 'text',
|
||||
language = language,
|
||||
text = text,
|
||||
}
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function mt:splitLine()
|
||||
self._cacheResult = nil
|
||||
self[#self+1] = {
|
||||
type = 'splitline',
|
||||
}
|
||||
return self
|
||||
end
|
||||
|
||||
function mt:emptyLine()
|
||||
self._cacheResult = nil
|
||||
self[#self+1] = {
|
||||
type = 'emptyline',
|
||||
}
|
||||
return self
|
||||
end
|
||||
|
||||
---@return string
|
||||
function mt:string(nl)
|
||||
if self._cacheResult then
|
||||
return self._cacheResult
|
||||
end
|
||||
local lines = {}
|
||||
local language = 'md'
|
||||
|
||||
local function concat(markdown)
|
||||
for _, obj in ipairs(markdown) do
|
||||
if obj.type == 'splitline' then
|
||||
if language ~= 'md' then
|
||||
lines[#lines+1] = '```'
|
||||
language = 'md'
|
||||
end
|
||||
if #lines > 0
|
||||
and lines[#lines] ~= '---' then
|
||||
lines[#lines+1] = ''
|
||||
lines[#lines+1] = '---'
|
||||
end
|
||||
elseif obj.type == 'emptyline' then
|
||||
if #lines > 0
|
||||
and lines[#lines] ~= '' then
|
||||
if language ~= 'md' then
|
||||
language = 'md'
|
||||
lines[#lines+1] = '```'
|
||||
end
|
||||
lines[#lines+1] = ''
|
||||
end
|
||||
elseif obj.type == 'markdown' then
|
||||
concat(obj.markdown)
|
||||
else
|
||||
if obj.language ~= language then
|
||||
if language ~= 'md' then
|
||||
lines[#lines+1] = '```'
|
||||
end
|
||||
if #lines > 0 then
|
||||
lines[#lines+1] = ''
|
||||
end
|
||||
if obj.language ~= 'md' then
|
||||
lines[#lines+1] = '```' .. obj.language
|
||||
end
|
||||
end
|
||||
if obj.language == 'md' and #lines > 0 then
|
||||
local last = lines[#lines]
|
||||
if obj.text:sub(1, 1) == '@'
|
||||
or last:sub(1, 1) == '@' then
|
||||
if lines[#lines] ~= '' then
|
||||
lines[#lines+1] = ''
|
||||
end
|
||||
elseif last == '---' then
|
||||
if lines[#lines] ~= '' then
|
||||
lines[#lines+1] = ''
|
||||
end
|
||||
end
|
||||
end
|
||||
lines[#lines + 1] = processSymbolReferences(obj.text)
|
||||
language = obj.language
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concat(self)
|
||||
if language ~= 'md' then
|
||||
lines[#lines+1] = '```'
|
||||
end
|
||||
while true do
|
||||
if lines[#lines] == '---'
|
||||
or lines[#lines] == '' then
|
||||
lines[#lines] = nil
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local result = table.concat(lines, nl or '\n')
|
||||
self._cacheResult = result
|
||||
return result
|
||||
end
|
||||
|
||||
return function ()
|
||||
return setmetatable({}, mt)
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
local suc, codeFormat = pcall(require, 'code_format')
|
||||
if not suc then
|
||||
return
|
||||
end
|
||||
|
||||
local config = require 'config'
|
||||
|
||||
local m = {}
|
||||
|
||||
m.loaded = false
|
||||
|
||||
function m.nameStyleCheck(uri, text)
|
||||
if not m.loaded then
|
||||
local value = config.get(nil, "Lua.nameStyle.config")
|
||||
codeFormat.update_name_style_config(value)
|
||||
m.loaded = true
|
||||
end
|
||||
|
||||
return codeFormat.name_style_analysis(uri, text)
|
||||
end
|
||||
|
||||
config.watch(function (_uri, key, value)
|
||||
if key == "Lua.nameStyle.config" then
|
||||
codeFormat.update_name_style_config(value)
|
||||
end
|
||||
end)
|
||||
|
||||
return m
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
local proto = require 'proto'
|
||||
local client = require 'client'
|
||||
local json = require "json"
|
||||
local config = require 'config'
|
||||
|
||||
local function refresh()
|
||||
if not client.isReady() then
|
||||
return
|
||||
end
|
||||
if not client.getAbility 'workspace.semanticTokens.refreshSupport' then
|
||||
return
|
||||
end
|
||||
log.debug('Refresh semantic tokens.')
|
||||
proto.request('workspace/semanticTokens/refresh', json.null)
|
||||
end
|
||||
|
||||
config.watch(function (_uri, key, _value, _oldValue)
|
||||
if key == '' then
|
||||
refresh()
|
||||
end
|
||||
if key:find '^Lua.runtime'
|
||||
or key:find '^Lua.workspace'
|
||||
or key:find '^Lua.semantic'
|
||||
or key:find '^files' then
|
||||
refresh()
|
||||
end
|
||||
end)
|
||||
|
||||
return {}
|
||||
@@ -0,0 +1,51 @@
|
||||
local suc, codeFormat = pcall(require, 'code_format')
|
||||
if not suc then
|
||||
return
|
||||
end
|
||||
|
||||
local fs = require 'bee.filesystem'
|
||||
local config = require 'config'
|
||||
local pformatting = require 'provider.formatting'
|
||||
|
||||
local m = {}
|
||||
|
||||
function m.loadDictionaryFromFile(filePath)
|
||||
return codeFormat.spell_load_dictionary_from_path(filePath)
|
||||
end
|
||||
|
||||
function m.loadDictionaryFromBuffer(buffer)
|
||||
return codeFormat.spell_load_dictionary_from_buffer(buffer)
|
||||
end
|
||||
|
||||
function m.addWord(word)
|
||||
return codeFormat.spell_load_dictionary_from_buffer(word)
|
||||
end
|
||||
|
||||
function m.spellCheck(uri, text)
|
||||
if not m._dictionaryLoaded then
|
||||
m.initDictionary()
|
||||
m._dictionaryLoaded = true
|
||||
end
|
||||
|
||||
local tempDict = config.get(uri, 'Lua.spell.dict')
|
||||
|
||||
return codeFormat.spell_analysis(uri, text, tempDict)
|
||||
end
|
||||
|
||||
function m.getSpellSuggest(word)
|
||||
local status, result = codeFormat.spell_suggest(word)
|
||||
if status then
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
function m.initDictionary()
|
||||
local basicDictionary = fs.path(METAPATH) / "spell/dictionary.txt"
|
||||
local luaDictionary = fs.path(METAPATH) / "spell/lua_dict.txt"
|
||||
|
||||
m.loadDictionaryFromFile(basicDictionary:string())
|
||||
m.loadDictionaryFromFile(luaDictionary:string())
|
||||
pformatting.updateNonStandardSymbols(config.get(nil, "Lua.runtime.nonstandardSymbol"))
|
||||
end
|
||||
|
||||
return m
|
||||
Reference in New Issue
Block a user