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