dotfiles from arch

This commit is contained in:
2025-09-28 11:39:12 +02:00
parent 75885729cd
commit d1c6923bbb
1358 changed files with 575835 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 {}

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
require 'provider.diagnostic'
return require 'provider.provider'

View File

@@ -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 {}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 {}

View File

@@ -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