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,235 @@
local guide = require 'parser.guide'
local files = require 'files'
local encoder = require 'encoder'
local offsetEncoding = 'utf16'
---@class converter
local m = {}
---@alias position {line: integer, character: integer}
---@param row integer
---@param col integer
---@return position
function m.position(row, col)
return {
line = row,
character = col,
}
end
---@param state parser.state
---@param pos integer
---@return position
local function rawPackPosition(state, pos)
local row, col = guide.rowColOf(pos)
if col > 0 then
local text = state.lua
if state and text then
local lineOffset = state.lines[row]
if lineOffset then
local start = lineOffset
local finish = lineOffset + col - 1
if start <= #text and finish <= #text then
col = encoder.len(offsetEncoding, text, lineOffset, lineOffset + col - 1)
end
else
col = 0
end
end
end
return {
line = row,
character = col,
}
end
---@param state parser.state
---@param pos integer
---@return position
local function diffedPackPosition(state, pos)
local offset = guide.positionToOffset(state, pos)
local originOffset = files.diffedOffsetBack(state, offset)
local originPos = guide.offsetToPositionByLines(state.originLines, originOffset)
local row, col = guide.rowColOf(originPos)
if col > 0 then
local text = state.originText
if text then
local lineOffset = state.originLines[row]
local finalOffset = math.min(lineOffset + col - 1, #text + 1)
col = encoder.len(offsetEncoding, text, lineOffset, finalOffset)
end
end
return {
line = row,
character = col,
}
end
---@param state parser.state
---@param pos integer
---@return position
function m.packPosition(state, pos)
if files.hasDiffed(state) then
return diffedPackPosition(state, pos)
else
return rawPackPosition(state, pos)
end
end
---@param state parser.state
---@param position position
---@return integer
local function rawUnpackPosition(state, position)
local row, col = position.line, position.character
if col > 0 then
local text = state.lua
if state and text then
local lineOffset = state.lines[row]
local textOffset = encoder.offset(offsetEncoding, text, col + 1, lineOffset)
if textOffset and lineOffset then
col = textOffset - lineOffset
end
end
end
local pos = guide.positionOf(row, col)
return pos
end
---@param state parser.state
---@param position position
---@return integer
local function diffedUnpackPosition(state, position)
local row, col = position.line, position.character
if col > 0 then
local lineOffset = state.originLines[row]
if lineOffset then
local textOffset = encoder.offset(offsetEncoding, state.originText, col + 1, lineOffset)
if textOffset and lineOffset then
col = textOffset - lineOffset
end
end
end
local originPos = guide.positionOf(row, col)
local originOffset = guide.positionToOffsetByLines(state.originLines, originPos)
local offset = files.diffedOffset(state, originOffset)
local pos = guide.offsetToPosition(state, offset)
return pos
end
---@param state parser.state
---@param position position
---@return integer
function m.unpackPosition(state, position)
if files.hasDiffed(state) then
return diffedUnpackPosition(state, position)
else
return rawUnpackPosition(state, position)
end
end
---@alias range {start: position, end: position}
---@param state parser.state
---@param start integer
---@param finish integer
---@return range
function m.packRange(state, start, finish)
local range = {
start = m.packPosition(state, start),
['end'] = m.packPosition(state, finish),
}
return range
end
---@param start position
---@param finish position
---@return range
function m.range(start, finish)
return {
start = start,
['end'] = finish,
}
end
---@param state parser.state
---@param range range
---@return integer start
---@return integer finish
function m.unpackRange(state, range)
local start = m.unpackPosition(state, range.start)
local finish = m.unpackPosition(state, range['end'])
return start, finish
end
---@alias location {uri: uri, range: range}
---@param uri string
---@param range range
---@return location
function m.location(uri, range)
return {
uri = uri,
range = range,
}
end
---@alias locationLink {targetUri:uri, targetRange: range, targetSelectionRange: range, originSelectionRange: range}
---@param uri string
---@param range range
---@param selection range
---@param origin range
---@return locationLink
function m.locationLink(uri, range, selection, origin)
return {
targetUri = uri,
targetRange = range,
targetSelectionRange = selection,
originSelectionRange = origin,
}
end
---@alias textEdit {range: range, newText: string}
---@param range range
---@param newtext string
---@return textEdit
function m.textEdit(range, newtext)
return {
range = range,
newText = newtext,
}
end
function m.setOffsetEncoding(encoding)
offsetEncoding = encoding:lower():gsub('%-', '')
end
---@param s string
---@param i? integer
---@param j? integer
---@return integer
function m.len(s, i, j)
return encoder.len(offsetEncoding, s, i, j)
end
---@class proto.command
---@field title string
---@field command string
---@field arguments any[]
---@param title string
---@param command string
---@param arguments any[]
---@return proto.command
function m.command(title, command, arguments)
return {
title = title,
command = command,
arguments = arguments,
}
end
return m

View File

@@ -0,0 +1,197 @@
local diag = require 'proto.diagnostic'
local m = {}
--- 诊断等级
m.DiagnosticSeverity = {
Error = 1,
Warning = 2,
Information = 3,
Hint = 4,
}
m.DiagnosticFileStatus = {
Any = 1,
Opened = 2,
None = 3,
}
--- 诊断类型与默认等级
m.DiagnosticDefaultSeverity = diag.getDefaultSeverity()
--- 诊断类型与需要的文件状态(可以控制只分析打开的文件、还是所有文件)
m.DiagnosticDefaultNeededFileStatus = diag.getDefaultStatus()
m.DiagnosticDefaultGroupSeverity = diag.getGroupSeverity()
m.DiagnosticDefaultGroupFileStatus = diag.getGroupStatus()
--- 诊断报告标签
m.DiagnosticTag = {
Unnecessary = 1,
Deprecated = 2,
}
m.DocumentHighlightKind = {
Text = 1,
Read = 2,
Write = 3,
}
m.MessageType = {
Error = 1,
Warning = 2,
Info = 3,
Log = 4,
}
m.FileChangeType = {
Created = 1,
Changed = 2,
Deleted = 3,
}
m.CompletionItemKind = {
Text = 1,
Method = 2,
Function = 3,
Constructor = 4,
Field = 5,
Variable = 6,
Class = 7,
Interface = 8,
Module = 9,
Property = 10,
Unit = 11,
Value = 12,
Enum = 13,
Keyword = 14,
Snippet = 15,
Color = 16,
File = 17,
Reference = 18,
Folder = 19,
EnumMember = 20,
Constant = 21,
Struct = 22,
Event = 23,
Operator = 24,
TypeParameter = 25,
}
m.ErrorCodes = {
-- Defined by JSON RPC
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
-- Defined by the protocol.
ContentModified = -32801,
RequestCancelled = -32800,
}
m.SymbolKind = {
File = 1,
Module = 2,
Namespace = 3,
Package = 4,
Class = 5,
Method = 6,
Property = 7,
Field = 8,
Constructor = 9,
Enum = 10,
Interface = 11,
Function = 12,
Variable = 13,
Constant = 14,
String = 15,
Number = 16,
Boolean = 17,
Array = 18,
Object = 19,
Key = 20,
Null = 21,
EnumMember = 22,
Struct = 23,
Event = 24,
Operator = 25,
TypeParameter = 26,
}
m.TokenModifiers = {
["declaration"] = 1 << 0,
["definition"] = 1 << 1,
["readonly"] = 1 << 2,
["static"] = 1 << 3,
["deprecated"] = 1 << 4,
["abstract"] = 1 << 5,
["async"] = 1 << 6,
["modification"] = 1 << 7,
["documentation"] = 1 << 8,
["defaultLibrary"] = 1 << 9,
["global"] = 1 << 10,
}
m.TokenTypes = {
["namespace"] = 00,
["type"] = 01,
["class"] = 02,
["enum"] = 03,
["interface"] = 04,
["struct"] = 05,
["typeParameter"] = 06,
["parameter"] = 07,
["variable"] = 08,
["property"] = 09,
["enumMember"] = 10,
["event"] = 11,
["function"] = 12,
["method"] = 13,
["macro"] = 14,
["keyword"] = 15,
["modifier"] = 16,
["comment"] = 17,
["string"] = 18,
["number"] = 19,
["regexp"] = 20,
["operator"] = 21,
["decorator"] = 22,
}
m.BuiltIn = {
['basic'] = 'default',
['bit'] = 'default',
['bit32'] = 'default',
['builtin'] = 'default',
['coroutine'] = 'default',
['debug'] = 'default',
['ffi'] = 'default',
['io'] = 'default',
['jit'] = 'default',
['jit.profile'] = 'default',
['jit.util'] = 'default',
['math'] = 'default',
['os'] = 'default',
['package'] = 'default',
['string'] = 'default',
['table'] = 'default',
['table.new'] = 'default',
['table.clear'] = 'default',
['utf8'] = 'default',
['string.buffer'] = 'default',
}
m.InlayHintKind = {
Other = 0,
Type = 1,
Parameter = 2,
}
return m

View File

@@ -0,0 +1,306 @@
local util = require 'utility'
---@class proto.diagnostic
local m = {}
---@alias DiagnosticSeverity
---| 'Hint'
---| 'Information'
---| 'Warning'
---| 'Error'
---@alias DiagnosticNeededFileStatus
---| 'Any'
---| 'Opened'
---| 'None'
---@class proto.diagnostic.info
---@field severity DiagnosticSeverity
---@field status DiagnosticNeededFileStatus
---@field group string
m.diagnosticDatas = {}
m.diagnosticGroups = {}
function m.register(names)
---@param info proto.diagnostic.info
return function (info)
for _, name in ipairs(names) do
m.diagnosticDatas[name] = {
severity = info.severity,
status = info.status,
}
if not m.diagnosticGroups[info.group] then
m.diagnosticGroups[info.group] = {}
end
m.diagnosticGroups[info.group][name] = true
end
end
end
m.register {
'unused-local',
'unused-function',
'unused-label',
'unused-vararg',
'trailing-space',
'redundant-return',
'empty-block',
'code-after-break',
'unreachable-code',
} {
group = 'unused',
severity = 'Hint',
status = 'Opened',
}
m.register {
'redundant-value',
'unbalanced-assignments',
'redundant-parameter',
'missing-parameter',
'missing-return-value',
'redundant-return-value',
'missing-return',
'missing-fields',
} {
group = 'unbalanced',
severity = 'Warning',
status = 'Any',
}
m.register {
'need-check-nil',
'undefined-field',
'cast-local-type',
'assign-type-mismatch',
'param-type-mismatch',
'cast-type-mismatch',
'return-type-mismatch',
'inject-field',
--'unnecessary-assert',
} {
group = 'type-check',
severity = 'Warning',
status = 'Opened',
}
m.register {
'duplicate-doc-alias',
'undefined-doc-class',
'undefined-doc-name',
'circle-doc-class',
'undefined-doc-param',
'duplicate-doc-param',
'doc-field-no-class',
'duplicate-doc-field',
'unknown-diag-code',
'unknown-cast-variable',
'unknown-operator',
} {
group = 'luadoc',
severity = 'Warning',
status = 'Any',
}
m.register {
'incomplete-signature-doc',
'missing-global-doc',
'missing-local-export-doc',
} {
group = 'luadoc',
severity = 'Warning',
status = 'None',
}
m.register {
'codestyle-check'
} {
group = 'codestyle',
severity = 'Warning',
status = 'None',
}
m.register {
'spell-check'
} {
group = 'codestyle',
severity = 'Information',
status = 'None',
}
m.register {
'name-style-check'
} {
group = 'codestyle',
severity = 'Warning',
status = 'None',
}
m.register {
'newline-call',
'newfield-call',
'ambiguity-1',
'count-down-loop',
'different-requires',
} {
group = 'ambiguity',
severity = 'Warning',
status = 'Any',
}
m.register {
'await-in-sync',
'not-yieldable',
} {
group = 'await',
severity = 'Warning',
status = 'None',
}
m.register {
'no-unknown',
} {
group = 'strong',
severity = 'Warning',
status = 'None',
}
m.register {
'redefined-local',
} {
group = 'redefined',
severity = 'Hint',
status = 'Opened',
}
m.register {
'undefined-global',
'global-in-nil-env',
} {
group = 'global',
severity = 'Warning',
status = 'Any',
}
m.register {
'lowercase-global',
'undefined-env-child',
} {
group = 'global',
severity = 'Information',
status = 'Any',
}
m.register {
'global-element',
} {
group = 'conventions',
severity = 'Warning',
status = 'None'
}
m.register {
'duplicate-index',
} {
group = 'duplicate',
severity = 'Warning',
status = 'Any',
}
m.register {
'duplicate-set-field',
} {
group = 'duplicate',
severity = 'Warning',
status = 'Opened',
}
m.register {
'close-non-object',
'deprecated',
'discard-returns',
'invisible',
} {
group = 'strict',
severity = 'Warning',
status = 'Any',
}
---@return table<string, DiagnosticSeverity>
function m.getDefaultSeverity()
local severity = {}
for name, info in pairs(m.diagnosticDatas) do
severity[name] = info.severity
end
return severity
end
---@return table<string, DiagnosticNeededFileStatus>
function m.getDefaultStatus()
local status = {}
for name, info in pairs(m.diagnosticDatas) do
status[name] = info.status
end
return status
end
function m.getGroupSeverity()
local group = {}
for name in pairs(m.diagnosticGroups) do
group[name] = 'Fallback'
end
return group
end
function m.getGroupStatus()
local group = {}
for name in pairs(m.diagnosticGroups) do
group[name] = 'Fallback'
end
return group
end
---@param name string
---@return string[]
m.getGroups = util.cacheReturn(function (name)
local groups = {}
for groupName, nameMap in pairs(m.diagnosticGroups) do
if nameMap[name] then
groups[#groups+1] = groupName
end
end
table.sort(groups)
return groups
end)
---@return table<string, true>
function m.getDiagAndErrNameMap()
if not m._diagAndErrNames then
local names = {}
for name in pairs(m.getDefaultSeverity()) do
names[name] = true
end
for _, fileName in ipairs {'parser.compile', 'parser.luadoc'} do
local path = package.searchpath(fileName, package.path)
if path then
local f = io.open(path)
if f then
for line in f:lines() do
local name = line:match([=[type%s*=%s*['"](%u[%u_]+%u)['"]]=])
if name then
local id = name:lower():gsub('_', '-')
names[id] = true
end
end
f:close()
end
end
end
table.sort(names)
m._diagAndErrNames = names
end
return m._diagAndErrNames
end
return m

View File

@@ -0,0 +1,3 @@
local proto = require 'proto.proto'
return proto

View File

@@ -0,0 +1,308 @@
local util = require 'utility'
local await = require 'await'
local pub = require 'pub'
local jsonrpc = require 'jsonrpc'
local define = require 'proto.define'
local json = require 'json'
local inspect = require 'inspect'
local platform = require 'bee.platform'
local fs = require 'bee.filesystem'
local net = require 'service.net'
local timer = require 'timer'
local reqCounter = util.counter()
local function logSend(buf)
if not RPCLOG then
return
end
log.info('rpc send:', buf)
end
local function logRecieve(proto)
if not RPCLOG then
return
end
log.info('rpc recieve:', json.encode(proto))
end
---@class proto
local m = {}
m.ability = {}
m.waiting = {}
m.holdon = {}
m.mode = 'stdio'
m.client = nil
function m.getMethodName(proto)
if proto.method:sub(1, 2) == '$/' then
return proto.method, true
else
return proto.method, false
end
end
---@param callback async fun()
function m.on(method, callback)
m.ability[method] = callback
end
function m.send(data)
local buf = jsonrpc.encode(data)
logSend(buf)
if m.mode == 'stdio' then
io.write(buf)
elseif m.mode == 'socket' then
m.client:write(buf)
end
end
function m.response(id, res)
if id == nil then
log.error('Response id is nil!', inspect(res))
return
end
if not m.holdon[id] then
log.error('Unknown response id!', id)
return
end
m.holdon[id] = nil
local data = {}
data.id = id
data.result = res == nil and json.null or res
m.send(data)
end
function m.responseErr(id, code, message)
if id == nil then
log.error('Response id is nil!', inspect(message))
return
end
if not m.holdon[id] then
log.error('Unknown response id!', id)
return
end
m.holdon[id] = nil
m.send {
id = id,
error = {
code = code,
message = message,
}
}
end
function m.notify(name, params)
m.send {
method = name,
params = params,
}
end
---@async
function m.awaitRequest(name, params)
local id = reqCounter()
m.send {
id = id,
method = name,
params = params,
}
local result, error = await.wait(function (resume)
m.waiting[id] = {
id = id,
method = name,
params = params,
resume = resume,
}
end)
if error then
log.warn(('Response of [%s] error [%d]: %s'):format(name, error.code, error.message))
end
return result
end
function m.request(name, params, callback)
local id = reqCounter()
m.send {
id = id,
method = name,
params = params,
}
m.waiting[id] = {
id = id,
method = name,
params = params,
resume = function (result, error)
if error then
log.warn(('Response of [%s] error [%d]: %s'):format(name, error.code, error.message))
end
if callback then
callback(result)
end
end
}
end
local secretOption = {
process = function (item, path)
if path[1] == 'params'
and path[2] == 'textDocument'
and path[3] == 'text'
and path[4] == nil then
return '"***"'
end
return item
end
}
m.methodQueue = {}
function m.applyMethod(proto)
logRecieve(proto)
local method, optional = m.getMethodName(proto)
local abil = m.ability[method]
if proto.id then
m.holdon[proto.id] = proto
end
if not abil then
if not optional then
log.warn('Recieved unknown proto: ' .. method)
end
if proto.id then
m.responseErr(proto.id, define.ErrorCodes.MethodNotFound, method)
end
return
end
await.call(function () ---@async
--log.debug('Start method:', method)
if proto.id then
await.setID('proto:' .. proto.id)
end
local clock = os.clock()
local ok = false
local res
-- 任务可能在执行过程中被中断通过close来捕获
local response <close> = function ()
local passed = os.clock() - clock
if passed > 0.5 then
log.warn(('Method [%s] takes [%.3f]sec. %s'):format(method, passed, inspect(proto, secretOption)))
end
--log.debug('Finish method:', method)
if not proto.id then
return
end
await.close('proto:' .. proto.id)
if ok then
m.response(proto.id, res)
else
m.responseErr(proto.id, proto._closeReason or define.ErrorCodes.InternalError, proto._closeMessage or res)
end
end
ok, res = xpcall(abil, log.error, proto.params, proto.id)
await.delay()
end)
end
function m.applyMethodQueue()
local queue = m.methodQueue
m.methodQueue = {}
local canceled = {}
for _, proto in ipairs(queue) do
if proto.method == '$/cancelRequest' then
canceled[proto.params.id] = true
end
end
for _, proto in ipairs(queue) do
if not canceled[proto.id] then
m.applyMethod(proto)
end
end
end
function m.doMethod(proto)
m.methodQueue[#m.methodQueue+1] = proto
if #m.methodQueue > 1 then
return
end
timer.wait(0, m.applyMethodQueue)
end
function m.close(id, reason, message)
local proto = m.holdon[id]
if not proto then
return
end
proto._closeReason = reason
proto._closeMessage = message
await.close('proto:' .. id)
end
function m.doResponse(proto)
logRecieve(proto)
local id = proto.id
local waiting = m.waiting[id]
if not waiting then
log.warn('Response id not found: ' .. inspect(proto))
return
end
m.waiting[id] = nil
if proto.error then
waiting.resume(nil, proto.error)
return
end
waiting.resume(proto.result)
end
function m.listen(mode, socketPort)
m.mode = mode
if mode == 'stdio' then
log.info('Listen Mode: stdio')
if platform.os == 'windows' then
local windows = require 'bee.windows'
windows.filemode(io.stdin, 'b')
windows.filemode(io.stdout, 'b')
end
io.stdin:setvbuf 'no'
io.stdout:setvbuf 'no'
pub.task('loadProtoByStdio')
elseif mode == 'socket' then
local unixFolder = LOGPATH .. '/unix'
fs.create_directories(fs.path(unixFolder))
local unixPath = unixFolder .. '/' .. tostring(socketPort)
local server = net.listen('unix', unixPath)
log.info('Listen Mode: socket')
log.info('Listen Port:', socketPort)
log.info('Listen Path:', unixPath)
assert(server)
local dummyClient = {
buf = '',
write = function (self, data)
self.buf = self.buf.. data
end,
update = function () end,
}
m.client = dummyClient
function server:on_accepted(client)
m.client = client
client:write(dummyClient.buf)
return true
end
function server:on_error(...)
log.error(...)
end
pub.task('loadProtoBySocket', {
port = socketPort,
unixPath = unixPath,
})
end
end
return m