永遠にWIP

monorepoでnull-ls.nvimをいい感じにしたい

nvim

はじめに

私はnull-ls.nvim(以後null-ls)を使ってformatやlintをしています。
例えば、普段開発しているプロジェクトではPrettierとESLintを使ってlintやformatをしています。

問題

上記のプロジェクトはmonorepo構成になっており、workspaceによってESLintの設定が違ったり、そもそもESLintがなかったりします。

例えば以下のような場合です。

.
├── packageA
│   ├── .eslintrc.js
│   └── src/
├── packageB
│   ├── .eslintrc.js
│   └── src/
└── packageC
    └── src/

このようなプロジェクトで例えばpackageA内のファイルを編集するとします。
packageAには.eslintrc.jsがあるのでnull-lsのESLintが有効になります。
しかし、この状態でpackageCのファイルを編集しようとするとESLintが有効になったままで、色々と不便です。

これはnull-lsがESLintなどのsourceを有効にするかどうかは最初のsetup関数実行時にのみ判定されます。

解決

これを解決するためにruntime_conditionというものがあります。
これを使うことで新規にファイルを開いたタイミングやファイルを保存したタイミングでnull-lsのsourceを実行するかどうかを決めることができます。

私は以下のように対象ファイルの親ディレクトリ内に設定ファイルがあるかどうかで判定するようにしています。

local b = null_ls.builtins
local h = require("null-ls.helpers")

local M = {}

M.eslint_setting_files = { ".eslintrc", ".eslintrc.js", ".eslintrc.cjs", ".eslintrc.json" }

local generate_runtime_condition = function(root_pattern, target_file)
	return h.cache.by_bufnr(function(params)
		local root_path = require("lspconfig").util.root_pattern(root_pattern)(params.bufname)
		if root_path == nil then
			return false
		end

		local condition = false

		for _, file in pairs(target_file) do
			condition = condition or vim.fn.findfile(file, root_path .. ";") ~= ""
		end

		return condition
	end)
end

local apply_eslint = function(setting)
	return setting.with({
		runtime_condition = generate_runtime_condition(M.root_dir, M.eslint_setting_files),
	})
end

local sources = {
	apply_eslint(b.code_actions.eslint_d),
	apply_eslint(b.diagnostics.eslint_d),
	apply_eslint(b.formatting.eslint_d),

}

runtime_conditionはかなりの頻度で実行されるものになっているので、なるべくcacheするようにしています。

https://github.com/jose-elias-alvarez/null-ls.nvim/issues/509

さいごに

この記事は IPFactory Advent Calendar 2022 の 10日目の記事です。

© 2020 DuGlaser