0
0
Просмотр исходного кода

feat(neovim): use native LSP config

This is a huge migration that tears apart a few things, and copy-pastes
in a bunch of stuff from `nvim-lspconfig`. LSPs are configured much more
cleanly now, and `proj-conf` and `save-formatter` got facelifts.
Joe 9 месяцев назад
Родитель
Сommit
7b7eb7d82c
27 измененных файлов с 457 добавлено и 409 удалено
  1. 1 3
      .config/nvim/custom/proj-conf/lua/proj-conf/init.lua
  2. 0 175
      .config/nvim/custom/proj-conf/lua/proj-conf/lsp/init.lua
  3. 3 7
      .config/nvim/custom/proj-conf/lua/proj-conf/projects/deno_typescript_monorepo.lua
  4. 4 58
      .config/nvim/custom/proj-conf/lua/proj-conf/projects/ruff_pyright.lua
  5. 0 6
      .config/nvim/custom/proj-conf/lua/proj-conf/save-formatter.lua
  6. 113 107
      .config/nvim/custom/save-formatter/lua/save-formatter/init.lua
  7. 47 0
      .config/nvim/init.lua
  8. 75 0
      .config/nvim/lsp/basedpyright.lua
  9. 7 0
      .config/nvim/lsp/bash-language-server.lua
  10. 26 0
      .config/nvim/lsp/deno.lua
  11. 13 0
      .config/nvim/lsp/gopls.lua
  12. 33 0
      .config/nvim/lsp/lua-language-server.lua
  13. 6 0
      .config/nvim/lsp/marksman.lua
  14. 18 0
      .config/nvim/lsp/ruff.lua
  15. 6 0
      .config/nvim/lsp/rust_analyzer.lua
  16. 6 0
      .config/nvim/lsp/sql-language-server.lua
  17. 6 0
      .config/nvim/lsp/taplo.lua
  18. 27 0
      .config/nvim/lsp/typescript-language-server.lua
  19. 12 0
      .config/nvim/lsp/vscode-css-language-server.lua
  20. 12 0
      .config/nvim/lsp/vscode-html-language-server.lua
  21. 9 0
      .config/nvim/lsp/vscode-json-language-server.lua
  22. 9 0
      .config/nvim/lsp/yaml-language-server.lua
  23. 3 7
      .config/nvim/lua/plugins/command-palette.lua
  24. 11 0
      .config/nvim/lua/plugins/completion.lua
  25. 0 42
      .config/nvim/lua/plugins/lsp.lua
  26. 0 4
      .config/nvim/lua/plugins/proj-conf.lua
  27. 10 0
      .config/nvim/lua/plugins/treesitter.lua

+ 1 - 3
.config/nvim/custom/proj-conf/lua/proj-conf/init.lua

@@ -29,11 +29,9 @@ local status = {
 }
 
 for _, i in pairs({
-    require("proj-conf.lsp"),
-    require("proj-conf.save-formatter"),
     require("proj-conf.telescope-shroud"),
+    require("proj-conf.projects.ruff_pyright"),
     require("proj-conf.projects.deno_typescript_monorepo"),
-    require("proj-conf.projects.ruff_pyright")
 }) do
     map.default = vim.list_extend(map.default, i.default)
     i.default = nil

+ 0 - 175
.config/nvim/custom/proj-conf/lua/proj-conf/lsp/init.lua

