From 98bb06deeb5e537e09518dabe7d340f9f411001c Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:26:47 +1000 Subject: [PATCH 01/14] Squshing commits. Features added: buftype options, and a preprocessor function, both assignable in opts. --- README.md | 40 +++++++++++++++++++------ doc/spellwarn.txt | 39 +++++++++++++++--------- lua/spellwarn/diagnostics.lua | 56 +++++++++++++++++++++++++++-------- lua/spellwarn/init.lua | 14 +++++++++ 4 files changed, 113 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index a92da13..4d7df87 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ This is a plugin to display spelling errors as diagnostics. Some language server A simpler solution, therefore, is to use Neovim's existing spellchecking and diagnostics features. This is done by iterating through the flagged words in a buffer and passing them to `vim.diagnostic.set()`. Neovim is fast enough that this is ~instantaneous for most files. See below for installation and configuration instructions. ## Installation + I recommend using [Lazy.nvim](https://github.com/folke/lazy.nvim): + ```lua { "ravibrock/spellwarn.nvim", @@ -23,7 +25,9 @@ I recommend using [Lazy.nvim](https://github.com/folke/lazy.nvim): Note that this uses Neovim's built-in spellchecking. This requires putting `vim.opt.spell = true` and `vim.opt.spelllang = [YOUR LANGUAGE HERE]` somewhere in your Neovim config if you haven't already. You may also want to add the word "spellwarn" to your Neovim dictionary. This can be done by putting the cursor onto "spellwarn" and hitting `zg`. ## Configuration + Pass any of the following options to `require("spellwarn").setup()`: + ```lua { event = { -- event(s) to refresh diagnostics on @@ -33,40 +37,58 @@ Pass any of the following options to `require("spellwarn").setup()`: "TextChangedI", "TextChangedP", }, + enable = true, -- enable diagnostics on startup + + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set + func_preprocess = function(bufnr, diag_tbl) + return diag_tbl + end, + + bt_config = { -- buffer types to run on + [""] = true, + }, + bt_default = false, -- default for types not in bt_config. + ft_config = { -- spellcheck method: "cursor", "iter", or boolean - alpha = false, - help = false, - lazy = false, + alpha = false, + help = false, + lazy = false, lspinfo = false, - mason = false, + mason = false, }, ft_default = true, -- default option for unspecified filetypes + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = "WARN", - spellcap = "HINT", + spellbad = "WARN", + spellcap = "HINT", spelllocal = "HINT", - spellrare = "INFO", + spellrare = "INFO", }, - suggest = false, -- show spelling suggestions in diagnostic message (works best with window-style message) - num_suggest = 3, -- number of spelling suggestions shown in diagnostic message + suggest = false, -- show spelling suggestions in diagnostic message + num_suggest = 3, -- number of suggestions shown in diagnostic message prefix = "possible misspelling(s): ", -- prefix for each diagnostic message diagnostic_opts = { severity_sort = true }, -- options for diagnostic display } ``` + Most options are overwritten (e.g. passing `ft_config = { python = false }` will mean that `alpha`, `mason`, etc. are set to true) but `severity` and `diagnostic_opts` are merged, so that (for example) passing `{ spellbad = "HINT" }` won't cause `spellcap` to be nil. You can pass any of `cursor`, `iter`, `treesitter`, `false`, or `true` as options to `ft_config`. The default method is `cursor`, which iterates through the buffer with `]s`. There is also `iter`, which uses Treesitter (if available) and the Lua API. Finally, `false` disables Spellwarn for that filetype and `true` uses the default (`cursor`). The `suggest` option adds spelling suggestions to the diagnostic message, it does not allow auto-complete. `num_suggest` specifies the number of suggestions to show in the diagnostic message. **If you have `suggest` set to `true`, you need to also have `num_suggest >= 1` or else you will have an error.** *Note: `iter` doesn't show `spellcap` errors, but works well other than that. I recommend it.* ## Usage + The plugin should be good to go after installation with the provided snippet. It has sensible defaults. Run `:Spellwarn enable`, `:Spellwarn disable`, or `:Spellwarn toggle` to enable/disable/toggle during runtime (though this will *not* override `max_file_size`, `ft_config`, or `ft_default`). You can also add keybindings for any of these (one possible usecase would be disabling by default with the `enable` key of the configuration table and then only enabling when needed). To disable diagnostics on a specific line, add `spellwarn:disable-next-line` to the line immediately above or `spellwarn:disable-line` to a comment at the end of the line. To disable diagnostics in a file, add a comment with `spellwarn:disable` to the *first or second* line of the file. ## Lua API + The Lua API matches the arguments for the `Spellwarn` command: - `require("spellwarn").disable()` to disable - `require("spellwarn").enable()` to enable - `require("spellwarn").toggle()` to toggle ## Contributing + PRs and issues welcome! Please consult `CONTRIBUTING.md` for style guidelines. diff --git a/doc/spellwarn.txt b/doc/spellwarn.txt index 42c1043..edc3947 100644 --- a/doc/spellwarn.txt +++ b/doc/spellwarn.txt @@ -1,4 +1,4 @@ -*spellwarn.txt* For NVIM v0.8.0 Last change: 2025 September 03 +*spellwarn.txt* For NVIM v0.8.0 Last change: 2026 March 09 ============================================================================== Table of Contents *spellwarn-table-of-contents* @@ -31,7 +31,7 @@ configuration instructions. INSTALLATION *spellwarn-spellwarn.nvim-installation* -I recommend using Lazy.nvim +I recommend using Lazy.nvim : >lua { @@ -41,7 +41,7 @@ I recommend using Lazy.nvim } < -Notethat this uses Neovim’s built-in spellchecking. This requires putting +Note that this uses Neovim’s built-in spellchecking. This requires putting `vim.opt.spell = true` and `vim.opt.spelllang = [YOUR LANGUAGE HERE]` somewhere in your Neovim config if you haven’t already. You may also want to add the word "spellwarn" to your Neovim dictionary. This can be done by putting the @@ -50,7 +50,7 @@ cursor onto "spellwarn" and hitting `zg`. CONFIGURATION *spellwarn-spellwarn.nvim-configuration* -Pass any of the following options to `require("spellwarn").setup()` +Pass any of the following options to `require("spellwarn").setup()`: >lua { @@ -61,30 +61,41 @@ Pass any of the following options to `require("spellwarn").setup()` "TextChangedI", "TextChangedP", }, + enable = true, -- enable diagnostics on startup + + func_preprocess = function() end, -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set + + bt_config = { -- buffer types to run on + [""] = true, + }, + bt_default = false, -- default for types not in bt_config. + ft_config = { -- spellcheck method: "cursor", "iter", or boolean - alpha = false, - help = false, - lazy = false, + alpha = false, + help = false, + lazy = false, lspinfo = false, - mason = false, + mason = false, }, ft_default = true, -- default option for unspecified filetypes + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = "WARN", - spellcap = "HINT", + spellbad = "WARN", + spellcap = "HINT", spelllocal = "HINT", - spellrare = "INFO", + spellrare = "INFO", }, - suggest = false, -- show spelling suggestions in diagnostic message (works best with window-style message) - num_suggest = 3, -- number of spelling suggestions shown in diagnostic message + suggest = false, -- show spelling suggestions in diagnostic message + num_suggest = 3, -- number of suggestions shown in diagnostic message prefix = "possible misspelling(s): ", -- prefix for each diagnostic message diagnostic_opts = { severity_sort = true }, -- options for diagnostic display } < -Mostoptions are overwritten (e.g. passing `ft_config = { python = false }` +Most options are overwritten (e.g. passing `ft_config = { python = false }` will mean that `alpha`, `mason`, etc. are set to true) but `severity` and `diagnostic_opts` are merged, so that (for example) passing `{ spellbad = "HINT" }` won’t cause `spellcap` to be nil. You can pass any of `cursor`, diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 0a9f0dd..4d78a83 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -16,11 +16,6 @@ function M.update_diagnostics(opts, bufnr) if opts.max_file_size and vim.api.nvim_buf_line_count(bufnr) > opts.max_file_size then return end - local ft = vim.fn.getbufvar(bufnr, "&filetype") - if opts.ft_config[ft] == false or (opts.ft_config[ft] == nil and opts.ft_default == false) then - vim.diagnostic.reset(namespace, bufnr) - return - end local diags = {} for _, error in pairs(require("spellwarn.spelling").get_spelling_errors_main(opts, bufnr) or {}) do local msg = opts.prefix .. error.word @@ -50,26 +45,61 @@ function M.update_diagnostics(opts, bufnr) end end end + + -- Pre-process, if a function is set in opts to do anything. + diags = opts.func_preprocess(bufnr, diags) + -- TODO: Add suffix diagnostics with type of spelling error the way that LSP diagnostics do vim.diagnostic.set(namespace, bufnr, diags, opts.diagnostic_opts) end +local function can_update(opts, bufnr) + local winid = vim.api.nvim_get_current_win() + if winid then + if not vim.wo[winid].spell then + return false + end + end + + -- Allow the buffer type, or file type, to cancel the attempt to process the buffer. + local is_ok = true + + -- Buffer type check. + local buftype = vim.api.nvim_get_option_value("buftype", { buf = bufnr }) + if opts.bt_config[buftype] then + is_ok = opts.bt_config[buftype] + else + is_ok = opts.bt_default + end + + if not is_ok then + return false + end + -- Still OK to proceed. + + -- File type check. + local ft = vim.fn.getbufvar(bufnr, "&filetype") + if opts.ft_config[ft] then + is_ok = opts.ft_config[ft] + else + is_ok = opts.ft_default + end + + return is_ok +end + function M.setup(opts) function M.enable() vim.api.nvim_create_augroup("Spellwarn", {}) vim.api.nvim_create_autocmd(opts.event, { group = "Spellwarn", callback = function() - local winid = vim.api.nvim_get_current_win() local bufnr = vim.fn.bufnr("%") - if winid then - if not vim.wo[winid].spell then - vim.diagnostic.reset(namespace, bufnr) -- ensure old are cleared if spell is toggled to off. - return - end + if can_update(opts, bufnr) then + M.update_diagnostics(opts, bufnr) + else + vim.diagnostic.reset(namespace, bufnr) end - - M.update_diagnostics(opts, bufnr) end, desc = "Update Spellwarn diagnostics", }) diff --git a/lua/spellwarn/init.lua b/lua/spellwarn/init.lua index aa51d0d..2b55357 100644 --- a/lua/spellwarn/init.lua +++ b/lua/spellwarn/init.lua @@ -8,7 +8,19 @@ local defaults = { "TextChangedI", "TextChangedP", }, + enable = true, -- enable diagnostics on startup + + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set + func_preprocess = function(bufnr, diag_tbl) + return diag_tbl + end, + + bt_config = { -- buffer types to run on + [""] = true, + }, + bt_default = false, -- default for types not in bt_config. + ft_config = { -- spellcheck method: "cursor", "iter", or boolean alpha = false, help = false, @@ -17,7 +29,9 @@ local defaults = { mason = false, }, ft_default = true, -- default option for unspecified filetypes + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + severity = { -- severity for each spelling error type (false to disable diagnostics for that type) spellbad = "WARN", spellcap = "HINT", From a8983c994adbb8bf7a678071a10f0a382a28eb17 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Mar 2026 03:28:26 +0000 Subject: [PATCH 02/14] chore(build): auto-generate vimdoc --- doc/spellwarn.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/spellwarn.txt b/doc/spellwarn.txt index edc3947..7ae5ef8 100644 --- a/doc/spellwarn.txt +++ b/doc/spellwarn.txt @@ -1,4 +1,4 @@ -*spellwarn.txt* For NVIM v0.8.0 Last change: 2026 March 09 +*spellwarn.txt* For NVIM v0.8.0 Last change: 2026 March 10 ============================================================================== Table of Contents *spellwarn-table-of-contents* @@ -64,7 +64,10 @@ Pass any of the following options to `require("spellwarn").setup()`: enable = true, -- enable diagnostics on startup - func_preprocess = function() end, -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set + func_preprocess = function(bufnr, diag_tbl) + return diag_tbl + end, bt_config = { -- buffer types to run on [""] = true, From db254bb6e023b5e446b201794b86b56824af16fd Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:37:58 +1000 Subject: [PATCH 03/14] User can return nil, if they want, in the preprocessor, to mean 'show no SpellWarn diagnostics' --- lua/spellwarn/diagnostics.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 4d78a83..5368e57 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -47,7 +47,7 @@ function M.update_diagnostics(opts, bufnr) end -- Pre-process, if a function is set in opts to do anything. - diags = opts.func_preprocess(bufnr, diags) + diags = opts.func_preprocess(bufnr, diags) or {} -- TODO: Add suffix diagnostics with type of spelling error the way that LSP diagnostics do vim.diagnostic.set(namespace, bufnr, diags, opts.diagnostic_opts) From 3174cc4527a3a712d503cf2ecdde1054f1d2d152 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Tue, 10 Mar 2026 20:42:55 +1000 Subject: [PATCH 04/14] Fix: Added explicit nil checks in can_update() --- lua/spellwarn/diagnostics.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 5368e57..1ed08bc 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -66,7 +66,7 @@ local function can_update(opts, bufnr) -- Buffer type check. local buftype = vim.api.nvim_get_option_value("buftype", { buf = bufnr }) - if opts.bt_config[buftype] then + if opts.bt_config[buftype] ~= nil then is_ok = opts.bt_config[buftype] else is_ok = opts.bt_default @@ -79,7 +79,7 @@ local function can_update(opts, bufnr) -- File type check. local ft = vim.fn.getbufvar(bufnr, "&filetype") - if opts.ft_config[ft] then + if opts.ft_config[ft] ~= nil then is_ok = opts.ft_config[ft] else is_ok = opts.ft_default From be925bf43f0a318f5bb0afd14252d7e2a6e40773 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:47:20 +1000 Subject: [PATCH 05/14] Breaking Change: Added prefixes for each spelling error type. --- lua/spellwarn/diagnostics.lua | 8 ++++---- lua/spellwarn/init.lua | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 1ed08bc..db5cd91 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -18,7 +18,7 @@ function M.update_diagnostics(opts, bufnr) end local diags = {} for _, error in pairs(require("spellwarn.spelling").get_spelling_errors_main(opts, bufnr) or {}) do - local msg = opts.prefix .. error.word + local msg = error.word if opts.suggest and opts.num_suggest > 0 then local suggestions = vim.fn.spellsuggest(error.word, opts.num_suggest) local addition = "\nSuggestions:\n" @@ -38,9 +38,9 @@ function M.update_diagnostics(opts, bufnr) diags[#diags + 1] = { col = error.col - 1, -- 0-indexed lnum = error.lnum - 1, -- 0-indexed - message = msg, - severity = vim.diagnostic.severity[opts.severity[error.type]], - source = "spellwarn", + message = opts.severity[error.type].prefix .. msg, + severity = vim.diagnostic.severity[opts.severity[error.type].icon], + source = "SpellWarn", } end end diff --git a/lua/spellwarn/init.lua b/lua/spellwarn/init.lua index 2b55357..1fdc971 100644 --- a/lua/spellwarn/init.lua +++ b/lua/spellwarn/init.lua @@ -11,6 +11,11 @@ local defaults = { enable = true, -- enable diagnostics on startup + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + + suggest = false, -- show spelling suggestions in diagnostic message + num_suggest = 3, -- number of suggestions shown in diagnostic message + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set func_preprocess = function(bufnr, diag_tbl) return diag_tbl @@ -30,18 +35,13 @@ local defaults = { }, ft_default = true, -- default option for unspecified filetypes - max_file_size = nil, -- maximum file size to check in lines (nil for no limit) - + diagnostic_opts = { severity_sort = true }, -- options for diagnostic display severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = "WARN", - spellcap = "HINT", - spelllocal = "HINT", - spellrare = "INFO", + spellbad = { icon = "WARN", prefix = "Unknown Word: " }, + spellcap = { icon = "HINT", prefix = "Missing capital: " }, + spelllocal = { icon = "HINT", prefix = "Word Localization: " }, + spellrare = { icon = "INFO", prefix = "Rare Word: " }, }, - suggest = false, -- show spelling suggestions in diagnostic message - num_suggest = 3, -- number of suggestions shown in diagnostic message - prefix = "possible misspelling(s): ", -- prefix for each diagnostic message - diagnostic_opts = { severity_sort = true }, -- options for diagnostic display } function M.setup(opts) From daa250f8231a697c71f2b7b061e2506dad3d91d9 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:50:31 +1000 Subject: [PATCH 06/14] Feat: Added suffix option for each spelling error type. --- lua/spellwarn/diagnostics.lua | 2 +- lua/spellwarn/init.lua | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index db5cd91..1204d04 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -38,7 +38,7 @@ function M.update_diagnostics(opts, bufnr) diags[#diags + 1] = { col = error.col - 1, -- 0-indexed lnum = error.lnum - 1, -- 0-indexed - message = opts.severity[error.type].prefix .. msg, + message = opts.severity[error.type].prefix .. msg .. opts.severity[error.type].suffix, severity = vim.diagnostic.severity[opts.severity[error.type].icon], source = "SpellWarn", } diff --git a/lua/spellwarn/init.lua b/lua/spellwarn/init.lua index 1fdc971..53cc430 100644 --- a/lua/spellwarn/init.lua +++ b/lua/spellwarn/init.lua @@ -37,10 +37,10 @@ local defaults = { diagnostic_opts = { severity_sort = true }, -- options for diagnostic display severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = { icon = "WARN", prefix = "Unknown Word: " }, - spellcap = { icon = "HINT", prefix = "Missing capital: " }, - spelllocal = { icon = "HINT", prefix = "Word Localization: " }, - spellrare = { icon = "INFO", prefix = "Rare Word: " }, + spellbad = { icon = "WARN", prefix = "Unknown Word: ", suffix = "" }, + spellcap = { icon = "HINT", prefix = "Missing capital: ", suffix = "" }, + spelllocal = { icon = "HINT", prefix = "Word Localization: ", suffix = "" }, + spellrare = { icon = "INFO", prefix = "Rare Word: ", suffix = "" }, }, } From f47fbd6e5ad1f3ced14373fbd79ee92ee6a761f2 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:51:50 +1000 Subject: [PATCH 07/14] Chore: Removed TODO for suffix feature. --- lua/spellwarn/diagnostics.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 1204d04..6a61c80 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -49,7 +49,6 @@ function M.update_diagnostics(opts, bufnr) -- Pre-process, if a function is set in opts to do anything. diags = opts.func_preprocess(bufnr, diags) or {} - -- TODO: Add suffix diagnostics with type of spelling error the way that LSP diagnostics do vim.diagnostic.set(namespace, bufnr, diags, opts.diagnostic_opts) end From e8b8328158f6402ce0faa5f2290e2e245c60d2fb Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:55:31 +1000 Subject: [PATCH 08/14] Chore: Rename 'icon' to more appropriate 'level' in severity tables. --- lua/spellwarn/diagnostics.lua | 2 +- lua/spellwarn/init.lua | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 6a61c80..4957e74 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -39,7 +39,7 @@ function M.update_diagnostics(opts, bufnr) col = error.col - 1, -- 0-indexed lnum = error.lnum - 1, -- 0-indexed message = opts.severity[error.type].prefix .. msg .. opts.severity[error.type].suffix, - severity = vim.diagnostic.severity[opts.severity[error.type].icon], + severity = vim.diagnostic.severity[opts.severity[error.type].level], source = "SpellWarn", } end diff --git a/lua/spellwarn/init.lua b/lua/spellwarn/init.lua index 53cc430..2738894 100644 --- a/lua/spellwarn/init.lua +++ b/lua/spellwarn/init.lua @@ -37,10 +37,10 @@ local defaults = { diagnostic_opts = { severity_sort = true }, -- options for diagnostic display severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = { icon = "WARN", prefix = "Unknown Word: ", suffix = "" }, - spellcap = { icon = "HINT", prefix = "Missing capital: ", suffix = "" }, - spelllocal = { icon = "HINT", prefix = "Word Localization: ", suffix = "" }, - spellrare = { icon = "INFO", prefix = "Rare Word: ", suffix = "" }, + spellbad = { level = "WARN", prefix = "Unknown Word: ", suffix = "" }, + spellcap = { level = "HINT", prefix = "Missing capital: ", suffix = "" }, + spelllocal = { level = "HINT", prefix = "Word Localization: ", suffix = "" }, + spellrare = { level = "INFO", prefix = "Rare Word: ", suffix = "" }, }, } From f4b3d367f1cd0165a1b5e9b2f41d1638bb7f449a Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Wed, 11 Mar 2026 01:59:17 +1000 Subject: [PATCH 09/14] Chore: Updated README.md. --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4d7df87..0ff1103 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,11 @@ Pass any of the following options to `require("spellwarn").setup()`: enable = true, -- enable diagnostics on startup + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + + suggest = false, -- show spelling suggestions in diagnostic message + num_suggest = 3, -- number of suggestions shown in diagnostic message + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set func_preprocess = function(bufnr, diag_tbl) return diag_tbl @@ -59,19 +64,13 @@ Pass any of the following options to `require("spellwarn").setup()`: }, ft_default = true, -- default option for unspecified filetypes - max_file_size = nil, -- maximum file size to check in lines (nil for no limit) - + diagnostic_opts = { severity_sort = true }, -- options for diagnostic display severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = "WARN", - spellcap = "HINT", - spelllocal = "HINT", - spellrare = "INFO", + spellbad = { level = "WARN", prefix = "Unknown Word: ", suffix = "" }, + spellcap = { level = "HINT", prefix = "Missing capital: ", suffix = "" }, + spelllocal = { level = "HINT", prefix = "Word Localization: ", suffix = "" }, + spellrare = { level = "INFO", prefix = "Rare Word: ", suffix = "" }, }, - suggest = false, -- show spelling suggestions in diagnostic message - num_suggest = 3, -- number of suggestions shown in diagnostic message - prefix = "possible misspelling(s): ", -- prefix for each diagnostic message - diagnostic_opts = { severity_sort = true }, -- options for diagnostic display -} ``` Most options are overwritten (e.g. passing `ft_config = { python = false }` will mean that `alpha`, `mason`, etc. are set to true) but `severity` and `diagnostic_opts` are merged, so that (for example) passing `{ spellbad = "HINT" }` won't cause `spellcap` to be nil. You can pass any of `cursor`, `iter`, `treesitter`, `false`, or `true` as options to `ft_config`. The default method is `cursor`, which iterates through the buffer with `]s`. There is also `iter`, which uses Treesitter (if available) and the Lua API. Finally, `false` disables Spellwarn for that filetype and `true` uses the default (`cursor`). The `suggest` option adds spelling suggestions to the diagnostic message, it does not allow auto-complete. `num_suggest` specifies the number of suggestions to show in the diagnostic message. **If you have `suggest` set to `true`, you need to also have `num_suggest >= 1` or else you will have an error.** From 5e7ef364a90899c27c47388944605924ed634c14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Mar 2026 16:13:30 +0000 Subject: [PATCH 10/14] chore(build): auto-generate vimdoc --- doc/spellwarn.txt | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/doc/spellwarn.txt b/doc/spellwarn.txt index 7ae5ef8..8308777 100644 --- a/doc/spellwarn.txt +++ b/doc/spellwarn.txt @@ -64,6 +64,11 @@ Pass any of the following options to `require("spellwarn").setup()`: enable = true, -- enable diagnostics on startup + max_file_size = nil, -- maximum file size to check in lines (nil for no limit) + + suggest = false, -- show spelling suggestions in diagnostic message + num_suggest = 3, -- number of suggestions shown in diagnostic message + -- function to do any custom processing of the diagnostics table before passing it to vim.diagnostic.set func_preprocess = function(bufnr, diag_tbl) return diag_tbl @@ -83,19 +88,13 @@ Pass any of the following options to `require("spellwarn").setup()`: }, ft_default = true, -- default option for unspecified filetypes - max_file_size = nil, -- maximum file size to check in lines (nil for no limit) - + diagnostic_opts = { severity_sort = true }, -- options for diagnostic display severity = { -- severity for each spelling error type (false to disable diagnostics for that type) - spellbad = "WARN", - spellcap = "HINT", - spelllocal = "HINT", - spellrare = "INFO", + spellbad = { level = "WARN", prefix = "Unknown Word: ", suffix = "" }, + spellcap = { level = "HINT", prefix = "Missing capital: ", suffix = "" }, + spelllocal = { level = "HINT", prefix = "Word Localization: ", suffix = "" }, + spellrare = { level = "INFO", prefix = "Rare Word: ", suffix = "" }, }, - suggest = false, -- show spelling suggestions in diagnostic message - num_suggest = 3, -- number of suggestions shown in diagnostic message - prefix = "possible misspelling(s): ", -- prefix for each diagnostic message - diagnostic_opts = { severity_sort = true }, -- options for diagnostic display - } < Most options are overwritten (e.g. passing `ft_config = { python = false }` From d47b01163f3cc7f908f3f98aada16544e65bc966 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:17:57 +1000 Subject: [PATCH 11/14] Feat: Added quickfix list of spelling errors. --- README.md | 3 +++ lua/spellwarn/diagnostics.lua | 4 +++- lua/spellwarn/init.lua | 3 +++ lua/spellwarn/qflist.lua | 20 ++++++++++++++++++++ lua/spellwarn/spelling.lua | 1 + 5 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 lua/spellwarn/qflist.lua diff --git a/README.md b/README.md index 0ff1103..116f28e 100644 --- a/README.md +++ b/README.md @@ -81,12 +81,15 @@ Most options are overwritten (e.g. passing `ft_config = { python = false }` will The plugin should be good to go after installation with the provided snippet. It has sensible defaults. Run `:Spellwarn enable`, `:Spellwarn disable`, or `:Spellwarn toggle` to enable/disable/toggle during runtime (though this will *not* override `max_file_size`, `ft_config`, or `ft_default`). You can also add keybindings for any of these (one possible usecase would be disabling by default with the `enable` key of the configuration table and then only enabling when needed). To disable diagnostics on a specific line, add `spellwarn:disable-next-line` to the line immediately above or `spellwarn:disable-line` to a comment at the end of the line. To disable diagnostics in a file, add a comment with `spellwarn:disable` to the *first or second* line of the file. +`Spellwarn qflist` will show the quickfix window, populated with the list of spelling mistakes in the current buffer. + ## Lua API The Lua API matches the arguments for the `Spellwarn` command: - `require("spellwarn").disable()` to disable - `require("spellwarn").enable()` to enable - `require("spellwarn").toggle()` to toggle +- `require("spellwarn").qflist()` to show the quickfix window ## Contributing diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 4957e74..181bcdd 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -132,13 +132,15 @@ function M.setup(opts) M.disable() elseif arg == "toggle" then M.toggle() + elseif arg == "qflist" then + require("spellwarn.qflist").qflist(opts) else vim.api.nvim_echo({ { "Invalid argument: " .. arg .. "\n" } }, true, { err = true }) end end, { nargs = 1, complete = function() - return { "disable", "enable", "toggle" } + return { "disable", "enable", "toggle", "qflist" } end, }) diff --git a/lua/spellwarn/init.lua b/lua/spellwarn/init.lua index 2738894..41b501a 100644 --- a/lua/spellwarn/init.lua +++ b/lua/spellwarn/init.lua @@ -58,6 +58,9 @@ function M.setup(opts) M.enable = require("spellwarn.diagnostics").enable M.disable = require("spellwarn.diagnostics").disable M.toggle = require("spellwarn.diagnostics").toggle + M.qflist = function() + require("spellwarn.qflist").qflist(defaults) + end end return M diff --git a/lua/spellwarn/qflist.lua b/lua/spellwarn/qflist.lua new file mode 100644 index 0000000..e49e155 --- /dev/null +++ b/lua/spellwarn/qflist.lua @@ -0,0 +1,20 @@ +local M = {} + +function M.qflist(opts) + local spelling = require("spellwarn.spelling").get_spelling_errors_main(opts, 0) + local list = {} + + for _, mistake in ipairs(spelling) do + local qf_item = { + bufnr = vim.api.nvim_get_current_buf(), + col = mistake.col, + lnum = mistake.lnum, + text = opts.severity[mistake.type].prefix .. mistake.word .. opts.severity[mistake.type].suffix, + } + list[#list + 1] = qf_item + end + vim.fn.setqflist(list, "r") + vim.cmd("copen") +end + +return M diff --git a/lua/spellwarn/spelling.lua b/lua/spellwarn/spelling.lua index 2f6256d..2cf44dd 100644 --- a/lua/spellwarn/spelling.lua +++ b/lua/spellwarn/spelling.lua @@ -20,6 +20,7 @@ function M.get_spelling_errors_main(opts, bufnr) local bufopts = opts.ft_config[vim.o.filetype] or opts.ft_default local disable_comment = string.find(vim.fn.getline(1) .. vim.fn.getline(2), "spellwarn:disable", 1, true) ~= nil + -- TODO: Is this test block necessary anymore? if vim.api.nvim_get_mode().mode == "i" or disable_comment From 1c640acf7a72c9a46dbfa7682bf604bf0d49ad9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Mar 2026 03:18:23 +0000 Subject: [PATCH 12/14] chore(build): auto-generate vimdoc --- doc/spellwarn.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/spellwarn.txt b/doc/spellwarn.txt index 8308777..f862dd1 100644 --- a/doc/spellwarn.txt +++ b/doc/spellwarn.txt @@ -1,4 +1,4 @@ -*spellwarn.txt* For NVIM v0.8.0 Last change: 2026 March 10 +*spellwarn.txt* For NVIM v0.8.0 Last change: 2026 March 13 ============================================================================== Table of Contents *spellwarn-table-of-contents* @@ -128,12 +128,16 @@ when needed). To disable diagnostics on a specific line, add diagnostics in a file, add a comment with `spellwarn:disable` to the _first or second_ line of the file. +`Spellwarn qflist` will show the quickfix window, populated with the list of +spelling mistakes in the current buffer. + LUA API *spellwarn-spellwarn.nvim-lua-api* The Lua API matches the arguments for the `Spellwarn` command: - `require("spellwarn").disable()` to disable - `require("spellwarn").enable()` -to enable - `require("spellwarn").toggle()` to toggle +to enable - `require("spellwarn").toggle()` to toggle - +`require("spellwarn").qflist()` to show the quickfix window CONTRIBUTING *spellwarn-spellwarn.nvim-contributing* From caa46a10eb3a8dc791a13e8755e1e71fb9e05e3e Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Sat, 21 Mar 2026 08:07:25 +1000 Subject: [PATCH 13/14] Added dirty flag to prevent unnecessary updates. --- lua/spellwarn/diagnostics.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 181bcdd..1092d34 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -1,6 +1,8 @@ local M = {} local namespace = vim.api.nvim_create_namespace("Spellwarn") +local flag_text_changed = true -- Prevent updating when nothing was changed (CursorHold navigation, etc) + local function get_bufs_loaded() local bufs_loaded = {} for i, buf_hndl in ipairs(vim.api.nvim_list_bufs()) do @@ -53,6 +55,10 @@ function M.update_diagnostics(opts, bufnr) end local function can_update(opts, bufnr) + if not flag_text_changed then + return false + end + local winid = vim.api.nvim_get_current_win() if winid then if not vim.wo[winid].spell then @@ -88,13 +94,23 @@ local function can_update(opts, bufnr) end function M.setup(opts) + local group_name = "Spellwarn" function M.enable() - vim.api.nvim_create_augroup("Spellwarn", {}) + vim.api.nvim_create_augroup(group_name, {}) + + vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { + group = group_name, + callback = function() + flag_text_changed = true + end, + }) + vim.api.nvim_create_autocmd(opts.event, { - group = "Spellwarn", + group = group_name, callback = function() local bufnr = vim.fn.bufnr("%") if can_update(opts, bufnr) then + flag_text_changed = false M.update_diagnostics(opts, bufnr) else vim.diagnostic.reset(namespace, bufnr) From 515b45ff1b189131207ceac19d1fd3a419221268 Mon Sep 17 00:00:00 2001 From: Corey B <14075562+coreyb-git@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:52:11 +1000 Subject: [PATCH 14/14] Fix: Added BufEnter trigger to set the dirty flag, forcing an initial run of the diagnostics being set. --- lua/spellwarn/diagnostics.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/spellwarn/diagnostics.lua b/lua/spellwarn/diagnostics.lua index 1092d34..a6601f0 100644 --- a/lua/spellwarn/diagnostics.lua +++ b/lua/spellwarn/diagnostics.lua @@ -55,10 +55,6 @@ function M.update_diagnostics(opts, bufnr) end local function can_update(opts, bufnr) - if not flag_text_changed then - return false - end - local winid = vim.api.nvim_get_current_win() if winid then if not vim.wo[winid].spell then @@ -97,8 +93,8 @@ function M.setup(opts) local group_name = "Spellwarn" function M.enable() vim.api.nvim_create_augroup(group_name, {}) - - vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, { + -- BufEnter to trigger an initial pass through. + vim.api.nvim_create_autocmd({ "BufEnter", "TextChanged", "TextChangedI" }, { group = group_name, callback = function() flag_text_changed = true @@ -108,6 +104,10 @@ function M.setup(opts) vim.api.nvim_create_autocmd(opts.event, { group = group_name, callback = function() + if not flag_text_changed then + return false + end + local bufnr = vim.fn.bufnr("%") if can_update(opts, bufnr) then flag_text_changed = false