fix: namespace all local packages and modules
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
local utils = require("utils")
|
||||
|
||||
return {
|
||||
enable = true,
|
||||
dependencies = {
|
||||
"npm",
|
||||
},
|
||||
mason = {
|
||||
"bash-language-server",
|
||||
dependencies = {
|
||||
{ "shellcheck" },
|
||||
{ "shfmt" },
|
||||
},
|
||||
},
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = { "shfmt", "-s", "-i", "4", "-" },
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"sh",
|
||||
"bash",
|
||||
"zsh",
|
||||
},
|
||||
cmd = { "bash-language-server", "start" },
|
||||
single_file_support = true,
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
mason = { "clangd" },
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "gs",
|
||||
rhs = vim.cmd.ClangdSwitchSourceHeader,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"c",
|
||||
"cpp",
|
||||
},
|
||||
cmd = {
|
||||
"clangd",
|
||||
"--clang-tidy",
|
||||
"--enable-config",
|
||||
|
||||
-- Fix for errors in files outside of project
|
||||
-- https://clangd.llvm.org/faq#how-do-i-fix-errors-i-get-when-opening-headers-outside-of-my-project-directory
|
||||
"--compile-commands-dir=build",
|
||||
},
|
||||
single_file_support = true,
|
||||
},
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
return {
|
||||
enable = true,
|
||||
dependencies = {
|
||||
"python3",
|
||||
},
|
||||
mason = {
|
||||
name = "cmake-language-server",
|
||||
-- version = "",
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"cmake",
|
||||
},
|
||||
cmd = { "cmake-language-server", },
|
||||
single_file_support = true,
|
||||
init_options = {
|
||||
buildDirectory = "build",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
local utils = require("utils")
|
||||
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
mason = { "gopls", dependencies = { "golines" } },
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = { "golines", "-m", "80", "--shorten-comments" },
|
||||
output = "stdout",
|
||||
})
|
||||
vim.lsp.buf.format({ async = true })
|
||||
end,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"go",
|
||||
"gomod",
|
||||
"gowork",
|
||||
"gotmpl",
|
||||
},
|
||||
cmd = { "gopls" },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
gopls = {
|
||||
staticcheck = true,
|
||||
semanticTokens = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
--[[
|
||||
Copyright 2023 Oscar Wallberg
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
]]
|
||||
|
||||
return {
|
||||
enable = false,
|
||||
dependencies = {
|
||||
"java",
|
||||
},
|
||||
mason = {
|
||||
"groovy-language-server",
|
||||
},
|
||||
lspconfig = {
|
||||
cmd = {
|
||||
"groovy-language-server",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
return {
|
||||
enable = true,
|
||||
mason = {
|
||||
name = "haskell-language-server",
|
||||
-- version = "",
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = { "haskell", "lhaskell", "cabal", },
|
||||
cmd = { "haskell-language-server-wrapper", "--lsp", },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
haskell = {
|
||||
cabalFormattingProvider = "cabalfmt",
|
||||
formattingProvider = "ormolu",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
-- spec:
|
||||
-- https://github.com/bmewburn/intelephense-docs/blob/master/installation.md
|
||||
-- https://github.com/bmewburn/vscode-intelephense/blob/master/package.json
|
||||
|
||||
local utils = require("utils")
|
||||
|
||||
return {
|
||||
enable = true,
|
||||
dependencies = {
|
||||
"npm",
|
||||
},
|
||||
mason = { "intelephense", dependencies = { {"phpcs", "pretty-php" } } },
|
||||
linters = {
|
||||
{
|
||||
cmd = {
|
||||
"phpcs",
|
||||
"--standard=PSR12",
|
||||
"--report=emacs",
|
||||
"-s",
|
||||
"-q",
|
||||
"-",
|
||||
},
|
||||
stdin = true,
|
||||
stdout = true,
|
||||
pattern = "^.+:(%d+):(%d+): (%w+) %- (.*) %((.*)%)$",
|
||||
groups = { "lnum", "col", "severity", "message", "source" },
|
||||
source = "phpcs",
|
||||
severity_map = {
|
||||
error = vim.diagnostic.severity.ERROR,
|
||||
warning = vim.diagnostic.severity.WARN,
|
||||
},
|
||||
},
|
||||
},
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = {
|
||||
"pretty-php",
|
||||
"--psr12",
|
||||
"--enable=align-comments",
|
||||
"-qq",
|
||||
"-",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = "x",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = {
|
||||
"pretty-php",
|
||||
"--psr12",
|
||||
"--enable=align-comments",
|
||||
"-qq",
|
||||
"-",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"php",
|
||||
},
|
||||
cmd = { "intelephense", "--stdio" },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
intelephense = {
|
||||
environment = {
|
||||
phpVersion = "7.4",
|
||||
},
|
||||
format = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
local utils = require("utils")
|
||||
|
||||
local ERROR = vim.diagnostic.severity.ERROR
|
||||
local WARN = vim.diagnostic.severity.WARN
|
||||
local INFO = vim.diagnostic.severity.INFO
|
||||
local HINT = vim.diagnostic.severity.HINT
|
||||
|
||||
local SEVERITY_MAP = {
|
||||
YTT = WARN,
|
||||
ANN = WARN,
|
||||
ASYNC = WARN,
|
||||
B = WARN,
|
||||
A = WARN,
|
||||
COM = WARN,
|
||||
C = WARN,
|
||||
DTZ = WARN,
|
||||
T = WARN,
|
||||
FIX = WARN,
|
||||
FA = WARN,
|
||||
ISC = WARN,
|
||||
PIE = WARN,
|
||||
PYI = WARN,
|
||||
PT = WARN,
|
||||
RET = WARN,
|
||||
SIM = WARN,
|
||||
TC = WARN,
|
||||
I = WARN,
|
||||
E = ERROR,
|
||||
W = WARN,
|
||||
DOC = WARN,
|
||||
D = INFO,
|
||||
F = WARN,
|
||||
PLC = WARN,
|
||||
PLE = ERROR,
|
||||
PLR = WARN,
|
||||
PLW = WARN,
|
||||
UP = WARN,
|
||||
RUF = WARN,
|
||||
}
|
||||
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
mason = { "jedi-language-server", dependencies = { "ruff" } },
|
||||
linters = {
|
||||
{
|
||||
cmd = {
|
||||
"ruff",
|
||||
"check",
|
||||
"--output-format=json",
|
||||
"-q",
|
||||
"-",
|
||||
},
|
||||
stdin = true,
|
||||
stdout = true,
|
||||
json = {
|
||||
lnum = "location.row",
|
||||
end_lnum = "end_location.row",
|
||||
col = "location.column",
|
||||
end_col = "end_location.column",
|
||||
code = "code",
|
||||
message = "message",
|
||||
callback = function(diag)
|
||||
if diag.severity or not diag.code then
|
||||
return
|
||||
end
|
||||
diag.severity = SEVERITY_MAP[diag.code:match("^(%u+)")]
|
||||
end,
|
||||
},
|
||||
source = "ruff",
|
||||
tags = {
|
||||
deprecated = {
|
||||
"PYI057",
|
||||
"PT020",
|
||||
"UP005",
|
||||
"UP019",
|
||||
"UP021",
|
||||
"UP023",
|
||||
"UP026",
|
||||
"UP035",
|
||||
},
|
||||
unnecessary = {
|
||||
"ARG001",
|
||||
"ARG002",
|
||||
"ARG003",
|
||||
"ARG004",
|
||||
"ARG005",
|
||||
"F401",
|
||||
"F504",
|
||||
"F522",
|
||||
"F811",
|
||||
"F841",
|
||||
"F842",
|
||||
"RUF029",
|
||||
"RUF059",
|
||||
"RUF100",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = {
|
||||
"ruff",
|
||||
"format",
|
||||
"--stdin-filename=%filename%",
|
||||
"--quiet",
|
||||
"-",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
utils.format({
|
||||
cmd = {
|
||||
"ruff",
|
||||
"check",
|
||||
"--select=I",
|
||||
"--fix",
|
||||
"--quiet",
|
||||
"-",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = "x",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = {
|
||||
"ruff",
|
||||
"format",
|
||||
"--stdin-filename=%filename%",
|
||||
"--quiet",
|
||||
"--range=%row_start%:%col_start%-%row_end%:%col_end%",
|
||||
"-",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"python",
|
||||
},
|
||||
cmd = { "jedi-language-server" },
|
||||
single_file_support = true,
|
||||
init_options = {
|
||||
completion = {
|
||||
disableSnippets = true,
|
||||
},
|
||||
diagnostics = {
|
||||
enable = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
-- spec: https://github.com/eclipse/lemminx/blob/main/docs/Configuration.md
|
||||
|
||||
local utils = require("utils")
|
||||
|
||||
return {
|
||||
enable = true,
|
||||
mason = {
|
||||
name = "lemminx",
|
||||
dependencies = { "xmlformatter" },
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"xml",
|
||||
"xsd",
|
||||
"xsl",
|
||||
"xslt",
|
||||
"svg",
|
||||
},
|
||||
cmd = { "lemminx", },
|
||||
single_file_support = true,
|
||||
init_options = {
|
||||
settings = {
|
||||
xml = {
|
||||
format = {
|
||||
enabled = true, -- is able to format document
|
||||
splitAttributes = true, -- each attribute is formatted onto new line
|
||||
joinCDATALines = false, -- normalize content inside CDATA
|
||||
joinCommentLines = false, -- normalize content inside comments
|
||||
formatComments = true, -- keep comment in relative position
|
||||
joinContentLines = false, -- normalize content inside elements
|
||||
spaceBeforeEmptyCloseLine = true, -- insert whitespace before self closing tag end bracket
|
||||
},
|
||||
validation = {
|
||||
noGrammar = "ignore",
|
||||
enabled = true,
|
||||
schema = true,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
-- spec: https://luals.github.io/wiki/settings/
|
||||
local utils = require("utils")
|
||||
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
dependencies = { "cargo" },
|
||||
mason = {
|
||||
"lua-language-server",
|
||||
post_install = { { cmd = { "cargo", "install", "stylua", "--features", "lua54" } } },
|
||||
},
|
||||
keymaps = {
|
||||
{
|
||||
mode = "n",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = { "stylua", "-" },
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = "x",
|
||||
lhs = "<leader>lf",
|
||||
rhs = function()
|
||||
utils.format({
|
||||
cmd = {
|
||||
"stylua",
|
||||
"-",
|
||||
"--range-start",
|
||||
"%byte_start%",
|
||||
"--range-end",
|
||||
"%byte_end%",
|
||||
},
|
||||
output = "stdout",
|
||||
})
|
||||
end,
|
||||
},
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = { "lua" },
|
||||
cmd = { "lua-language-server" },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
Lua = {
|
||||
completion = { showWord = "Disable" },
|
||||
diagnostics = {
|
||||
-- disable = { "missing-fields" },
|
||||
},
|
||||
runtime = {
|
||||
version = "LuaJIT",
|
||||
path = {
|
||||
-- "?.lua",
|
||||
"?.lua",
|
||||
"?/init.lua",
|
||||
-- "?.lua",
|
||||
-- "lua/?.lua",
|
||||
-- "lua/?/init.lua",
|
||||
-- "?/lua/?.lua",
|
||||
-- "?/lua/?/init.lua",
|
||||
},
|
||||
-- pathStrict = true,
|
||||
},
|
||||
workspace = {
|
||||
library = {
|
||||
vim.env.VIMRUNTIME,
|
||||
"~/repos/awesome-code-doc",
|
||||
"/usr/share/awesome/lib",
|
||||
vim.fn.stdpath("data") .. "/lazy",
|
||||
},
|
||||
checkThirdParty = false,
|
||||
},
|
||||
hint = {
|
||||
enable = false,
|
||||
arrayIndex = "Disable",
|
||||
await = true,
|
||||
paramName = "All",
|
||||
paramType = true,
|
||||
semicolon = "Disable",
|
||||
setType = true,
|
||||
},
|
||||
telemetry = { enable = false },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
mason = { "mesonlsp" },
|
||||
lspconfig = {
|
||||
---@type fun(filename: string, bufnr: number): string?
|
||||
root_dir = function(filename, bufnr)
|
||||
local parent = require("lspconfig").util.find_git_ancestor(filename)
|
||||
if not parent then
|
||||
local win = vim.fn.bufwinid(bufnr)
|
||||
parent = vim.fn.getcwd(win)
|
||||
end
|
||||
return parent
|
||||
end,
|
||||
settings = {
|
||||
others = {
|
||||
disableInlayHints = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
mason = { "pyright" },
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"python",
|
||||
},
|
||||
cmd = { "pyright-langserver", "--stdio" },
|
||||
single_file_support = true,
|
||||
-- https://microsoft.github.io/pyright/#/settings
|
||||
settings = {
|
||||
python = {
|
||||
analysis = {
|
||||
autoSearchPaths = true,
|
||||
diagnosticMode = "openFilesOnly",
|
||||
useLibraryCodeForTypes = true,
|
||||
typeCheckingMode = "strict",
|
||||
},
|
||||
},
|
||||
pyright = {
|
||||
disableLanguageServices = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
-- spec: https://rust-analyzer.github.io/manual.html#configuration
|
||||
|
||||
return {
|
||||
enable = true,
|
||||
mason = {
|
||||
name = "rust-analyzer",
|
||||
-- version = "",
|
||||
},
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"rust",
|
||||
},
|
||||
cmd = { "rust-analyzer", },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
["rust-analyzer"] = {
|
||||
inlayHints = {
|
||||
chainingHints = {
|
||||
enable = false,
|
||||
},
|
||||
parameterHints = {
|
||||
enable = false,
|
||||
},
|
||||
typeHints = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
--[[ assist = {
|
||||
emitMustUse = false,
|
||||
expressionFillDefault = false,
|
||||
},
|
||||
cachePriming = {
|
||||
enable = false,
|
||||
},
|
||||
cargo = {
|
||||
autoreload = false,
|
||||
buildScripts = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
checkOnSave = false,
|
||||
completion = {
|
||||
autoimport = {
|
||||
enable = false,
|
||||
},
|
||||
autoself = {
|
||||
enable = false,
|
||||
},
|
||||
callable = {
|
||||
snippets = false,
|
||||
},
|
||||
fullFunctionSignatures = {
|
||||
enable = false,
|
||||
},
|
||||
postfix = {
|
||||
enable = false,
|
||||
},
|
||||
privateEditable = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
diagnostics = {
|
||||
enable = false,
|
||||
},
|
||||
highlightRelated = {
|
||||
breakPoints = {
|
||||
enable = false,
|
||||
},
|
||||
closureCaptures = {
|
||||
enable = false,
|
||||
},
|
||||
exitPoints = {
|
||||
enable = false,
|
||||
},
|
||||
references = {
|
||||
enable = false,
|
||||
},
|
||||
yieldPoints = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
hover = {
|
||||
actions = {
|
||||
enable = false,
|
||||
},
|
||||
documentation = {
|
||||
enable = true,
|
||||
},
|
||||
links = {
|
||||
enable = false,
|
||||
},
|
||||
memoryLayout = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
imports = {
|
||||
group = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
inlayHints = {
|
||||
bindingModeHints = {
|
||||
enable = false,
|
||||
},
|
||||
chainingHints = {
|
||||
enable = false,
|
||||
},
|
||||
closingBraceHints = {
|
||||
enable = false,
|
||||
},
|
||||
closureCaptureHints = {
|
||||
enable = false,
|
||||
},
|
||||
closureReturnTypeHints = {
|
||||
enable = false,
|
||||
},
|
||||
discriminantHints = {
|
||||
enable = false,
|
||||
},
|
||||
expressionAdjustmentHints = {
|
||||
enable = false,
|
||||
},
|
||||
lifetimeElisionHints = {
|
||||
enable = false,
|
||||
},
|
||||
parameterHints = {
|
||||
enable = false,
|
||||
},
|
||||
reborrowHints = {
|
||||
enable = false,
|
||||
},
|
||||
typeHints = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
joinLines = {
|
||||
joinAssignments = false,
|
||||
joinElseIf = false,
|
||||
removeTrailingComma = false,
|
||||
unwrapTrivialBlock = false,
|
||||
},
|
||||
lens = {
|
||||
enable = false,
|
||||
},
|
||||
notifications = {
|
||||
cargoTomlNotFound = false,
|
||||
},
|
||||
procMacro = {
|
||||
enable = false,
|
||||
},
|
||||
references = {
|
||||
excludeImports = false,
|
||||
},
|
||||
rustfmt = {
|
||||
rangeFormatting = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
semanticHighlighting = {
|
||||
doc = {
|
||||
comment = {
|
||||
inject = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
nonStandardTokens = false,
|
||||
operator = {
|
||||
enable = false,
|
||||
},
|
||||
punctuation = {
|
||||
enable = false,
|
||||
},
|
||||
strings = {
|
||||
enable = false,
|
||||
},
|
||||
},
|
||||
signatureInfo = {
|
||||
documentation = {
|
||||
enable = true,
|
||||
},
|
||||
},
|
||||
typing = {
|
||||
autoClosingAngleBrackets = {
|
||||
enable = false,
|
||||
},
|
||||
}, ]]
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
-- spec: https://github.com/zigtools/zls#configuration-options
|
||||
|
||||
---@type ServerConfig
|
||||
return {
|
||||
enable = true,
|
||||
dependencies = {
|
||||
"zig",
|
||||
},
|
||||
-- mason = {
|
||||
-- name = "zls",
|
||||
-- -- version = "",
|
||||
-- },
|
||||
lspconfig = {
|
||||
filetypes = {
|
||||
"zig",
|
||||
"zir",
|
||||
},
|
||||
cmd = { "zls", },
|
||||
single_file_support = true,
|
||||
settings = {
|
||||
zls = {
|
||||
warn_style = true,
|
||||
highlight_global_var_declarations = true,
|
||||
inlay_hints_show_variable_type_hints = false,
|
||||
inlay_hints_show_struct_literal_field_type = false,
|
||||
inlay_hints_show_parameter_name = false,
|
||||
inlay_hints_show_builtin = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
-- Mappings.
|
||||
-- See `:help vim.lsp.*` for documentation on any of the below functions
|
||||
|
||||
local utils = require("utils")
|
||||
|
||||
---@class Keymap
|
||||
---@field mode string|string[]
|
||||
---@field lhs string
|
||||
---@field rhs string|function
|
||||
---@field opts? vim.keymap.set.Opts
|
||||
|
||||
local MODE_TYPES = { "n", "v", "s", "x", "o", "i", "l", "c" }
|
||||
|
||||
local M = {}
|
||||
|
||||
---@type table<number, vim.api.keyset.keymap[]>
|
||||
M.old = {}
|
||||
|
||||
---@type table<number, Keymap[]>
|
||||
M.new = {}
|
||||
|
||||
--- Load LSP keybinds
|
||||
---@param server Server
|
||||
function M:init(server, bufnr)
|
||||
self.old[bufnr] = {}
|
||||
for _, mode in ipairs(MODE_TYPES) do
|
||||
vim.tbl_extend(
|
||||
"error",
|
||||
self.old[bufnr],
|
||||
vim.api.nvim_buf_get_keymap(bufnr, mode)
|
||||
)
|
||||
end
|
||||
|
||||
self.new[bufnr] = {
|
||||
{ mode = { "n" }, lhs = "<leader>df", rhs = vim.diagnostic.open_float },
|
||||
{
|
||||
mode = { "n" },
|
||||
lhs = "[d",
|
||||
rhs = function()
|
||||
vim.diagnostic.jump({ count = -1, float = true })
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = { "n" },
|
||||
lhs = "]d",
|
||||
rhs = function()
|
||||
vim.diagnostic.jump({ count = 1, float = true })
|
||||
end,
|
||||
},
|
||||
{ mode = { "n" }, lhs = "gD", rhs = vim.lsp.buf.declaration },
|
||||
{
|
||||
mode = { "n", "i" },
|
||||
lhs = "<C-k>",
|
||||
rhs = function()
|
||||
vim.lsp.buf.hover({ border = "single" })
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = { "n", "i" },
|
||||
lhs = "<C-j>",
|
||||
rhs = function()
|
||||
vim.lsp.buf.signature_help({ border = "single" })
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = { "n", "i" },
|
||||
lhs = "<C-h>",
|
||||
rhs = vim.lsp.buf.document_highlight,
|
||||
},
|
||||
{ mode = { "n", "x" }, lhs = "<leader>lf", rhs = vim.lsp.buf.format },
|
||||
{
|
||||
mode = { "n" },
|
||||
lhs = "<leader>ld",
|
||||
rhs = function()
|
||||
vim.diagnostic.enable(not vim.diagnostic.is_enabled())
|
||||
end,
|
||||
},
|
||||
{
|
||||
mode = { "n", "i" },
|
||||
lhs = "<C-l>",
|
||||
rhs = function()
|
||||
vim.lsp.buf.clear_references()
|
||||
vim.cmd.nohlsearch()
|
||||
vim.schedule(vim.cmd.diffupdate)
|
||||
return "<C-l>"
|
||||
end,
|
||||
opts = { expr = true },
|
||||
},
|
||||
}
|
||||
|
||||
local telescope = utils.try_require("telescope.builtin")
|
||||
|
||||
if telescope then
|
||||
vim.list_extend(self.new[bufnr], {
|
||||
{ mode = "n", lhs = "<leader>dl", rhs = telescope.diagnostics },
|
||||
{ mode = "n", lhs = "grt", rhs = telescope.lsp_type_definitions, },
|
||||
{ mode = "n", lhs = "gd", rhs = telescope.lsp_definitions },
|
||||
{ mode = "n", lhs = "gri", rhs = telescope.lsp_implementations },
|
||||
{ mode = "n", lhs = "grr", rhs = telescope.lsp_references },
|
||||
})
|
||||
else
|
||||
vim.list_extend(self.new[bufnr], {
|
||||
{ mode = "n", lhs = "<leader>dl", rhs = vim.diagnostic.setloclist },
|
||||
{ mode = "n", lhs = "grt", rhs = vim.lsp.buf.type_definition, },
|
||||
{ mode = "n", lhs = "gd", rhs = vim.lsp.buf.definition },
|
||||
})
|
||||
end
|
||||
|
||||
if server.config.keymaps then
|
||||
vim.list_extend(self.new[bufnr], server.config.keymaps)
|
||||
end
|
||||
|
||||
for _, keymap in ipairs(self.new[bufnr]) do
|
||||
keymap.opts = vim.tbl_extend(
|
||||
"force",
|
||||
keymap.opts or {},
|
||||
{ buffer = bufnr, remap = true }
|
||||
)
|
||||
vim.keymap.set(keymap.mode, keymap.lhs, keymap.rhs, keymap.opts)
|
||||
end
|
||||
end
|
||||
|
||||
function M:deinit(bufnr)
|
||||
if self.new[bufnr] then
|
||||
for _, keymap in ipairs(self.new[bufnr]) do
|
||||
-- pcall to avoid error if keymap was already removed,
|
||||
-- for example if server.config.keymaps overrides a default LSP keymap
|
||||
pcall(vim.keymap.del, keymap.mode, keymap.lhs, { buffer = bufnr })
|
||||
end
|
||||
self.new[bufnr] = nil
|
||||
end
|
||||
|
||||
if self.old[bufnr] then
|
||||
for _, keymap in ipairs(self.old[bufnr]) do
|
||||
vim.cmd.mapset(keymap)
|
||||
end
|
||||
self.old[bufnr] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,401 +0,0 @@
|
||||
local utils = require("utils")
|
||||
|
||||
---@class Linter
|
||||
---@field name string
|
||||
---@field namespace number
|
||||
---@field augroup number
|
||||
---@field buffers number[]
|
||||
---@field config LinterConfig
|
||||
M = {}
|
||||
M.__index = M
|
||||
|
||||
---@alias Group
|
||||
---| "lnum"
|
||||
---| "end_lnum"
|
||||
---| "col"
|
||||
---| "end_col"
|
||||
---| "severity"
|
||||
---| "message"
|
||||
---| "source"
|
||||
---| "code"
|
||||
|
||||
---@class JsonConfig
|
||||
---@field diagnostics_root? string
|
||||
---@field lnum? string
|
||||
---@field end_lnum? string
|
||||
---@field col? string
|
||||
---@field end_col? string
|
||||
---@field severity? string
|
||||
---@field message? string
|
||||
---@field source? string
|
||||
---@field code? string
|
||||
---@field callback? fun(diag: vim.Diagnostic)
|
||||
|
||||
---@class DiagnosticTagMap
|
||||
---@field unnecessary? string[]
|
||||
---@field deprecated? string[]
|
||||
|
||||
---@class LinterConfig
|
||||
---@field cmd string[] Command to run. The following keywords get replaces by the specified values:
|
||||
--- * %file% - path to the current file
|
||||
--- * %filename% - name of the current file
|
||||
---@field stdin? boolean
|
||||
---@field stdout? boolean
|
||||
---@field stderr? boolean
|
||||
---@field pattern? string
|
||||
---@field groups? Group[]
|
||||
---@field severity_map? table<string, vim.diagnostic.Severity>
|
||||
---@field source? string
|
||||
---@field debounce? number
|
||||
---@field json? JsonConfig
|
||||
---@field tags? DiagnosticTagMap
|
||||
---@field zero_idx_lnum? boolean
|
||||
---@field zero_idx_col? boolean
|
||||
M.config = {}
|
||||
|
||||
-- Extract a value from a JSON object using a path
|
||||
---@param obj table The JSON object
|
||||
---@param path string Path to the value (dot notation string)
|
||||
---@return any The value at the specified path, or nil if not found
|
||||
function M.get_json_value(obj, path)
|
||||
if not obj then
|
||||
return nil
|
||||
end
|
||||
|
||||
local current = obj
|
||||
local parts = {}
|
||||
|
||||
for part in path:gmatch("[^%.]+") do
|
||||
table.insert(parts, part)
|
||||
end
|
||||
|
||||
for _, key in ipairs(parts) do
|
||||
if tonumber(key) ~= nil then
|
||||
key = tonumber(key)
|
||||
end
|
||||
|
||||
if type(current) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
current = current[key]
|
||||
if current == nil then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return current
|
||||
end
|
||||
|
||||
--- Clamp column to line length
|
||||
---@param diag vim.Diagnostic
|
||||
---@param bufnr number
|
||||
function M:clamp_col(diag, bufnr)
|
||||
local lines =
|
||||
vim.api.nvim_buf_get_lines(bufnr, diag.lnum, diag.lnum + 1, false)
|
||||
if #lines == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local line_len = #lines[1] - 1
|
||||
if diag.col > line_len then
|
||||
diag.col = line_len
|
||||
end
|
||||
end
|
||||
|
||||
--- Add diagnostic tags
|
||||
---@param diag vim.Diagnostic
|
||||
function M:add_tags(diag)
|
||||
if not self.config.tags then
|
||||
return
|
||||
end
|
||||
|
||||
local have_unnecessary = vim.islist(self.config.tags.unnecessary)
|
||||
local have_deprecated = vim.islist(self.config.tags.deprecated)
|
||||
|
||||
if not have_unnecessary and not have_deprecated then
|
||||
return
|
||||
end
|
||||
|
||||
diag._tags = {}
|
||||
|
||||
if
|
||||
have_unnecessary
|
||||
and vim.list_contains(self.config.tags.unnecessary, diag.code)
|
||||
then
|
||||
diag._tags.unnecessary = true
|
||||
diag.severity = vim.diagnostic.severity.HINT
|
||||
end
|
||||
|
||||
if
|
||||
have_deprecated
|
||||
and vim.list_contains(self.config.tags.deprecated, diag.code)
|
||||
then
|
||||
diag._tags.deprecated = true
|
||||
diag.severity = vim.diagnostic.severity.WARN
|
||||
end
|
||||
end
|
||||
|
||||
--- Resolve 0/1-based indexing for lnum/col
|
||||
---@param diag vim.Diagnostic
|
||||
function M:fix_indexing(diag)
|
||||
if not self.config.zero_idx_lnum then
|
||||
if diag.lnum then
|
||||
diag.lnum = diag.lnum - 1
|
||||
end
|
||||
|
||||
if diag.end_lnum then
|
||||
diag.end_lnum = diag.end_lnum - 1
|
||||
end
|
||||
end
|
||||
|
||||
if not self.config.zero_idx_col then
|
||||
if diag.col then
|
||||
diag.col = diag.col - 1
|
||||
end
|
||||
|
||||
if diag.end_col then
|
||||
diag.end_col = diag.end_col - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:process_json_output(json, bufnr)
|
||||
---@type vim.Diagnostic[]
|
||||
local diagnostics = {}
|
||||
|
||||
local items = json
|
||||
if self.config.json.diagnostics_root then
|
||||
items = M.get_json_value(json, self.config.json.diagnostics_root)
|
||||
end
|
||||
|
||||
if type(items) ~= "table" then
|
||||
utils.err("diagnostics root is not an array or object")
|
||||
return
|
||||
end
|
||||
|
||||
if not vim.islist(items) then
|
||||
items = { items }
|
||||
end
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
local diag = {}
|
||||
|
||||
for field, path in pairs(self.config.json) do
|
||||
if field ~= "diagnostics_root" and field ~= "callback" then
|
||||
diag[field] = M.get_json_value(item, path)
|
||||
end
|
||||
end
|
||||
|
||||
diag.source = diag.source or self.config.source
|
||||
|
||||
if
|
||||
diag.severity
|
||||
and self.config.severity_map
|
||||
and self.config.severity_map[diag.severity]
|
||||
then
|
||||
diag.severity = self.config.severity_map[diag.severity]
|
||||
end
|
||||
|
||||
self:fix_indexing(diag)
|
||||
self:clamp_col(diag, bufnr)
|
||||
self:add_tags(diag)
|
||||
|
||||
if type(self.config.json.callback) == "function" then
|
||||
self.config.json.callback(diag)
|
||||
end
|
||||
|
||||
table.insert(diagnostics, diag)
|
||||
end
|
||||
|
||||
return diagnostics
|
||||
end
|
||||
|
||||
--- Validate input
|
||||
---@param name string
|
||||
---@param config LinterConfig
|
||||
---@return boolean
|
||||
function M.validate(name, config)
|
||||
local ok, resp = pcall(vim.validate, {
|
||||
name = { name, "string" },
|
||||
config = { config, "table" },
|
||||
})
|
||||
|
||||
if ok then
|
||||
ok, resp = pcall(vim.validate, {
|
||||
cmd = {
|
||||
config.cmd,
|
||||
function(t)
|
||||
return utils.is_list(t, "string")
|
||||
end,
|
||||
"list of strings",
|
||||
},
|
||||
stdin = { config.stdin, "boolean", true },
|
||||
stdout = { config.stdout, "boolean", true },
|
||||
stderr = { config.stderr, "boolean", true },
|
||||
pattern = { config.pattern, "string", true },
|
||||
groups = {
|
||||
config.groups,
|
||||
function(t)
|
||||
return utils.is_list(t, "string")
|
||||
end,
|
||||
true,
|
||||
"list of strings",
|
||||
},
|
||||
severity_map = {
|
||||
config.severity_map,
|
||||
function(t)
|
||||
return utils.is_map(t, "string", "number")
|
||||
end,
|
||||
true,
|
||||
"map of string and number",
|
||||
},
|
||||
debounce = { config.debounce, "number", true },
|
||||
source = { config.source, "string", true },
|
||||
json = { config.json, "table", true },
|
||||
tags = { config.tags, "table", true },
|
||||
})
|
||||
end
|
||||
|
||||
if not ok then
|
||||
utils.err(("Invalid config for linter:\n%s"):format(resp))
|
||||
return false
|
||||
end
|
||||
|
||||
if not config.json and (not config.pattern or not config.groups) then
|
||||
utils.err("Either json or pattern and groups must be provided")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function M:run(bufnr)
|
||||
local input
|
||||
|
||||
if self.config.stdin then
|
||||
input = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
end
|
||||
|
||||
local cmd = vim.fn.copy(self.config.cmd)
|
||||
local file = vim.fn.expand("%:.")
|
||||
local filename = vim.fn.expand("%:t")
|
||||
for i, arg in ipairs(cmd) do
|
||||
arg = arg:gsub("%%file%%", file)
|
||||
arg = arg:gsub("%%filename%%", filename)
|
||||
cmd[i] = arg
|
||||
end
|
||||
|
||||
local ok, resp = pcall(
|
||||
vim.system,
|
||||
cmd,
|
||||
{ stdin = input },
|
||||
---@param out vim.SystemCompleted
|
||||
vim.schedule_wrap(function(out)
|
||||
local output
|
||||
|
||||
if self.config.stdout then
|
||||
output = out.stdout or ""
|
||||
end
|
||||
|
||||
if self.config.stderr then
|
||||
output = out.stderr or ""
|
||||
elseif out.stderr and out.stderr ~= "" then
|
||||
utils.err(out.stderr)
|
||||
end
|
||||
|
||||
if self.config.json then
|
||||
local ok, json = pcall(
|
||||
vim.json.decode,
|
||||
output,
|
||||
{ luanil = { object = true, array = true } }
|
||||
)
|
||||
if not ok then
|
||||
utils.err("Failed to parse JSON: " .. json)
|
||||
return
|
||||
end
|
||||
|
||||
local diagnostics = self:process_json_output(json, bufnr)
|
||||
if diagnostics then
|
||||
vim.diagnostic.set(self.namespace, bufnr, diagnostics)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local output_lines = vim.fn.split(output, "\n", false)
|
||||
local diagnostics = {}
|
||||
for _, line in ipairs(output_lines) do
|
||||
local ok, resp = pcall(
|
||||
vim.diagnostic.match,
|
||||
line,
|
||||
self.config.pattern,
|
||||
self.config.groups,
|
||||
self.config.severity_map
|
||||
)
|
||||
|
||||
if not ok then
|
||||
utils.err(tostring(resp))
|
||||
return
|
||||
elseif resp then
|
||||
resp.source = resp.source or self.config.source
|
||||
self:clamp_col(resp, bufnr)
|
||||
self:add_tags(resp)
|
||||
self:fix_indexing(resp)
|
||||
table.insert(diagnostics, resp)
|
||||
end
|
||||
end
|
||||
|
||||
vim.diagnostic.set(self.namespace, bufnr, diagnostics)
|
||||
end)
|
||||
)
|
||||
|
||||
if not ok then
|
||||
utils.err(("Failed to run %s:\n%s"):format(self.config.cmd[1], resp))
|
||||
end
|
||||
end
|
||||
|
||||
function M:init(bufnr)
|
||||
table.insert(self.buffers, bufnr)
|
||||
vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
|
||||
buffer = bufnr,
|
||||
callback = utils.debounce(function()
|
||||
self:run(bufnr)
|
||||
end, self.config.debounce),
|
||||
group = self.augroup,
|
||||
})
|
||||
self:run(bufnr)
|
||||
end
|
||||
|
||||
function M:deinit()
|
||||
for _, bufnr in ipairs(self.buffers) do
|
||||
vim.api.nvim_buf_clear_namespace(bufnr, self.namespace, 0, -1)
|
||||
end
|
||||
self.buffers = {}
|
||||
|
||||
vim.api.nvim_clear_autocmds({ group = self.augroup })
|
||||
end
|
||||
|
||||
--- Create a new instance
|
||||
---@param name string
|
||||
---@param config LinterConfig
|
||||
---@return Linter|nil
|
||||
function M.new(name, config)
|
||||
if not M.validate(name, config) then
|
||||
return
|
||||
end
|
||||
|
||||
config.debounce = config.debounce or 100
|
||||
|
||||
local linter = {
|
||||
name = name,
|
||||
namespace = vim.api.nvim_create_namespace(name),
|
||||
augroup = vim.api.nvim_create_augroup(name, {}),
|
||||
buffers = {},
|
||||
config = config,
|
||||
}
|
||||
|
||||
return setmetatable(linter, M)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,341 +0,0 @@
|
||||
local utils = require("utils")
|
||||
|
||||
---@class PostInstallStep
|
||||
---@field cmd string[]
|
||||
|
||||
---@class MasonPackage
|
||||
---@field dependencies? MasonPackage[]
|
||||
---@field config MasonPackageConfig
|
||||
local M = {}
|
||||
M.__index = M
|
||||
|
||||
---@class MasonPackageConfig
|
||||
---@field [1]? string
|
||||
---@field name? string
|
||||
---@field version? string
|
||||
---@field dependencies? string[]|MasonPackageConfig[]
|
||||
---@field post_install? PostInstallStep[]
|
||||
M.config = {}
|
||||
|
||||
--- Validate MasonPackageConfig
|
||||
---@param config string|MasonPackageConfig
|
||||
---@return boolean
|
||||
function M.validate(config)
|
||||
local ok, resp = pcall(vim.validate, { config = { config, { "table" } } })
|
||||
if ok then
|
||||
ok, resp = pcall(vim.validate, {
|
||||
name = { config.name or config[1], { "string" }, true },
|
||||
version = { config.version, { "string" }, true },
|
||||
dependencies = {
|
||||
config.dependencies,
|
||||
function(f)
|
||||
if not f then
|
||||
return true
|
||||
end
|
||||
|
||||
for _, dep in ipairs(f) do
|
||||
local t = type(dep)
|
||||
if
|
||||
(t ~= "string" and t ~= "table")
|
||||
or (type(dep) == "table" and not M.validate(dep))
|
||||
then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
"list of string|MasonPackageConfig",
|
||||
},
|
||||
post_install = {
|
||||
config.post_install,
|
||||
function(field)
|
||||
if not field then
|
||||
return true
|
||||
end
|
||||
|
||||
if not utils.is_list(field) then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, step in ipairs(field) do
|
||||
local o, r = pcall(vim.validate, { step = { step, { "table" } } })
|
||||
if o then
|
||||
o, r = pcall(vim.validate, {
|
||||
cmd = {
|
||||
step.cmd,
|
||||
function(f)
|
||||
return utils.is_list(f, "string")
|
||||
end,
|
||||
"list of strings or nil",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
if not o then
|
||||
utils.err(("Invalid config for post_install step: %s"):format(r), "LSP")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
"list of steps",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Invalid config for %s:\n%s"):format(config.name or config[1] or config, resp),
|
||||
"LSP"
|
||||
)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Run post installation steps
|
||||
---@param pkg Package
|
||||
function M:post_install(pkg)
|
||||
---@param step PostInstallStep
|
||||
---@param msg string
|
||||
local function log_err(step, msg)
|
||||
local cmd = table.concat(step.cmd, " ")
|
||||
|
||||
utils.err(
|
||||
("Post installation step for %s:\n`%s`\nfailed with:\n%s"):format(
|
||||
self.config.name,
|
||||
cmd,
|
||||
msg
|
||||
),
|
||||
"LSP"
|
||||
)
|
||||
end
|
||||
|
||||
if self.config.post_install then
|
||||
for _, step in ipairs(self.config.post_install) do
|
||||
utils.info("running post install step", "LSP")
|
||||
local cwd = pkg:get_install_path()
|
||||
local args = step.cmd
|
||||
local prog = table.remove(args, 1)
|
||||
|
||||
if prog:find("[/\\]") then
|
||||
prog = vim.fn.resolve(("%s/%s"):format(cwd, prog))
|
||||
end
|
||||
|
||||
if not utils.is_executable(prog) then
|
||||
log_err(step, "command not executable")
|
||||
return
|
||||
end
|
||||
|
||||
local job = require("plenary.job"):new({
|
||||
command = prog,
|
||||
args = args,
|
||||
cwd = pkg:get_install_path(),
|
||||
enabled_recording = true,
|
||||
on_exit = vim.schedule_wrap(function(job, code, signal)
|
||||
if code ~= 0 or signal ~= 0 then
|
||||
log_err(step, table.concat(job:stderr_result(), "\n"))
|
||||
return
|
||||
end
|
||||
utils.info("post install step done", "LSP")
|
||||
end),
|
||||
})
|
||||
job:start()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handle installation
|
||||
---@param pkg Package
|
||||
---@param old_version? string
|
||||
---@param new_version? string
|
||||
---@param on_done? fun(success: boolean)
|
||||
function M:handle_install(pkg, old_version, new_version, on_done)
|
||||
local version_str = ""
|
||||
if old_version and new_version then
|
||||
version_str = (" %s -> %s"):format(old_version, new_version)
|
||||
elseif new_version then
|
||||
version_str = " " .. new_version
|
||||
end
|
||||
|
||||
utils.info(("Installing %s%s"):format(self.config.name, version_str), "LSP")
|
||||
local handle = pkg:install({ version = new_version })
|
||||
local err
|
||||
handle:on(
|
||||
"stderr",
|
||||
vim.schedule_wrap(function(msg)
|
||||
err = (err or "") .. msg
|
||||
end)
|
||||
)
|
||||
|
||||
handle:once(
|
||||
"closed",
|
||||
vim.schedule_wrap(function()
|
||||
local is_installed = pkg:is_installed()
|
||||
|
||||
if is_installed then
|
||||
utils.info(("Successfully installed %s"):format(self.config.name), "LSP")
|
||||
self:post_install(pkg)
|
||||
else
|
||||
if err then
|
||||
err = ":\n" .. err
|
||||
else
|
||||
err = ""
|
||||
end
|
||||
|
||||
utils.err(("Failed to install %s%s"):format(self.config.name, err), "LSP")
|
||||
end
|
||||
|
||||
if on_done then
|
||||
on_done(is_installed)
|
||||
end
|
||||
end)
|
||||
)
|
||||
end
|
||||
|
||||
--- Perform installation
|
||||
---@param on_done fun(success: boolean)?
|
||||
function M:install(on_done)
|
||||
local registry = require("mason-registry")
|
||||
local success, result = pcall(registry.get_package, self.config.name)
|
||||
|
||||
if not success then
|
||||
utils.err(("Failed to get package %s:\n%s"):format(self.config.name, result), "LSP")
|
||||
|
||||
if on_done then
|
||||
on_done(false)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local pkg = result
|
||||
|
||||
if not pkg:is_installed() then
|
||||
self:handle_install(pkg, nil, self.config.version, on_done)
|
||||
elseif self.config.version then
|
||||
pkg:get_installed_version(function(ok, version_or_err)
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Failed to check currently installed version for %s:\n%s"):format(
|
||||
self.config.name,
|
||||
version_or_err
|
||||
),
|
||||
"LSP"
|
||||
)
|
||||
elseif self.config.version ~= version_or_err then
|
||||
self:handle_install(pkg, version_or_err, self.config.version, on_done)
|
||||
return
|
||||
end
|
||||
|
||||
if on_done then
|
||||
on_done(ok)
|
||||
end
|
||||
end)
|
||||
else
|
||||
pkg:check_new_version(function(update_available, res)
|
||||
local ok = type(res) == "table" or res == "Package is not outdated."
|
||||
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Failed to check for update for %s:\n%s"):format(self.config.name, res),
|
||||
"LSP"
|
||||
)
|
||||
elseif update_available then
|
||||
self:handle_install(pkg, res.current_version, res.latest_version, on_done)
|
||||
return
|
||||
end
|
||||
|
||||
if on_done then
|
||||
on_done(ok)
|
||||
end
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
--- Install package dependencies
|
||||
---@param on_done fun(success: boolean)?
|
||||
function M:install_dependencies(on_done)
|
||||
local total = #self.dependencies
|
||||
local completed = 0
|
||||
local all_successful = true
|
||||
|
||||
--- Handle install result
|
||||
---@param success boolean
|
||||
local function handle_result(success)
|
||||
completed = completed + 1
|
||||
|
||||
if not success then
|
||||
all_successful = false
|
||||
end
|
||||
|
||||
if completed == total and on_done then
|
||||
on_done(all_successful)
|
||||
end
|
||||
end
|
||||
|
||||
for _, dep in ipairs(self.dependencies) do
|
||||
dep:install_with_dependencies(handle_result)
|
||||
end
|
||||
end
|
||||
|
||||
--- Install package and any defined dependencies
|
||||
---@param on_done fun(success: boolean)?
|
||||
function M:install_with_dependencies(on_done)
|
||||
--- Handle install result
|
||||
---@param success boolean
|
||||
local function handle_result(success)
|
||||
if success then
|
||||
self:install(on_done)
|
||||
elseif on_done then
|
||||
on_done(success)
|
||||
end
|
||||
end
|
||||
|
||||
require("mason-registry").refresh(function()
|
||||
if self.dependencies and #self.dependencies ~= 0 then
|
||||
self:install_dependencies(handle_result)
|
||||
else
|
||||
self:install(on_done)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- Create a new instance
|
||||
---@param config MasonPackageConfig|string
|
||||
---@return MasonPackage|nil
|
||||
function M.new(config)
|
||||
if type(config) == "string" then
|
||||
config = { config }
|
||||
end
|
||||
|
||||
if not M.validate(config) then
|
||||
return
|
||||
end
|
||||
|
||||
config.name = config.name or config[1]
|
||||
|
||||
local pkg = { config = config }
|
||||
|
||||
if pkg.config.dependencies then
|
||||
pkg.dependencies = {}
|
||||
|
||||
for _, dep_cfg in ipairs(config.dependencies) do
|
||||
local dep = M.new(dep_cfg)
|
||||
if dep then
|
||||
table.insert(pkg.dependencies, dep)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(pkg, M)
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -1,344 +0,0 @@
|
||||
local keymap = require("lsp.keymap")
|
||||
local utils = require("utils")
|
||||
|
||||
---@class Linter
|
||||
local Linter = require("lsp.linter")
|
||||
|
||||
---@class MasonPackage
|
||||
local MasonPackage = require("lsp.package")
|
||||
|
||||
---@class Server
|
||||
---@field name? string
|
||||
---@field mason? MasonPackage
|
||||
---@field client? vim.lsp.Client
|
||||
---@field attached_buffers? number[]
|
||||
---@field manager lspconfig.Manager
|
||||
---@field linters? Linter[]
|
||||
---@field config ServerConfig
|
||||
local M = {}
|
||||
|
||||
M.__index = M
|
||||
|
||||
---@class ServerConfig
|
||||
---@field enable? boolean
|
||||
---@field dependencies? string[]
|
||||
---@field mason? string|MasonPackageConfig
|
||||
---@field keymaps? Keymap[]
|
||||
---@field linters? LinterConfig[]
|
||||
---@field lspconfig? lspconfig.Config
|
||||
M.config = {}
|
||||
|
||||
--- Validate ServerConfig
|
||||
---@param config ServerConfig
|
||||
---@return boolean
|
||||
function M.validate(name, config)
|
||||
local ok, resp = pcall(vim.validate, { config = { config, { "table" } } })
|
||||
|
||||
if ok then
|
||||
ok, resp = pcall(vim.validate, {
|
||||
enable = { config.enable, { "boolean" }, true },
|
||||
dependencies = {
|
||||
config.dependencies,
|
||||
function(f)
|
||||
return utils.is_list_or_nil(f, "string")
|
||||
end,
|
||||
"list of strings or nil",
|
||||
},
|
||||
mason = { config.mason, { "string", "table" }, true },
|
||||
keymaps = {
|
||||
config.keymaps,
|
||||
function(f)
|
||||
if not f then
|
||||
return true
|
||||
end
|
||||
|
||||
if not utils.is_list(f, "table") then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, key in ipairs(f) do
|
||||
local o, r = pcall(vim.validate, {
|
||||
mode = { key.mode, { "string", "table" } },
|
||||
lhs = { key.lhs, "string" },
|
||||
rhs = { key.rhs, { "string", "function" } },
|
||||
opts = { key.opts, "table", true },
|
||||
})
|
||||
|
||||
if not o then
|
||||
utils.err(("Invalid keymap:\n%s"):format(r))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
"list of keymaps",
|
||||
},
|
||||
lspconfig = { config.lspconfig, { "table" }, true },
|
||||
})
|
||||
end
|
||||
|
||||
if not ok then
|
||||
utils.err(("Invalid config for %s:\n%s"):format(name, resp))
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- Called when language server attaches
|
||||
---@param client vim.lsp.Client
|
||||
---@param bufnr integer
|
||||
function M:on_attach(client, bufnr)
|
||||
if self.client and self.client.id ~= client.id then
|
||||
self.client:stop(true)
|
||||
end
|
||||
self.client = client
|
||||
self.attached_buffers = self.attached_buffers or {}
|
||||
table.insert(self.attached_buffers, bufnr)
|
||||
|
||||
keymap:init(self, bufnr)
|
||||
if self.linters then
|
||||
for _, linter in ipairs(self.linters) do
|
||||
local bin = linter.config.cmd[1]
|
||||
if utils.is_executable(bin) then
|
||||
linter:init(bufnr)
|
||||
else
|
||||
utils.warn(
|
||||
("Not adding %s because it is not installed"):format(bin)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- For document highlight
|
||||
vim.cmd.highlight({ "link LspReferenceRead Visual", bang = true })
|
||||
vim.cmd.highlight({ "link LspReferenceText Visual", bang = true })
|
||||
vim.cmd.highlight({ "link LspReferenceWrite Visual", bang = true })
|
||||
|
||||
---@alias lsp.Client vim.lsp.Client
|
||||
-- require("lsp_compl").attach(client, bufnr, {
|
||||
-- server_side_fuzzy_completion = true,
|
||||
-- })
|
||||
end
|
||||
|
||||
--- Configure the LSP client
|
||||
function M:configure_client()
|
||||
local lspconfig = require("lspconfig")
|
||||
|
||||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
local cmp_nvim_lsp = utils.try_require("cmp_nvim_lsp")
|
||||
if cmp_nvim_lsp then
|
||||
capabilities = vim.tbl_deep_extend(
|
||||
"force",
|
||||
capabilities,
|
||||
cmp_nvim_lsp.default_capabilities()
|
||||
)
|
||||
end
|
||||
|
||||
-- local epo = utils.try_require("epo")
|
||||
-- if epo then
|
||||
-- capabilities = vim.tbl_deep_extend(
|
||||
-- "force",
|
||||
-- capabilities,
|
||||
-- epo.register_cap()
|
||||
-- )
|
||||
-- end
|
||||
|
||||
-- local lsp_compl = utils.try_require("lsp_compl")
|
||||
-- if lsp_compl then
|
||||
-- capabilities = vim.tbl_deep_extend("force", capabilities, lsp_compl.capabilities())
|
||||
-- end
|
||||
--
|
||||
|
||||
self.config.lspconfig.capabilities = capabilities
|
||||
self.config.lspconfig.on_attach = function(client, bufnr)
|
||||
local ok, ret = pcall(self.on_attach, self, client, bufnr)
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Failed to load on_attach for %s:\n%s"):format(self.name, ret),
|
||||
"lsp.server:configure_client"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
local ok, ret = pcall(lspconfig[self.name].setup, self.config.lspconfig)
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Failed to setup LSP server %s with lspconfig: %s"):format(
|
||||
self.name,
|
||||
ret
|
||||
)
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
self.manager = lspconfig[self.name].manager
|
||||
for _, bufnr in ipairs(self:get_ft_buffers()) do
|
||||
self.manager:try_add_wrapper(bufnr)
|
||||
end
|
||||
|
||||
if self.config.linters then
|
||||
self.linters = {}
|
||||
for i, config in ipairs(self.config.linters) do
|
||||
local linter =
|
||||
Linter.new(("%s_linter%d"):format(self.name, i), config)
|
||||
|
||||
if linter then
|
||||
table.insert(self.linters, linter)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M:get_ft_buffers()
|
||||
local filetypes = self.config.lspconfig.filetypes or {}
|
||||
if not vim.list_contains(filetypes, self.config.lspconfig.filetype) then
|
||||
table.insert(filetypes, self.config.lspconfig.filetype)
|
||||
end
|
||||
if #filetypes == 0 then
|
||||
return {}
|
||||
end
|
||||
return vim.tbl_filter(function(bufnr)
|
||||
return vim.list_contains(filetypes, vim.bo[bufnr].filetype)
|
||||
end, vim.api.nvim_list_bufs())
|
||||
end
|
||||
|
||||
--- Check for and return missing dependencies
|
||||
---@return table<string>
|
||||
function M:get_missing_unmanaged_deps()
|
||||
local missing_deps = {}
|
||||
if self.config.dependencies ~= nil then
|
||||
for _, dep in ipairs(self.config.dependencies) do
|
||||
if not utils.is_executable(dep) then
|
||||
table.insert(missing_deps, dep)
|
||||
end
|
||||
end
|
||||
end
|
||||
return missing_deps
|
||||
end
|
||||
|
||||
--- Install LSP server
|
||||
---@param on_done fun(success: boolean)?
|
||||
function M:install(on_done)
|
||||
--- Handle install result
|
||||
---@param success boolean
|
||||
local function handle_result(success)
|
||||
if not success then
|
||||
self.config.enable = false
|
||||
end
|
||||
if on_done then
|
||||
on_done(success)
|
||||
end
|
||||
end
|
||||
|
||||
self.mason:install_with_dependencies(handle_result)
|
||||
end
|
||||
|
||||
--- Setup LSP server
|
||||
function M:setup(on_done)
|
||||
local missing_deps = self:get_missing_unmanaged_deps()
|
||||
|
||||
if #missing_deps > 0 then
|
||||
utils.warn(
|
||||
(
|
||||
"Disabling %s because the following package(s)"
|
||||
.. "are not installed: %s"
|
||||
):format(self.name, table.concat(missing_deps, ", "))
|
||||
)
|
||||
self.config.enable = false
|
||||
return
|
||||
end
|
||||
|
||||
if self.mason then
|
||||
self:install(function(success)
|
||||
if success then
|
||||
self:configure_client()
|
||||
end
|
||||
|
||||
if on_done then
|
||||
on_done(success)
|
||||
end
|
||||
end)
|
||||
elseif vim.fn.executable(self.config.lspconfig.cmd[1]) == 1 then
|
||||
self:configure_client()
|
||||
else
|
||||
utils.warn(self.name .. " not installed, disabling")
|
||||
self.config.enable = false
|
||||
end
|
||||
end
|
||||
|
||||
--- Load autocmd for setting up LSP server upon entering a buffer of related
|
||||
--- filetype
|
||||
function M:init(on_done)
|
||||
local group = vim.api.nvim_create_augroup("lsp_bootstrap_" .. self.name, {})
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
once = true,
|
||||
pattern = self.config.lspconfig.filetypes or {},
|
||||
callback = function()
|
||||
self:setup(on_done)
|
||||
end,
|
||||
group = group,
|
||||
})
|
||||
end
|
||||
|
||||
function M:deinit()
|
||||
if self.attached_buffers then
|
||||
for _, bufnr in ipairs(self.attached_buffers) do
|
||||
keymap:deinit(bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
if self.client then
|
||||
self.client:stop(true)
|
||||
self.client = nil
|
||||
end
|
||||
|
||||
vim.api.nvim_clear_autocmds({ group = "lsp_bootstrap_" .. self.name })
|
||||
|
||||
if self.linters then
|
||||
for _, linter in ipairs(self.linters) do
|
||||
linter:deinit()
|
||||
end
|
||||
|
||||
self.linters = nil
|
||||
end
|
||||
|
||||
require("lspconfig")[self.name] = nil
|
||||
end
|
||||
|
||||
--- Create a new instance
|
||||
---@param name string
|
||||
---@param config? ServerConfig
|
||||
---@return Server|nil
|
||||
function M.new(name, config)
|
||||
config = config or {}
|
||||
|
||||
if not M.validate(name, config) then
|
||||
return
|
||||
end
|
||||
|
||||
local ok, resp = pcall(require, "lspconfig.configs." .. name)
|
||||
|
||||
if not ok then
|
||||
utils.err(
|
||||
("Server with name %s does not exist in lspconfig"):format(name)
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
config.lspconfig =
|
||||
vim.tbl_deep_extend("keep", config.lspconfig or {}, resp.default_config)
|
||||
local server = { name = name, config = config }
|
||||
|
||||
if server.config.mason then
|
||||
local pkg = MasonPackage.new(server.config.mason)
|
||||
if pkg then
|
||||
server.mason = pkg
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(server, M)
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user