@@ -1,175 +0,0 @@
-local lspconfig = require("lspconfig")
-local capabilities = require("cmp_nvim_lsp").default_capabilities()
-
-return {
-    default = {
-        "default_lsp"
-    },
-    default_lsp = {
-        "default_bashls_config", "default_basedpyright_config_and_typechecking",
-        "default_cssls_config", "default_denols_config", "default_gopls_config",
-        "default_html_config", "default_jsonls_config", "default_lua_ls_config",
-        "default_marksman_config", "default_rust_analyzer_config",
-        "default_sqlls_config", "default_taplo_config", "default_ts_ls_config",
-        "default_yamlls_config" },
-    default_bashls_config = function()
-        lspconfig.bashls.setup({ capabilities = capabilities })
-    end,
-    default_basedpyright_config = function()
-        lspconfig.basedpyright.setup({
-            capabilities = capabilities,
-            settings = {
-                basedpyright = {
-                    analysis = {
-                        typeCheckingMode = "standard",
-                        diagnosticMode = "openFilesOnly",
-                        autoSearchPaths = true,
-                    },
-                    openFilesOnly = true,
-                    autoImportCompletions = true,
-                    disableOrganizeImports = true,
-                }
-            }
-        })
-    end,
-    default_cssls_config = function()
-        lspconfig.cssls.setup({ capabilities = capabilities })
-    end,
-    default_denols_config = function()
-        lspconfig.denols.setup({
-            capabilities = capabilities,
-            root_dir = lspconfig.util
-                .root_pattern("deno.json", "deno.jsonc"),
-            single_file_support = false
-        })
-    end,
-    default_gopls_config = function()
-        lspconfig.gopls.setup({
-            capabilities = capabilities,
-            settings = {
-                gopls = {
-                    analyses = { unusedparams = true },
-                    staticcheck = true,
-                    gofumpt = true
-                }
-            }
-        })
-    end,
-    default_html_config = function()
-        lspconfig.html.setup({ capabilities = capabilities })
-    end,
-    default_jsonls_config = function()
-        lspconfig.jsonls.setup({ capabilities = capabilities })
-    end,
-    default_lua_ls_config = function()
-        lspconfig.lua_ls.setup({
-            capabilities = capabilities,
-            settings = {
-                Lua = {
-                    workspace = { checkThirdParty = false },
-                    telemetry = { enable = false },
-                    hint = { enable = true },
-                    diagnostics = { globals = { "vim" } },
-                    format = {
-                        enable = true,
-                        defaultConfig = {
-                            indent_style = "space",
-                            indent_size = "4",
-                            max_line_length = "80",
-                        }
-                    },
-                }
-            }
-        })
-    end,
-    default_marksman_config = function()
-        lspconfig.marksman.setup({ capabilities = capabilities })
-    end,
-    default_ruff_config = function()
-        lspconfig.ruff.setup({ capabilities = capabilities })
-    end,
-    default_rust_analyzer_config = function()
-        lspconfig.rust_analyzer.setup({ capabilities = capabilities })
-    end,
-    default_sqlls_config = function()
-        lspconfig.sqlls.setup({ capabilities = capabilities })
-    end,
-    default_taplo_config = function()
-        lspconfig.taplo.setup({ capabilities = capabilities })
-    end,
-    default_ts_ls_config = function()
-        local inlayHints = {
-            includeInlayEnumMemberValueHints = true,
-            includeInlayFunctionLikeReturnTypeHints = true,
-            includeInlayFunctionParameterTypeHints = true,
-            includeInlayParameterNameHints = "all",
-            includeInlayParameterNameHintsWhenArgumentMatchesName = true,
-            includeInlayPropertyDeclarationTypeHints = true,
-            includeInlayVariableTypeHints = true
-        }
-        lspconfig.ts_ls.setup({
-            capabilities = capabilities,
-            javascript = { inlayHints = inlayHints },
-            typescript = { inlayHints = inlayHints },
-            root_dir = lspconfig.util.root_pattern("package.json"),
-            single_file_support = false
-        })
-    end,
-    default_yamlls_config = function()
-        lspconfig.yamlls.setup({ capabilities = capabilities })
-    end,
-
-    default_basedpyright_config_and_typechecking = {
-        "default_basedpyright_config",
-        "default_basedpyright_typechecking_options"
-    },
-    default_basedpyright_typechecking_options = function()
-        local pickers = require("telescope.pickers")
-        local themes = require("telescope.themes")
-        local actions = require("telescope.actions")
-        local action_state = require("telescope.actions.state")
-        local finders = require("telescope.finders")
-        local conf = require("telescope.config").values
-        local get_basedpyright = function()
-            return vim.tbl_filter(function(l)
-                return l.name == "basedpyright"
-            end, vim.lsp.get_active_clients())[1]
-        end
-        require("command-palette").add({
-            { function()
-                if get_basedpyright() ~= nil then
-                    return "BasedPyright Typechecking"
-                end
-            end, "Set `typeCheckingMode` of BasedPyright",
-                function(opts)
-                    local current = get_basedpyright().settings.basedpyright
-                        .analysis.typeCheckingMode
-                    if current == nil then current = "unset" end
-                    pickers.new(themes.get_dropdown({}), {
-                        prompt_title = "BasedPyright Typechecking (Current: " ..
-                            current .. ")",
-                        finder = finders.new_table({
-                            results = vim.tbl_filter(
-                                function(r) return r ~= current end,
-                                { "off", "basic", "standard", "strict", "all" }),
-                        }),
-                        sorter = conf.generic_sorter(opts),
-                        attach_mappings = function(prompt_bufnr, _)
-                            actions.select_default:replace(function()
-                                actions.close(prompt_bufnr)
-                                local selection = action_state
-                                    .get_selected_entry()
-                                local client = get_basedpyright()
-                                client.settings.basedpyright.analysis.typeCheckingMode =
-                                    selection.value
-                                client.notify("workspace/didChangeConfiguration",
-                                    client.settings)
-                            end)
-                            return true
-                        end,
-                    }):find()
-                end
-            }
-        })
-    end
-}

