I'd like to run flyspell on several Latex-files in the same directory and output all the words which are misspelled.
1 Answer
I presume that the question mentioned flyspell
specifically because it knows about TeX syntax and is good at ignoring math, TeX commands, etc. Although aspell
can also do this (more on that below), I also took a stab at doing it in Emacs Lisp.
Flyspell uses overlays to mark misspelled words. First, we need to loop over each overlay in a buffer and collect a list of the words corresponding to each overlay. In the code below, flyspell-overlay-p
checks to make sure it's a flyspell
overlay, not some other overlay. The mapcar
part applies a function to each overlay to extract the word from the buffer:
(let* ((overlays (overlays-in (point-min) (point-max)))
(words (mapcar
(lambda (o)
(when (flyspell-overlay-p o)
(buffer-substring-no-properties
(overlay-start o) (overlay-end o))))
overlays)))
...)
Now, with that we need a way to list all .tex
files in some directory dir
. We need directory-files
for that:
(directory-files dir 'full "\\.tex\\'")
Now, to glue this all together we'll need to open each file, run flyspell-buffer
, collect the words, and print them out somehow. Putting it all together, here is a function:
;; flyspell-tex-batch.el
(defun flyspell-tex-batch (dir)
(let ((buf (get-buffer-create "*flyspell-tex-batch*"))
(tex-files (directory-files dir 'full "\\.tex\\'")))
(switch-to-buffer buf)
(dolist (file tex-files)
;; Print a header for each file.
(insert "# " (file-name-nondirectory file) "\n\n")
;; Open the file and run flyspell.
(with-current-buffer (find-file-noselect file)
(flyspell-mode 1)
(flyspell-buffer)
;; Look at all flyspell overlays and extract the words.
(let* ((overlays (overlays-in (point-min) (point-max)))
(words (mapcar
(lambda (o)
(when (flyspell-overlay-p o)
(buffer-substring-no-properties
(overlay-start o) (overlay-end o))))
overlays)))
;; Insert the words in the main spell check buffer.
(with-current-buffer buf
(mapcar (lambda (w) (insert "- " w "\n")) words)
(insert "\n")))
;; Close the tex file buffer
(kill-buffer)))
(message (buffer-string))))
It takes a directory name as the argument and creates a temporary buffer, *flyspell-tex-batch*
, for collecting the results. Just for kicks, I formatted them in Markdown as a list, but that should be relatively easy to customize as you like.
Since you asked about running this in batch mode, the message
line at the end will print the results to the console. You'll also need to save this function in a file, say flyspell-tex-batch.el
.
To test the function, I made two test ".tex" files. The TeX commands and math should not show up, while the words inside the commands should be reported.
Contents of test-1.tex:
\texcommand{mispelled}
\begin{equation*}
\exp(x) = \sum_{k=0}^\infty \frac{x^k}{x!}
\end{equation}
Contents of test-2.tex:
\textbf{abracadababra}
Then, assuming these are all in the same directory, we can run Emacs in batch mode like so:
emacs -q --batch -l flyspell.el -l flyspell-tex-batch.el --eval '(flyspell-tex-batch ".")'
The -q
asks to ignore your usual init file (but still load the system init files). We need to make sure flyspell
is loaded, so -l flyspell.el
takes care of that. Finally, we need to load the file containing the function above. The --eval
part takes care of calling the function and providing the directory ("." here, since the tex files are in the same directory).
Here's the output:
# test-1.tex
- mispelled
# test-2.tex
- abracadababra
Having done all that work, as lawlist mentioned aspell
does indeed have the capacity to ignore TeX commands (--mode=tex
), so you can achieve the same thing on the command line . Furthermore, you can ask it not to suggest corrections (--dont-suggest
). There is still some cruft in the output to get rid of, but after that:
% cat test-1.tex test-2.tex | \
aspell -a --dont-suggest --mode=tex | \
tail -n +2 | awk '{print $2}' | grep -v "^$"
mispelled
abracadababra
So, without additional information this is probably a better way to go about simply listing misspelled words, but perhaps there is another good reason not mentioned to work with Emacs.
-
Thanks for your detailed answer. Clearly, using aspell directly is preferable. However, it is not working very well for me on the command line in Windows, but for some reason flyspell inside emacs is working.– arvidjCommented Jun 22, 2017 at 11:15
ispell
(whichflyspell
uses), to achieve your goal. However,flyspell
is probably not needed at all and only serves to distract readers who might otherwise be inclined to write-up an answer. In fact,ispell
may not be needed. The key issue is whether the something likeaspell
/hunspell
provides the output you desire, and how to loop through each file ... Have you looked at command line options from the underlying spellcheck utilities to see if the output is is something that suits your needs, or whether Lisp would be needed?aspell
leads me to believe that the output can be in the form of a list of misspelled words, and there are filters for TeX commands. I only spent a few minutes skimming Google results and a couple of links, but that may be an idea you (or another forum participant) wish to pursue.