|
;;; writegood-mode.el --- Polish up poor writing on the fly
|
|
;;
|
|
;; Author: Benjamin Beckwith
|
|
;; Created: 2010-8-12
|
|
;; Version: 2.0
|
|
;; Package-Version: 20180525.1343
|
|
;; Last-Updated: 2015-03-25
|
|
;; URL: http://github.com/bnbeckwith/writegood-mode
|
|
;; Keywords: writing weasel-words grammar
|
|
;; Compatability:
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Commentary:
|
|
;;
|
|
;; This minor mode tries to find and highlight problems with your
|
|
;; writing (in english).
|
|
;;
|
|
;; Behavior inspired by the weaselwords scripts to aid in good
|
|
;; writing.
|
|
;; http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Change Log:
|
|
;;
|
|
;; 2.0.3 Add in a small decription of the Flesch-Kincaid score
|
|
;; 2.0.2 Fix Formatting in Org-mode files, make faces underline
|
|
;; 2.0.1 Make user additions to word lists dynamic
|
|
;; 2.0.0 Flesch-Kincaid scoring added to functionality
|
|
;; 1.3.0 Several pull requests added, comments checked, passive voice regexp fixed
|
|
;; 1.2.0 Fixed weasel-words regexp to have word boundaries
|
|
;; 1.1.0 Fixed regexps to be multiline.
|
|
;; 1.0.0 Initial version
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; This program is free software; you can redistribute it and/or
|
|
;; modify it under the terms of the GNU General Public License as
|
|
;; published by the Free Software Foundation; either version 3, or
|
|
;; (at your option) any later version.
|
|
;;
|
|
;; This program is distributed in the hope that it will be useful,
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;; General Public License for more details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License
|
|
;; along with this program; see the file COPYING. If not, write to
|
|
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
;; Floor, Boston, MA 02110-1301, USA.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Test Text:
|
|
;;
|
|
;; This mode will improve various aspects of your writing in many ways.
|
|
;; With this mode, text within comments will be searched for the
|
|
;; the duplicate problem.
|
|
;; The text is searched and aspects (even within comments) are
|
|
;; highlighted.
|
|
;; Another benefit is the the finding of duplicates.
|
|
;;
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;;; Code:
|
|
|
|
(eval-when-compile
|
|
(require 'cl))
|
|
|
|
(require 'regexp-opt)
|
|
(require 'faces)
|
|
|
|
(defgroup writegood nil
|
|
"Minor mode for highlighting bad english writing."
|
|
:prefix "writegood-"
|
|
:group 'help
|
|
:link '(url-link "http://github.com/bnbeckwith/writegood-mode"))
|
|
|
|
(defconst writegood-version "2.0"
|
|
"WriteGood mode version")
|
|
|
|
;; Weaselwords
|
|
(defface writegood-weasels-face
|
|
'((((supports :underline (:style wave)))
|
|
:underline (:style wave :color "DarkOrange"))
|
|
(((class color) (background light))
|
|
(:inherit font-lock-warning-face :background "moccasin"))
|
|
(((class color) (background dark))
|
|
(:inherit font-lock-warning-face :background "DarkOrange")))
|
|
"Writegood face for weasel words"
|
|
:group 'writegood)
|
|
|
|
(defcustom writegood-weasel-words
|
|
'("many" "various" "very" "fairly" "several" "extremely"
|
|
"exceedingly" "quite" "remarkably" "few" "surprisingly"
|
|
"mostly" "largely" "huge" "tiny" "are a number" "is a number"
|
|
"excellent" "interestingly" "significantly" "substantially"
|
|
"clearly" "vast" "relatively" "completely" "literally"
|
|
"not rocket science" "outside the box")
|
|
"The weasel words to use"
|
|
:group 'writegood
|
|
:type '(repeat string))
|
|
|
|
(defun writegood-weasels-font-lock-keywords-regexp ()
|
|
"Generate regex that matches weasel-words"
|
|
(concat "\\b" (regexp-opt writegood-weasel-words) "\\b"))
|
|
|
|
(defun writegood-weasels-font-lock-keywords ()
|
|
(list (list (writegood-weasels-font-lock-keywords-regexp)
|
|
0 (quote 'writegood-weasels-face) 'prepend)))
|
|
|
|
;; Passive Voice
|
|
(defface writegood-passive-voice-face
|
|
'((((supports :underline (:style wave)))
|
|
:underline (:style wave :color "cyan"))
|
|
(((class color))
|
|
(:inherit font-lock-warning-face :background "LemonChiffon")))
|
|
"Writegood face for passive-voice"
|
|
:group 'writegood)
|
|
|
|
(defcustom writegood-passive-voice-irregulars
|
|
'("awoken" "been" "born" "beat" "become" "begun" "bent" "beset"
|
|
"bet" "bid" "bidden" "bound" "bitten" "bled" "blown" "broken"
|
|
"bred" "brought" "broadcast" "built" "burnt" "burst" "bought"
|
|
"cast" "caught" "chosen" "clung" "come" "cost" "crept" "cut"
|
|
"dealt" "dug" "dived" "done" "drawn" "dreamt" "driven" "drunk"
|
|
"eaten" "fallen" "fed" "felt" "fought" "found" "fit" "fled"
|
|
"flung" "flown" "forbidden" "forgotten" "foregone" "forgiven"
|
|
"forsaken" "frozen" "gotten" "given" "gone" "ground" "grown"
|
|
"hung" "heard" "hidden" "hit" "held" "hurt" "kept" "knelt" "knit"
|
|
"known" "laid" "led" "leapt" "learnt" "left" "lent" "let" "lain"
|
|
"lighted" "lost" "made" "meant" "met" "misspelt" "mistaken" "mown"
|
|
"overcome" "overdone" "overtaken" "overthrown" "paid" "pled" "proven"
|
|
"put" "quit" "read" "rid" "ridden" "rung" "risen" "run" "sawn"
|
|
"said" "seen" "sought" "sold" "sent" "set" "sewn" "shaken" "shaven"
|
|
"shorn" "shed" "shone" "shod" "shot" "shown" "shrunk" "shut"
|
|
"sung" "sunk" "sat" "slept" "slain" "slid" "slung" "slit"
|
|
"smitten" "sown" "spoken" "sped" "spent" "spilt" "spun" "spit"
|
|
"split" "spread" "sprung" "stood" "stolen" "stuck" "stung"
|
|
"stunk" "stridden" "struck" "strung" "striven" "sworn" "swept"
|
|
"swollen" "swum" "swung" "taken" "taught" "torn" "told" "thought"
|
|
"thrived" "thrown" "thrust" "trodden" "understood" "upheld" "upset"
|
|
"woken" "worn" "woven" "wed" "wept" "wound" "won" "withheld"
|
|
"withstood" "wrung" "written")
|
|
"List of passive voice irregular verbs"
|
|
:group 'writegood
|
|
:type '(repeat string))
|
|
|
|
(defcustom writegood-sentence-punctuation
|
|
'(?. ?? ?!)
|
|
"List of punctuation denoting sentence end"
|
|
:group 'writegood
|
|
:type '(repeat character))
|
|
|
|
(defun writegood-passive-voice-font-lock-keywords-regexp ()
|
|
"Generate font-lock keywords regexp for passive-voice"
|
|
(concat "\\b\\(am\\|are\\|were\\|being\\|is\\|been\\|was\\|be\\)\\b\\([[:space:]]\\|\\s<\\|\\s>\\)+\\([[:word:]]+ed\\|"
|
|
(regexp-opt writegood-passive-voice-irregulars)
|
|
"\\)\\b"))
|
|
|
|
(defun writegood-passive-voice-font-lock-keywords ()
|
|
(list (list (writegood-passive-voice-font-lock-keywords-regexp)
|
|
0 (quote 'writegood-passive-voice-face) 'prepend)))
|
|
|
|
;; Duplicates
|
|
(defface writegood-duplicates-face
|
|
'((((supports :underline (:style wave)))
|
|
:underline (:style wave :color "DeepPink"))
|
|
(((class color) (background light))
|
|
(:inherit font-lock-warning-face :background "MistyRose"))
|
|
(((class color) (background dark))
|
|
(:inherit font-lock-warning-face :background "DeepPink")))
|
|
"Writegood face for duplicate words"
|
|
:group 'writegood)
|
|
|
|
(defvar writegood-duplicates-font-lock-keywords-regexp
|
|
"\\b\\([[:word:]]+\\)\\([[:space:]]\\|\\s<\\|\\s>\\)+\\1\\b"
|
|
"Font-lock keywords for duplicates")
|
|
|
|
(defun writegood-duplicates-font-lock-keywords ()
|
|
(list (list writegood-duplicates-font-lock-keywords-regexp
|
|
0 (quote 'writegood-duplicates-face) 'prepend)))
|
|
|
|
;;;;;;;;;;;;;;;;;;;; Functions:
|
|
|
|
(defun writegood-version ()
|
|
"Tell the version you are using"
|
|
(interactive)
|
|
(message writegood-version))
|
|
|
|
(defun writegood-weasels-turn-on ()
|
|
"Turn on syntax highlighting for weasels"
|
|
(font-lock-add-keywords nil (writegood-weasels-font-lock-keywords) t))
|
|
|
|
(defun writegood-passive-voice-turn-on ()
|
|
"Turn on warnings for passive voice"
|
|
(font-lock-add-keywords nil (writegood-passive-voice-font-lock-keywords) t))
|
|
|
|
(defun writegood-duplicates-turn-on ()
|
|
"Turn on warnings for duplicate words"
|
|
(font-lock-add-keywords nil (writegood-duplicates-font-lock-keywords) t))
|
|
|
|
(defun writegood-weasels-turn-off ()
|
|
"Turn on syntax highlighting for weasels"
|
|
(font-lock-remove-keywords nil (writegood-weasels-font-lock-keywords)))
|
|
|
|
(defun writegood-passive-voice-turn-off ()
|
|
"Turn on warnings for passive voice"
|
|
(font-lock-remove-keywords nil (writegood-passive-voice-font-lock-keywords)))
|
|
|
|
(defun writegood-duplicates-turn-off ()
|
|
"Turn on warnings for duplicate words"
|
|
(font-lock-remove-keywords nil (writegood-duplicates-font-lock-keywords)))
|
|
|
|
(defun writegood-turn-on ()
|
|
"Turn on writegood-mode."
|
|
(make-local-variable 'font-lock-keywords-case-fold-search)
|
|
(setq font-lock-keywords-case-fold-search t)
|
|
(writegood-weasels-turn-on)
|
|
(writegood-passive-voice-turn-on)
|
|
(writegood-duplicates-turn-on))
|
|
|
|
(defun writegood-turn-off ()
|
|
"Turn off writegood-mode."
|
|
(writegood-weasels-turn-off)
|
|
(writegood-passive-voice-turn-off)
|
|
(writegood-duplicates-turn-off))
|
|
|
|
(defun writegood-count-words (rstart rend)
|
|
"Count the words specified by the region bounded by RSTART and REND."
|
|
(if (boundp 'count-words)
|
|
(count-words rstart rend)
|
|
(how-many "[[:word:]]+" rstart rend)))
|
|
|
|
(defun writegood-count-sentences (rstart rend)
|
|
"Count the sentences specified by the region bounded by RSTART and REND."
|
|
(how-many (regexp-opt-charset writegood-sentence-punctuation) rstart rend))
|
|
|
|
(defun writegood-count-syllables (rstart rend)
|
|
"Count the (approximate) number of syllables in the region bounded by RSTART and REND.
|
|
|
|
Consecutive vowels count as one syllable. The endings -es -ed
|
|
and -e are not counted as syllables.
|
|
"
|
|
(- (how-many "[aeiouy]+" rstart rend)
|
|
(how-many "\\(es\\|ed\\|e\\)\\b" rstart rend)))
|
|
|
|
(defun writegood-fk-parameters (&optional rstart rend)
|
|
"Flesch-Kincaid reading parameters"
|
|
(let* ((start (cond (rstart rstart)
|
|
((and transient-mark-mode mark-active) (region-beginning))
|
|
('t (point-min))))
|
|
(end (cond (rend rend)
|
|
((and transient-mark-mode mark-active) (region-end))
|
|
('t (point-max))))
|
|
(words (float (writegood-count-words start end)))
|
|
(syllables (float (writegood-count-syllables start end)))
|
|
(sentences (float (writegood-count-sentences start end))))
|
|
(list sentences words syllables)))
|
|
|
|
(defun writegood-reading-ease-score->comment (score)
|
|
"Rough interpreation of the Flesch-Kincaid Reading ease SCORE.
|
|
|
|
From Wikipedia URL `https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_tests'."
|
|
(cond
|
|
((< score 0) "Ouch! (Proust literature)")
|
|
((and (<= 0 score) (< score 30.0)) "Very difficult (college graduate)")
|
|
((and (<= 30.0 score) (< score 50.0)) "Difficult (almost college)")
|
|
((and (<= 50.0 score) (< score 60.0)) "Fairly difficult (10-12th grade)")
|
|
((and (<= 60.0 score) (< score 70.0)) "Plain English (8-9th grade)")
|
|
((and (<= 70.0 score) (< score 80.0)) "Fairly easy (7th grade)")
|
|
((and (<= 80.0 score) (< score 90.0)) "Easy (6th grade)")
|
|
((<= 90.0 score) "Very easy (5th grade)")))
|
|
|
|
;;;###autoload
|
|
(defun writegood-reading-ease (&optional start end)
|
|
"Flesch-Kincaid reading ease test in the region bounded by START and END.
|
|
|
|
Scores roughly between 0 and 100."
|
|
(interactive)
|
|
(let* ((params (writegood-fk-parameters start end))
|
|
(sentences (nth 0 params))
|
|
(words (nth 1 params))
|
|
(syllables (nth 2 params))
|
|
(score (- 206.835 (* 1.015 (/ words sentences)) (* 84.6 (/ syllables words)))))
|
|
(message "Flesch-Kincaid reading ease score: %.2f %s" score
|
|
(writegood-reading-ease-score->comment score))))
|
|
|
|
;;;###autoload
|
|
(defun writegood-grade-level (&optional start end)
|
|
"Flesch-Kincaid grade level test. Converts reading ease score to a grade level (Score ~ years of school needed to read passage)."
|
|
(interactive)
|
|
(let* ((params (writegood-fk-parameters start end))
|
|
(sentences (nth 0 params))
|
|
(words (nth 1 params))
|
|
(syllables (nth 2 params))
|
|
(score (+ (* 0.39 (/ words sentences)) (* 11.8 (/ syllables words)) -15.59)))
|
|
(message "Flesch-Kincaid grade level score: %.2f" score)))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode writegood-mode
|
|
"Colorize issues with the writing in the buffer."
|
|
:lighter " Wg"
|
|
(progn
|
|
(if writegood-mode
|
|
(writegood-turn-on)
|
|
(writegood-turn-off))
|
|
(font-lock-mode 1)))
|
|
|
|
(provide 'writegood-mode)
|
|
|
|
;;; writegood-mode.el ends here
|