# Can the vim spell checker be used non-interactively?

Background I want to spell check latex documents which were mostly generated by software (R). Some contain a modeline specifying spelllang. At the moment I'm manually checking them, but replacing in the generated text doesn't make much sense as they will regenerate wrong.

I was experimenting with aspell/ispell but I'm disappointed so far. They do a much worse job than the vim spell checker (which I have nicely configured for my needs by now) to identify environments that shouldn't be checked.

The majority of content is tables and text inside Tikz figures - so it's not trivial to figure out what should be checked. The errors are mostly from bad entries in my input data, but also a few wrong (automatic) plurals and accidentally missing word separators.

Question Is there a way I can call the vim spellchecker non-interactively in a way similar to aspell list so it will give me a list of bad words? And preferable respects the spelllang in the modeline.

The workflow I'm looking for is to have some kind of unit test to quickly check my generated tex files for errors.
The main problem I'm battling that I currently drown in "spelling errors" of latex parameters, formulas, citation keys and chemical names. And while I can reasonable exclude blocks, there are sub items that I want to have checked again. E.g. in a figure I don't want the drawing commands checked, but I do need the labels to be checked.

Over the last year I have developed a reasonable configuration for vim by extending the syntax definition of tex.

I know that aspell and the like would fit my workflow better. I gave it a try and notice I will need to create my own .aspell.conf. What put me down is that the default config for tex lacks not only definitions for macros I consider standard (like babel and natbib) but I also saw no way how to configure it to spell check, when I didn't want the parent spell checked (the figure example). I gave ispell a shorter try but quickly was frustrated, as information about defining tex statements was practically non-existent.

Talking about the modeline - sometimes it contains:

% vim: spelllang=de


TLDR: not sure if a configuration like I require is possible at all, definitely requires serious configuration. As I have already a reasonable configured utility at hand I would prefer to use that.

Examples Unfortunately I can't share my actual files, but my main issues would be:

Text inside equations, that should be spelled:

$\textrm{Let } x = \textrm{ number of cats}$


Avoiding additional errors from environments that take an option:

\begin{tabular}{lcc}

• You make a good point. I added some explanation. My main issue is I would prefer not having to configure and maintain another tool. – bdecaf Jan 26 '20 at 8:56
• Cool. BTW, for what it's worth, I saw some aspell vs ispell comparison in, I think, the aspell documentation and it said one of the (few) things ispell does better than aspell is spellcheck (La)Tex docs. Doesn't sound like you had much luck with ispell but perhaps there's something you missed. (Wish I could say what...I just remember that blurb.) – B Layer Jan 26 '20 at 12:12
• Well it's not bad. However the latex support feels dated and unaware of modern syntax and plugins. Also vim has extremely powerful syntax definitions. And good documentation. – bdecaf Jan 27 '20 at 16:37

While vim can be run in "batch mode" by passing commands you want to run from the command-line, spellchecking is a very interactive affair that doesn't translate to such a workflow.

So the next question to ask is whether some vimscript could be written to accomplish something like that. Well, there's a glimmer of hope due to the existence of a function called spellbadword(). It has two modes. With the first you pass it a sentence and it will return the first spelling error it identifies. The second mode is the relevant one: pass nothing and it'll check words under or after the cursor to the end of the cursor line. If something is found the word is returned and the cursor moved to it. Otherwise empty string is returned and the cursor left where it is.

So one can imagine a script that marches line by line through a file(s) and dumps all the reported bad words. Run vim in the aforementioned batch mode wherein the command to invoke the script is specified along with the files to check. It might be slow but it could work.

If this sounds usable to you I could write such a script fairly easily I think. Otherwise I believe the answer to the question about using vim how you suggest is: "not possible".

UPDATE: Whipped this up as a proof-of-concept. Not going to win any awards for it but it works...

func! SpellCheckBatch(outfile, ...)
try
set spell
exe "redi! >>" . fnameescape(a:outfile)
argdo call SpellCheckFile(a:0 ? a:1 : 0)
finally
redi END
set nospell
endtry
endfunc

func! SpellCheckFile(limit)
let currline = nextnonblank(1)
let found = 0
while currline
call setpos(".", [0, currline, 1,  0])

while len(spellret[0])
" ignoring: 'rare', 'local' (region), 'caps'
let curpos = getcurpos()
echom "File:" bufname("") "Line:" curpos[1] "Col:" curpos[2] "Word:" spellret[0]

let found += 1
if a:limit && found == a:limit
return
endif
endif

" move cursor to next word. for now hack it with norm
norm! W
endwhile

let currline = nextnonblank(currline + 1)
endwhile
endfunc


Vim invocation...

vim -R -e -S SCRIPTFILE -c ':call SpellCheckBatch("OUTFILE")' -c 'q!' file1 file2...


Where SCRIPTFILE is a file containing the two functions above, OUTFILE is wherever you want the results to go, and file1, file2, etc are the files to spellcheck.

UPDATE 2: I modified the script so you can optionally supply a limit to the number of spell errors identified per file. The vim invocation above has no limit. If you want to display no more than one spell error per file add a parameter with value 1 to the function call. So that part of the command line would look like: ':call SpellCheckBatch("OUTFILE", 1)'

Conclusion: So turning to your original questions we can summarize like so:

Is there a way I can call the vim spellchecker non-interactively...?

Yes, though some scripting is required.

...in a way similar to aspell list so it will give me a list of bad words?

Yes, as demonstrated. The exact output format is flexible...the script can be modified to suit individual needs.

...preferable respects the spelllang in the modeline?

Yes. Tested this by running an input file containing German text and modeline vim: spelllang=de through the script. Test passed.

• BTW, it would be nice to write something like this for GrammarousCheck. – eyal karni Jan 26 '20 at 23:58
• Looks interesting - in the mean time I was digging in neovims test for spell if they had something more elegant. Only real difference is they use norm ]s. Hmmm thinking about it - as I aim for a no error scenario maybe I could get away just outputting the first reported spelling error... – bdecaf Jan 27 '20 at 16:43
• I wanted to tweak a couple things in the script so while doing that I made it optionally accept a per-file limit on displayed spell errors. See the "UPDATE 2" section. – B Layer Jan 27 '20 at 17:58
• @eyalkarni What is that? – B Layer Jan 27 '20 at 18:00
• It is a plugin that provides grammer checks, and contrary to ALE could fix just some of the problems – eyal karni Jan 27 '20 at 18:11