[Neovim] HTML 記述時に CSS ファイルで定義したクラス名を自動補完してほしい

<!---
# 結論

既存のプラグイン・LSP の設定では実現できないようだったので、妥協策として HTML と CSS ファイルを両方バッファーとして開いた状態で、[cmp-buffer](https://github.com/hrsh7th/cmp-buffer) でバッファーの内容を自動補完する機能で対応しました。この方法の短所として対象の CSS ファイルを開いておく必要があることが挙げられます。なお、単一の `styles.css` ファイルで作業しているような場合は、この方法で十分に対応できるかと思います。
--->


# したいこと

HTML ファイルを Neovim で編集しているときに、別の CSS ファイルで記述したクラス名を自動補完したい。
例えば、以下の styles.css ファイルで定義した `.test-class` クラスを以下の index.html を編集している状態で自動補完の候補として提示してほしい。

```css:styles.css
...
.test-class {
    width: 200px;
}
...
```

```html:index.html
...
    <div class=".tes <--- ここまで入力した段階で自動補完のオプションを提示してほしい
...
```

自動補完については、HTML と CSS の LSP をインストールしていると、同タイプのバッファー（例えば、`.html` ファイルならば他の `.html` ファイル）にあるクラス名などは自動補完できますが、この場合はファイルタイプが異なるため、候補として提示されません。
HTML と CSS の LSP を連携させることができれば実現できそうですが、調べた限りではそのような情報は見当たりませんでした。

# VSCode の場合

VSCode では、[プラグイン IntelliSense for CSS class names in HTML](https://marketplace.visualstudio.com/items?itemName=Zignd.html-css-class-completion) でこの機能が実現されています。[ソースコード](https://github.com/zignd/HTML-CSS-Class-Completion/blob/master/src/parse-engines/common/css-class-extractor.ts)を覗くと、CSS ファイルからクラスを抽出するパーサーを使っているようです。

Neovim でこのようなプラグインは見当たりませんでした。この VSCode のプラグインに倣えば、Neovim 用の同様のプラグインが作れそうですね。また今度試してみようと思います。

# 代替策

自分の場合は、小規模のプロジェクトで単一の CSS ファイルしかないので、そのファイルに記述されているクラス名を自動補完できれば十分です。こちらはこのような状況での対応策になります。
自動補完にプラグイン [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) を利用している場合、プラグイン [cmp-buffer](https://github.com/hrsh7th/cmp-buffer) でバッファーの内容を候補に追加できます。
デフォルトでは扱っているバッファーのみが対象のようなので、開いているすべてのバッファーを対象にするために以下の option を nvim-cmp の setup() 関数を実行するときに指定します。

詳細は [ドキュメント](https://github.com/hrsh7th/cmp-buffer?tab=readme-ov-file#get_bufnrs-type-fun-number) にあります。

```my_cmp_settings.lua
require("cmp").setup({

    ...

    sources = cmp.config.sources(
        {
            -- Prioritize snippets
            { name = "luasnip", priority = 5 },

            -- Uses LSP to provide auto-completions for variable_names, function_names etc.
            { name = "nvim_lsp", priority = 3, max_item_count = 20 },

            -- Auto-complete any file/directory path
            { name = "path", priority = 3 },

            -- Auto-complete any word in open buffers (not only current buffer)
            {
                name = "buffer",
                keyword_length = 3,
                max_item_count = 3,

                -- ここから
                 -- 開いているすべてのバッファーを対象とする
                option = {
                    get_bufnrs = function()
                        return vim.api.nvim_list_bufs()
                    end,
                },
                -- ここまで
            },
        }
    ),
})
```

この設定をした上で、HTML ファイルと CSS ファイルの両方を開いていれば、両方のバッファーの内容が候補に提示されます。開いているバッファーは `:buffer` コマンドで確認できます。

自分の手元では次のように、CSS で定義している `.test-class` クラスを HTML ファイルの中で自動補完できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1045794/2a259583-19db-6bba-9570-5b52dd4067e4.png)

こちらの方法には上記の VSCode プラグインに対する優位点もあります。VSCode のプラグインでは HTML ファイル編集時に CSS ファイルで定義したクラス名を自動補完できますが、逆は対応していないようです。すなわち、HTML ファイルでクラスを先に指定し、その後、CSS ファイルでクラスを定義するときに、クラス名の自動補完は利用できません。
それに対し、こちらの代替案はそのような場合でも利用できます。

# 検証環境
念のため自分が利用しているバージョンをおいておきます。
- Neovim version --> NVIM v0.10.0-dev
- Autocomplete plugin --> [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) (Commit Id 538e37b)
- Autocomplete source --> [cmp-buffer](https://github.com/hrsh7th/cmp-buffer) (Commit Id 3022dbc)

# まとめ

長くなりましたが、要は nvim-cmp と cmp-buffer プラグインを利用して、開いているすべてのバッファーの単語を自動補完に候補に追加することで、CSS や HTML ファイルのクラス名を自動補完できるようにしました。