1

I'd like to run flyspell on several Latex-files in the same directory and output all the words which are misspelled.

2
  • 2
    I think you really want to know about how to run ispell (which flyspell 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 like aspell / 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?
    – lawlist
    Commented Jun 16, 2017 at 6:35
  • A brief Google of command-line options for 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.
    – lawlist
    Commented Jun 16, 2017 at 7:00

1 Answer 1

2
+50

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.

1
  • 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.
    – arvidj
    Commented Jun 22, 2017 at 11:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.