diff --git a/lua/astrocommunity/pack/typescript-biome/README.md b/lua/astrocommunity/pack/typescript-biome/README.md new file mode 100644 index 000000000..de9b996d2 --- /dev/null +++ b/lua/astrocommunity/pack/typescript-biome/README.md @@ -0,0 +1,31 @@ +# TypeScript Biome Language Pack + +This plugin pack does the following: + +- Adds `typescript`, `javascript`, `tsx`, and `jsdoc` Treesitter parsers +- Adds `vtsls` and `biome` language server +- Adds [JSON language support](../json) +- Adds support for dap for JS +- Adds [nvim-vtsls](https://github.com/yioneko/nvim-vtsls) for language specific tooling +- Adds [package-info.nvim](https://github.com/vuki656/package-info.nvim) for project package management +- Adds [nvim-lsp-file-operations](https://github.com/antosha417/nvim-lsp-file-operations) to handles file imports on rename or move within neo-tree + +## How do I use biome without mason? + +If you have biome as a dependency, then it's recommended to run the LSP through that dependency. This is because the biome linter and formatter +is prone to change between versions meaning your LSP can show different diagnostics compared to the actual version of biome you have installed. + +```lua +return { + "AstroNvim/astrolsp", + servers = { + "biome", + }, + config = { + biome = { + -- Use the package manager of your project: npx, yarn, pnpm, bun... + cmd = { "yarn", "biome", "lsp-proxy" } + } + } +} +``` diff --git a/lua/astrocommunity/pack/typescript-biome/init.lua b/lua/astrocommunity/pack/typescript-biome/init.lua new file mode 100644 index 000000000..f47ac0da3 --- /dev/null +++ b/lua/astrocommunity/pack/typescript-biome/init.lua @@ -0,0 +1,133 @@ +local function decode_json(filename) + -- Open the file in read mode + local file = io.open(filename, "r") + if not file then + return false -- File doesn't exist or cannot be opened + end + + -- Read the contents of the file + local content = file:read "*all" + file:close() + + -- Parse the JSON content + local json_parsed, json = pcall(vim.fn.json_decode, content) + if not json_parsed or type(json) ~= "table" then + return false -- Invalid JSON format + end + return json +end + +local function check_json_key_exists(json, ...) return vim.tbl_get(json, ...) ~= nil end + +local lsp_rooter, prettierrc_rooter +local function has_prettier(bufnr) + if type(bufnr) ~= "number" then bufnr = vim.api.nvim_get_current_buf() end + + local rooter = require "astrocore.rooter" + + if not lsp_rooter then + lsp_rooter = rooter.resolve("lsp", { + ignore = { + servers = function(client) return not vim.tbl_contains({ "eslint" }, client.name) end, + }, + }) + end + + if not prettierrc_rooter then + prettierrc_rooter = rooter.resolve { + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yml", + ".prettierrc.yaml", + ".prettierrc.json5", + ".prettierrc.js", + ".prettierrc.cjs", + "prettier.config.js", + ".prettierrc.mjs", + "prettier.config.mjs", + "prettier.config.cjs", + ".prettierrc.toml", + } + end + + local prettier_dependency = false + + for _, root in ipairs(require("astrocore").list_insert_unique(lsp_rooter(bufnr), { vim.fn.getcwd() })) do + local package_json = decode_json(root .. "/package.json") + if + package_json + and ( + check_json_key_exists(package_json, "dependencies", "prettier") + or check_json_key_exists(package_json, "devDependencies", "prettier") + ) + then + prettier_dependency = true + break + end + end + return prettier_dependency or next(prettierrc_rooter(bufnr)) +end + +local function has_biome() + local bufnr = vim.api.nvim_get_current_buf() + local rooter = require "astrocore.rooter" + + local biomejson_rooter = rooter.resolve { + "biome.json", + } + + return next(biomejson_rooter(bufnr)) +end + +local biome_format_filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact", "json", "jsonc" } +local prettier_format_filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact" } + +local function null_ls_formatter(params) + -- prefer biome for the file types it can format + if has_biome() then return not vim.tbl_contains(biome_format_filetypes, params.filetype) end + + if vim.tbl_contains(prettier_format_filetypes, params.filetype) then return has_prettier(params.bufnr) end + return true +end + +return { + { import = "astrocommunity.pack.typescript.core" }, + { + "williamboman/mason-lspconfig.nvim", + opts = function(_, opts) + opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "biome" }) + end, + }, + { + "jay-babu/mason-null-ls.nvim", + optional = true, + opts = function(_, opts) + if not opts.handlers then opts.handlers = {} end + + opts.handlers.prettierd = function(source_name, methods) + local null_ls = require "null-ls" + for _, method in ipairs(methods) do + null_ls.register(null_ls.builtins[method][source_name].with { runtime_condition = null_ls_formatter }) + end + end + end, + }, + { + "stevearc/conform.nvim", + optional = true, + opts = function(_, opts) + if not opts.formatters_by_ft then opts.formatters_by_ft = {} end + + for _, filetype in ipairs(biome_format_filetypes) do + opts.formatters_by_ft[filetype] = function() + -- let the biome lsp handle this + if has_biome() then return {} end + + if vim.tbl_contains(prettier_format_filetypes, filetype) and has_prettier() then return { "prettierd" } end + + return {} + end + end + end, + }, +} diff --git a/lua/astrocommunity/pack/typescript/core.lua b/lua/astrocommunity/pack/typescript/core.lua new file mode 100644 index 000000000..0dfb23bed --- /dev/null +++ b/lua/astrocommunity/pack/typescript/core.lua @@ -0,0 +1,108 @@ +return { + { import = "astrocommunity.pack.json" }, + { import = "astrocommunity.lsp.nvim-lsp-file-operations" }, + { + "nvim-treesitter/nvim-treesitter", + optional = true, + opts = function(_, opts) + if opts.ensure_installed ~= "all" then + opts.ensure_installed = + require("astrocore").list_insert_unique(opts.ensure_installed, { "javascript", "typescript", "tsx", "jsdoc" }) + end + end, + }, + { + "AstroNvim/astrolsp", + ---@type AstroLSPOpts + opts = { + ---@diagnostic disable: missing-fields + config = { + vtsls = { + settings = { + typescript = { + updateImportsOnFileMove = { enabled = "always" }, + inlayHints = { + parameterNames = { enabled = "all" }, + parameterTypes = { enabled = true }, + variableTypes = { enabled = true }, + propertyDeclarationTypes = { enabled = true }, + functionLikeReturnTypes = { enabled = true }, + enumMemberValues = { enabled = true }, + }, + }, + javascript = { + updateImportsOnFileMove = { enabled = "always" }, + inlayHints = { + parameterNames = { enabled = "literals" }, + parameterTypes = { enabled = true }, + variableTypes = { enabled = true }, + propertyDeclarationTypes = { enabled = true }, + functionLikeReturnTypes = { enabled = true }, + enumMemberValues = { enabled = true }, + }, + }, + vtsls = { + enableMoveToFileCodeAction = true, + }, + }, + }, + }, + }, + }, + { + "williamboman/mason-lspconfig.nvim", + opts = function(_, opts) + opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "vtsls" }) + end, + }, + { + "jay-babu/mason-nvim-dap.nvim", + optional = true, + opts = function(_, opts) + opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "js" }) + end, + }, + { + "WhoIsSethDaniel/mason-tool-installer.nvim", + optional = true, + opts = function(_, opts) + opts.ensure_installed = + require("astrocore").list_insert_unique(opts.ensure_installed, { "vtsls", "js-debug-adapter" }) + end, + }, + { + "vuki656/package-info.nvim", + dependencies = { "MunifTanjim/nui.nvim" }, + opts = {}, + event = "BufRead package.json", + }, + { + "yioneko/nvim-vtsls", + lazy = true, + dependencies = { + "AstroNvim/astrocore", + opts = { + autocmds = { + nvim_vtsls = { + { + event = "LspAttach", + desc = "Load nvim-vtsls with vtsls", + callback = function(args) + if assert(vim.lsp.get_client_by_id(args.data.client_id)).name == "vtsls" then + require("vtsls")._on_attach(args.data.client_id, args.buf) + vim.api.nvim_del_augroup_by_name "nvim_vtsls" + end + end, + }, + }, + }, + }, + }, + config = function(_, opts) require("vtsls").config(opts) end, + }, + { + "dmmulroy/tsc.nvim", + cmd = "TSC", + opts = {}, + }, +} diff --git a/lua/astrocommunity/pack/typescript/init.lua b/lua/astrocommunity/pack/typescript/init.lua index 10fe79165..4f06ba5e3 100644 --- a/lua/astrocommunity/pack/typescript/init.lua +++ b/lua/astrocommunity/pack/typescript/init.lua @@ -27,9 +27,7 @@ local has_prettier = function(bufnr) if not lsp_rooter then lsp_rooter = rooter.resolve("lsp", { ignore = { - servers = function(client) - return not vim.tbl_contains({ "vtsls", "typescript-tools", "volar", "eslint", "tsserver" }, client.name) - end, + servers = function(client) return not vim.tbl_contains({ "eslint" }, client.name) end, }, }) end @@ -73,18 +71,7 @@ end local conform_formatter = function(bufnr) return has_prettier(bufnr) and { "prettierd" } or {} end return { - { import = "astrocommunity.pack.json" }, - { import = "astrocommunity.lsp.nvim-lsp-file-operations" }, - { - "nvim-treesitter/nvim-treesitter", - optional = true, - opts = function(_, opts) - if opts.ensure_installed ~= "all" then - opts.ensure_installed = - require("astrocore").list_insert_unique(opts.ensure_installed, { "javascript", "typescript", "tsx", "jsdoc" }) - end - end, - }, + { import = "astrocommunity.pack.typescript.core" }, { "AstroNvim/astrolsp", ---@type AstroLSPOpts @@ -99,44 +86,12 @@ return { }, }, }, - ---@diagnostic disable: missing-fields - config = { - vtsls = { - settings = { - typescript = { - updateImportsOnFileMove = { enabled = "always" }, - inlayHints = { - parameterNames = { enabled = "all" }, - parameterTypes = { enabled = true }, - variableTypes = { enabled = true }, - propertyDeclarationTypes = { enabled = true }, - functionLikeReturnTypes = { enabled = true }, - enumMemberValues = { enabled = true }, - }, - }, - javascript = { - updateImportsOnFileMove = { enabled = "always" }, - inlayHints = { - parameterNames = { enabled = "literals" }, - parameterTypes = { enabled = true }, - variableTypes = { enabled = true }, - propertyDeclarationTypes = { enabled = true }, - functionLikeReturnTypes = { enabled = true }, - enumMemberValues = { enabled = true }, - }, - }, - vtsls = { - enableMoveToFileCodeAction = true, - }, - }, - }, - }, }, }, { "williamboman/mason-lspconfig.nvim", opts = function(_, opts) - opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "vtsls", "eslint" }) + opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "eslint" }) end, }, { @@ -164,56 +119,12 @@ return { end end, }, - { - "jay-babu/mason-nvim-dap.nvim", - optional = true, - opts = function(_, opts) - opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, { "js" }) - end, - }, { "WhoIsSethDaniel/mason-tool-installer.nvim", optional = true, opts = function(_, opts) - opts.ensure_installed = require("astrocore").list_insert_unique( - opts.ensure_installed, - { "vtsls", "eslint-lsp", "prettierd", "js-debug-adapter" } - ) + opts.ensure_installed = + require("astrocore").list_insert_unique(opts.ensure_installed, { "eslint-lsp", "prettierd" }) end, }, - { - "vuki656/package-info.nvim", - dependencies = { "MunifTanjim/nui.nvim" }, - opts = {}, - event = "BufRead package.json", - }, - { - "yioneko/nvim-vtsls", - lazy = true, - dependencies = { - "AstroNvim/astrocore", - opts = { - autocmds = { - nvim_vtsls = { - { - event = "LspAttach", - desc = "Load nvim-vtsls with vtsls", - callback = function(args) - if assert(vim.lsp.get_client_by_id(args.data.client_id)).name == "vtsls" then - require("vtsls")._on_attach(args.data.client_id, args.buf) - vim.api.nvim_del_augroup_by_name "nvim_vtsls" - end - end, - }, - }, - }, - }, - }, - config = function(_, opts) require("vtsls").config(opts) end, - }, - { - "dmmulroy/tsc.nvim", - cmd = "TSC", - opts = {}, - }, }