## 事象 たとえば、[[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/)