+ 3 - 7
.config/nvim/custom/proj-conf/lua/proj-conf/projects/deno_typescript_monorepo.lua

@@ -1,13 +1,9 @@
-local formatter = require("save-formatter")
-
 return {
     default = {},
     deno_typescript_monorepo = { "deno_typescript_monorepo_format", "default_lsp" },
     deno_typescript_monorepo_format = function()
-        formatter.enable(vim.tbl_deep_extend("force", formatter.default(), {
-            json = formatter.commands().deno,
-            jsonc = formatter.commands().deno,
-            markdown = formatter.commands().deno,
-        }))
+        if pcall(require, "save-formatter") then
+            require("save-formatter").add("lsp", "deno", nil, true)
+        end
     end
 }

+ 4 - 58
.config/nvim/custom/proj-conf/lua/proj-conf/projects/ruff_pyright.lua

@@ -1,63 +1,9 @@
-local lspconfig = require("lspconfig")
-local capabilities = require("cmp_nvim_lsp").default_capabilities()
-local formatter = require("save-formatter")
-
-local ruff_pyright_lsp_base = function(mode, extra)
-    lspconfig.ruff.setup({
-        capabilities = capabilities,
-        on_attach = function(client, _)
-            client.server_capabilities.hoverProvider = false
-        end,
-    })
-    lspconfig.basedpyright.setup({
-        capabilities = capabilities,
-        settings = {
-            basedpyright = {
-                analysis = vim.tbl_deep_extend("force", {
-                    include = { "*" },
-                    exclude = { "**/node_modules", "**/__pycache__", "**/build" },
-                    typeCheckingMode = mode,
-                    autoSearchPaths = true,
-                    diagnosticMode = "openFilesOnly",
-                    useLibraryCodeForTypes = true,
-                    analyzeUnannotatedFunctions = true,
-                    reportUnreachable = true,
-                }, extra),
-                openFilesOnly = true,
-                autoImportCompletions = true,
-                disableOrganizeImports = true,
-            }
-        }
-    })
-end
-
 return {
     default = {},
-    ruff_pyright = { "ruff_pyright_base", "ruff_pyright_lsp_strict" },
-    ruff_pyright_light = { "ruff_pyright_base", "ruff_pyright_lsp_off" },
-    ruff_pyright_minimal = { "ruff_pyright_base", "ruff_pyright_lsp_basic" },
-    ruff_pyright_base = { "ruff_pyright_format", "default_jsonls_config",
-        "default_marksman_config", "default_sqlls_config",
-        "default_yamlls_config", "default_basedpyright_typechecking_options" },
+    ruff_pyright = { "ruff_pyright_format" },
     ruff_pyright_format = function()
-        formatter.enable(vim.tbl_deep_extend("force", formatter.default(), {
-            python = false,
-            ruff = true,
-        }))
+        if pcall(require, "save-formatter") then
+            require("save-formatter").add("lsp", "ruff", nil, true)
+        end
     end,
-    ruff_pyright_lsp_strict = function()
-        ruff_pyright_lsp_base('strict', {})
-    end,
-    ruff_pyright_lsp_off = function()
-        ruff_pyright_lsp_base('off', {})
-    end,
-    ruff_pyright_lsp_basic = function()
-        ruff_pyright_lsp_base('basic', {
-            reportUnknownArgumentType = false,
-            reportUnknownLambdaType = false,
-            reportUnknownMemberType = false,
-            reportUnknownParameterType = false,
-            reportUnknownVariableType = false,
-        })
-    end
 }

+ 0 - 6
.config/nvim/custom/proj-conf/lua/proj-conf/save-formatter.lua

@@ -1,6 +0,0 @@
-local formatter = require("save-formatter")
-
-return {
-    default = {},
-    enable_global_save_formatter = function() formatter.enable() end
-}

+ 113 - 107
.config/nvim/custom/save-formatter/lua/save-formatter/init.lua

