feat: add mason lsp
This commit is contained in:
parent
18fc525867
commit
a9cb122592
13 changed files with 574 additions and 197 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
require "settings.plugins"
|
require "settings.plugins"
|
||||||
require "settings.options"
|
require "settings.options"
|
||||||
require "settings.lint"
|
|
||||||
require "settings.cmp"
|
require "settings.cmp"
|
||||||
require "settings.lsp"
|
require "settings.lsp"
|
||||||
require "settings.devicons"
|
require "settings.devicons"
|
||||||
|
|
@ -15,14 +14,12 @@ require "settings.indentline"
|
||||||
require "settings.impatient"
|
require "settings.impatient"
|
||||||
require "settings.project"
|
require "settings.project"
|
||||||
require "settings.dashboard"
|
require "settings.dashboard"
|
||||||
require "settings.vimtex"
|
|
||||||
require "settings.autocommands"
|
|
||||||
require "settings.haskell"
|
require "settings.haskell"
|
||||||
require "settings.keymaps"
|
require "settings.keymaps"
|
||||||
require "settings.null-ls"
|
require "settings.null-ls"
|
||||||
require "settings.prettier"
|
require "settings.prettier"
|
||||||
require "settings.gitsigns"
|
require "settings.gitsigns"
|
||||||
require "settings.rainbow"
|
require "settings.rainbow"
|
||||||
require "settings.cpp-dap"
|
require "settings.dap"
|
||||||
require "settings.dap-ui"
|
|
||||||
require "settings.telescope"
|
require "settings.telescope"
|
||||||
|
require "settings.autocommands"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,2 @@
|
||||||
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
|
vim.cmd('let g:vimtex_view_general_viewer = "evince"')
|
||||||
callback = function()
|
vim.cmd('let g:vimtex_compiler_method = "latexmk"')
|
||||||
require("lint").try_lint()
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ bufferline.setup {
|
||||||
-- and so changing this is NOT recommended, this is intended
|
-- and so changing this is NOT recommended, this is intended
|
||||||
-- as an escape hatch for people who cannot bear it for whatever reason
|
-- as an escape hatch for people who cannot bear it for whatever reason
|
||||||
indicator = {
|
indicator = {
|
||||||
style = 'underline',
|
style = 'icon',
|
||||||
},
|
},
|
||||||
buffer_close_icon = "",
|
buffer_close_icon = "",
|
||||||
-- buffer_close_icon = '',
|
-- buffer_close_icon = '',
|
||||||
|
|
@ -23,51 +23,28 @@ bufferline.setup {
|
||||||
-- close_icon = '',
|
-- close_icon = '',
|
||||||
left_trunc_marker = "",
|
left_trunc_marker = "",
|
||||||
right_trunc_marker = "",
|
right_trunc_marker = "",
|
||||||
--- name_formatter can be used to change the buffer's label in the bufferline.
|
|
||||||
--- Please note some names can/will break the
|
|
||||||
--- bufferline so use this at your discretion knowing that it has
|
|
||||||
--- some limitations that will *NOT* be fixed.
|
|
||||||
-- name_formatter = function(buf) -- buf contains a "name", "path" and "bufnr"
|
|
||||||
-- -- remove extension from markdown files for example
|
|
||||||
-- if buf.name:match('%.md') then
|
|
||||||
-- return vim.fn.fnamemodify(buf.name, ':t:r')
|
|
||||||
-- end
|
|
||||||
-- end,
|
-- end,
|
||||||
max_name_length = 30,
|
max_name_length = 30,
|
||||||
max_prefix_length = 30, -- prefix used when a buffer is de-duplicated
|
max_prefix_length = 30, -- prefix used when a buffer is de-duplicated
|
||||||
tab_size = 21,
|
tab_size = 21,
|
||||||
diagnostics = false, -- | "nvim_lsp" | "coc",
|
diagnostics = false, -- | "nvim_lsp" | "coc",
|
||||||
diagnostics_update_in_insert = false,
|
diagnostics_update_in_insert = false,
|
||||||
-- diagnostics_indicator = function(count, level, diagnostics_dict, context)
|
|
||||||
-- return "("..count..")"
|
|
||||||
-- end,
|
|
||||||
-- NOTE: this will be called a lot so don't do any heavy processing here
|
|
||||||
-- custom_filter = function(buf_number)
|
|
||||||
-- -- filter out filetypes you don't want to see
|
|
||||||
-- if vim.bo[buf_number].filetype ~= "<i-dont-want-to-see-this>" then
|
|
||||||
-- return true
|
|
||||||
-- end
|
|
||||||
-- -- filter out by buffer name
|
|
||||||
-- if vim.fn.bufname(buf_number) ~= "<buffer-name-I-dont-want>" then
|
|
||||||
-- return true
|
|
||||||
-- end
|
|
||||||
-- -- filter out based on arbitrary rules
|
|
||||||
-- -- e.g. filter out vim wiki buffer from tabline in your work repo
|
|
||||||
-- if vim.fn.getcwd() == "<work-repo>" and vim.bo[buf_number].filetype ~= "wiki" then
|
|
||||||
-- return true
|
|
||||||
-- end
|
|
||||||
-- end,
|
|
||||||
offsets = { { filetype = "NvimTree", text = "", padding = 1 } },
|
offsets = { { filetype = "NvimTree", text = "", padding = 1 } },
|
||||||
show_buffer_icons = true,
|
show_buffer_icons = true,
|
||||||
show_buffer_close_icons = true,
|
show_buffer_close_icons = true,
|
||||||
show_close_icon = true,
|
show_close_icon = true,
|
||||||
show_tab_indicators = true,
|
show_tab_indicators = false,
|
||||||
persist_buffer_sort = true,-- whether or not custom sorted buffers should persist
|
persist_buffer_sort = true,-- whether or not custom sorted buffers should persist
|
||||||
-- can also be a table containing 2 custom separators
|
-- can also be a table containing 2 custom separators
|
||||||
-- [focused and unfocused]. eg: { '|', '|' }
|
-- [focused and unfocused]. eg: { '|', '|' }
|
||||||
separator_style = "thin", -- | "thick" | "thin" | { 'any', 'any' },
|
separator_style = "slant", -- | "thick" | "thin" | { 'any', 'any' },
|
||||||
enforce_regular_tabs = true,
|
enforce_regular_tabs = true,
|
||||||
always_show_bufferline = true,
|
always_show_bufferline = true,
|
||||||
|
hover = {
|
||||||
|
enabled = true,
|
||||||
|
delay = 200,
|
||||||
|
reveal = {'close'}
|
||||||
|
},
|
||||||
-- sort_by = 'id' | 'extension' | 'relative_directory' | 'directory' | 'tabs' | function(buffer_a, buffer_b)
|
-- sort_by = 'id' | 'extension' | 'relative_directory' | 'directory' | 'tabs' | function(buffer_a, buffer_b)
|
||||||
-- -- add custom logic
|
-- -- add custom logic
|
||||||
-- return buffer_a.modified > buffer_b.modified
|
-- return buffer_a.modified > buffer_b.modified
|
||||||
|
|
@ -157,10 +134,10 @@ bufferline.setup {
|
||||||
fg = { attribute = "bg", highlight = "Normal" },
|
fg = { attribute = "bg", highlight = "Normal" },
|
||||||
bg = { attribute = "bg", highlight = "Normal" },
|
bg = { attribute = "bg", highlight = "Normal" },
|
||||||
},
|
},
|
||||||
-- separator_visible = {
|
separator_visible = {
|
||||||
-- guifg = {attribute='bg',highlight='TabLine'},
|
fg = {attribute='bg',highlight='TabLine'},
|
||||||
-- guibg = {attribute='bg',highlight='TabLine'}
|
bg = {attribute='bg',highlight='TabLine'}
|
||||||
-- },
|
},
|
||||||
indicator_selected = {
|
indicator_selected = {
|
||||||
fg = { attribute = "fg", highlight = "LspDiagnosticsDefaultHint" },
|
fg = { attribute = "fg", highlight = "LspDiagnosticsDefaultHint" },
|
||||||
bg = { attribute = "bg", highlight = "Normal" },
|
bg = { attribute = "bg", highlight = "Normal" },
|
||||||
|
|
|
||||||
126
nvim/lua/settings/dap.lua
Normal file
126
nvim/lua/settings/dap.lua
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
local dap = require('dap')
|
||||||
|
dap.adapters.cppdbg = {
|
||||||
|
id = 'cppdbg',
|
||||||
|
type = 'executable',
|
||||||
|
command = '/home/dashie/.config/nvim/plugged/cpptools/extension/debugAdapters/bin/OpenDebugAD7',
|
||||||
|
}
|
||||||
|
|
||||||
|
dap.configurations.cpp = {
|
||||||
|
{
|
||||||
|
name = "Launch file",
|
||||||
|
type = "cppdbg",
|
||||||
|
request = "launch",
|
||||||
|
program = function()
|
||||||
|
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/build/', 'file')
|
||||||
|
end,
|
||||||
|
cwd = '${workspaceFolder}',
|
||||||
|
stopAtEntry = true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "Launch file --gui",
|
||||||
|
type = "cppdbg",
|
||||||
|
request = "launch",
|
||||||
|
program = function()
|
||||||
|
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/build/', 'file')
|
||||||
|
end,
|
||||||
|
args = {'--gui'},
|
||||||
|
cwd = '${workspaceFolder}',
|
||||||
|
stopAtEntry = true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = 'Attach to gdbserver :1234',
|
||||||
|
type = 'cppdbg',
|
||||||
|
request = 'launch',
|
||||||
|
MIMode = 'gdb',
|
||||||
|
miDebuggerServerAddress = 'localhost:1234',
|
||||||
|
miDebuggerPath = '/usr/bin/gdb',
|
||||||
|
cwd = '${workspaceFolder}',
|
||||||
|
program = function()
|
||||||
|
return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/build/', 'file')
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dap.configurations.c = dap.configurations.cpp
|
||||||
|
dap.configurations.rust = dap.configurations.cpp
|
||||||
|
|
||||||
|
require("dapui").setup({
|
||||||
|
icons = { expanded = "▾", collapsed = "▸", current_frame = "▸" },
|
||||||
|
mappings = {
|
||||||
|
-- Use a table to apply multiple mappings
|
||||||
|
expand = { "<CR>", "<2-LeftMouse>" },
|
||||||
|
open = "o",
|
||||||
|
remove = "d",
|
||||||
|
edit = "e",
|
||||||
|
repl = "r",
|
||||||
|
toggle = "t",
|
||||||
|
},
|
||||||
|
-- Expand lines larger than the window
|
||||||
|
-- Requires >= 0.7
|
||||||
|
expand_lines = vim.fn.has("nvim-0.7") == 1,
|
||||||
|
-- Layouts define sections of the screen to place windows.
|
||||||
|
-- The position can be "left", "right", "top" or "bottom".
|
||||||
|
-- The size specifies the height/width depending on position. It can be an Int
|
||||||
|
-- or a Float. Integer specifies height/width directly (i.e. 20 lines/columns) while
|
||||||
|
-- Float value specifies percentage (i.e. 0.3 - 30% of available lines/columns)
|
||||||
|
-- Elements are the elements shown in the layout (in order).
|
||||||
|
-- Layouts are opened in order so that earlier layouts take priority in window sizing.
|
||||||
|
layouts = {
|
||||||
|
{
|
||||||
|
elements = {
|
||||||
|
-- Elements can be strings or table with id and size keys.
|
||||||
|
{ id = "scopes", size = 0.25 },
|
||||||
|
"breakpoints",
|
||||||
|
"stacks",
|
||||||
|
"watches",
|
||||||
|
},
|
||||||
|
size = 40, -- 40 columns
|
||||||
|
position = "left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
elements = {
|
||||||
|
"repl",
|
||||||
|
"console",
|
||||||
|
},
|
||||||
|
size = 0.25, -- 25% of total lines
|
||||||
|
position = "bottom",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
controls = {
|
||||||
|
-- Requires Neovim nightly (or 0.8 when released)
|
||||||
|
enabled = true,
|
||||||
|
-- Display controls in this element
|
||||||
|
element = "repl",
|
||||||
|
icons = {
|
||||||
|
pause = "",
|
||||||
|
play = "",
|
||||||
|
step_into = "",
|
||||||
|
step_over = "",
|
||||||
|
step_out = "",
|
||||||
|
step_back = "",
|
||||||
|
run_last = "↻",
|
||||||
|
terminate = "□",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
floating = {
|
||||||
|
max_height = nil, -- These can be integers or a float between 0 and 1.
|
||||||
|
max_width = nil, -- Floats will be treated as percentage of your screen.
|
||||||
|
border = "single", -- Border style. Can be "single", "double" or "rounded"
|
||||||
|
mappings = {
|
||||||
|
close = { "q", "<Esc>" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
windows = { indent = 1 },
|
||||||
|
render = {
|
||||||
|
max_type_length = nil, -- Can be integer or nil.
|
||||||
|
max_value_lines = 100, -- Can be integer or nil.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
require("mason-nvim-dap").setup({
|
||||||
|
ensure_installed = { "cpptools",
|
||||||
|
"bash-debug-adapter",
|
||||||
|
"firefox-debug-adapter",
|
||||||
|
"js-debug-adapter",
|
||||||
|
"node-debug2-adapter"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -5,12 +5,12 @@ end
|
||||||
|
|
||||||
local dashboard = require("alpha.themes.dashboard")
|
local dashboard = require("alpha.themes.dashboard")
|
||||||
dashboard.section.header.val = {
|
dashboard.section.header.val = {
|
||||||
[[ __ ]],
|
[[ _______ ___ _______. __ __ __ _______ ]],
|
||||||
[[ ___ ___ ___ __ __ /\_\ ___ ___ ]],
|
[[| \ / \ / || | | | | | | ____|]],
|
||||||
[[ / _ `\ / __`\ / __`\/\ \/\ \\/\ \ / __` __`\ ]],
|
[[| .--. | / ^ \ | (----`| |__| | | | | |__ ]],
|
||||||
[[/\ \/\ \/\ __//\ \_\ \ \ \_/ |\ \ \/\ \/\ \/\ \ ]],
|
[[| | | | / /_\ \ \ \ | __ | | | | __| ]],
|
||||||
[[\ \_\ \_\ \____\ \____/\ \___/ \ \_\ \_\ \_\ \_\]],
|
[[| '--' | / _____ \ .----) | | | | | | | | |____ ]],
|
||||||
[[ \/_/\/_/\/____/\/___/ \/__/ \/_/\/_/\/_/\/_/]],
|
[[|_______/ /__/ \__\ |_______/ |__| |__| |__| |_______|]]
|
||||||
}
|
}
|
||||||
dashboard.section.buttons.val = {
|
dashboard.section.buttons.val = {
|
||||||
dashboard.button("f", " Find file", ":Telescope find_files <CR>"),
|
dashboard.button("f", " Find file", ":Telescope find_files <CR>"),
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,11 @@ keymap("n", "<F7>" , ':lua require("dap").step_into()<CR>', opts)
|
||||||
keymap("n", "<F8>", ':lua require("dap").continue()<CR>', opts)
|
keymap("n", "<F8>", ':lua require("dap").continue()<CR>', opts)
|
||||||
keymap("n", "<F9>", ':lua require("dap").close()<CR> :lua require("dapui").toggle()<CR>', opts)
|
keymap("n", "<F9>", ':lua require("dap").close()<CR> :lua require("dapui").toggle()<CR>', opts)
|
||||||
keymap("n", "<F10>", ':lua require("dap").continue()<CR> :lua require("dapui").toggle()<CR>', opts)
|
keymap("n", "<F10>", ':lua require("dap").continue()<CR> :lua require("dapui").toggle()<CR>', opts)
|
||||||
|
|
||||||
|
keymap("n", "t", ':lua require("nvim-tree").toggle()<CR>', opts)
|
||||||
|
keymap("n", "f", ':lua require("nvim-tree").focus()<CR>', opts)
|
||||||
|
|
||||||
|
keymap("n", "<F1>", ':BufferLineMoveNext<CR>', opts)
|
||||||
|
keymap("n", "<F2>", ':BufferLineMovePrev<CR>', opts)
|
||||||
|
|
||||||
|
keymap("n", "<F4>", ':lua vim.lsp.buf.format { async = true }<CR>', opts)
|
||||||
|
|
|
||||||
|
|
@ -3,78 +3,156 @@ if not status_ok then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require("mason").setup({
|
||||||
require'lspconfig'.hls.setup {}
|
|
||||||
require'lspconfig'.pyright.setup {}
|
|
||||||
require'lspconfig'.clangd.setup {}
|
|
||||||
require'lspconfig'.html.setup {}
|
|
||||||
require'lspconfig'.eslint.setup {}
|
|
||||||
require'lspconfig'.texlab.setup {}
|
|
||||||
require'lspconfig'.jdtls.setup {}
|
|
||||||
require'lspconfig'.sumneko_lua.setup {}
|
|
||||||
require'lspconfig'.gopls.setup {}
|
|
||||||
require'lspconfig'.jsonls.setup {}
|
|
||||||
require'lspconfig'.cssls.setup {}
|
|
||||||
require'lspconfig'.csharp_ls.setup {}
|
|
||||||
require'lspconfig'.tsserver.setup {}
|
|
||||||
require'lspconfig'.sqls.setup {}
|
|
||||||
require'lspconfig'.rust_analyzer.setup {}
|
|
||||||
require'lspconfig'.bashls.setup {}
|
|
||||||
|
|
||||||
|
|
||||||
require("nvim-lsp-installer").setup({
|
|
||||||
automatic_installation = true, -- automatically detect which servers to install (based on which servers are set up via lspconfig)
|
|
||||||
ui = {
|
ui = {
|
||||||
icons = {
|
icons = {
|
||||||
server_installed = "✓",
|
package_installed = "✓",
|
||||||
server_pending = "➜",
|
package_pending = "➜",
|
||||||
server_uninstalled = "✗"
|
package_uninstalled = "✗"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
require("mason-lspconfig").setup({
|
||||||
local status_ok2, lsp_installer = pcall(require, "nvim-lsp-installer")
|
ensure_installed = {
|
||||||
if not status_ok2 then
|
"cssls", -- css
|
||||||
return
|
"html", -- html
|
||||||
end
|
"eslint", -- latex
|
||||||
|
"clangd", -- cpp / c
|
||||||
local lspconfig = require("lspconfig")
|
"tsserver", -- python
|
||||||
local servers = {"jdtls" , "sumneko_lua" , "texlab", "pyright" , "eslint" , "html" , "cssls" , "rust_analyzer" , "bashls" , "csharp_ls" , "sqls" , "clangd" }
|
"texlab", -- latex
|
||||||
|
"sumneko_lua", -- lua
|
||||||
lsp_installer.setup {
|
"pyright", -- python
|
||||||
ensure_installed = servers
|
"rust_analyzer", -- rust
|
||||||
}
|
"jdtls", -- jdtls
|
||||||
|
"cmake", -- cmake
|
||||||
for _, server in pairs(servers) do
|
"bashls", -- shell
|
||||||
local opts = {
|
"ansiblels", -- ansible
|
||||||
on_attach = require("settings.lsp_config.handlers").on_attach,
|
"csharp_ls", -- dotnot
|
||||||
capabilities = require("settings.lsp_config.handlers").capabilities,
|
"hls" -- haskel
|
||||||
}
|
|
||||||
local has_custom_opts, server_custom_opts = pcall(require, "settings.lsp_config." .. server)
|
|
||||||
if has_custom_opts then
|
|
||||||
opts = vim.tbl_deep_extend("force", server_custom_opts, opts)
|
|
||||||
end
|
|
||||||
lspconfig[server].setup(opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local null_ls_status_ok, null_ls = pcall(require, "null-ls")
|
|
||||||
if not null_ls_status_ok then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/formatting
|
|
||||||
local formatting = null_ls.builtins.formatting
|
|
||||||
-- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/diagnostics
|
|
||||||
local diagnostics = null_ls.builtins.diagnostics
|
|
||||||
|
|
||||||
null_ls.setup({
|
|
||||||
debug = false,
|
|
||||||
sources = {
|
|
||||||
formatting.prettier.with({ extra_args = { "--no-semi", "--single-quote", "--jsx-single-quote" } }),
|
|
||||||
formatting.black.with({ extra_args = { "--fast" } }),
|
|
||||||
formatting.stylua,
|
|
||||||
-- diagnostics.flake8
|
|
||||||
},
|
},
|
||||||
|
automatic_installation = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
require('lspconfig')['cssls'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['html'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['eslint'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['clangd'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['tsserver'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['texlab'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['sumneko_lua'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['pyright'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['rust_analyzer'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['jdtls'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['cmake'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['bashls'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['ansiblels'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['csharp_ls'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
require('lspconfig')['hls'].setup{
|
||||||
|
capabilities = capabilities,
|
||||||
|
on_attach = on_attach
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--local status_ok2, lsp_installer = pcall(require, "mason")
|
||||||
|
--if not status_ok2 then
|
||||||
|
-- return
|
||||||
|
--end
|
||||||
|
--
|
||||||
|
--local servers = {"jdtls" , "sumneko_lua" , "texlab", "pyright" , "eslint_d" , "html" , "cssls" , "rust_analyzer" , "bashls" , "csharp_ls" , "sqls" , "clangd" }
|
||||||
|
--
|
||||||
|
--lsp_installer.setup {
|
||||||
|
-- ensure_installed = servers
|
||||||
|
--}
|
||||||
|
--
|
||||||
|
--for _, server in pairs(servers) do
|
||||||
|
-- local opts = {
|
||||||
|
-- on_attach = require("settings.lsp_config.handlers").on_attach,
|
||||||
|
-- capabilities = require("settings.lsp_config.handlers").capabilities,
|
||||||
|
-- }
|
||||||
|
-- local has_custom_opts, server_custom_opts = pcall(require, "settings.lsp_config." .. server)
|
||||||
|
-- if has_custom_opts then
|
||||||
|
-- opts = vim.tbl_deep_extend("force", server_custom_opts, opts)
|
||||||
|
-- end
|
||||||
|
-- lspconfig[server].setup(opts)
|
||||||
|
--end
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--local null_ls_status_ok, null_ls = pcall(require, "null-ls")
|
||||||
|
--if not null_ls_status_ok then
|
||||||
|
-- return
|
||||||
|
--end
|
||||||
|
--
|
||||||
|
---- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/formatting
|
||||||
|
--local formatting = null_ls.builtins.formatting
|
||||||
|
---- https://github.com/jose-elias-alvarez/null-ls.nvim/tree/main/lua/null-ls/builtins/diagnostics
|
||||||
|
--local diagnostics = null_ls.builtins.diagnostics
|
||||||
|
--
|
||||||
|
--null_ls.setup({
|
||||||
|
-- debug = false,
|
||||||
|
-- sources = {
|
||||||
|
-- formatting.prettier.with({ extra_args = { "--no-semi", "--single-quote", "--jsx-single-quote" } }),
|
||||||
|
-- formatting.black.with({ extra_args = { "--fast" } }),
|
||||||
|
-- formatting.stylua,
|
||||||
|
-- -- diagnostics.flake8
|
||||||
|
-- },
|
||||||
|
--})
|
||||||
|
|
|
||||||
165
nvim/lua/settings/lsp_config/eslint_d.lua
Normal file
165
nvim/lua/settings/lsp_config/eslint_d.lua
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
local util = require 'lspconfig.util'
|
||||||
|
local lsp = vim.lsp
|
||||||
|
|
||||||
|
local function fix_all(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
|
||||||
|
local eslint_lsp_client = util.get_active_client_by_name(opts.bufnr, 'eslint')
|
||||||
|
if eslint_lsp_client == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local request
|
||||||
|
if opts.sync then
|
||||||
|
request = function(bufnr, method, params)
|
||||||
|
eslint_lsp_client.request_sync(method, params, nil, bufnr)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
request = function(bufnr, method, params)
|
||||||
|
eslint_lsp_client.request(method, params, nil, bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = util.validate_bufnr(opts.bufnr or 0)
|
||||||
|
request(0, 'workspace/executeCommand', {
|
||||||
|
command = 'eslint.applyAllFixes',
|
||||||
|
arguments = {
|
||||||
|
{
|
||||||
|
uri = vim.uri_from_bufnr(bufnr),
|
||||||
|
version = lsp.util.buf_versions[bufnr],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local bin_name = 'vscode-eslint-language-server'
|
||||||
|
local cmd = { bin_name, '--stdio' }
|
||||||
|
|
||||||
|
if vim.fn.has 'win32' == 1 then
|
||||||
|
cmd = { 'cmd.exe', '/C', bin_name, '--stdio' }
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
default_config = {
|
||||||
|
cmd = cmd,
|
||||||
|
filetypes = {
|
||||||
|
'javascript',
|
||||||
|
'javascriptreact',
|
||||||
|
'javascript.jsx',
|
||||||
|
'typescript',
|
||||||
|
'typescriptreact',
|
||||||
|
'typescript.tsx',
|
||||||
|
'vue',
|
||||||
|
},
|
||||||
|
-- https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-file-formats
|
||||||
|
root_dir = util.root_pattern(
|
||||||
|
'.eslintrc',
|
||||||
|
'.eslintrc.js',
|
||||||
|
'.eslintrc.cjs',
|
||||||
|
'.eslintrc.yaml',
|
||||||
|
'.eslintrc.yml',
|
||||||
|
'.eslintrc.json',
|
||||||
|
'package.json'
|
||||||
|
),
|
||||||
|
-- Refer to https://github.com/Microsoft/vscode-eslint#settings-options for documentation.
|
||||||
|
settings = {
|
||||||
|
validate = 'on',
|
||||||
|
packageManager = 'npm',
|
||||||
|
useESLintClass = false,
|
||||||
|
codeActionOnSave = {
|
||||||
|
enable = false,
|
||||||
|
mode = 'all',
|
||||||
|
},
|
||||||
|
format = true,
|
||||||
|
quiet = false,
|
||||||
|
onIgnoredFiles = 'off',
|
||||||
|
rulesCustomizations = {},
|
||||||
|
run = 'onType',
|
||||||
|
-- nodePath configures the directory in which the eslint server should start its node_modules resolution.
|
||||||
|
-- This path is relative to the workspace folder (root dir) of the server instance.
|
||||||
|
nodePath = '',
|
||||||
|
-- use the workspace folder location or the file location (if no workspace folder is open) as the working directory
|
||||||
|
workingDirectory = { mode = 'location' },
|
||||||
|
codeAction = {
|
||||||
|
disableRuleComment = {
|
||||||
|
enable = true,
|
||||||
|
location = 'separateLine',
|
||||||
|
},
|
||||||
|
showDocumentation = {
|
||||||
|
enable = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
on_new_config = function(config, new_root_dir)
|
||||||
|
-- The "workspaceFolder" is a VSCode concept. It limits how far the
|
||||||
|
-- server will traverse the file system when locating the ESLint config
|
||||||
|
-- file (e.g., .eslintrc).
|
||||||
|
config.settings.workspaceFolder = {
|
||||||
|
uri = new_root_dir,
|
||||||
|
name = vim.fn.fnamemodify(new_root_dir, ':t'),
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Support Yarn2 (PnP) projects
|
||||||
|
local pnp_cjs = util.path.join(new_root_dir, '.pnp.cjs')
|
||||||
|
local pnp_js = util.path.join(new_root_dir, '.pnp.js')
|
||||||
|
if util.path.exists(pnp_cjs) or util.path.exists(pnp_js) then
|
||||||
|
config.cmd = vim.list_extend({ 'yarn', 'exec' }, cmd)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
handlers = {
|
||||||
|
['eslint/openDoc'] = function(_, result)
|
||||||
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local sysname = vim.loop.os_uname().sysname
|
||||||
|
if sysname:match 'Windows' then
|
||||||
|
os.execute(string.format('start %q', result.url))
|
||||||
|
elseif sysname:match 'Linux' then
|
||||||
|
os.execute(string.format('xdg-open %q', result.url))
|
||||||
|
else
|
||||||
|
os.execute(string.format('open %q', result.url))
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
['eslint/confirmESLintExecution'] = function(_, result)
|
||||||
|
if not result then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return 4 -- approved
|
||||||
|
end,
|
||||||
|
['eslint/probeFailed'] = function()
|
||||||
|
vim.notify('[lspconfig] ESLint probe failed.', vim.log.levels.WARN)
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
['eslint/noLibrary'] = function()
|
||||||
|
vim.notify('[lspconfig] Unable to find ESLint library.', vim.log.levels.WARN)
|
||||||
|
return {}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
commands = {
|
||||||
|
EslintFixAll = {
|
||||||
|
function()
|
||||||
|
fix_all { sync = true, bufnr = 0 }
|
||||||
|
end,
|
||||||
|
description = 'Fix all eslint problems for this buffer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docs = {
|
||||||
|
description = [[
|
||||||
|
https://github.com/hrsh7th/vscode-langservers-extracted
|
||||||
|
`vscode-eslint-language-server` is a linting engine for JavaScript / Typescript.
|
||||||
|
It can be installed via `npm`:
|
||||||
|
```sh
|
||||||
|
npm i -g vscode-langservers-extracted
|
||||||
|
```
|
||||||
|
`vscode-eslint-language-server` provides an `EslintFixAll` command that can be used to format a document on save:
|
||||||
|
```vim
|
||||||
|
autocmd BufWritePre *.tsx,*.ts,*.jsx,*.js EslintFixAll
|
||||||
|
```
|
||||||
|
See [vscode-eslint](https://github.com/microsoft/vscode-eslint/blob/55871979d7af184bf09af491b6ea35ebd56822cf/server/src/eslintServer.ts#L216-L229) for configuration options.
|
||||||
|
Messages handled in lspconfig: `eslint/openDoc`, `eslint/confirmESLintExecution`, `eslint/probeFailed`, `eslint/noLibrary`
|
||||||
|
Additional messages you can handle: `eslint/noConfig`
|
||||||
|
]],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ local function lsp_keymaps(bufnr)
|
||||||
)
|
)
|
||||||
vim.api.nvim_buf_set_keymap(bufnr, "n", "]d", '<cmd>lua vim.diagnostic.goto_next({ border = "rounded" })<CR>', opts)
|
vim.api.nvim_buf_set_keymap(bufnr, "n", "]d", '<cmd>lua vim.diagnostic.goto_next({ border = "rounded" })<CR>', opts)
|
||||||
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>q", "<cmd>lua vim.diagnostic.setloclist()<CR>", opts)
|
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>q", "<cmd>lua vim.diagnostic.setloclist()<CR>", opts)
|
||||||
vim.cmd [[ command! Format execute 'lua vim.lsp.buf.formatting()' ]]
|
vim.cmd [[ command! Format execute 'lua vim.lsp.buf.format { async = true }' ]]
|
||||||
end
|
end
|
||||||
|
|
||||||
M.on_attach = function(client, bufnr)
|
M.on_attach = function(client, bufnr)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,44 @@
|
||||||
local null_ls = require("null-ls")
|
local null_ls = require("null-ls")
|
||||||
|
|
||||||
|
require("mason-null-ls").setup({
|
||||||
|
ensure_installed = {
|
||||||
|
"prettier",
|
||||||
|
"clang_format",
|
||||||
|
"latexindent",
|
||||||
|
"shellharden",
|
||||||
|
"sql_formatter",
|
||||||
|
"fixjson",
|
||||||
|
"autopep8",
|
||||||
|
"stylua",
|
||||||
|
"rustfmt",
|
||||||
|
"stylish-haskell",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
null_ls.setup({
|
null_ls.setup({
|
||||||
on_attach = function(client, bufnr)
|
on_attach = function(client, bufnr)
|
||||||
if client.server_capabilities.documentFormattingProvider then
|
if client.server_capabilities.documentFormattingProvider then
|
||||||
vim.cmd("nnoremap <silent><buffer> <Leader>f :lua vim.lsp.buf.formatting()<CR>")
|
vim.cmd("nnoremap <silent><buffer> <Leader>f :lua vim.lsp.buf.format { async = true }<CR>")
|
||||||
|
|
||||||
-- format on save
|
-- format on save
|
||||||
vim.cmd("autocmd BufWritePost <buffer> lua vim.lsp.buf.formatting()")
|
vim.cmd("autocmd BufWritePost <buffer> lua vim.lsp.buf.format { async = true }")
|
||||||
end
|
end
|
||||||
|
|
||||||
if client.server_capabilities.documentRangeFormattingProvider then
|
if client.server_capabilities.documentRangeFormattingProvider then
|
||||||
vim.cmd("xnoremap <silent><buffer> <Leader>f :lua vim.lsp.buf.range_formatting({})<CR>")
|
vim.cmd("xnoremap <silent><buffer> <Leader>f :lua vim.lsp.buf.range_formatting({})<CR>")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
sources = {
|
||||||
|
require("null-ls").builtins.completion.spell,
|
||||||
|
require("null-ls").builtins.formatting.prettier,
|
||||||
|
require("null-ls").builtins.formatting.clang_format,
|
||||||
|
require("null-ls").builtins.formatting.latexindent,
|
||||||
|
require("null-ls").builtins.formatting.shellharden,
|
||||||
|
require("null-ls").builtins.formatting.sql_formatter,
|
||||||
|
require("null-ls").builtins.formatting.fixjson,
|
||||||
|
require("null-ls").builtins.formatting.autopep8,
|
||||||
|
require("null-ls").builtins.formatting.stylua,
|
||||||
|
require("null-ls").builtins.formatting.rustfmt,
|
||||||
|
require("null-ls").builtins.formatting.stylish_haskell,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@ if not config_status_ok then
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Replaces auto_close
|
-- Replaces auto_close
|
||||||
local tree_cb = nvim_tree_config.nvim_tree_callback
|
--local tree_cb = nvim_tree_config.nvim_tree_callback
|
||||||
vim.api.nvim_create_autocmd("BufEnter", {
|
--vim.api.nvim_create_autocmd("BufEnter", {
|
||||||
nested = true,
|
-- nested = true,
|
||||||
callback = function()
|
-- callback = function()
|
||||||
if #vim.api.nvim_list_wins() == 1 and vim.api.nvim_buf_get_name(0):match("NvimTree_") ~= nil then
|
-- if #vim.api.nvim_list_wins() == 1 and vim.api.nvim_buf_get_name(0):match("NvimTree_") ~= nil then
|
||||||
vim.cmd "quit"
|
-- vim.cmd "quit"
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
})
|
--})
|
||||||
|
|
||||||
|
|
||||||
nvim_tree.setup { -- BEGIN_DEFAULT_OPTS
|
nvim_tree.setup { -- BEGIN_DEFAULT_OPTS
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,54 @@
|
||||||
local Plug = vim.fn['plug#']
|
local Plug = vim.fn['plug#']
|
||||||
vim.call('plug#begin', '~/.config/nvim/plugged')
|
vim.call('plug#begin', '~/.config/nvim/plugged')
|
||||||
Plug('folke/twilight.nvim')
|
Plug 'folke/twilight.nvim'
|
||||||
Plug('nvim-lua/popup.nvim') -- An implementation of the Popup API from vim in Neovim
|
Plug 'nvim-lua/popup.nvim' -- An implementation of the Popup API from vim in Neovim
|
||||||
Plug('nvim-lua/plenary.nvim') -- Useful lua functions used ny lots of plugins
|
Plug 'nvim-lua/plenary.nvim' -- Useful lua functions used ny lots of plugins
|
||||||
Plug('EdenEast/nightfox.nvim')
|
Plug 'EdenEast/nightfox.nvim'
|
||||||
Plug('kyazdani42/nvim-web-devicons')
|
Plug 'kyazdani42/nvim-web-devicons'
|
||||||
Plug('feline-nvim/feline.nvim')
|
Plug 'feline-nvim/feline.nvim'
|
||||||
Plug('lewis6991/gitsigns.nvim')
|
Plug 'lewis6991/gitsigns.nvim'
|
||||||
Plug('tanvirtin/vgit.nvim')
|
Plug 'tanvirtin/vgit.nvim'
|
||||||
Plug('nvim-lua/plenary.nvim')
|
Plug 'nvim-lua/plenary.nvim'
|
||||||
Plug('b3nj5m1n/kommentary')
|
Plug 'b3nj5m1n/kommentary'
|
||||||
Plug('antoinemadec/FixCursorHold.nvim')
|
Plug 'antoinemadec/FixCursorHold.nvim'
|
||||||
Plug('lambdalisue/fern.vim')
|
Plug 'lambdalisue/fern.vim'
|
||||||
Plug('neovim/nvim-lspconfig')
|
Plug "williamboman/mason.nvim"
|
||||||
Plug('nvim-treesitter/nvim-treesitter', {['do']= vim.fn[':TSUpdate']})
|
Plug "williamboman/mason-lspconfig.nvim"
|
||||||
Plug('williamboman/nvim-lsp-installer')
|
Plug 'neovim/nvim-lspconfig'
|
||||||
Plug('hrsh7th/nvim-cmp')
|
|
||||||
Plug('hrsh7th/cmp-nvim-lsp')
|
|
||||||
Plug('hrsh7th/cmp-buffer')
|
|
||||||
Plug('hrsh7th/cmp-path')
|
|
||||||
Plug('hrsh7th/cmp-cmdline')
|
|
||||||
Plug('saadparwaiz1/cmp_luasnip')
|
|
||||||
Plug('L3MON4D3/LuaSnip')
|
|
||||||
Plug 'MarcWeber/vim-addon-mw-utils'
|
|
||||||
Plug 'tomtom/tlib_vim'
|
|
||||||
Plug 'garbas/vim-snipmate'
|
|
||||||
Plug('kyazdani42/nvim-tree.lua')
|
|
||||||
Plug('windwp/nvim-autopairs')
|
|
||||||
Plug('akinsho/bufferline.nvim')
|
|
||||||
Plug('numToStr/Comment.nvim')
|
|
||||||
Plug('lukas-reineke/indent-blankline.nvim')
|
|
||||||
Plug('lewis6991/impatient.nvim')
|
|
||||||
Plug('lewis6991/spellsitter.nvim')
|
|
||||||
Plug('moll/vim-bbye')
|
|
||||||
Plug('nvim-telescope/telescope.nvim')
|
|
||||||
Plug('ahmedkhalf/project.nvim')
|
|
||||||
Plug('goolord/alpha-nvim')
|
|
||||||
Plug('lervag/vimtex')
|
|
||||||
Plug('weilbith/nvim-code-action-menu')
|
|
||||||
Plug('mfussenegger/nvim-lint')
|
|
||||||
Plug 'jose-elias-alvarez/null-ls.nvim'
|
Plug 'jose-elias-alvarez/null-ls.nvim'
|
||||||
Plug 'MunifTanjim/prettier.nvim'
|
Plug 'jayp0521/mason-null-ls.nvim'
|
||||||
Plug 'rafamadriz/friendly-snippets'
|
|
||||||
Plug 'p00f/nvim-ts-rainbow'
|
|
||||||
Plug 'mfussenegger/nvim-dap'
|
Plug 'mfussenegger/nvim-dap'
|
||||||
Plug 'rcarriga/nvim-dap-ui'
|
Plug 'rcarriga/nvim-dap-ui'
|
||||||
Plug 'theHamsta/nvim-dap-virtual-text'
|
Plug 'theHamsta/nvim-dap-virtual-text'
|
||||||
|
Plug 'jayp0521/mason-nvim-dap.nvim'
|
||||||
|
Plug('nvim-treesitter/nvim-treesitter', { ['do'] = vim.fn[':TSUpdate'] })
|
||||||
|
Plug 'hrsh7th/nvim-cmp'
|
||||||
|
Plug 'hrsh7th/cmp-nvim-lsp'
|
||||||
|
Plug 'hrsh7th/cmp-buffer'
|
||||||
|
Plug 'hrsh7th/cmp-path'
|
||||||
|
Plug 'hrsh7th/cmp-cmdline'
|
||||||
|
Plug 'saadparwaiz1/cmp_luasnip'
|
||||||
|
Plug 'L3MON4D3/LuaSnip'
|
||||||
|
Plug 'MarcWeber/vim-addon-mw-utils'
|
||||||
|
Plug 'tomtom/tlib_vim'
|
||||||
|
Plug 'garbas/vim-snipmate'
|
||||||
|
Plug 'kyazdani42/nvim-tree.lua'
|
||||||
|
Plug 'windwp/nvim-autopairs'
|
||||||
|
Plug 'akinsho/bufferline.nvim'
|
||||||
|
Plug 'numToStr/Comment.nvim'
|
||||||
|
Plug 'lukas-reineke/indent-blankline.nvim'
|
||||||
|
Plug 'lewis6991/impatient.nvim'
|
||||||
|
Plug 'lewis6991/spellsitter.nvim'
|
||||||
|
Plug 'moll/vim-bbye'
|
||||||
|
Plug 'nvim-telescope/telescope.nvim'
|
||||||
|
Plug 'ahmedkhalf/project.nvim'
|
||||||
|
Plug 'goolord/alpha-nvim'
|
||||||
|
Plug 'lervag/vimtex'
|
||||||
|
Plug 'weilbith/nvim-code-action-menu'
|
||||||
|
Plug 'MunifTanjim/prettier.nvim'
|
||||||
|
Plug 'rafamadriz/friendly-snippets'
|
||||||
|
Plug 'p00f/nvim-ts-rainbow'
|
||||||
Plug 'nvim-telescope/telescope-ui-select.nvim'
|
Plug 'nvim-telescope/telescope-ui-select.nvim'
|
||||||
Plug 'nvim-telescope/telescope-file-browser.nvim'
|
Plug 'nvim-telescope/telescope-file-browser.nvim'
|
||||||
Plug 'nvim-telescope/telescope-fzy-native.nvim'
|
Plug 'nvim-telescope/telescope-fzy-native.nvim'
|
||||||
vim.call('plug#end')
|
vim.call('plug#end')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ end
|
||||||
|
|
||||||
require'nvim-treesitter.configs'.setup {
|
require'nvim-treesitter.configs'.setup {
|
||||||
-- A list of parser names, or "all"
|
-- A list of parser names, or "all"
|
||||||
ensure_installed = { "latex" , "c", "cpp" , "lua", "haskell" , "java" , "javascript" , "typescript" },
|
ensure_installed = { "latex" , "c", "cpp" , "rust" , "lua", "haskell" , "java" , "javascript" , "typescript" },
|
||||||
highlight = {
|
highlight = {
|
||||||
enable = true,
|
enable = true,
|
||||||
additional_vim_regex_highlighting = false,
|
additional_vim_regex_highlighting = false,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue