Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
dannd4 opened this issue May 14, 2024 · 20 comments
Open

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

dannd4 opened this issue May 14, 2024 · 20 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@dannd4
Copy link

dannd4 commented May 14, 2024

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

@dannd4 dannd4 added the enhancement New feature or request label May 14, 2024
@Uzaaft
Copy link
Member

Uzaaft commented May 14, 2024

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

@mehalter mehalter added good first issue Good for newcomers help wanted Extra attention is needed labels May 14, 2024
@willparsons
Copy link

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.

@mehalter mehalter removed the help wanted Extra attention is needed label May 24, 2024
@willparsons
Copy link

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
Copy link
Contributor

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
Copy link
Member

Uzaaft commented May 25, 2024

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
Copy link

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

Having everything supported is pretty finicky :/

@taskylizard
Copy link
Contributor

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
Copy link
Member

Uzaaft commented Jul 29, 2024

@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
Copy link
Contributor

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
Copy link
Member

Uzaaft commented Jul 29, 2024

Great!

@taskylizard
Copy link
Contributor

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
Copy link

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
Copy link
Contributor

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
Copy link
Member

Uzaaft commented Jul 30, 2024

@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
Copy link
Contributor

taskylizard commented Jul 30, 2024

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
Copy link
Member

Uzaaft commented Jul 30, 2024

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

@Uzaaft
Copy link
Member

Uzaaft commented Jul 30, 2024

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
Copy link

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
Copy link

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
Copy link
Contributor

How does your config work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants