diff --git a/lua/lazyvim/config/init.lua b/lua/lazyvim/config/init.lua index 63355fcc..5a4020b6 100644 --- a/lua/lazyvim/config/init.lua +++ b/lua/lazyvim/config/init.lua @@ -348,13 +348,81 @@ function M.init() M.json.load() end ----@alias LazyVimDefault {name: string, extra: string, enabled?: boolean, origin?: "global" | "default" | "extra" } - +---@alias LazyVimDefault {name: string, group: string, extra: string, import: string, enabled?: boolean, origin?: "global" | "default" | "extra" } +--- local default_extras ---@type table + +---@param name string +---@param extras LazyVimDefault[] +function M.register_defaults(name, extras) + assert(default_extras, "defaults should be loaded by now, this should never happen") + local valid = vim.tbl_map(function(extra) + return extra.name + end, extras) --[[@as string[] ]] + + local origin = "default" + local ret ---@type LazyVimDefault? + local use ---@type string? + + local global = vim.g["lazyvim_" .. name] + if vim.tbl_contains(valid, global) then + origin = "global" -- was set by the user in their config + use = global + else + if global and global ~= "auto" then + vim.notify( + ("Invalid value for `vim.g.lazyvim_%s`: `%s`\nValid options are: %s"):format( + name, + global, + table.concat(valid, ", ") + ), + vim.log.levels.ERROR, + { title = "LazyVim" } + ) + end + for _, extra in ipairs(extras) do + if LazyVim.has_extra(extra.extra) then + use = extra.name -- was imported by the user in their lazy spec or added by LazyExtras + origin = "extra" + break + end + end + end + + use = use or valid[1] -- fallback to the first one if nothing was set + + for _, extra in ipairs(extras) do + local import = "lazyvim.plugins.extras." .. extra.extra + extra = vim.deepcopy(extra) + extra.enabled = extra.name == use + extra.import = import + extra.group = name + if extra.enabled then + extra.origin = origin + ret = extra + end + default_extras[import] = extra + end + + return assert(ret, "One of the extras should be enabled, this should never happen") +end + +---@param group string +---@return LazyVimDefault? +function M.get_default(group) + for _, extra in pairs(M.get_defaults()) do + if extra.group == group and extra.enabled then + return extra + end + end +end + function M.get_defaults() if default_extras then return default_extras end + default_extras = {} + ---@type table local checks = { picker = { @@ -378,36 +446,10 @@ function M.get_defaults() table.insert(checks.explorer, 1, table.remove(checks.explorer, 2)) end - default_extras = {} - for name, check in pairs(checks) do - local valid = {} ---@type string[] - for _, extra in ipairs(check) do - if extra.enabled ~= false then - valid[#valid + 1] = extra.name - end - end - local origin = "default" - local use = vim.g["lazyvim_" .. name] - use = vim.tbl_contains(valid, use or "auto") and use or nil - origin = use and "global" or origin - for _, extra in ipairs(use and {} or check) do - if extra.enabled ~= false and LazyVim.has_extra(extra.extra) then - use = extra.name - break - end - end - origin = use and "extra" or origin - use = use or valid[1] - for _, extra in ipairs(check) do - local import = "lazyvim.plugins.extras." .. extra.extra - extra = vim.deepcopy(extra) - extra.enabled = extra.name == use - if extra.enabled then - extra.origin = origin - end - default_extras[import] = extra - end + for name, extras in pairs(checks) do + M.register_defaults(name, extras) end + return default_extras end diff --git a/lua/lazyvim/plugins/extras/lang/typescript/init.lua b/lua/lazyvim/plugins/extras/lang/typescript/init.lua index 5d478314..70a10ae8 100644 --- a/lua/lazyvim/plugins/extras/lang/typescript/init.lua +++ b/lua/lazyvim/plugins/extras/lang/typescript/init.lua @@ -1,11 +1,7 @@ -if lazyvim_docs then - -- LSP Server to use for TypeScript. - vim.g.lazyvim_ts_lsp = "vtsls" -- currently the default - -- Set to "tsgo" to use the new typescript-language-server implementation instead of tsserver. - vim.g.lazyvim_ts_lsp = "tsgo" -end - -local lsp = vim.g.lazyvim_ts_lsp or "vtsls" +local extra = LazyVim.config.register_defaults("ts_lsp", { + { name = "vtsls", extra = "lang.typescript.vtsls" }, + { name = "tsgo", extra = "lang.typescript.tsgo" }, +}) return { recommended = function() @@ -22,200 +18,13 @@ return { }) end, + { import = extra.import }, + -- correctly setup lspconfig - { - "neovim/nvim-lspconfig", - opts = { - -- make sure mason installs the server - servers = { - ---@type lspconfig.settings.tsgo - tsgo = { - -- explicitly add default filetypes, so that we can extend - -- them in related extras - filetypes = { - "javascript", - "javascriptreact", - "javascript.jsx", - "typescript", - "typescriptreact", - "typescript.tsx", - }, - settings = { - typescript = { - inlayHints = { - parameterNames = { - enabled = "literals", - suppressWhenArgumentMatchesName = true, - }, - parameterTypes = { enabled = true }, - variableTypes = { enabled = true }, - propertyDeclarationTypes = { enabled = true }, - functionLikeReturnTypes = { enabled = true }, - enumMemberValues = { enabled = true }, - }, - }, - }, - }, - vtsls = { - -- explicitly add default filetypes, so that we can extend - -- them in related extras - filetypes = { - "javascript", - "javascriptreact", - "javascript.jsx", - "typescript", - "typescriptreact", - "typescript.tsx", - }, - settings = { - complete_function_calls = true, - vtsls = { - enableMoveToFileCodeAction = true, - autoUseWorkspaceTsdk = true, - experimental = { - maxInlayHintLength = 30, - completion = { - enableServerSideFuzzyMatch = true, - }, - }, - }, - typescript = { - updateImportsOnFileMove = { enabled = "always" }, - suggest = { - completeFunctionCalls = true, - }, - inlayHints = { - enumMemberValues = { enabled = true }, - functionLikeReturnTypes = { enabled = true }, - parameterNames = { enabled = "literals" }, - parameterTypes = { enabled = true }, - propertyDeclarationTypes = { enabled = true }, - variableTypes = { enabled = false }, - }, - }, - }, - keys = { - { - "gD", - function() - local win = vim.api.nvim_get_current_win() - local params = vim.lsp.util.make_position_params(win, "utf-16") - LazyVim.lsp.execute({ - command = "typescript.goToSourceDefinition", - arguments = { params.textDocument.uri, params.position }, - open = true, - }) - end, - desc = "Goto Source Definition", - }, - { - "gR", - function() - LazyVim.lsp.execute({ - command = "typescript.findAllFileReferences", - arguments = { vim.uri_from_bufnr(0) }, - open = true, - }) - end, - desc = "File References", - }, - { - "cM", - LazyVim.lsp.action["source.addMissingImports.ts"], - desc = "Add missing imports", - }, - { - "cD", - LazyVim.lsp.action["source.fixAll.ts"], - desc = "Fix all diagnostics", - }, - { - "cV", - function() - LazyVim.lsp.execute({ - title = "Select TypeScript Version", - filter = "vtsls", - command = "typescript.selectTypeScriptVersion", - }) - end, - desc = "Select TS workspace version", - }, - }, - }, - }, - setup = { - --- @deprecated -- tsserver renamed to ts_ls but not yet released, so keep this for now - --- the proper approach is to check the nvim-lspconfig release version when it's released to determine the server name dynamically - tsserver = function() - -- disable tsserver - return true - end, - ts_ls = function() - -- disable tsserver - return true - end, - vtsls = function(_, opts) - Snacks.util.lsp.on({ name = "vtsls" }, function(buffer, client) - client.commands["_typescript.moveToFileRefactoring"] = function(command, ctx) - ---@type string, string, lsp.Range - local action, uri, range = unpack(command.arguments) - - local function move(newf) - client:request("workspace/executeCommand", { - command = command.command, - arguments = { action, uri, range, newf }, - }) - end - - local fname = vim.uri_to_fname(uri) - client:request("workspace/executeCommand", { - command = "typescript.tsserverRequest", - arguments = { - "getMoveToRefactoringFileSuggestions", - { - file = fname, - startLine = range.start.line + 1, - startOffset = range.start.character + 1, - endLine = range["end"].line + 1, - endOffset = range["end"].character + 1, - }, - }, - }, function(_, result) - ---@type string[] - local files = result.body.files - table.insert(files, 1, "Enter new path...") - vim.ui.select(files, { - prompt = "Select move destination:", - format_item = function(f) - return vim.fn.fnamemodify(f, ":~:.") - end, - }, function(f) - if f and f:find("^Enter new path") then - vim.ui.input({ - prompt = "Enter move destination:", - default = vim.fn.fnamemodify(fname, ":h") .. "/", - completion = "file", - }, function(newf) - return newf and move(newf) - end) - elseif f then - move(f) - end - end) - end) - end - end) - -- copy typescript settings to javascript - opts.settings.javascript = - vim.tbl_deep_extend("force", {}, opts.settings.typescript, opts.settings.javascript or {}) - end, - }, - }, - }, - { "neovim/nvim-lspconfig", opts = function(_, opts) + local lsp = extra.name or "vtsls" local servers = { "tsserver", "ts_ls", "vtsls", "tsgo", lsp } for _, server in ipairs(servers) do opts.servers[server] = opts.servers[server] or {} diff --git a/lua/lazyvim/plugins/extras/lang/typescript/tsgo.lua b/lua/lazyvim/plugins/extras/lang/typescript/tsgo.lua new file mode 100644 index 00000000..97f27693 --- /dev/null +++ b/lua/lazyvim/plugins/extras/lang/typescript/tsgo.lua @@ -0,0 +1,58 @@ +return { + recommended = function() + return LazyVim.extras.wants({ + ft = { + "javascript", + "javascriptreact", + "javascript.jsx", + "typescript", + "typescriptreact", + "typescript.tsx", + }, + root = { "tsconfig.json", "package.json", "jsconfig.json" }, + }) + end, + + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = { + -- make sure mason installs the server + servers = { + ---@type lspconfig.settings.tsgo + tsgo = { + -- explicitly add default filetypes, so that we can extend + -- them in related extras + filetypes = { + "javascript", + "javascriptreact", + "javascript.jsx", + "typescript", + "typescriptreact", + "typescript.tsx", + }, + settings = { + typescript = { + inlayHints = { + parameterNames = { + enabled = "literals", + suppressWhenArgumentMatchesName = true, + }, + parameterTypes = { enabled = true }, + variableTypes = { enabled = true }, + propertyDeclarationTypes = { enabled = true }, + functionLikeReturnTypes = { enabled = true }, + enumMemberValues = { enabled = true }, + }, + }, + }, + }, + }, + }, + }, + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = {}, + }, +} diff --git a/lua/lazyvim/plugins/extras/lang/typescript/vtsls.lua b/lua/lazyvim/plugins/extras/lang/typescript/vtsls.lua new file mode 100644 index 00000000..b21c3805 --- /dev/null +++ b/lua/lazyvim/plugins/extras/lang/typescript/vtsls.lua @@ -0,0 +1,174 @@ +return { + recommended = function() + return LazyVim.extras.wants({ + ft = { + "javascript", + "javascriptreact", + "javascript.jsx", + "typescript", + "typescriptreact", + "typescript.tsx", + }, + root = { "tsconfig.json", "package.json", "jsconfig.json" }, + }) + end, + + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = { + -- make sure mason installs the server + servers = { + vtsls = { + -- explicitly add default filetypes, so that we can extend + -- them in related extras + filetypes = { + "javascript", + "javascriptreact", + "javascript.jsx", + "typescript", + "typescriptreact", + "typescript.tsx", + }, + settings = { + complete_function_calls = true, + vtsls = { + enableMoveToFileCodeAction = true, + autoUseWorkspaceTsdk = true, + experimental = { + maxInlayHintLength = 30, + completion = { + enableServerSideFuzzyMatch = true, + }, + }, + }, + typescript = { + updateImportsOnFileMove = { enabled = "always" }, + suggest = { + completeFunctionCalls = true, + }, + inlayHints = { + enumMemberValues = { enabled = true }, + functionLikeReturnTypes = { enabled = true }, + parameterNames = { enabled = "literals" }, + parameterTypes = { enabled = true }, + propertyDeclarationTypes = { enabled = true }, + variableTypes = { enabled = false }, + }, + }, + }, + keys = { + { + "gD", + function() + local win = vim.api.nvim_get_current_win() + local params = vim.lsp.util.make_position_params(win, "utf-16") + LazyVim.lsp.execute({ + command = "typescript.goToSourceDefinition", + arguments = { params.textDocument.uri, params.position }, + open = true, + }) + end, + desc = "Goto Source Definition", + }, + { + "gR", + function() + LazyVim.lsp.execute({ + command = "typescript.findAllFileReferences", + arguments = { vim.uri_from_bufnr(0) }, + open = true, + }) + end, + desc = "File References", + }, + { + "cM", + LazyVim.lsp.action["source.addMissingImports.ts"], + desc = "Add missing imports", + }, + { + "cD", + LazyVim.lsp.action["source.fixAll.ts"], + desc = "Fix all diagnostics", + }, + { + "cV", + function() + LazyVim.lsp.execute({ + title = "Select TypeScript Version", + filter = "vtsls", + command = "typescript.selectTypeScriptVersion", + }) + end, + desc = "Select TS workspace version", + }, + }, + }, + }, + setup = { + vtsls = function(_, opts) + Snacks.util.lsp.on({ name = "vtsls" }, function(buffer, client) + client.commands["_typescript.moveToFileRefactoring"] = function(command, ctx) + ---@type string, string, lsp.Range + local action, uri, range = unpack(command.arguments) + + local function move(newf) + client:request("workspace/executeCommand", { + command = command.command, + arguments = { action, uri, range, newf }, + }) + end + + local fname = vim.uri_to_fname(uri) + client:request("workspace/executeCommand", { + command = "typescript.tsserverRequest", + arguments = { + "getMoveToRefactoringFileSuggestions", + { + file = fname, + startLine = range.start.line + 1, + startOffset = range.start.character + 1, + endLine = range["end"].line + 1, + endOffset = range["end"].character + 1, + }, + }, + }, function(_, result) + ---@type string[] + local files = result.body.files + table.insert(files, 1, "Enter new path...") + vim.ui.select(files, { + prompt = "Select move destination:", + format_item = function(f) + return vim.fn.fnamemodify(f, ":~:.") + end, + }, function(f) + if f and f:find("^Enter new path") then + vim.ui.input({ + prompt = "Enter move destination:", + default = vim.fn.fnamemodify(fname, ":h") .. "/", + completion = "file", + }, function(newf) + return newf and move(newf) + end) + elseif f then + move(f) + end + end) + end) + end + end) + -- copy typescript settings to javascript + opts.settings.javascript = + vim.tbl_deep_extend("force", {}, opts.settings.typescript, opts.settings.javascript or {}) + end, + }, + }, + }, + + -- correctly setup lspconfig + { + "neovim/nvim-lspconfig", + opts = {}, + }, +} diff --git a/lua/lazyvim/util/extras.lua b/lua/lazyvim/util/extras.lua index 06339a92..3a1dce8b 100644 --- a/lua/lazyvim/util/extras.lua +++ b/lua/lazyvim/util/extras.lua @@ -115,6 +115,13 @@ function M.get_extra(source, modname) recommended = M.wants(recommended) end + -- language extras that are disabled because a conflict with another extra is enabled are not recommended + local defaults = LazyVim.config.get_defaults() + local def = defaults[modname] + if def and def.enabled == false and vim.startswith(modname, "lazyvim.plugins.extras.lang.") then + recommended = false + end + ---@type LazyExtra return { source = source,