@@ -1,145 +1,151 @@
-local commands = {
-    prettier =
-    [[!prettier --write --ignore-path $XDG_CONFIG_HOME/prettier/ignore --config $XDG_CONFIG_HOME/prettier/config %:p]],
-    deno = [[!deno fmt %:p]],
-    ruff = [[!ruff format %:p]],
+local M = {
+    formatters = {
+        lsp = {},
+        filetype = {}
+    },
+    commands = {}
 }
 
-local default = {
-    json = commands.prettier,
-    jsonc = commands.prettier,
-    html = commands.prettier,
-    css = commands.prettier,
-    markdown = commands.prettier,
-    denols = commands.deno,
-    python = commands.ruff,
-    typescriptreact = true,
-    lua_ls = true,
-}
-local enabled = {}
-
-local save_format = function(source)
-    if enabled[source] ~= nil then
-        if enabled[source] == true then
-            vim.api.nvim_exec("lua vim.lsp.buf.format()", true)
-            vim.api.nvim_exec("noautocmd write", true)
-        else
-            vim.api.nvim_exec(enabled[source], true)
-        end
-    end
-    vim.api.nvim_exec("e", true)
-end
-
-local global_autocmd_group = vim.api.nvim_create_augroup("SaveFormatterGlobal",
-    { clear = true })
-local setup = function()
+M.setup = function()
+    local group = vim.api.nvim_create_augroup("SaveFormatter", { clear = true })
     vim.api.nvim_create_autocmd({ "BufWritePost" }, {
+        group = group,
         pattern = { "*" },
-        group = global_autocmd_group,
-        callback = function()
-            for _, lsp_data in pairs(vim.lsp.get_active_clients()) do
-                for buf_num, buf_active in pairs(lsp_data.attached_buffers) do
-                    if buf_num == vim.api.nvim_get_current_buf() and buf_active ==
-                        true and enabled[lsp_data.name] then
-                        save_format(lsp_data.name)
-                        return
-                    end
-                end
-            end
-            if enabled[vim.bo.filetype] ~= nil then
-                save_format(vim.bo.filetype)
+        callback = function(args) M.format(args.buf, true) end
+    })
+    vim.api.nvim_create_autocmd({ "BufWritePre" }, {
+        group = group,
+        pattern = { "*" },
+        callback = function(args) M.format(args.buf, false) end
+    })
+    vim.api.nvim_create_autocmd({ "LspAttach" }, {
+        group = group,
+        callback = function(args)
+            local client = vim.lsp.get_client_by_id(args.data.client_id)
+            if client ~= nil and client:supports_method('textDocument/format') then
+                M.add("lsp", client.name, nil, false)
             end
         end
     })
 end
 
-local enable = function(items)
-    if not items then
-        enabled = vim.deepcopy(default)
-    else
-        enabled = vim.deepcopy(items)
+--- @param buf integer
+--- @param did_write boolean
+M.format = function(buf, did_write)
+    local filetype = vim.bo[buf].filetype
+    local lsps = vim.lsp.get_clients({ bufnr = buf })
+
+    for _, client in pairs(lsps) do
+        if M.formatters.lsp[client.name] ~= nil and M.formatters.lsp[client.name].enabled then
+            if not did_write then
+                vim.lsp.buf.format({ bufnr = buf })
+            end
+            return
+        end
     end
-    for key, value in pairs(enabled) do
-        if value == false then
-            enabled[key] = nil
+    if M.formatters.filetype[filetype] ~= nil and M.formatters.filetype[filetype].enabled and M.commands[M.formatters.filetype[filetype].command] ~= nil then
+        M.commands[M.formatters.filetype[filetype].command](buf)
+        if did_write then
+            vim.api.nvim_buf_call(buf, function()
+                vim.api.nvim_exec2("noautocmd edit", {})
+            end)
         end
     end
-    return enabled
 end
-local disable = function()
-    default = vim.deepcopy(enabled)
-    enabled = {}
-    return enabled
+
+---@param type "lsp"|"filename"
+---@param name string
+---@param enabled boolean
+M.toggle = function(type, name, enabled)
+    if M.formatters[type] == nil or M.formatters[type][name] == nil then return end
+    if enabled == nil then
+        M.formatters[type][name].enabled = not M.formatters[type][name].enabled
+    else
+        M.formatters[type][name].enabled = enabled
+    end
 end
-local add = function(item, value)
-    if value == nil then value = vim.deepcopy(default[item]) end
-    enabled[item] = vim.deepcopy(value)
-    return value
+
+---@param type "lsp"|"filetype"
+---@param name string
+---@param command string|nil
+---@param enable boolean|nil
+M.add = function(type, name, command, enable)
+    if M.formatters[type] == nil then return end
+    local formatter = { enabled = false }
+    if type == "filetype" then
+        if command == nil then return end
+        formatter.command = command
+    end
+    if enable then formatter.enabled = true end
+    if M.formatters[type][name] ~= nil then
+        formatter.enabled = M.formatters[type][name].enabled
+    end
+    M.formatters[type][name] = formatter
 end
-local remove = function(item)
-    default[item] = vim.deepcopy(enabled[item])
-    enabled[item] = nil
+
+---@param name string
+---@param value function
+M.command = function(name, value)
+    M.commands[name] = value
 end
 
-local pickers = require("telescope.pickers")
-local themes = require("telescope.themes")
-local actions = require("telescope.actions")
-local action_state = require("telescope.actions.state")
-local finders = require("telescope.finders")
-local conf = require("telescope.config").values
-local entry_display = require("telescope.pickers.entry_display")
-local picker = function(opts)
+M.picker = function()
+    if pcall(require, "telescope.pickers") then else return end
+    local pickers = require("telescope.pickers")
+    local themes = require("telescope.themes")
+    local actions = require("telescope.actions")
+    local action_state = require("telescope.actions.state")
+    local finders = require("telescope.finders")
+    local conf = require("telescope.config").values
+    local entry_display = require("telescope.pickers.entry_display")
+
     local items = {}
-    for key, value in pairs(enabled) do
-        if value ~= nil then
-            table.insert(items, { key, value })
-        end
+    for key, value in pairs(M.formatters.lsp) do
+        table.insert(items, {
+            type = "lsp",
+            name = key,
+            enabled = value.enabled
+        })
+    end
+    for key, value in pairs(M.formatters.filetype) do
+        table.insert(items, {
+            type = "filetype",
+            name = key,
+            enabled = value.enabled,
+            command = value.command
+        })
     end
     pickers.new(themes.get_dropdown({}), {
         prompt_title = "Save Formatter",
         finder = finders.new_table({
             results = items,
             entry_maker = function(entry)
-                local desc = "LSP Format"
-                if type(entry[2]) == "string" then desc = entry[2] end
                 local displayer = entry_display.create({
                     separator = " ▏",
-                    items = { { width = 16 }, { remaining = true } }
+                    items = { { width = 32 }, { width = 8 }, { remaining = true } }
                 })
+                local command = "LSP"
+                if entry.command ~= nil then command = entry.command end
                 return {
                     value = entry,
-                    display = function() return displayer({ { entry[1] }, { desc } }) end,
-                    ordinal = entry[1],
+                    display = function()
+                        return displayer({ { entry.name }, { entry.enabled }, { command } })
+                    end,
+                    ordinal = entry.name
                 }
-            end,
+            end
         }),
-        sorter = conf.generic_sorter(opts),
-        attach_mappings = function(prompt_bufnr, map)
-            map("n", "d", function(_)
-                local entry = action_state.get_selected_entry()
-                if entry ~= nil then
-                    remove(entry.value[1])
-                    actions.close(prompt_bufnr)
-                end
-            end)
+        sorter = conf.generic_sorter({}),
+        attach_mappings = function(buf, _)
             actions.select_default:replace(function()
-                if add(action_state.get_current_line()) ~= nil then
-                    actions.close(prompt_bufnr)
-                end
+                local t = action_state.get_selected_entry().value
+                M.formatters[t.type][t.name].enabled = not t.enabled
+                actions.close(buf)
+                M.picker()
             end)
             return true
         end
     }):find()
 end
 
-return {
-    add = add,
-    disable = disable,
-    enable = enable,
-    picker = picker,
-    remove = remove,
-    setup = setup,
-    commands = function() return commands end,
-    default = function() return default end,
-    enabled = function() return enabled end,
-}
+return M

+ 47 - 0
.config/nvim/init.lua

@@ -55,6 +55,7 @@ vim.opt.shiftwidth = 4
 -- Bootstrapping `lazy`, as directed by GitHub docs:
 -- https://github.com/folke/lazy.nvim#-installation
 local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
+---@diagnostic disable-next-line: undefined-field
 if not vim.loop.fs_stat(lazypath) then
     vim.fn.system({
         "git", "clone", "--filter=blob:none",
@@ -109,3 +110,49 @@ vim.keymap.set("n", "Syal", function() yank_filename(true, true) end)
 vim.keymap.set("n", "Syrn", function() yank_filename(false, false) end)
 vim.keymap.set("n", "Syrl", function() yank_filename(false, true) end)
 vim.keymap.set("n", "Syf", function() yank_filename(false, false) end)
+-- === === === === === === === === === === === === === === === === === === ===
+--
+-- Language Servers
+--
+-- === === === === === === === === === === === === === === === === === === ===
+-- See also configurations in `lsp/*.lua` and `custom/proj-conf/`
+vim.lsp.config("*", {
+    cmd_cwd = vim.fn.expand("~"),
+    root_markers = { ".git" },
+})
+-- Enable all language servers with extant configurations
+for _, filename in pairs(vim.split(io.popen("ls -a " .. vim.fn.stdpath("config")
+    .. "/lsp"):read("*a"), "\n")) do
+    if filename:match(".lua$") ~= nil then
+        vim.lsp.enable(filename:sub(1, -5))
+    end
+end
+
+-- LSP Keymaps
+vim.api.nvim_create_autocmd("LspAttach", {
+    callback = function(_)
+        vim.keymap.set("n", "SR", vim.lsp.buf.rename)
+        vim.keymap.set("n", "Sx", vim.lsp.buf.code_action)
+        vim.keymap.set("n", "<Space>", vim.lsp.buf.hover)
+        vim.keymap.set("n", "SI", function()
+            local diagnostic_config = vim.diagnostic.config()
+            if (vim.lsp.inlay_hint.is_enabled()) then
+                vim.lsp.inlay_hint.enable(false)
+                vim.diagnostic.config({
+                    virtual_lines = {
+                        current_line = true,
+                        ---@diagnostic disable-next-line: need-check-nil, undefined-field
+                        format = diagnostic_config.format
+                    }
+                })
+            else
+                vim.lsp.inlay_hint.enable(true)
+                vim.diagnostic.config({
+                    virtual_lines = true,
+                    ---@diagnostic disable-next-line: need-check-nil, undefined-field
+                    format = diagnostic_config.format
+                })
+            end
+        end)
+    end
+})

+ 75 - 0
.config/nvim/lsp/basedpyright.lua

@@ -0,0 +1,75 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'basedpyright-langserver', '--stdio' },
+    filetypes = { 'python' },
+    root_markers = {
+        'pyproject.toml',
+        'setup.py',
+        'setup.cfg',
+        'requirements.txt',
+        'Pipfile',
+        'pyrightconfig.json',
+    },
+    single_file_support = true,
+    settings = {
+        basedpyright = {
+            analysis = {
+                typeCheckingMode = "standard",
+                diagnosticMode = "openFilesOnly",
+                autoSearchPaths = true,
+                include = { "*" },
+                exclude = { "**/node_modules", "**/__pycache__", "**/build" },
+                useLibraryCodeForTypes = true,
+                analyzeUnannotatedFunctions = true,
+                reportUnreachable = true,
+            },
+            openFilesOnly = true,
+            autoImportCompletions = true,
+            disableOrganizeImports = true,
+        }
+    },
+    on_init = function(client, _)
+        local typecheckingPicker = function(opts)
+            local pickers = require("telescope.pickers")
+            local themes = require("telescope.themes")
+            local actions = require("telescope.actions")
+            local action_state = require("telescope.actions.state")
+            local finders = require("telescope.finders")
+            local conf = require("telescope.config").values
+            local current = client.settings.basedpyright.analysis
+                .typeCheckingMode
+            if current == nil then current = "unset" end
+            pickers.new(themes.get_dropdown({}), {
+                prompt_title = "BasedPyright Typechecking (Current: " ..
+                    current .. ")",
+                finder = finders.new_table({
+                    results = vim.tbl_filter(
+                        function(r) return r ~= current end,
+                        { "off", "basic", "standard", "strict", "all" }),
+                }),
+                sorter = conf.generic_sorter(opts),
+                attach_mappings = function(prompt_bufnr, _)
+                    actions.select_default:replace(function()
+                        actions.close(prompt_bufnr)
+                        local selection = action_state
+                            .get_selected_entry()
+                        client.settings.basedpyright.analysis.typeCheckingMode =
+                            selection.value
+                        client:notify(
+                            "workspace/didChangeConfiguration",
+                            client.settings)
+                    end)
+                    return true
+                end,
+            }):find()
+        end
+
+        if pcall(require, "command-palette") and pcall(require, "telescope.pickers") then
+            require("command-palette").add({ {
+                "BasedPyright Typechecking",
+                "Set `typeCheckingMode` of BasedPyright",
+                typecheckingPicker }
+            })
+        end
+    end,
+}

+ 7 - 0
.config/nvim/lsp/bash-language-server.lua

@@ -0,0 +1,7 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'bash-language-server', 'start' },
+    settings = {},
+    filetypes = { 'bash', 'sh', 'zsh' },
+    single_file_support = true,
+}

