Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSpell Multi Config Support #62

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
!.yarn/versions
node_modules/
.tests/
.direnv
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ local config = {
-- See the currently supported files in https://github.com/davidmh/cspell.nvim/blob/main/lua/cspell/helpers.lua
config_file_preferred_name = 'cspell.json',

-- A list of directories that contain additional cspell.json config files or
-- support the creation of a new config file from a code action
--
-- looks for a cspell config in the ~/.config/ directory, or creates a file in the directory
-- using 'config_file_preferred_name' when a code action for one of the locations is selected
cspell_config_dirs = { "~/.config/" }

--- A way to define your own logic to find the CSpell configuration file.
---@params cwd The same current working directory defined in the source,
---@params directory The same directory defined in the source,
-- defaulting to vim.loop.cwd()
---@return string|nil The path of the json file
find_json = function(cwd)
find_json = function(directory)
end,

-- Will find and read the cspell config file synchronously, as soon as the
Expand Down
5 changes: 5 additions & 0 deletions custom-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ luassert
realpath
neotest
Neovim
stdpath
joinpath
fnamemodify
itable
fnameescape
88 changes: 57 additions & 31 deletions lua/cspell/code_actions/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ end

---@param code_action_config CSpellSourceConfig
---@param params GeneratorParams
local get_config_info = function(code_action_config, params)
---@param cspell_json_path string
local get_config_info = function(code_action_config, params, cspell_json_path)
-- In theory, the call to async_get_config_info in the diagnostics source
-- should already have been loaded, that's why we're defaulting reading the
-- config synchronously here.
if code_action_config.read_config_synchronously then
return h.sync_get_config_info(params)
return h.sync_get_config_info(params, cspell_json_path)
end

return h.async_get_config_info(params)
return h.async_get_config_info(params, cspell_json_path)
end

return make_builtin({
Expand All @@ -58,7 +59,22 @@ return make_builtin({
---@type CSpellSourceConfig
local code_action_config =
vim.tbl_extend("force", { read_config_synchronously = true }, params:get_config())
local cspell = get_config_info(code_action_config, params)

---@type table<number|string, string>
local cspell_config_paths = {}

local cspell_config_directories = code_action_config.cspell_config_dirs or {}
table.insert(cspell_config_directories, params.cwd)

for _, cspell_config_directory in pairs(cspell_config_directories) do
local cspell_config_path = h.get_config_path(params, cspell_config_directory)
if cspell_config_path == nil then
cspell_config_path = h.generate_cspell_config_path(params, cspell_config_directory)
end
cspell_config_paths[cspell_config_directory] = cspell_config_path
end

local default_cspell_config = get_config_info(code_action_config, params, cspell_config_paths[params.cwd])

---@type table<number, CodeAction>
local actions = {}
Expand Down Expand Up @@ -94,15 +110,19 @@ return make_builtin({
vim.log.levels.INFO,
{ title = "cspell.nvim" }
)
on_success(cspell and cspell.path, params, "use_suggestion")
on_success(
default_cspell_config and default_cspell_config.path,
params,
"use_suggestion"
)
end

if on_use_suggestion then
---@type UseSuggestionSuccess
local payload = {
misspelled_word = diagnostic.user_data.misspelled,
suggestion = suggestion,
cspell_config_path = cspell and cspell.path,
cspell_config_path = default_cspell_config and default_cspell_config.path,
generator_params = params,
}
on_use_suggestion(payload)
Expand All @@ -112,34 +132,40 @@ return make_builtin({
end

local word = h.get_word(diagnostic)

-- add word to "words" in cspell.json
table.insert(
actions,
make_add_to_json({
diagnostic = diagnostic,
word = word,
params = params,
})
)

if cspell == nil then
break
local dictionary_cspell_configs = {}

for _, cspell_config_path in pairs(cspell_config_paths) do
-- add word to "words" in cspell.json
table.insert(
actions,
make_add_to_json({
diagnostic = diagnostic,
word = word,
params = params,
cspell_config_path = cspell_config_path,
})
)
local cspell_config = get_config_info(code_action_config, params, cspell_config_path)
if cspell_config and cspell_config.config.dictionaryDefinitions then
dictionary_cspell_configs[cspell_config_path] = cspell_config
end
end

-- add word to a custom dictionary
for _, dictionary in ipairs(cspell.config.dictionaryDefinitions or {}) do
if dictionary ~= nil and dictionary.addWords then
table.insert(
actions,
make_add_to_dictionary_action({
diagnostic = diagnostic,
word = word,
params = params,
cspell = cspell,
dictionary = dictionary,
})
)
for _, cspell_config in pairs(dictionary_cspell_configs) do
for _, dictionary in ipairs(cspell_config.config.dictionaryDefinitions) do
if dictionary ~= nil and dictionary.addWords then
table.insert(
actions,
make_add_to_dictionary_action({
diagnostic = diagnostic,
word = word,
params = params,
cspell = cspell_config,
dictionary = dictionary,
})
)
end
end
end
end
Expand Down
7 changes: 6 additions & 1 deletion lua/cspell/code_actions/make_add_to_dictionary_action.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ return function(opts)
-- user_data. And only use the word from the range to trigger a new diagnostic.
-- See: https://github.com/jose-elias-alvarez/null-ls.nvim/issues/1630
local misspelled_word = opts.diagnostic.user_data.misspelled
local dictionary = opts.dictionary.name
local shortened_path = h.shorten_path(opts.cspell.path)

return {
title = 'Add "' .. misspelled_word .. '" to dictionary "' .. opts.dictionary.name .. '"',
title = h.format(
'Add "${word}" to dictionary "${dictionary}" from "${config_path}"',
{ word = misspelled_word, dictionary = dictionary, config_path = shortened_path }
),
action = function()
if opts.dictionary == nil then
return
Expand Down
19 changes: 13 additions & 6 deletions lua/cspell/code_actions/make_add_to_json.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local h = require("cspell.helpers")
---@class AddToJSONAction
---@field diagnostic Diagnostic
---@field word string
---@field cspell_config_path string
---@field params GeneratorParams

---@param opts AddToJSONAction
Expand All @@ -17,22 +18,28 @@ return function(opts)
local misspelled_word = opts.diagnostic.user_data.misspelled

return {
title = 'Add "' .. misspelled_word .. '" to cspell json file',
title = h.format(
'Add "${word}" to "${config_path}"',
{ word = misspelled_word, config_path = h.shorten_path(opts.cspell_config_path) }
),

action = function()
local cspell_config_path = opts.cspell_config_path
-- get a fresh config when the action is performed, which can be much later than when the action was generated
local cspell = h.async_get_config_info(opts.params)
local config_path = h.get_config_path(opts.params)
if not cspell and config_path and Path:new(config_path):exists() then
local cspell = h.async_get_config_info(opts.params, cspell_config_path)
local path_exists = Path:new(cspell_config_path):exists()
if not cspell and path_exists then
h.cache_word_for_json(misspelled_word)
return
end
cspell = cspell or h.create_cspell_json(opts.params)

cspell = cspell or h.create_default_cspell_json(opts.params, cspell_config_path)

if not cspell.config.words then
cspell.config.words = {}
end

h.add_words_to_json(opts.params, { misspelled_word })
h.add_words_to_json(opts.params, { misspelled_word }, cspell_config_path)

-- replace word in buffer to trigger cspell to update diagnostics
h.set_word(opts.diagnostic, opts.word)
Expand Down
27 changes: 20 additions & 7 deletions lua/cspell/diagnostics/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,33 @@ return h.make_builtin({
command = "cspell",
---@param params GeneratorParams
args = function(params)
params.cwd = params.cwd or vim.loop.cwd()

local cspell_args = {
"lint",
"--language-id",
params.ft,
"stdin://" .. params.bufname,
}
params.cwd = params.cwd or vim.loop.cwd()

---@type CSpellSourceConfig
local diagnostics_config = params and params:get_config() or {}

local config_path = helpers.get_config_path(params)
if config_path then
cspell_args = vim.list_extend({ "-c", config_path }, cspell_args)
---@type table<number|string, string>
local cspell_config_paths = {}

local cspell_config_directories = diagnostics_config.cspell_config_dirs or {}
table.insert(cspell_config_directories, params.cwd)

for _, cspell_config_directory in pairs(cspell_config_directories) do
local cspell_config_path = helpers.get_config_path(params, cspell_config_directory)
if cspell_config_path == nil then
cspell_config_path = helpers.generate_cspell_config_path(params, cspell_config_directory)
end
cspell_config_paths[cspell_config_directory] = cspell_config_path
end
local merged_config = helpers.create_merged_cspell_json(params, cspell_config_paths)

cspell_args = vim.list_extend({ "-c", merged_config.path }, cspell_args)

local code_action_source = require("null-ls.sources").get({
name = "cspell",
Expand All @@ -43,11 +57,10 @@ return h.make_builtin({
cspell_args = vim.list_extend({ "--show-suggestions" }, cspell_args)

local code_action_config = code_action_source.config or {}
local diagnostics_config = params and params:get_config() or {}

if helpers.matching_configs(code_action_config, diagnostics_config) then
-- warm up the config cache so we have the config ready by the time we call the code action
helpers.async_get_config_info(params)
helpers.async_get_config_info(params, cspell_config_paths[params.cwd])
elseif needs_warning then
needs_warning = false
vim.notify(
Expand Down
Loading
Loading