AstroNvim / astrocommunity

A community repository of common plugin specifications
https://astronvim.github.io/astrocommunity/
GNU General Public License v3.0
1.22k stars 243 forks source link

[Typescript pack] support biome for linter and formatter #962

Open dannd4 opened 6 months ago

dannd4 commented 6 months ago

Is your feature related to a problem?

No response

Describe the new feature

Enable biome lsp for lspconfig and null-ls when project has @biomejs/biome dependency or has biome.json

Additional context

No response

Uzaaft commented 6 months ago

Great idea! Feel free to open up a PR. :)

willparsons commented 6 months ago

I'm running into this problem at the moment with biome and eslint+prettier bumping into each other. I'll try and sort out the config on my end and then contribute it back here.

willparsons commented 6 months ago

This is a bit annoying as the 95% of use cases is that you're either in a project that uses biome OR eslint+prettier, which is fine to configure, but it's possible that some people may want something like eslint with biome for formatting or some other configuration.

There's even the argument about HOW we run the LSP. I'd say we expect it to be in the projects dependencies and invoke it via npx/yarn/pnpm biome lsp-proxy, but they COULD use mason...

Do you guys want to cover all use cases or just go for the most common one?

ammuench commented 6 months ago

I'm in two projects currently where we're using biome as the formatter and eslint as the linter while migrating a bunch of stuff.

Could we check for the existence of a biome.json config and use that to make decisions? It might be short-sighted but in my head any project where biome has "formatter": { "enabled": true } turned on I think would be the true formatter even if prettier is still in node modules somewhere, since you have to manually opt-into Biome formatting by declaring it. Same thing in reverse for "linter": { "enabled": true } (thought I can't think of many scenarios where you'd use biome as a linter and prettier as a formatter)

Uzaaft commented 6 months ago

This is a bit annoying as the 95% of use cases is that you're either in a project that uses biome OR eslint+prettier, which is fine to configure, but it's possible that some people may want something like eslint with biome for formatting or some other configuration.

There's even the argument about HOW we run the LSP. I'd say we expect it to be in the projects dependencies and invoke it via npx/yarn/pnpm biome lsp-proxy, but they COULD use mason...

Do you guys want to cover all use cases or just go for the most common one?

Go for the most common one, and install it trough mason for now.

willparsons commented 6 months ago

I've started work on this here: https://github.com/AstroNvim/astrocommunity/pull/1006/files

Having everything supported is pretty finicky :/

taskylizard commented 4 months ago

That PR was closed, so I'll like to continue on this, but before that I'd like to investigate what approach we can take to suit everyone's needs.

Uzaaft commented 4 months ago

@taskylizard Sure. I'd say copy the approach done with the ruff pack would be a great starting point. @willparsons What was the bits that were finicky?

taskylizard commented 4 months ago

Sure thing, it's been a long while since I last contributed here, so it will take me a bit. I'll look around the different packs and see what solution we can apply.

Uzaaft commented 4 months ago

Great!

taskylizard commented 4 months ago

I decided to kang @willparsons's work on their PR and improve on bit, seems I'm a bit lost on conform support and the first task on the PR todo. I'll send a draft PR soon though.

willparsons commented 4 months ago

The awkward bits were just deciding when to switch biome on and off, when it should take precedency over eslint or prettier in a repo with both, how do you determine if it's 'configured' to run, etc. Not to mention doing it for null-ls, conform and nvim-lint.

taskylizard commented 4 months ago

There's even the argument about HOW we run the LSP. I'd say we expect it to be in the projects dependencies and invoke it via npx/yarn/pnpm biome lsp-proxy, but they COULD use mason...

I found the answer for this one, so to resolve the location of the biome binary, the pack can look into the following places in order:

  1. The project's local dependencies (node_modules)
  2. The path specified in the configuration option (I should look into how)
  3. The one installed from Mason

As for 1: the vscode extension seems to determine by the package manager lockfile present in the project and executing it with that respective package manager, however I'm not sure if this is the appropriate approach to take.

Uzaaft commented 4 months ago

@taskylizard I like that idea. I believe prettierd does that internally(i.e nodemodules vs the bundled option) As for 2: Is that a field in the biome json config?