+ 26 - 0
.config/nvim/lsp/deno.lua

@@ -0,0 +1,26 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'deno', 'lsp' },
+    cmd_env = { NO_COLOR = true },
+    filetypes = {
+        'javascript',
+        'javascriptreact',
+        'javascript.jsx',
+        'typescript',
+        'typescriptreact',
+        'typescript.tsx',
+    },
+    root_markers = { "deno.json", "deno.jsonc" },
+    settings = {
+        deno = {
+            enable = true,
+            suggest = {
+                imports = {
+                    hosts = {
+                        ['https://deno.land'] = true,
+                    },
+                },
+            },
+        },
+    }
+}

+ 13 - 0
.config/nvim/lsp/gopls.lua

@@ -0,0 +1,13 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'gopls' },
+    filetypes = { 'go', 'gomod', 'gowork', 'gotmpl' },
+    single_file_support = true,
+    settings = {
+        gopls = {
+            analyses = { unusedparams = true },
+            staticcheck = true,
+            gofumpt = true
+        }
+    }
+}

+ 33 - 0
.config/nvim/lsp/lua-language-server.lua

@@ -0,0 +1,33 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { "lua-language-server" },
+    filetypes = { "lua" },
+    root_markers = { ".luarc.json", ".luarc.jsonc" },
+    on_attach = function(_, _)
+        if pcall(require, "save-formatter") then
+            local formatter = require("save-formatter")
+            formatter.enable(vim.tbl_deep_extend("force", formatter.enabled(), {
+                ["lua-language-server"] = true
+            }))
+        end
+    end,
+    settings = {
+        Lua = {
+            runtime = { version = "LuaJIT" },
+            telemetry = { enable = false },
+            hint = { enable = true },
+            workspace = {
+                checkThirdParty = false,
+                library = { vim.env.VIMRUNTIME }
+            },
+            format = {
+                enable = true,
+                defaultConfig = {
+                    indent_style = "space",
+                    indent_size = "4",
+                    max_line_length = "80",
+                }
+            },
+        }
+    }
+}

