## 事象
たとえば、[[Volar]]と[[Prettier]]を設定したとき。
```lua
-- lspconfigのVolar設定
lspconfig.volar.setup({
filetypes = { "typescript", "javascript", "javascriptreact", "typescriptreact", "vue", "json" },
capabilities = capabilities,
on_new_config = function(new_config, new_root_dir)
new_config.init_options.typescript.tsdk = get_typescript_server_path(new_root_dir)
end,
})
```
```lua
-- none-lsのPrettier設定
null_ls.builtins.formatting.prettier.with({
only_local = "node_modules/.bin",
condition = function(utils)
local biome_support_filetypes =
{ "javascript", "typescript", "javascriptreact", "typescriptreact", "json", "jsonc" }
-- biome.json が対応していなければprettier
if not vim.tbl_contains(biome_support_filetypes, vim.bo.filetype) then
return true
end
-- biome.json が対応しててもbiome.jsonがなければprettier
return not utils.root_has_file({ "biome.json" })
end,
}),
```
```lua
-- none-lsの自動保存設定
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
vim.lsp.buf.format({
async = false,
})
end,
})
end
end,
```
`biome.json`のあるプロジェクトで保存すると、フォーマットが必ず2回かかる。しかも、順不同。
### 詳細
`:LspInfo`コマンドを実行すると、以下の傾向があった。
#### 最終的にBiomeのフォーマット実行結果になるとき
1. biome (フォーマット以外)
2. volar
3. null (biome)
#### 最終的にBiomeのフォーマット実行結果にならないとき
1. null (biome)
2. biome (フォーマット以外)
3. volar
## 原因
[[Volar]]には[[フォーマッター]]としての機能もあり、それがバッファ保存時のタイミングで、[[Biome]]のフォーマットと一緒に実行されるから。
以下のコマンドで[[LSPクライアント]]アタッチ後に、フォーマットとしての機能をクライアント持っていれば、自動で[[LSP]]のフォーマットコマンドを呼び出すようになっている。
```lua
-- none-lsの自動保存設定
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
vim.lsp.buf.format({
async = false,
})
end,
})
end
end,
```
[[Biome]]はこの処理によって登録されるが、`vim.lsp.buf.format`は[[LSPクライアント]]としてのフォーマットも実行される可能性があり、[[Volar]]はフォーマット機能をもっているのでそれにあたる。
#### 最終的にBiomeのフォーマット実行結果になるとき
1. biome (フォーマット以外)
2. volar
3. null (biome)
[[Volar]]のフォーマット -> [[Biome]]のフォーマット の順で実行される。もともと、[[Biome]]でフォーマットしていたコードの場合、[[Volar]]のフォーマットで一瞬コードが変更され、すぐに再び[[Biome]]のフォーマットで元に戻る。今回問題となった事象の典型例。
#### 最終的にBiomeのフォーマット実行結果にならないとき
1. null (biome)
2. biome (フォーマット以外)
3. volar
[[Biome]]のフォーマット -> [[Volar]]のフォーマット の順で実行される。もともと、[[Biome]]でフォーマットしていたコードの場合、[[Biome]]のフォーマットでは実行結果が変わらず、その後の[[Volar]]のフォーマットで結果が変わる。そのため、2回実行したことに気づかず、結果が変わってしまったという状態と勘違いしやすい。
## 解決方法
`vim.lsp.buf.format`関数の中で、フォーマットを利用したくない[[LSPクライアント]]が指定されたときに処理(フォーマット)を実行しないようにする。
```lua
-- none-lsの自動保存設定
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
buffer = bufnr,
callback = function()
vim.lsp.buf.format({
async = false,
filter = function(c)
local disabled_format_clients = { "lua_ls", "volar" }
return not vim.tbl_contains(disabled_format_clients, c.name)
end,
})
end,
})
end
end,
```
上記は[[lua-language-server]]と[[Volar]]のフォーマット機能を無効化した例。それぞれ、[[StyLua]]と[[Prettier]]/[[Biome]]でフォーマットするため。
## 参考
- [Disable formatting for typescript\-language\-server : r/neovim](https://www.reddit.com/r/neovim/comments/yturkn/disable_formatting_for_typescriptlanguageserver/)