🤖

BigQuery の LSP を .bq ファイルでのみ動作させる nvim-lspconfig

2022/03/26に公開

@dr666m1 さんが公開されている BigQuery(標準 SQL)の LSPnvim-lsp に設定してご機嫌に使わせていただいている(↓記事参照)のですが、.sql ファイル一律に動作してしまうと困ることもありそうなので、その対策をしておきます。

https://zenn.dev/a24k/articles/a8b882019da106

以下、↑記事と同様の設定が済んでいる前提となります。

はじめに

色々方法はあると思うのですが、今回は以下の方針で対処してみます。

  • BigQuery 標準 SQL のファイルは、拡張子 .bq で保存するものとする
  • .bq ファイルを SQL ファイルとして認識させる
  • さらに、.bq を開いた時のみ LSP を動作させる

.bq ファイルを SQL ファイルとして認識させる

neovim のファイルタイプの認識については、:help filetype に記載のある通りです。runtimepath 内の ftdetect サブディレクトリに含まれる .vim ファイルが設定として読み込まれる様ですので、こんな感じでファイルを置いてあげれば .bq ファイルを SQL ファイルとして認識する様になります。

~/.config/nvim/ftdetect/bq.vim
au BufNewFile,BufRead *.bq setf sql

.bq ファイルを開いた時のみ LSP を動作させる lspconfig

:help lspconfig-setup を見ると、root_dir と言うオプションがあることがわかります。この設定は、本来同じプロジェクト内(git リポジトリ内など)のファイルを開く際に、すでに起動している LSP を使いまわすための設定の様です(:help lspconfig-root-detection 参照)。

root_dir オプションの型は、

function(filename, bufnr)

となっていますので、ファイル名とバッファ番号を引数として受け取る「関数」を設定することになります。また、この関数の返却値は

Returns either a filepath (string) or nil.
The language server will only start if the function returns a filepath.

とのことですので、これを上手く使って、

  • 起動したい場合には適切な filepath
  • 起動したくない場合には nil

を、それぞれ返してあげれば良さそうです。こんな感じ。

local lspConfig = require('lspconfig')
local lspInstaller = require("nvim-lsp-installer")

local serverOpts = {} -- key: server-name, value: options

local function rootOfFileWith(pattern, singleFileSupport)
    return function(filename, _)
        if not(string.match(filename, pattern)) then return nil end
        local gitAncestor = lspConfig.util.find_git_ancestor(filename)
        local singleFileDir = singleFileSupport and lspConfig.util.path.dirname(filename) or nil
        return gitAncestor or singleFileDir
    end
end

serverOpts['bqls'] = {
    root_dir = rootOfFileWith("%.bq$", true),
    single_file_support = false,
}

lspInstaller.on_server_ready(function(server)
    local opts = {
        on_attach = on_attach,  -- e.g.
    }

    if serverOpts[server.name] then
        for k, v in pairs(serverOpts[server.name]) do opts[k] = v end
    end

    server:setup(opts)
    vim.cmd [[ do User LspAttachBuffers ]]
end)

rootOfFileWith と言う関数で、root_dir に設定する関数を作成しています。作成した関数内では、ファイル名に対して正規表現マッチをかけて、マッチしない場合は nil を返す様な処理にしています。lua の正規表現は少し個性的ですね。私は、基本的に .bq ファイルを git 管理しているので、素直にリポジトリのルートディレクトリを返すような設定にしています。

なお、single_file_supporttrue になっていると、root_dir の返却値に関わらず LSP が起動してしまうので、false に設定しておきます。代わりに、rootOfFileWith 関数の singleFileSupport 引数に true を渡すことで、リポジトリルートが見つからなかった場合に単にそのファイルがあるディレクトリを返す様になっており、git 外のファイルでも動作する様にしています。

ちなみに、lua には三項演算子が無いようで、A and B or C が三項演算子の代わりになっています。きゅ〜と。

おわりに

最近の私は、BigQuery 叩く時くらいしか SQL を書かないので、いったんこの設定で過ごしてみます。他の SQL 用 LSP を併用する場合にも、同じ様な方法で排他制御できるのではないかと思います。

Discussion