+ 6 - 0
.config/nvim/lsp/marksman.lua

@@ -0,0 +1,6 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { "marksman", "server" },
+    filetypes = { 'markdown', 'markdown.mdx' },
+    single_file_support = true
+}

+ 18 - 0
.config/nvim/lsp/ruff.lua

@@ -0,0 +1,18 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'ruff', 'server' },
+    filetypes = { 'python' },
+    root_markers = {
+        'pyproject.toml',
+        'setup.py',
+        'setup.cfg',
+        'requirements.txt',
+        'Pipfile',
+        'pyrightconfig.json',
+    },
+    single_file_support = true,
+    settings = {},
+    on_attach = function(client, _)
+        client.server_capabilities.hoverProvider = false
+    end
+}

+ 6 - 0
.config/nvim/lsp/rust_analyzer.lua

@@ -0,0 +1,6 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'rust-analyzer' },
+    filetypes = { 'rust' },
+    single_file_support = true
+}

+ 6 - 0
.config/nvim/lsp/sql-language-server.lua

@@ -0,0 +1,6 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'sql-language-server', 'up', '--method', 'stdio' },
+    filetypes = { 'sql', 'mysql' },
+    settings = {},
+}

+ 6 - 0
.config/nvim/lsp/taplo.lua

@@ -0,0 +1,6 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'taplo', 'lsp', 'stdio' },
+    filetypes = { 'toml' },
+    single_file_support = true,
+}

