write-good
Version:
Naive linter for English prose
326 lines (228 loc) • 9.47 kB
Markdown
# write good [](https://travis-ci.org/btford/write-good)
Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too.
## Use
```shell
npm install write-good
```
**Important:** Do not use this tool to be a jerk to other people about their writing.
## API
`writeGood` is a function that takes a string and returns an array of suggestions.
```javascript
var writeGood = require('write-good');
var suggestions = writeGood('So the cat was stolen.');
// suggestions:
//
// [{
// reason: "omit 'So' from the beginning of sentences",
// index: 0, offset: 2
// }, {
// reason: "'was stolen' is passive voice",
// index: 11, offset: 10
// }]
```
`writeGood` takes an optional second argument that allows you to disable certain checks.
You can disable checking for passive voice like this:
```javascript
var writeGood = require('write-good');
var suggestions = writeGood('So the cat was stolen', { passive: false});
// suggestions: []
```
You can use the second argument's `checks` property to pass in custom checks instead of `write-good`'s default linting configuration.
Like this, you can check non-English documents, for example with the linter extension for German, [schreib-gut](https://github.com/TimKam/schreib-gut):
```javascript
var schreibGut = require('schreib-gut');
writeGood('Aller Wahrscheinlichkeit nach können Entwickler nicht gut schreiben', { weasel-words: false, checks: schreibGut});
// suggestions
// [{index : 0, offset : 29, reason : '"Aller Wahrscheinlichkeit nach" is wordy or unneeded' }]
```
You can use the second argument's `whitelist` property to pass in a list of strings to whitelist from suggestions.
For example, normally `only` would be picked up as a bad word to use, but you might want to exempt `read-only` from that:
```javascript
var writeGood = require('write-good');
var suggestions = writeGood('Never write read-only sentences.');
// suggestions: [{ index: 17, offset: 4, reason: '"only" can weaken meaning' }]
var filtered = writeGood('Never write read-only sentences.', { whitelist: ['read-only'] });
// filtered: []
```
## CLI
You can use `write-good` as a command-line tool by installing it globally:
```shell
npm install -g write-good
```
If you have npm version 5.2.0 or later installed, you can use npx to run write-good without installing it:
```shell
npx write-good *.md
```
`write-good` takes a [glob](https://github.com/isaacs/node-glob) and prints suggestions to stdout:
```shell
$ write-good *.md
In README.md
=============
= writeGood('So the cat was stolen.');
^^^^^^^^^^
"was stolen" is passive voice on line 20 at column 40
-------------
// suggestion: "'was stolen' is passive voice",
^^^^^^^^^^
"was stolen" is passive voice on line 28 at column 19
```
You can run just specific checks like this:
```shell
write-good *.md --weasel --so
```
Or exclude checks like this:
```shell
write-good *.md --no-passive
```
Or include checks like this:
```shell
# E-Prime is disabled by default.
write-good *.md --yes-eprime
```
**Note:** The ``--yes`` prefix only works for *E-Prime*, because the other checks are included by default, anyway.
You can run just with text without supplying files:
```shell
write-good --text="It should have been defined there."
```
You can even supply multi-line text:
```shell
write-good --text="I can't see a problem there that's not been defined yet.
Should be defined again."
```
You can also pass other arguments:
```shell
write-good --text="It should have been defined there." --no-passive
```
You can even fetch output from a remote file:
```shell
write-good --text="$(curl https://raw.githubusercontent.com/btford/write-good/master/README.md)"
```
Use the ``--parse`` option to activate parse-happy output and a more conventional Unix exit code:
```shell
write-good *.md --parse
```
To specify a custom checks extension, for example [schreib-gut](https://github.com/TimKam/schreib-gut), run:
```shell
npm install -g schreib-gut
write-good *.md --checks=schreib-gut
```
To view all available options use the ``--help`` option:
```shell
write-good --help
```
## Checks
You can disable any combination of the following by providing a key with value `false` as the second argument to `writeGood`.
### `passive`
Checks for passive voice.
### `illusion`
Checks for lexical illusions – cases where a word is repeated.
### `so`
Checks for `so` at the beginning of the sentence.
### `thereIs`
Checks for `there is` or `there are` at the beginning of the sentence.
### `weasel`
Checks for "weasel words."
### `adverb`
Checks for adverbs that can weaken meaning: really, very, extremely, etc.
### `tooWordy`
Checks for wordy phrases and unnecessary words.
### `cliches`
Checks for common cliches.
### `eprime`
Checks for ["to-be"](https://en.wikipedia.org/wiki/E-Prime) verbs. _Disabled by default_
## Extensions
Users can create their own `write-good` language checks. As described above,
you can specify such extensions when running `write-good` on the command line
or calling it in your JavaScript code.
The following 3rd-party `write-good` extensions are available:
* [schreib-gut](https://github.com/timkam/schreib-gut): A basic extension for
the German language
If you know of any `write-good` extensions that are not in this list, please open a pull request!
### Interface
An extension is a Node.js module that exposes an object containing a check
function (``fn``) and an ``explanation`` string for each new check:
```javascript
module.exports = {
check1: {
fn: function(text) {
…
},
explanation: '…'
},
check2: {
fn: function(text) {
…
},
explanation: '…'
}
}
```
Each check function takes a string input and determines a list of style
violation objects, each with an ``index`` and an ``offset``:
```javascript
/**
* @param {text} text Input text
* @return {{index:number, offset:number}[]} List of all violations
*/
```
The ``index`` defines the position of the match in the input text, whereas the
``offset`` specifies the length of the match.
The following example extension provides a check that determines if the input
text contains a set of forbidden terms (*Tom Riddle* and *Voldemort*):
```javascript
module.exports = {
voldemort: {
fn: function (text) {
var positives = ['Tom Riddle', 'Voldemort']
var re = new RegExp('\\b(' + positives.join('|') + ')\\b', 'gi');
var suggestions = [];
while (match = re.exec(text)) {
suggestions.push({
index: match.index,
offset: match[0].length,
});
}
return suggestions;
},
explanation: 'You must not name Him-Who-Must-Not-Be-Named'
}
}
```
## Docker
### From Dockerhub
You can also run this application in [Docker](https://www.docker.com). Using a pre-built [image from Dockerhub](https://hub.docker.com/r/hochzehn/write-good/), the write-good can be run with this command:
`docker run --rm --volume $PWD:/app hochzehn/write-good *.md`
### Building locally
Or you can first build the image locally:
`docker build -t btford/write-good .`
And then run using:
`docker run -it --rm -v "$(pwd)":/srv/app -w /srv/app btford/write-good:latest *.md`
## See also
I came across these resources while doing research to make this module.
They might be helpful.
### Code
* [shell script for avoiding "weasel words"](http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/) – I based my initial implementation on this
* [Academic Writing Check](https://github.com/devd/Academic-Writing-Check) – a perl script similar to above
* [writegood mode](https://github.com/bnbeckwith/writegood-mode) for emacs
* [natural](https://github.com/NaturalNode/natural) – general purpose NLP toolkit in JavaScript
* [WordNet](http://wordnet.princeton.edu/) – lexical database of the English language
* [LanguageTool](https://languagetool.org/) – style and grammar checker implemented in Java
### Prose
* [Elements of Style](http://www.bartleby.com/141/)
* [Flesch–Kincaid readability](http://en.wikipedia.org/wiki/Flesch%E2%80%93Kincaid_readability_test)
* [Fear and Loathing of the English passive](http://www.lel.ed.ac.uk/~gpullum/passive_loathing.pdf)
* [Words to Avoid in Educational Writing](http://css-tricks.com/words-avoid-educational-writing/)
### Apps
This is not an endorsement.
These apps have similar functionality that you may find useful.
* [Hemingway App](http://www.hemingwayapp.com/)
* [Nitpicker](http://nitpickertool.com)
* [Grammarly](https://app.grammarly.com)
## Other projects using write good
* [linter-write-good](https://github.com/gepoch/linter-write-good) for [Atom](https://atom.io/)
* [Write Good action](https://actions.getdrafts.com/a/1RA) for [Drafts](https://getdrafts.com) iOS App
* [Write Good Linter](https://marketplace.visualstudio.com/items?itemName=travisthetechie.write-good-linter) for [Visual Studio Code](https://code.visualstudio.com)
* [Vim ALE](https://github.com/w0rp/ale) realtime linter for [Vim](http://www.vim.org/) with included support for write-good.
* [Write Better](https://github.com/justiceo/write-better) A [Chrome extension](https://chrome.google.com/webstore/detail/write-better/nnnnnpmcdcloafmfkiihafnjidjkfmek) for Google Docs.
## License
MIT