Alternate implementation for spell checker


Frustrated by some limitations of the default spell plugin, I decided to try and rewrite it from scratch to make it more flexible and hackable - which involves writing as little KakScript as possible (for me at least)


  • leverage existing lint.kak plugin for jumping to mistakes
  • show a menu with suggestions
  • New: add and remove kakoune selections to and from personal word list - this is what I missed the most when coming from vim
  • New: use any backend supported by enchant, not just aspell

Enough talk, show me the code!

There are two parts:

The first part is a Python script that wraps enchant:

Note that the logic to display the kakoune completion menu is implemented there, where it is easy to test/debug :slight_smile:

The second part is a rewrite from scratch of the spell.kak implementation. You can see the diff on GitHub

I hope you’ll agree it’s easier to read, maintain and extend.

Future work

Right now the plugin can only be used by patching the code of kakoune and installing a separate python project (and deal with pyenchant)

This not ideal, so if you like the idea of such a plugin and its implementation, feel free to tell me below and we can discuss easier ways to distribute the plugin - I kinda like the idea of just a Rust binary, ala kak-slp.

Also, I’m pretty satisfied with the features so far, but let me know if you have requests that would be hard to implement in the original spell.kak plugin (besides checking several languages at once - this is sadly not supported by Enchant)



I will test with it soon, it sounds very promising!
For distributing I suggest that you put it into a git repository so that it can be loaded by plug.kak

The second part is a rewrite from scratch of the plug.kak implementation.

You mean of the spell.kak implementation?

You mean of the spell.kak implementation?

Woops sorry :slight_smile: My mind was already thinking about distribution and plug.kak was typed …

1 Like

I am going to give this a run, currently using dictcomplete.kak. I’ll be back, bye.

A stumbling block for me is lint-on-save cannot find any reference to this command. Can you please explain its purpose, usage, or help me if I am doing something wrong. Thanks Dmere.

A stumbling block for me is lint-on-save

Woops, sorry. This is a command that adds a hook to call :lint when the buffer is saved.

Here is its implementation:

define-command lint-on-save "hook buffer BufWritePre .* lint"

It lives in my kakrc config file and I forgot that kak-spell depended on it …

Thank you. I’ll be back with some feedback or more questions, probably both :smile:

A little more hand-holding please, after viewing the pictures below to double-check the setup is correct. The problem is I get a lint-output debug buffer with no corresponding errors, regardless of the buffers contents which I have just parsed with kak-spell.

Obviously, this is an incorrect setup issue on my behalf, is there a pitfall that I may be unaware of?

As always any help you can provide is appreciated.




I have $kak_opt_spell_lang hard coded to en-AU.
aspell is also installed on the machine.

You can try running kak-spell directly from the command line and see what happens:

kak-spell --lang en_AU check path/to/file

Fun fact: I’m also the maintainer of PyEnchant, and you may want to upgrade to the latest 3.0 release candidate:

python3 -m pip install enchant==3.0.0rc3

Thanks, I had to go with a work around as my last major use with python was 3 years ago.

I maintained the above dmerejkowsky.kak file and just replaced every occurrence of kak-spell … with:
python3 $kak_config/plugins/kak-spell/kak_spell/ …
and updated the line 8 from .checker import Checker to from checker import Checker just removing the . seemed to do the trick.

Take a look at this readme’s Building, Installing sections and graalvm. If a user has limited or no experience with gradle, java, or graalvm it wouldn’t be problematic to use this repo.

I’m not saying this is what you have too or should do, we’re all friends, so just some positive feedback on some teething pains I had to get going with kak-spell. It’s a good problem, as you have expanded my knowledge with enchant spell-checker.

Which I have still implemented incorrectly :frowning:. But it now works :slight_smile:.

Thanks dmerejkowsky.

I worked out this coexistence with inbuilt spell which may help out:
define-command lint-on-save "hook buffer BufWritePre .*[.](md|markdown) lint"
I can now use your/my adapted enchant-spell and the default spell on the same buffer without lintcmd conflicts.

Yeah, I really should tackle the “future work” one day or another …

I guess I could at least take the time to properly release the project on so it gets easier to install - but to do that I need to finish working on the 3.0 release of PyEnchant itself.

Well, that’s a very nice way to put it. Thank you.

Cool, I did a little digging into Poetry something I am defiantly going to look at in more depth. Thanks again.

I finally got around to testing this.
I had to do a few extra steps to get it working.

  1. Install the python module path.
  2. Adapt the source code of spell.kak to make it up to date with the changes in lint.kak. And to actually call python instead of kak-spell which does not yet exist.

To make distribution a bit easier, this folder can simply be put into your autoload directory to enable the plugin.

I added some configuration which makes it easier for me to use:

    set-option global spell_lang en
    declare-user-mode spell
    map global spell -docstring "Enable" e ': spell %opt{spell_lang}<ret>'
    map global spell -docstring "Lint" u ': lint-buffer<ret>'
    map global spell -docstring "Next" n ': lint-next-message<ret>'
    map global spell -docstring "Replace" r ': spell-replace<ret>'
    map global spell -docstring "Clear" c ': spell-clear<ret>'


lint-next-message does not jump to the correct column, which makes it harder to see which word is actually wrong, and you have to manually jump to it to replace it. I have not looked into it to check if this is a lint.kak or a kak-spell issue.

I would suggest that you make a separate git repository with as contents the folder I provided.
It might also be a good idea to add a default spell_lang and declare a user mode as shown above.

Previously, lint.kak stored lint messages in a range-specs option, which included a line and column for both the start and end points… but it didn’t have end point information, so it set the end point to the same as the start point. And then it didn’t really use the column of the start-point either, it always displayed the lint message next to the cursor. So I simplified the whole thing to use a line-specs option instead, which only stores line numbers.

It’d be neat to generalise lint.kak to support all the common variants of the traditional Unix diagnostic format:

filename:line: message
filename:line:column: message
filename:line:column:line:column: message

…but that’s a job for a future contributor, I think.

1 Like

So I simplified the whole thing to use a line-specs option instead, which only stores line numbers.

I don’t follow. Did you change kak-sepll.kak or lint.kak ?

Should we fix the output of kak-spell to contain start and end columns ?

Anyway, I published kak-spell on, which makes its installation much easier, and adapted the README.

Thank you everyone for your kind words and suggestions.

Hopefully installation should be much more smooth now.

@JJK96 I think that mappings do not belong in the plugin itself so I just left them in the README

I rewrote large chunks of lint.kak. The new version has mostly the same API, except that lint-{next,previous}-error has become lint-{next,previous}-message, and the lint_errors option (of type range-specs) has become the lint_messages option (of type line-specs).

If your plugin is just setting lintcmd and running :lint or :lint-buffer, you should be fine and shouldn’t need to change anything.

Cool :slight_smile:

Nice that it is published in an easier way now!

I think the reference lint-on-save command should either be left out of the kak-spell.kak file or the implementation should be included, possibly with a setting so you can set it on or off in your configuration.

Currently the code depends on the lint-on-save command being defined, which is not a good idea IMO.

So, it turns out that the latest changes in lint.kak broke my plugin. See this example:

this is some text
there is two spellig errors herre

With the latestlint.kak, I get a line-spec on line 2, but what I need are two range specs to select spellig and herre

I’ve just published version 0.2.1. where I’ve rewritten the plugin to not depend on lint.kak at all.
See the changelog for more details - I had to make a few breaking changes.