+ 27 - 0
.config/nvim/lsp/typescript-language-server.lua

@@ -0,0 +1,27 @@
+local inlayHints = {
+    includeInlayEnumMemberValueHints = true,
+    includeInlayFunctionLikeReturnTypeHints = true,
+    includeInlayFunctionParameterTypeHints = true,
+    includeInlayParameterNameHints = "all",
+    includeInlayParameterNameHintsWhenArgumentMatchesName = true,
+    includeInlayPropertyDeclarationTypeHints = true,
+    includeInlayVariableTypeHints = true
+}
+
+--- @type vim.lsp.ClientConfig
+return {
+    init_options = { hostInfo = 'neovim' },
+    cmd = { 'typescript-language-server', '--stdio' },
+    filetypes = {
+        'javascript',
+        'javascriptreact',
+        'javascript.jsx',
+        'typescript',
+        'typescriptreact',
+        'typescript.tsx',
+    },
+    root_markers = { 'tsconfig.json', 'jsconfig.json', 'package.json' },
+    javascript = { inlayHints = inlayHints },
+    typescript = { inlayHints = inlayHints },
+    single_file_support = false
+}

+ 12 - 0
.config/nvim/lsp/vscode-css-language-server.lua

@@ -0,0 +1,12 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'vscode-css-language-server', '--stdio' },
+    filetypes = { 'css', 'scss', 'less' },
+    init_options = { provideFormatter = true },
+    single_file_support = true,
+    settings = {
+        css = { validate = true },
+        scss = { validate = true },
+        less = { validate = true },
+    },
+}

+ 12 - 0
.config/nvim/lsp/vscode-html-language-server.lua

@@ -0,0 +1,12 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'vscode-html-language-server', '--stdio' },
+    filetypes = { 'html', 'templ' },
+    single_file_support = true,
+    settings = {},
+    init_options = {
+        provideFormatter = true,
+        embeddedLanguages = { css = true, javascript = true },
+        configurationSection = { 'html', 'css', 'javascript' },
+    },
+}

+ 9 - 0
.config/nvim/lsp/vscode-json-language-server.lua

