diff --git a/nvim/.config/nvim/after/ftplugin/c.lua b/nvim/.config/nvim/after/ftplugin/c.lua index 87717f2..6ece482 100644 --- a/nvim/.config/nvim/after/ftplugin/c.lua +++ b/nvim/.config/nvim/after/ftplugin/c.lua @@ -1,22 +1,4 @@ -local default_capabilities = { - textDocument = { - completion = { - editsNearCursor = true, - }, - }, - offsetEncoding = { 'utf-8', 'utf-16' }, -} +local lsp_utils = require('lsp-utils') +local clangd_config = lsp_utils.clangd_config() -local root_files = { '.clangd', '.clang-tidy', '.clang-format', 'compile_commands.json', 'compile_flags.txt' } -local clangd_cmd = { "clangd", "--background-index", "--pch-storage=memory", "--clang-tidy", "--header-insertion=never" } -local path = vim.fs.find(root_files, { type = "file" }) -local root = vim.fs.dirname(path[1]) - -vim.lsp.start({ - name = "clangd", - cmd = clangd_cmd, - root_dir = root, - filetypes = { 'c', 'cpp' }, - single_file_support = true, - capabilities = default_capabilities, -}) +vim.lsp.start(clangd_config) diff --git a/nvim/.config/nvim/after/ftplugin/go.lua b/nvim/.config/nvim/after/ftplugin/go.lua index 4625ea2..b1999b3 100644 --- a/nvim/.config/nvim/after/ftplugin/go.lua +++ b/nvim/.config/nvim/after/ftplugin/go.lua @@ -1,9 +1,4 @@ -local path = vim.fs.find({ 'go.mod', 'go.work', '.git' }, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local gopls_config = lsp_utils.gopls_config() -vim.lsp.start({ - name = "gopls", - cmd = { "gopls" }, - root_dir = root, - filetypes = { 'go', 'gomod', 'gowork', 'gotmpl' }, -}) +vim.lsp.start(gopls_config) diff --git a/nvim/.config/nvim/after/ftplugin/haskell.lua b/nvim/.config/nvim/after/ftplugin/haskell.lua index 23fcbab..d0573c7 100644 --- a/nvim/.config/nvim/after/ftplugin/haskell.lua +++ b/nvim/.config/nvim/after/ftplugin/haskell.lua @@ -1,12 +1,4 @@ -local root_files = { 'hie.yaml', 'stack.yaml', 'cabal.project', '*.cabal', 'package.yaml' } -local path = vim.fs.find(root_files, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local hls_config = lsp_utils.hls_config() -vim.lsp.start({ - name = "hls", - cmd = { 'haskell-language-server-wrapper', '--lsp' }, - root_dir = root, - filetypes = { 'haskell' }, - single_file_support = true, - settings = { haskell = { formattingProvider = 'ormolu' } }, -}) +vim.lsp.start(hls_config) diff --git a/nvim/.config/nvim/after/ftplugin/javascript.lua b/nvim/.config/nvim/after/ftplugin/javascript.lua index cde7bc2..34850a1 100644 --- a/nvim/.config/nvim/after/ftplugin/javascript.lua +++ b/nvim/.config/nvim/after/ftplugin/javascript.lua @@ -1,37 +1,7 @@ -local root_files = { 'jsconfig.json', 'tsconfig.json', 'package.json' } -local file_types = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx' } -local path = vim.fs.find(root_files, { type = "file" }) +local lsp_utils = require('lsp-utils') +local tsserver_config = lsp_utils.tsserver_config() -vim.lsp.start({ - name = "tsserver", - cmd = { "typescript-language-server", "--stdio" }, - root_dir = vim.fs.dirname(path[1]), - filetypes = file_types, - settings = { - typescript = { - inlayHints = { - includeInlayParameterNameHints = 'all', - includeInlayParameterNameHintsWhenArgumentMatchesName = false, - includeInlayFunctionParameterTypeHints = true, - includeInlayVariableTypeHints = true, - includeInlayPropertyDeclarationTypeHints = true, - includeInlayFunctionLikeReturnTypeHints = true, - includeInlayEnumMemberValueHints = true, - } - }, - javascript = { - inlayHints = { - includeInlayParameterNameHints = 'all', - includeInlayParameterNameHintsWhenArgumentMatchesName = false, - includeInlayFunctionParameterTypeHints = true, - includeInlayVariableTypeHints = true, - includeInlayPropertyDeclarationTypeHints = true, - includeInlayFunctionLikeReturnTypeHints = true, - includeInlayEnumMemberValueHints = true, - } - } - } -}) +vim.lsp.start(tsserver_config) vim.keymap.set('n', 'gq', "!npx prettier --write %:p:e", { noremap=true, silent=true, buffer=0 }) diff --git a/nvim/.config/nvim/after/ftplugin/python.lua b/nvim/.config/nvim/after/ftplugin/python.lua index a634e16..b22e0e6 100644 --- a/nvim/.config/nvim/after/ftplugin/python.lua +++ b/nvim/.config/nvim/after/ftplugin/python.lua @@ -1,26 +1,7 @@ -local root_files = { 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile' } -local path = vim.fs.find(root_files, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local jedi_config = lsp_utils.jedi_config() -vim.lsp.start({ - name = "jedi-language-server", - cmd = { "jedi-language-server" }, - filetypes = { 'python' }, - root_dir = root, - init_options = { - completion = { - resolveEagerly = true, - }, - jediSettings = { - caseInsensitiveCompletion = false, - }, - workspace = { - symbols = { - maxSymbols = 50 - }, - }, - }, -}) +vim.lsp.start(jedi_config) vim.bo.textwidth = 0 vim.bo.formatprg = "black -q -" diff --git a/nvim/.config/nvim/after/ftplugin/racket.lua b/nvim/.config/nvim/after/ftplugin/racket.lua index eac0abd..d4e13ea 100644 --- a/nvim/.config/nvim/after/ftplugin/racket.lua +++ b/nvim/.config/nvim/after/ftplugin/racket.lua @@ -1,10 +1,4 @@ -local path = vim.fs.find({ '.git' }, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local scheme_config = lsp_utils.scheme_config() -vim.lsp.start({ - name = "racket-langserver", - cmd = { 'racket', '--lib', 'racket-langserver' }, - filetypes = { 'scheme', 'racket' }, - root_dir = root, - single_file_support = true, -}) +vim.lsp.start(scheme_config) diff --git a/nvim/.config/nvim/after/ftplugin/rust.lua b/nvim/.config/nvim/after/ftplugin/rust.lua index a67b799..6f8fca5 100644 --- a/nvim/.config/nvim/after/ftplugin/rust.lua +++ b/nvim/.config/nvim/after/ftplugin/rust.lua @@ -1,21 +1,7 @@ -local path = vim.fs.find({ "Cargo.toml" }, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local ra_config = lsp_utils.ra_config() -vim.lsp.start({ - name = "rust-analyzer", - cmd = { "rust-analyzer" }, - root_dir = root, - settings = { - ["rust-analyzer"] = { - procMacro = { - enable = true - }, - checkOnSave = { - command = "clippy" - }, - }, - }, -}) +vim.lsp.start(ra_config) vim.cmd.compiler('cargo') vim.bo.makeprg = 'cargo build --all' diff --git a/nvim/.config/nvim/after/ftplugin/scheme.lua b/nvim/.config/nvim/after/ftplugin/scheme.lua index eac0abd..d4e13ea 100644 --- a/nvim/.config/nvim/after/ftplugin/scheme.lua +++ b/nvim/.config/nvim/after/ftplugin/scheme.lua @@ -1,10 +1,4 @@ -local path = vim.fs.find({ '.git' }, { type = "file" }) -local root = vim.fs.dirname(path[1]) +local lsp_utils = require('lsp-utils') +local scheme_config = lsp_utils.scheme_config() -vim.lsp.start({ - name = "racket-langserver", - cmd = { 'racket', '--lib', 'racket-langserver' }, - filetypes = { 'scheme', 'racket' }, - root_dir = root, - single_file_support = true, -}) +vim.lsp.start(scheme_config) diff --git a/nvim/.config/nvim/after/ftplugin/sh.lua b/nvim/.config/nvim/after/ftplugin/sh.lua index 77bd3ca..8ce66b1 100644 --- a/nvim/.config/nvim/after/ftplugin/sh.lua +++ b/nvim/.config/nvim/after/ftplugin/sh.lua @@ -1,14 +1,4 @@ -vim.lsp.start({ - name = "bashls", - cmd = { 'bash-language-server', 'start' }, - root_dir = vim.fn.getcwd(), - filetypes = { 'sh' }, - single_file_support = true, - cmd_env = { - -- Prevent recursive scanning which will cause issues when opening a file - -- directly in the home directory (e.g. ~/foo.sh). - -- - -- Default upstream pattern is "**/*@(.sh|.inc|.bash|.command)". - GLOB_PATTERN = vim.env.GLOB_PATTERN or '*@(.sh|.inc|.bash|.command)', - }, -}) +local lsp_utils = require('lsp-utils') +local bashls_config = lsp_utils.bashls_config() + +vim.lsp.start(bashls_config) diff --git a/nvim/.config/nvim/after/ftplugin/typescript.lua b/nvim/.config/nvim/after/ftplugin/typescript.lua index cde7bc2..24f80cd 100644 --- a/nvim/.config/nvim/after/ftplugin/typescript.lua +++ b/nvim/.config/nvim/after/ftplugin/typescript.lua @@ -1,37 +1,7 @@ -local root_files = { 'jsconfig.json', 'tsconfig.json', 'package.json' } -local file_types = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx' } -local path = vim.fs.find(root_files, { type = "file" }) +local lsp_utils = require('lsp-utils') +local typescript_config = lsp_utils.typescript_config() -vim.lsp.start({ - name = "tsserver", - cmd = { "typescript-language-server", "--stdio" }, - root_dir = vim.fs.dirname(path[1]), - filetypes = file_types, - settings = { - typescript = { - inlayHints = { - includeInlayParameterNameHints = 'all', - includeInlayParameterNameHintsWhenArgumentMatchesName = false, - includeInlayFunctionParameterTypeHints = true, - includeInlayVariableTypeHints = true, - includeInlayPropertyDeclarationTypeHints = true, - includeInlayFunctionLikeReturnTypeHints = true, - includeInlayEnumMemberValueHints = true, - } - }, - javascript = { - inlayHints = { - includeInlayParameterNameHints = 'all', - includeInlayParameterNameHintsWhenArgumentMatchesName = false, - includeInlayFunctionParameterTypeHints = true, - includeInlayVariableTypeHints = true, - includeInlayPropertyDeclarationTypeHints = true, - includeInlayFunctionLikeReturnTypeHints = true, - includeInlayEnumMemberValueHints = true, - } - } - } -}) +vim.lsp.start(typescript_config) vim.keymap.set('n', 'gq', "!npx prettier --write %:p:e", { noremap=true, silent=true, buffer=0 }) diff --git a/nvim/.config/nvim/lua/lsp-utils.lua b/nvim/.config/nvim/lua/lsp-utils.lua new file mode 100644 index 0000000..20e7a91 --- /dev/null +++ b/nvim/.config/nvim/lua/lsp-utils.lua @@ -0,0 +1,294 @@ +local M = {} + +-- All configuration is taken from nvim-lspconfig except for may be minor changes +-- +-- Deno specific functions +local buf_cache = function(bufnr, client) + local params = {} + params['referrer'] = { uri = vim.uri_from_bufnr(bufnr) } + params['uris'] = {} + client.request_sync('deno/cache', params) +end + +local virtual_text_document_handler = function(uri, res, client) + if not res then + return nil + end + + local lines = vim.split(res.result, '\n') + local bufnr = vim.uri_to_bufnr(uri) + + local current_buf = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + if #current_buf ~= 0 then + return nil + end + + vim.api.nvim_buf_set_lines(bufnr, 0, -1, nil, lines) + + vim.api.nvim_buf_set_option(bufnr, 'readonly' , true) + vim.api.nvim_buf_set_option(bufnr, 'modified' , false) + vim.api.nvim_buf_set_option(bufnr, 'modifiable', false) + + vim.lsp.buf_attach_client(bufnr, client.id) +end + +local virtual_text_document = function(uri, client) + local params = { + textDocument = { + uri = uri, + }, + } + local result = client.request_sync('deno/virtualTextDocument', params) + + virtual_text_document_handler(uri, result, client) +end + +local denols_handler = function(err, result, ctx) + if not result or vim.tbl_isempty(result) then + return nil + end + + local client = vim.lsp.get_client_by_id(ctx.client_id) + + for _, res in pairs(result) do + local uri = res.uri or res.targetUri + + if uri:match '^deno:' then + virtual_text_document(uri, client) + res['uri'] = uri + res['targetUri'] = uri + end + end + + vim.lsp.handlers[ctx.method](err, result, ctx) +end +-- End of deno specific functions + +local get_root_directory = function(root_files) + local path = vim.fs.find(root_files, { type = "file" }) + + return vim.fs.dirname(path[1]) +end + +function M.bashls_config() + local root_directory = vim.fn.getcwd() + + return { + name = "bashls", + cmd = { 'bash-language-server', 'start' }, + root_dir = root_directory, + filetypes = { 'sh' }, + single_file_support = true, + cmd_env = { + -- Prevent recursive scanning which will cause issues when opening a file + -- directly in the home directory (e.g. ~/foo.sh). + -- + -- Default upstream pattern is "**/*@(.sh|.inc|.bash|.command)". + GLOB_PATTERN = vim.env.GLOB_PATTERN or '*@(.sh|.inc|.bash|.command)', + }, + } +end + +function M.clangd_config() + local default_capabilities = { + textDocument = { + completion = { + editsNearCursor = true, + }, + }, + offsetEncoding = { 'utf-8', 'utf-16' }, + } + local root_files = { '.clangd', '.clang-tidy', '.clang-format', 'compile_commands.json', 'compile_flags.txt' } + local clangd_cmd = { "clangd", "--background-index", "--pch-storage=memory", "--clang-tidy", "--header-insertion=never" } + local root_directory = get_root_directory(root_files) + + return { + name = "clangd", + cmd = clangd_cmd, + root_dir = root_directory, + filetypes = { 'c', 'cpp' }, + single_file_support = true, + capabilities = default_capabilities, + } +end + + +function M.deno_config() + local root_files = { 'deno.json', 'deno.jsonc' } + local file_types = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx' } + local root_directory = get_root_directory(root_files) + + return { + cmd = { 'deno', 'lsp' }, + filetypes = file_types, + root_dir = root_directory, + init_options = { + enable = true, + unstable = false, + }, + handlers = { + ['textDocument/definition'] = denols_handler, + ['textDocument/typeDefinition'] = denols_handler, + ['textDocument/references'] = denols_handler, + ['workspace/executeCommand'] = function(err, result, context) + if context.params.command == 'deno.cache' then + buf_cache(context.bufnr, vim.lsp.get_client_by_id(context.client_id)) + else + vim.lsp.handlers[context.method](err, result, context) + end + end, + }, + commands = { + DenolsCache = { + function() + local clients = vim.lsp.get_active_clients() + for _, client in ipairs(clients) do + if client.name == 'denols' then + buf_cache(0, client) + break + end + end + end, + description = 'Cache a module and all of its dependencies.', + }, + } + } +end + +function M.gopls_config() + local root_files = { 'go.mod', 'go.work', '.git' } + local root_directory = get_root_directory(root_files) + + return { + name = "gopls", + cmd = { "gopls" }, + root_dir = root_directory, + filetypes = { 'go', 'gomod', 'gowork', 'gotmpl' }, + } +end + +function M.hls_config() + local root_files = { 'hie.yaml', 'stack.yaml', 'cabal.project', '*.cabal', 'package.yaml' } + local root_directory = get_root_directory(root_files) + + return { + name = "hls", + cmd = { 'haskell-language-server-wrapper', '--lsp' }, + root_dir = root_directory, + filetypes = { 'haskell' }, + single_file_support = true, + settings = { haskell = { formattingProvider = 'ormolu' } }, + } +end + +function M.jedi_config() + local root_files = { 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile' } + local root_directory = get_root_directory(root_files) + + return { + name = "jedi-language-server", + cmd = { "jedi-language-server" }, + filetypes = { 'python' }, + root_dir = root_directory, + init_options = { + completion = { + resolveEagerly = true, + }, + jediSettings = { + caseInsensitiveCompletion = false, + }, + workspace = { + symbols = { + maxSymbols = 50 + }, + }, + }, + } +end + +function M.ra_config() + local root_files = { "Cargo.toml" } + local root_directory = get_root_directory(root_files) + + return { + name = "rust-analyzer", + cmd = { "rust-analyzer" }, + root_dir = root_directory, + settings = { + ["rust-analyzer"] = { + procMacro = { + enable = true + }, + checkOnSave = { + command = "clippy" + }, + }, + }, + } +end + +function M.scheme_config() + local root_files = { '.git' } + local root_directory = get_root_directory(root_files) + + return { + name = "racket-langserver", + cmd = { 'racket', '--lib', 'racket-langserver' }, + filetypes = { 'scheme', 'racket' }, + root_dir = root_directory, + single_file_support = true, + } +end + +function M.tsserver_config() + local root_files = { 'jsconfig.json', 'tsconfig.json', 'package.json' } + local file_types = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx' } + local root_directory = get_root_directory(root_files) + + return { + name = "tsserver", + cmd = { "typescript-language-server", "--stdio" }, + root_dir = root_directory, + filetypes = file_types, + settings = { + typescript = { + inlayHints = { + includeInlayParameterNameHints = 'all', + includeInlayParameterNameHintsWhenArgumentMatchesName = false, + includeInlayFunctionParameterTypeHints = true, + includeInlayVariableTypeHints = true, + includeInlayPropertyDeclarationTypeHints = true, + includeInlayFunctionLikeReturnTypeHints = true, + includeInlayEnumMemberValueHints = true, + } + }, + javascript = { + inlayHints = { + includeInlayParameterNameHints = 'all', + includeInlayParameterNameHintsWhenArgumentMatchesName = false, + includeInlayFunctionParameterTypeHints = true, + includeInlayVariableTypeHints = true, + includeInlayPropertyDeclarationTypeHints = true, + includeInlayFunctionLikeReturnTypeHints = true, + includeInlayEnumMemberValueHints = true, + } + } + } + } +end + +function M.typescript_config() + local root_files = { 'jsconfig.json', 'tsconfig.json', 'package.json' } + local root_dir = get_root_directory(root_files) + + -- Look for npm based typescript files first and if found return tsserver + -- configuration, else return deno configuration. This allows us to work + -- with both npm and deno based typescript projects. + if root_dir then + return M.tsserver_config() + else + return M.deno_config() + end +end + +return M