[Mirror] A linter for prose.
Writing is notoriously hard, even for the best writers, and it's not for lack of good advice — a tremendous amount of knowledge about the craft is strewn across usage guides, dictionaries, technical manuals, essays, pamphlets, websites, and the hearts and minds of great authors and editors. But poring over Strunk & White hardly makes one a better writer — it turns you into neither Strunk nor White. And nobody has the capacity to apply all the advice from Garner’s Modern English Usage, an 1100-page usage guide, to everything they write. In fact, the whole notion that one becomes a better writer by reading advice on writing rests on untenable assumptions about learning and memory. The traditional formats of knowledge about writing are thus essentially inert, waiting to be transformed.
We devised a simple solution: proselint, a linter for English prose. A linter
is a computer program that, akin to a spell checker, scans through a file and
detects issues — like how a real lint roller helps you get unwanted lint off of
your shirt.
proselint places the world's greatest writers and editors by your side, where
they whisper suggestions on how to improve your prose. You’ll be guided by
advice inspired by Bryan Garner, David Foster Wallace, Chuck Palahniuk, Steve
Pinker, Mary Norris, Mark Twain, Elmore Leonard, George Orwell, Matthew
Butterick, William Strunk, Elwyn White, Philip Corbett, Ernest Gowers, and the
editorial staff of the world’s finest literary magazines and newspapers, among
others. Our goal is to aggregate knowledge about best practices in writing and
to make that knowledge immediately accessible to all authors in the form of a
linter for prose; all in a neat command-line utility that you can integrate into
other tools, scripts, and workflows.
To get this up and running, install it using pip.
pip install proselint
sudo dnf install proselint
sudo apt install python3-proselint
sudo add-apt-repository universe
sudo apt install python3-proselint
proselint is packaged by nixpkgs.
To use proselint with pre-commit, or prek, point its config at this repository:
repos:
- repo: https://github.com/amperser/proselint
rev: v0.16.0
hooks:
- id: proselint
Be sure to change rev to use the desired proselint git tag or revision.
Thanks to Andy Airey for the pre-commit configuration.
environment.systemPackages = [pkgs.proselint];
nix profile install nixpkgs#proselint
proselint is available on:
ALEFlycheckMegaLinternone-ls.nvimThe following plugins are also available, but they are archived or unmaintained:
IntelliJ plugin by Victor KroppFlymake plugin by Manuel UbertiSublime Text plugin by Jordan SuchowPhabricator Arc plugin by Jeff VerkoeyenVisual Studio Code plugin by Patryk PeszkoCoala plugin by the Coala Development GroupDanger plugin by David Grandinetti and Orta TheroxSyntastic plugin by LCD 047, M Bussonnier, and Daniel M. CappellaSuppose you have a document text.md with the following text:
John is very unique.
You can run proselint over the document using the command line:
proselint check text.md
This prints a list of suggestions to stdout, one per line. Each suggestion is of the form:
file:<line>:<column>: <check_name>: <message>
For example,
text.md:1:9: uncomparables: Comparison of an uncomparable: 'very unique' is not comparable.
The command-line utility can also print suggestions in JSON using the
--output-format json option. In this case, the output is considerably richer,
following our stable wire schema.
…
To run the linter as part of another Python program, you can use the LintFile
class in proselint.tools. This requires CheckRegistry to be populated.
from proselint.checks import __register__
from proselint.registry import CheckRegistry
from proselint.tools import LintFile
CheckRegistry().register_many(__register__)
suggestions = LintFile("source-name", "This sentence is very unique").lint()
This will return a list of suggestions:
[LintResult(
check_result=CheckResult(
check_path='uncomparables',
message="Comparison of an uncomparable: 'very unique' is not comparable.",
span=(18, 29),
replacements=None,
),
pos=(1, 18),
)]
[!NOTE]
proselintsearches for configuration files in the order described below, and loads the first file it finds. If no configuration files are found, it falls back to its built in defaults.
- The file given by the
--configflag, if specifiedproselint.jsonin the current working directoryproselint.jsonin any directory above the current working directory$XDG_CONFIG_HOME/proselint/config.json, defaulting to~/.config/proselint/config.jsonifXDG_CONFIG_HOMEis not set
This section's examples use JSONC for comments and trailing commas. Please ensure you remove these features from your own configuration to avoid errors.
The configuration schema and defaults are as follows:
{
// The maximum allowable number of results proselint can output.
"max_errors": 1000,
// Checks to select or deselect.
"checks": {
// ...
},
// Checks to select or deselect on a per-file basis. These are merged with
// the selections specified in "checks" so you don't have to repeat them.
"per_file_checks": {},
}
Check selection is granular at any level, illustrated in the following example:
{
"checks": {
"typography": true,
"typography.symbols": false,
"typography.symbols.curly_quotes": true,
"typography.punctuation.hyperbole": false
}
}
This configuration would enable all checks in the typography module, excluding
typography.punctuation.hyperbole and those in typography.symbols, but
preserving typography.symbols.curly_quotes. Using this system allows you to
concisely and precisely select checks at an individual level.
[!WARNING] The path globs described in this section work differently than you may be used to. Refer to the Python documentation for partial non-recursive globs for more information.
You can select different checks for different files, or files matching given partial globs. Take the following example.
{
"checks": {
"typography": true,
// ...
},
"per_file_checks": {
"*.md": {
"typography": false,
},
},
}
This configuration would select typography for all files except markdown
files. Values in per_file_checks are the same as in checks, and have exactly
the same features.
annotations
Catch annotations left in the text
archaism
Avoid archaic forms
cliches.hell
Avoid a common cliché regarding hell
cliches.misc
Avoid clichés
dates_times.am_pm
Format the time of day correctly
dates_times.dates
Format dates appropriately
hedging
Avoid undermining yourself with uncertainty
industrial_language.airlinese
Avoid jargon of the airline industry
industrial_language.bureaucratese
Avoid bureaucratese
industrial_language.chatspeak
Avoid lolling and other chatspeak
industrial_language.commercialese
Avoid jargon of the commercial world
industrial_language.corporate_speak
Avoid corporate buzzwords
industrial_language.jargon
Avoid miscellaneous jargon
lexical_illusions
Avoid repeating words or phrases
malapropisms
Avoid common malapropisms
misc.apologizing
Be confident and avoid excessive apologizing
misc.back_formations
Avoid redundant backformations
misc.but
Do not start a paragraph with "But..."
misc.capitalization
Capitalize only what ought to be capitalized
misc.composition
Adhere to principles of composition
misc.currency
Avoid redundant currency symbols
misc.debased
Avoid debased language
misc.false_plurals
Avoid false plurals
misc.greylist
Avoid greylisted terms
misc.illogic
Avoid illogical forms
misc.inferior_superior
Superior to, not than
misc.institution_name
Use the correct names of institutions
misc.latin
Avoid overuse of Latin phrases
misc.many_a
Use singular forms with "many a"
misc.metadiscourse
Avoid discussing the discussio
No open issues yet, or sync has not completed.