Open3

Conform.nvimでdeno fmtとprettierを出しわける

ras0qras0q

Node.jsとDenoの出し分けはいろんな方が記事を出しているしよく使うので汎用関数に切り分ける

~/.config/nvim/lua/utils.lua
local M = {}

function M.get_js_runtime(bufnr)
  local bufname = vim.fs.normalize(vim.api.nvim_buf_get_name(bufnr))
  local dir = vim.fs.dirname(bufname)
  local findopts = { upward = true, path = dir }
  local deno_files = vim.fs.find({ "deno.json", "deno.jsonc", "deno.lock" }, findopts)
  local node_files = vim.fs.find({ "package.json" }, findopts)

  return {
    is_deno = #deno_files > 0,
    is_node = #node_files > 0,
    deno_root_dir = #deno_files > 0 and vim.fs.dirname(deno_files[1]) or dir,
    node_root_dir = #node_files > 0 and vim.fs.dirname(node_files[1]) or dir,
  }
end

return M

ちなみにLSPでは以下のように使う

~/.config/nvim/after/lsp/denols.lua
---@type vim.lsp.Config
return {
  single_file_support = true,
  workspace_required = true,
  root_markers = {},
  root_dir = function(bufnr, cb)
    local js_runtime = require("utils").get_js_runtime(bufnr)
    local enabled = js_runtime.is_deno
        or (not js_runtime.is_node and vim.bo.filetype ~= "markdown")
    if enabled then
      return cb(js_runtime.deno_root_dir)
    end
  end,
}
ras0qras0q

一旦こんな感じで。Lazy.nvimで構成しています。

  • 同じように切り分けたい FileTypeがいくつかあるので列挙してまとめて設定する
  • stop_after_first = true: 複数のフォーマッターが同時に使われるのを防ぐ
    • Go で gofmtgoimportsを同時に走らせたいときとかにはこの設定をしない方が良さそう
  • lsp_format = "fallback": 該当のフォーマッターがなければ Language Server のフォーマッターにフォールバックする
    • 他に neverprefer などがある
conform_nvim.lua
local js_formatter = function(bufnr)
  local js_runtime = require("utils").get_js_runtime(bufnr)
  if js_runtime.is_deno or not js_runtime.is_node then
    return {}
  else
    return { "biome", "prettierd", "prettier", stop_after_first = true }
  end
end

---@type LazyPluginSpec
return {
  "stevearc/conform.nvim",
  event = { "BufReadPre", "BufNewFile" },
  opts = function()
    local js_fts = vim.lsp.config["denols"].filetypes or {
      "javascript",
      "javascriptreact",
      "javascript.jsx",
      "typescript",
      "typescriptreact",
      "typescript.tsx",
      "json",
      "jsonc",
    }

    local formatters_by_ft = {}
    for _, ft in ipairs(js_fts) do
      formatters_by_ft[ft] = js_formatter
    end

    return {
      default_format_opts = {
        lsp_format = "fallback",
      },
      format_on_save = {
        lsp_format = "fallback",
      },
      formatters_by_ft = formatters_by_ft,
    }
  end,
}