local function lsp_highlight_document(client) if client.server_capabilities.document_highlight then vim.api.nvim_create_augroup("lsp_document_highlight", { clear = true }) vim.api.nvim_create_autocmd("CursorHold", { group = "lsp_document_highlight", buffer = 0, callback = function() vim.lsp.buf.document_highlight() end, }) vim.api.nvim_create_autocmd("CursorMoved", { group = "lsp_document_highlight", buffer = 0, callback = function() vim.lsp.buf.clear_references() end, }) end end local function lsp_keymaps(bufnr) local opts = { noremap = true, silent = true } vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "lua vim.lsp.buf.definition()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "lua vim.lsp.buf.declaration()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "lua vim.lsp.buf.implementation()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "K", "lua vim.lsp.buf.hover()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "", "lua vim.lsp.buf.signature_help()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "lua vim.lsp.buf.references()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "[d", 'lua vim.diagnostic.goto_prev({border="rounded"})', opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "]d", 'lua vim.diagnostic.goto_next({border="rounded"})', opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "lr", "lua vim.lsp.buf.rename()", opts) vim.api.nvim_buf_set_keymap(bufnr, "n", "q", "lua vim.diagnostic.setloclist()", opts) vim.api.nvim_buf_set_keymap( bufnr, "n", "td", "lua require 'telescope.builtin'.diagnostics()", opts ) vim.api.nvim_buf_set_keymap( bufnr, "n", "tr", "lua require 'telescope.builtin'.lsp_references()", opts ) end local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp") if not status_ok then print("Failed to require cmp_nvim_lsp") return end local client_capabilities = vim.lsp.protocol.make_client_capabilities() local capabilities = cmp_nvim_lsp.default_capabilities(client_capabilities) capabilities.offsetEncoding = { "utf-8", "utf-16" } --- @brief --- --- 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: --- ```lua --- vim.lsp.config('eslint', { --- --- ... --- on_attach = function(client, bufnr) --- vim.api.nvim_create_autocmd("BufWritePre", { --- buffer = bufnr, --- command = "EslintFixAll", --- }) --- end, --- }) --- ``` --- --- 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` local lsp = vim.lsp return { cmd = { 'vscode-eslint-language-server', '--stdio' }, filetypes = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx', 'vue', 'svelte', 'astro', }, on_attach = function(client, bufnr) lsp_keymaps(bufnr) lsp_highlight_document(client) vim.api.nvim_buf_create_user_command(0, 'LspEslintFixAll', function() local bufnr = vim.api.nvim_get_current_buf() client:exec_cmd({ title = 'Fix all Eslint errors for current buffer', command = 'eslint.applyAllFixes', arguments = { { uri = vim.uri_from_bufnr(bufnr), version = lsp.util.buf_versions[bufnr], }, }, }, { bufnr = bufnr }) end, {}) vim.api.nvim_create_autocmd("BufWritePre", { buffer = bufnr, command = "LspEslintFixAll", }) end, -- https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-file-formats root_dir = function(bufnr, on_dir) local root_file_patterns = { '.eslintrc', '.eslintrc.js', '.eslintrc.cjs', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc.json', 'eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs', 'eslint.config.ts', 'eslint.config.mts', 'eslint.config.cts', '.git', 'package.json', 'package-lock.json', '.prettierrc', } local fname = vim.api.nvim_buf_get_name(bufnr) local root_dir = vim.fs.dirname(vim.fs.find(root_file_patterns, { path = fname, upward = true })[1]) on_dir(root_dir) end, -- Refer to https://github.com/Microsoft/vscode-eslint#settings-options for documentation. settings = { validate = 'on', packageManager = nil, useESLintClass = false, experimental = { useFlatConfig = false, }, codeActionOnSave = { enable = false, mode = 'all', }, format = true, quiet = false, onIgnoredFiles = 'off', rulesCustomizations = {}, run = 'onType', problems = { shortenToSingleLine = false, }, -- 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, }, }, }, before_init = function(_, config) -- 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). local root_dir = config.root_dir if root_dir then config.settings = config.settings or {} config.settings.workspaceFolder = { uri = root_dir, name = vim.fn.fnamemodify(root_dir, ':t'), } -- Support flat config local flat_config_files = { 'eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs', 'eslint.config.ts', 'eslint.config.mts', 'eslint.config.cts', } for _, file in ipairs(flat_config_files) do if vim.fn.filereadable(root_dir .. '/' .. file) == 1 then config.settings.experimental = config.settings.experimental or {} config.settings.experimental.useFlatConfig = true break end end -- Support Yarn2 (PnP) projects local pnp_cjs = root_dir .. '/.pnp.cjs' local pnp_js = root_dir .. '/.pnp.js' if vim.uv.fs_stat(pnp_cjs) or vim.uv.fs_stat(pnp_js) then local cmd = config.cmd config.cmd = vim.list_extend({ 'yarn', 'exec' }, cmd) end end end, handlers = { ['eslint/openDoc'] = function(_, result) if result then vim.ui.open(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, }, }