Skip to content

Commit

Permalink
feat: add support for using multiple cspell.json as config sources
Browse files Browse the repository at this point in the history
  • Loading branch information
JateNensvold authored and davidmh committed Aug 14, 2024
1 parent 508b64a commit c423159
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 139 deletions.
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

0 comments on commit c423159

Please sign in to comment.