@@ -0,0 +1,9 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'vscode-json-language-server', '--stdio' },
+    filetypes = { 'json', 'jsonc' },
+    init_options = {
+        provideFormatter = true,
+    },
+    single_file_support = true,
+}

+ 9 - 0
.config/nvim/lsp/yaml-language-server.lua

@@ -0,0 +1,9 @@
+--- @type vim.lsp.ClientConfig
+return {
+    cmd = { 'yaml-language-server', '--stdio' },
+    filetypes = { 'yaml', 'yaml.docker-compose', 'yaml.gitlab' },
+    single_file_support = true,
+    settings = {
+        redhat = { telemetry = { enabled = false } },
+    },
+}

+ 3 - 7
.config/nvim/lua/plugins/command-palette.lua

@@ -87,13 +87,9 @@ return {
         local formatter = require("save-formatter")
         formatter.setup()
         vim.keymap.set("n", "Sw",
-            function() vim.api.nvim_exec("w", true) end)
-        local palette = require("command-palette")
-
-        palette.add({
-            {
-                "Save Formatter", "Automatic formatting on save", formatter
-                .picker }
+            function() vim.api.nvim_exec2("write", {}) end)
+        require("command-palette").add({ {
+            "Save Formatter", "Configure automatic formatters", formatter.picker }
         })
     end
 }

+ 11 - 0
.config/nvim/lua/plugins/completion.lua

@@ -52,5 +52,16 @@ return {
                 { name = "cmdline", option = { ignore_cmds = { "Man", "!" } } }
             })
         })
+
+        vim.api.nvim_create_autocmd("LspAttach", {
+            callback = function(args)
+                local client = vim.lsp.get_client_by_id(args.data.client_id)
+                if client == nil then return end
+                if client:supports_method('textDocument/completion') then
+                    vim.lsp.completion.enable(true, client.id, args.buf,
+                        { autotrigger = false })
+                end
+            end
+        })
     end
 }

+ 0 - 42
.config/nvim/lua/plugins/lsp.lua

@@ -1,42 +0,0 @@
-return {
-    {
-        "neovim/nvim-lspconfig",
-        enabled = true,
-        lazy = false,
-        config = function(_, _)
-            -- Configuration on attach
-            vim.api.nvim_create_autocmd("LspAttach", {
-                group = vim.api.nvim_create_augroup("UserLspConfig", {}),
-                callback = function(args)
-                    if vim.treesitter ~= nil and type(vim.treesitter.highlighter.active[args.buf]) ~= "nil" then
-                        local client = vim.lsp.get_client_by_id(args.data
-                            .client_id)
-                        client.server_capabilities.semanticTokensProvider = nil
-                    end
-
-                    vim.keymap.set("n", "SR", vim.lsp.buf.rename)
-                    vim.keymap.set("n", "Sx", vim.lsp.buf.code_action)
-                    vim.keymap.set("n", "SI", function()
-                        local diagnostic_config = vim.diagnostic.config()
-                        if (vim.lsp.inlay_hint.is_enabled()) then
-                            vim.lsp.inlay_hint.enable(false)
-                            vim.diagnostic.config({
-                                virtual_lines = {
-                                    current_line = true,
-                                    format = diagnostic_config.format
-                                }
-                            })
-                        else
-                            vim.lsp.inlay_hint.enable(true)
-                            vim.diagnostic.config({
-                                virtual_lines = true,
-                                format = diagnostic_config.format
-                            })
-                        end
-                    end)
-                    vim.keymap.set("n", "<Space>", vim.lsp.buf.hover)
-                end
-            })
-        end
-    }
-}

+ 0 - 4
.config/nvim/lua/plugins/proj-conf.lua

@@ -2,10 +2,6 @@ return {
     dir = vim.fn.stdpath("config") .. "/custom/proj-conf",
     name = "proj-conf",
     dependencies = {
-        "nvim-telescope/telescope.nvim",
-        "neovim/nvim-lspconfig",
-        "hrsh7th/nvim-cmp",
-        "command-palette",
         "telescope-shroud"
     },
     enabled = true,

+ 10 - 0
.config/nvim/lua/plugins/treesitter.lua

@@ -16,6 +16,16 @@ return {
             })
             vim.opt.foldmethod = "expr"
             vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
+
+            vim.api.nvim_create_autocmd("LspAttach", {
+                callback = function(args)
+                    local client = vim.lsp.get_client_by_id(args.data.client_id)
+                    if vim.treesitter ~= nil and client ~= nil and
+                        type(vim.treesitter.highlighter.active[args.buf]) ~= "nil" then
+                        client.server_capabilities.semanticTokensProvider = nil
+                    end
+                end
+            })
         end,
         build = function()
             require("nvim-treesitter.install").update({ with_sync = true })