A textlint flycheck checker in Emacs

Posted on December 29, 2018

Linting natural language with Flycheck

textlint is an open source pluggable linting tool for natural language text.

This post shows how to integrate textlint with flycheck in the Emacs text editor, e.g.

This configuration uses textlint plugins:

It supports modes:

For other modes see “Configuration” below.

textlint installation

Install npx:

sudo npm install -g npx


Download the textlint rules and the latex plugin:

npm install textlint write-good textlint-plugin-latex textlint-rule-write-good textlint-rule-no-start-duplicated-conjunction textlint-rule-max-comma textlint-rule-terminology textlint-rule-period-in-list-item textlint-rule-unexpanded-acronym textlint-rule-abbr-within-parentheses textlint-rule-alex textlint-rule-common-misspellings textlint-rule-en-max-word-count textlint-rule-diacritics textlint-rule-stop-words


Now test the file by creating a file file.txt:

echo "Mistakes were made." > file.txt


Now try textlint:

npx textlint --rule write-good --rule no-start-duplicated-conjunction --rule max-comma --rule terminology --rule period-in-list-item --rule period-in-list-item --rule unexpanded-acronym --rule abbr-within-parentheses --rule alex --rule common-misspellings --rule en-max-word-count --rule diacritics --rule stop-words file.txt


This should print:

file.txt:1:10: "were made" may be passive voice [Error/write-good]

flycheck integration

This GitHub gist inspires the elisp below.

Put it in your ~/.emacs, ~/.emacs.el, or ~/.emacs.d/init.el file:

Edit the location of .textlintrc for the --config flag.

(require 'flycheck)
(flycheck-define-checker textlint
  "A linter for textlint."
  :command ("npx" "textlint"
            "--config" "/home/rob/.emacs.d/.textlintrc"
            "--format" "unix"
            "--rule" "write-good"
            "--rule" "no-start-duplicated-conjunction"
            "--rule" "max-comma"
            "--rule" "terminology"
            "--rule" "period-in-list-item"
            "--rule" "abbr-within-parentheses"
            "--rule" "alex"
            "--rule" "common-misspellings"
            "--rule" "en-max-word-count"
            "--rule" "diacritics"
            "--rule" "stop-words"
            "--plugin"
            (eval
             (if (derived-mode-p 'tex-mode)
                 "latex"
               "@textlint/text"))
            source-inplace)
  :error-patterns
  ((warning line-start (file-name) ":" line ":" column ": "
            (message (one-or-more not-newline)
                     (zero-or-more "\n" (any " ") (one-or-more not-newline)))
            line-end))
  :modes (text-mode latex-mode org-mode markdown-mode)
  )
(add-to-list 'flycheck-checkers 'textlint)


It supports text-mode, latex-mode, org-mode and markdown-mode.

If flycheck-mode is not enabled for your buffer: M-x flcheck-mode. Display the textlint warnings for file.txt with: C-c ! l .

Debugging If flycheck errors buffer shows no warnings, debug textlint by running C-c ! C-c which will run the textlint command in a new buffer. It should show:

npx textlint --config /home/rob/.emacs.d/.textlintrc --format unix --rule write-good --rule no-start-duplicated-conjunction --rule max-comma --rule terminology --rule period-in-list-item --rule abbr-within-parentheses --rule alex --rule common-misspellings --rule en-max-word-count --rule diacritics --rule stop-words --plugin \@textlint/text mistakes.txt
file.txt:1:10: "were made" may be passive voice [Error/write-good]

1 problem

Customising textlint rules

textlint is customisable:

  1. textlint applies no rules by default, rules must be used with the --rule flag.

  2. Each rule is configurable, e.g. passive mode checks can be disabled in the write-good plugin.

You can modify .textlintrc to customise the rules. The hyperlinks above provide details on rule specific customisations.

E.g. to disable some checks in the write-good rule:

{
  "rules": {
    "write-good": {
      "passive": false,
      "thereIs": false
    }
  }
}


Supporting additional file types

There are two ways to support additional file types:

  1. Develop a plugin, e.g. the latex textlint plugin, which parses latex content, which removes latex syntax leaving just text content. This eliminates textlint false positives (on spurious syntax e.g. \begin{section} .. \end{section}), but requires more engineering than the 2nd approach. I’ve started an org-mode plugin, a pull request to complete the implementation is welcome :)

  2. Add additional file extensions to be treated as plain text files, where spurious syntax may be picked up by textlint rules.

    E.g. adding the following to the .textlintrc file tells textlint to treat .org files (org mode) as text files:

{
    "plugins": {
        "@textlint/text": {
            "extensions": [".org"]
        }
    }
}


Acknowledgement

Thanks to Blair Archibald for feedback on a draft of this post and Malcolm Purvis for improving the flycheck integration.

9 January 2019 update: this post has since been packaged as an Emacs package, which is configured with a textlintrc file (including which plugins to use): https://github.com/kisaragi-hiu/flycheck-textlint