taskylizard commented 4 months ago

Nope, it's something we will provide from the pack, if the user wants to use a downloaded biome binary or something, as you can install biome from homebrew or manually.

Uzaaft commented 4 months ago

How will you do that? Is that an opt that will be passed to the pack?

Uzaaft commented 4 months ago

The preferred way to install biome is to have it in your project. I think we should do that and only that for now.

willparsons commented 3 months ago

Yeah biome is prone to changing lints on version changes (not major version), so if your biome lsp differs from the installed version you will get 2 different results that can conflict.

You can go around using the package manager by going straight into the node_modules and running the binary.

wiscaksono commented 1 month ago

I use this for biome / prettier

local function biome_lsp_or_prettier(bufnr)
  local has_biome_lsp = vim.lsp.get_clients({
    bufnr = bufnr,
    name = "biome",
  })[1]
  if has_biome_lsp then return {} end
  local has_prettier = vim.fs.find({
    ".prettierrc",
    ".prettierrc.json",
    ".prettierrc.yml",
    ".prettierrc.yaml",
    ".prettierrc.json5",
    ".prettierrc.js",
    ".prettierrc.cjs",
    ".prettierrc.toml",
    "prettier.config.js",
    "prettier.config.cjs",
  }, { upward = true })[1]
  if has_prettier then return { "prettier", "prettierd" } end
  return { "biome" }
end

return {
  "stevearc/conform.nvim",
  event = "User AstroFile",
  cmd = "ConformInfo",
  specs = {
    { "AstroNvim/astrolsp", optional = true, opts = { formatting = { disabled = true } } },
    { "jay-babu/mason-null-ls.nvim", optional = true, opts = { methods = { formatting = false } } },
  },
  dependencies = {
    { "williamboman/mason.nvim", optional = true },
    {
      "AstroNvim/astrocore",
      opts = {
        options = { opt = { formatexpr = "v:lua.require'conform'.formatexpr()" } },
        commands = {
          Format = {
            function(args)
              local range = nil
              if args.count ~= -1 then
                local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1]
                range = {
                  start = { args.line1, 0 },
                  ["end"] = { args.line2, end_line:len() },
                }
              end
              require("conform").format { async = true, lsp_format = "fallback", range = range }
            end,
            desc = "Format buffer",
            range = true,
          },
        },
        mappings = {
          n = {
            ["<Leader>lf"] = { function() vim.cmd.Format() end, desc = "Format buffer" },
            ["<Leader>uf"] = {
              function()
                if vim.b.autoformat == nil then
                  if vim.g.autoformat == nil then vim.g.autoformat = true end
                  vim.b.autoformat = vim.g.autoformat
                end
                vim.b.autoformat = not vim.b.autoformat
                require("astrocore").notify(
                  string.format("Buffer autoformatting %s", vim.b.autoformat and "on" or "off")
                )
              end,
              desc = "Toggle autoformatting (buffer)",
            },
            ["<Leader>uF"] = {
              function()
                if vim.g.autoformat == nil then vim.g.autoformat = true end
                vim.g.autoformat = not vim.g.autoformat
                vim.b.autoformat = nil
                require("astrocore").notify(
                  string.format("Global autoformatting %s", vim.g.autoformat and "on" or "off")
                )
              end,
              desc = "Toggle autoformatting (global)",
            },
          },
        },
      },
    },
  },
  opts = {
    format_on_save = function(bufnr)
      if vim.g.autoformat == nil then vim.g.autoformat = true end
      local autoformat = vim.b[bufnr].autoformat
      if autoformat == nil then autoformat = vim.g.autoformat end
      if autoformat then return { timeout_ms = 5000, lsp_format = "fallback" } end
    end,

    formatters_by_ft = {
      typescript = biome_lsp_or_prettier,
      javascript = biome_lsp_or_prettier,
      javascriptreact = biome_lsp_or_prettier,
      typescriptreact = biome_lsp_or_prettier,
      json = { "biome" },
      jsonc = { "biome" },
      markdown = { "biome" },
      toml = { "biome" },
    },
  },
}
taskylizard commented 1 month ago

How does your config work?