Skip to content

mkdocs-spellcheck

This is a simple plugin for mkdocs but maybe too simple. It has one option to disable spelling checks in code blocks. But i want it also to disable all tags as well.

deprecated !!!

This solution is deprecated since we installed mkdocs-spellcheck 1.1.1

Note that you can skip this chapter, since we got problems with warnings of the deprecated usage of .path() in de the mkdocs-spellcheck module. So we install 1.1.1 to fix that. Instead of re-merging the ignore_anchors option we just re-wrote the md files to just include links like this :

[name](url) # url part is not checked for spelling
# instead of 
<url> # url part will be spell-checked.

So from vim that means you should first search in the .md file for tags that are not meant as urls,like :

<CR> 
template<int>

If there are a lot of those do this per line :

:.s/<\([^>]*\)>/[visit](\1)/g

If not do the complete file at once :

:%s/<\([^>]*\)>/[visit](\1)/g

TLDR

See above.. not needed anymore !!

pip3 install mkdocs
export PATH=$PATH:~/.local/bin
mkdocs
cp -r ~/projects/doc/klopt/mkdocs_spellcheck ~/.local/lib/python3.11/site-packages/
# or when using virtual env :
cp mkdocs-spellcheck/src/mkdocs_spellcheck/*.py venv/lib/python3.11/site-packages/mkdocs_spellcheck/

mkdocs

I usually just put <> around a link which turns it into a full text link.

<https://www.vrotgnak.com>    # in md
# becomes in html :
<a href="https://www.gnakvrot.com">https://www.vrotgnak.com</a>

Now if you don't have vrotgnak in your dict this will give an error. And urls do that all the time, the dict.txt file is littered with fixes for urls.

So i altered the mkdocs-spellcheck code in ~/projects/doc/mkdocs-spellcheck.

cd ~/project/mkdocs-spellcheck/src/mkdocs-spellcheck
git diff

The changes are just a blunt copy of the code option. In mkdocs.yml you can see

plugins:
- search
- spellcheck:
    # known_words can also be a list of words
    known_words: dict.txt

    # ignore words in <code> tags
    ignore_code: yes

    # just added this myself !
    ignore_anchors: yes

...

The mechanism is exactly the same except

  • ignore_code disables content
  • ignore_anchors disables content

Now it is possible that an contains

<a>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur quis ex id rutrum. Donec commodo, dolor in auctor feugiat, mi elit volutpat eros, in condimentum lorem urna id ex. Pellentesque viverra eros ligula, et luctus nibh faucibus eu. Fusce viverra tellus et mattis tristique. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sit amet sem vel augue aliquam feugiat. Ut maximus elit vitae nibh malesuada ullamcorper.</a>

But why do that ? (i don't), you get what you deserve.

todo: how to install the correct way.

The best way in my opinion is to make the altered mkdocs-spellcheck code part of the docs repository and fix the path so it takes that version.

So a good position would be in the mkdocs directory where we run the server.

top directory
cd ~/projects/doc/klopt
mkdocs serve

If you want to see the path currently used, you can do:

sys.path
python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
+>>> import sys
+>>> print('\n'.join(sys.path))
output
/usr/lib/python39.zip
/usr/lib/python3.9
/usr/lib/python3.9/lib-dynload
/home/kees/.local/lib/python3.9/site-packages
/usr/local/lib/python3.9/dist-packages
/usr/local/lib/python3.9/dist-packages/ibapi-9.76.1-py3.9.egg
/usr/local/lib/python3.9/dist-packages/pythonwhois-2.4.3-py3.9.egg
/usr/local/lib/python3.9/dist-packages/argparse-1.4.0-py3.9.egg
/usr/local/lib/python3.9/dist-packages/nassl-1.1.3-py3.9-linux-x86_64.egg
/usr/lib/python3/dist-packages
/usr/lib/python3.9/dist-packages

Note that the first path is an empty line, let's see if that means current directory. First install mkdocs-spellcheck in the current directory.

pip3 install mkdocs-spellcheck -t .

Now here is how you can see what is actually used in python

check used path
import mkdocs_spellcheck
print(mkdocs_spellcheck.__file__)
output
/data/projects/tasks/vue/tasks/mkdocs_spellcheck/__init__.py

That is indeed the one we want !. BUT that does not mean mkdocs will use it.

which mkdocs
/usr/local/bin/mkdocs

It would be a safety hazard to make that program use the current directory first!. So it figures that it uses the system directories first. And that is indeed what it does.

So if we want this current directory to be used we probably have to install mkdocs itself in the current directory as well. May be a bit too much.

What would be better is to make an installer for the package and use that.

local change

I just installed the changes into the local installation for user kees. The site-packages for that are in :

~/.local/lib/python3.9/site-packages/mkdocs_spellcheck 

So reinstalling this will disable the changes again !! Repair with :

cp src/mkdocs_spellcheck/*.py ~/.local/lib/python3.9/site-packages/mkdocs_spellcheck/

You will see if you have a correct path if this WARNING message disappears:

INFO     -  Building documentation...
WARNING  -  Config value: 'plugins'. Warning: ('ignore_anchors', 'Unrecognised
            configuration name: ignore_anchors')

Last time it was missing, the actual restore commands were :

cd ~/projects/doc/mkdocs-spellcheck
cp mkdocs-spellcheck/src/mkdocs_spellcheck/*.py venv/lib/python3.11/site-packages/mkdocs_spellcheck/

all changes

For now here is a diff

diff /home/kees/.local/lib/python3.9/site-packages/mkdocs_spellcheck/plugin.py mkdocs_spellcheck/plugin.py
43d42
<         ("ignore_anchors", MkType(bool, default=True)),
68d66
<         self.ignore_anchors = self.config["ignore_anchors"]
99d96
<                 ignore_anchors=self.ignore_anchors,
Common subdirectories: /home/kees/.local/lib/python3.9/site-packages/mkdocs_spellcheck/__pycache__ and mkdocs_spellcheck/__pycache__
diff /home/kees/.local/lib/python3.9/site-packages/mkdocs_spellcheck/words.py mkdocs_spellcheck/words.py
13c13
<     def __init__(self, ignore_code=True, ignore_anchors=True):
---
>     def __init__(self, ignore_code=True):
20d19
<         self.ignore_anchors = ignore_anchors
22d20
<         self.in_anchor_tag = False
27,28c25
<         if tag == "a":
<             self.in_anchor_tag = True
---
>         self.text.write(" ")
30c27
<     def handle_endtag(self, tag):
---
>     def handle_stoptag(self, tag, attrs):
33,34d29
<         if tag == "a":
<             self.in_anchor_tag = False
37,42c32,33
<         if (self.ignore_code and self.in_code_tag):
<             return
<         if (self.ignore_anchors and self.in_anchor_tag):
<             return
< 
<         self.text.write(data)
---
>         if not (self.ignore_code and self.in_code_tag):
>             self.text.write(data)
48,49c39,40
< def _strip_tags(html, ignore_code, ignore_anchors):
<     stripper = _MLStripper(ignore_code, ignore_anchors)
---
> def _strip_tags(html, ignore_code):
>     stripper = _MLStripper(ignore_code)
89d79
<     ignore_anchors: bool = True,
100d89
<         ignore_anchors: Ignore words in anchors.
108c97
<     filtered = filter(keep, _normalize(_strip_tags(html, ignore_code, ignore_anchors), allow_unicode).split("-"))
---
>     filtered = filter(keep, _normalize(_strip_tags(html, ignore_code), allow_unicode).split("-"))