Add contributed words to avoid. Fix lexical illusion detection.
[robmyers:scripts.git] / artbollocks-mode.el
1 ;; artbollocks-mode.el - A minor mode to guide art writers.
2 ;; Copyright (c) 2011,2012 Rob Myers <rob@robmyers.org>
3 ;;
4 ;; Based on fic-mode.el
5 ;; Copyright (C) 2010, Trey Jackson <bigfaceworm(at)gmail(dot)com>
6 ;;
7 ;; Non-artbollocks words from: http://matt.might.net/articles/
8 ;;
9 ;; This program is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13 ;;
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;; Extra words thanks to:
24 ;; Brian van den Broek (contextuality, dialetic, problematize)
25 ;; Isabel Brison (alterity, mise en abyme)
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27
28 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
29 ;; Usage
30 ;;
31 ;; To use, save artbollocks-mode.el to a directory in your load-path.
32 ;;
33 ;; (require 'artbollocks-mode)
34 ;; (add-hook 'text-mode-hook 'turn-on-artbollocks-mode)
35 ;; (add-hook 'org-mode-hook 'turn-on-artbollocks-mode)
36 ;;
37 ;; or
38 ;;
39 ;; M-x artbollocks-mode
40 ;;
41 ;; NOTE: If you manually turn on artbollocks-mode,
42 ;; you you might need to force re-fontification initially:
43 ;;
44 ;;   M-x font-lock-fontify-buffer
45 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
46
47 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48 ;; Customization
49 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
50
51 ;; Enable features individually
52
53 (defcustom lexical-illusions t
54   "Whether to check for lexical illusions"
55   :type '(boolean)
56   :group 'artbollocks-mode)
57
58 (defcustom passive-voice t
59   "Whether to check for passive voice"
60   :type '(boolean)
61   :group 'artbollocks-mode)
62
63 (defcustom weasel-words t
64   "Whether to check for weasel words"
65   :type '(boolean)
66   :group 'artbollocks-mode)
67
68 (defcustom artbollocks t
69   "Whether to check for artbollocks"
70   :type '(boolean)
71   :group 'artbollocks-mode)
72
73 ;; Lexical illusion face
74
75 (defcustom lexical-illusions-foreground-color "black"
76   "Lexical illusions face foreground colour"
77   :group 'artbollocks-mode)
78
79 (defcustom lexical-illusions-background-color "magenta"
80   "Lexical illusions face background color"
81   :group 'artbollocks-mode)
82
83 (defcustom font-lock-lexical-illusions-face 'font-lock-lexical-illusions-face
84   "The face for lexical illusions in artbollocks mode"
85   :group 'artbollocks-mode)
86
87 (make-face 'font-lock-lexical-illusions-face)
88 (modify-face 'font-lock-lexical-illusions-face
89              lexical-illusions-foreground-color
90              lexical-illusions-background-color
91              nil t nil t nil nil)
92
93 ;; Passive voice face
94
95 (defcustom passive-voice-foreground-color "Gray"
96   "Passive voice face foreground colour"
97   :group 'artbollocks-mode)
98
99 (defcustom passive-voice-background-color "White"
100   "Passive voice face background color"
101   :group 'artbollocks-mode)
102
103 (defcustom font-lock-passive-voice-face 'font-lock-passive-voice-face
104   "The face for passive voice words in artbollocks mode"
105   :group 'artbollocks-mode)
106
107 (make-face 'font-lock-passive-voice-face)
108 (modify-face 'font-lock-passive-voice-face passive-voice-foreground-color
109              passive-voice-background-color nil t nil t nil nil)
110
111 ;; Weasel words face
112
113 (defcustom weasel-words-foreground-color "Brown"
114   "Weasel words face foreground colour"
115   :group 'artbollocks-mode)
116
117 (defcustom weasel-words-background-color "White"
118   "Weasel words face background color"
119   :group 'artbollocks-mode)
120
121 (defcustom font-lock-weasel-words-face 'font-lock-weasel-words-face
122   "The face for weasel-words words in artbollocks mode"
123   :group 'artbollocks-mode)
124
125 (make-face 'font-lock-weasel-words-face)
126 (modify-face 'font-lock-weasel-words-face weasel-words-foreground-color
127              weasel-words-background-color nil t nil t nil nil)
128
129 ;; Artbollocks face
130
131 (defcustom artbollocks-foreground-color "Purple"
132   "Font foreground colour"
133   :group 'artbollocks-mode)
134
135 (defcustom artbollocks-background-color "White"
136   "Font background color"
137   :group 'artbollocks-mode)
138
139 (defcustom font-lock-artbollocks-face 'font-lock-artbollocks-face
140   "The face for artbollocks words in artbollocks mode"
141   :group 'artbollocks-mode)
142
143 (make-face 'font-lock-artbollocks-face)
144 (modify-face 'font-lock-artbollocks-face artbollocks-foreground-color
145              artbollocks-background-color nil t nil t nil nil)
146
147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
148 ;; Lexical illusions
149 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
150
151 (defconst lexical-illusions-regex "\\b\\(\\w+\\)[ \t\r\n]+\\(\\1\\)")
152
153 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
154 ;; Passive voice
155 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
156
157 (defconst passive-voice-regex "\\b\\(am\\|are\\|were\\|being\\|is\\|been\\|was\\|be\\)\\s-+\\(\\w+ed\\|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\\)\\b")
158
159 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160 ;; Weasel words
161 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
162
163 (defconst weasel-words-regex "\\b\\(many\\|various\\|very\\|fairly\\|several\\|extremely\\|exceedingly\\|quite\\|remarkably\\|few\\|surprisingly\\|mostly\\|largely\\|huge\\|tiny\\|\\(\\(are\\|is\\) a number\\)\\|excellent\\|interestingly\\|significantly\\|substantially\\|clearly\\|vast\\|relatively\\|completely\\)\\b")
164
165 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
166 ;; Artbollocks
167 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
168
169 (defconst artbollocks-regex "\\b\\(a priori\\|ad hoc\\|affirmation\\|affirm\\|affirms\\|alterity\\|altermodern\\|aporia\\|aporetic\\|appropriates\\|appropriation\\|archetypal\\|archetypical\\|archetype\\|archetypes\\|autonomous\\|autonomy\\|baudrillardian\\|baudrillarian\\|commodification\\|committed\\|commitment\\|commonalities\\|contemporaneity\\|context\\|contexts\\|contextual\\|contextualise\\|contextualises\\|contextualisation\\|contextialize\\|contextializes\\|contextualization\\|contextuality\\|convention\\|conventional\\|conventions\\|coterminous\\|critique\\|cunning\\|cunningly\\|death of the author\\|debunk\\|debunked\\|debunking\\|debunks\\|deconstruct\\|deconstruction\\|deconstructs\\|deleuzian\\|desire\\|desires\\|dialectic\\|dialectical\\|dialectically\\|discourse\\|discursive\\|disrupt\\|disrupts\\|engage\\|engagement\\|engages\\|episteme\\|epistemic\\|ergo\\|fetish\\|fetishes\\|fetishise\\|fetishised\\|fetishize\\|fetishized\\|gaze\\|gender\\|gendered\\|historicise\\|historicisation\\|historicize\\|historicization\\|hegemonic\\|hegemony\\|identity\\|identity politics\\|intensifies\\|intensify\\|intensifying\\|interrogate\\|interrogates\\|interrogation\\|intertextual\\|intertextuality\\|irony\\|ironic\\|ironical\\|ironically\\|ironisation\\|ironization\\|ironises\\|ironizes\\|jouissance\\|juxtapose\\|juxtaposes\\|juxtaposition\\|lacanian\\|lack\\|loci\\|locus\\|locuses\\|matrix\\|mise en abyme\\|mocking\\|mockingly\\|modalities\\|modality\\|myth\\|mythologies\\|mythology\\|myths\\|narrative\\|narrativisation\\|narrativization\\|narrativity\\|nexus\\|nodal\\|node\\|normative\\|normativity\\|notion\\|notions\\|objective\\|objectivity\\|objectivities\\|objet petit a\\|ontology\\|ontological\\|operate\\|operates\\|otherness\\|othering\\|paradigm\\|paradigmatic\\|paradigms\\|parody\\|parodic\\|parodies\\|physicality\\|plenitude\\|poetics\\|popular notions\\|position\\|post hoc\\|post internet\\|post-internet\\|postmodernism\\|postmodernist\\|postmodernity\\|postmodern\\|practice\\|practise\\|praxis\\|problematic\\|problematics\\|problematise\\|problematize\\|proposition\\|qua\\|reading\\|readings\\|reification\\|relation\\|relational\\|relationality\\|relations\\|representation\\|representations\\|rhizomatic\\|rhizome\\|simulacra\\|simulacral\\|simulation\\|simulationism\\|simulationism\\|situate\\|situated\\|situates\\|stereotype\\|stereotypes\\|strategy\\|strategies\\|subjective\\|subjectivity\\|subjectivities\\|subvert\\|subversion\\|subverts\\|text\\|textual\\|textuality\\|thinker\\|thinkers\\|trajectory\\|transgress\\|transgresses\\|transgression\\|transgressive\\|unfolding\\|undermine\\|undermining\\|undermines\\|work\\|works\\|wry\\|wryly\\|zizekian\\|zi┼żekian\\)\\b")
170
171 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
172 ;; Highlighting
173 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174
175 (defun search-for-keyword (regex limit)
176   "Match the provided regex in the buffer"
177   (let ((match-data-to-set nil)
178         found)
179     (save-match-data
180       (while (and (null match-data-to-set)
181                   (re-search-forward regex limit t))
182             (setq match-data-to-set (match-data))))
183     (when match-data-to-set
184       (set-match-data match-data-to-set)
185       (goto-char (match-end 0)) 
186       t)))
187
188 (defun lexical-illusions-search-for-keyword (limit)
189   (search-for-keyword lexical-illusions-regex limit))
190
191 (defun passive-voice-search-for-keyword (limit)
192   (search-for-keyword passive-voice-regex limit))
193
194 (defun weasel-words-search-for-keyword (limit)
195   (search-for-keyword weasel-words-regex limit))
196
197 (defun artbollocks-search-for-keyword (limit)
198   (search-for-keyword artbollocks-regex limit))
199
200 (defconst lexicalkwlist '((lexical-illusions-search-for-keyword 
201                            (2 'font-lock-lexical-illusions-face t))))
202
203 (defconst passivekwlist '((passive-voice-search-for-keyword 
204                               (0 'font-lock-passive-voice-face t))))
205
206 (defconst weaselkwlist '((weasel-words-search-for-keyword 
207                              (0 'font-lock-weasel-words-face t))))
208
209 (defconst artbollockskwlist '((artbollocks-search-for-keyword 
210                                   (0 'font-lock-artbollocks-face t))))
211
212 (defun add-artbollocks-keywords ()
213   (when lexical-illusions
214     (font-lock-add-keywords nil lexicalkwlist))
215   (when passive-voice
216     (font-lock-add-keywords nil passivekwlist))
217   (when weasel-words
218     (font-lock-add-keywords nil weaselkwlist))
219   (when artbollocks
220     (font-lock-add-keywords nil artbollockskwlist)))
221
222 (defun remove-artbollocks-keywords ()
223   (font-lock-remove-keywords nil lexicalkwlist)
224   (font-lock-remove-keywords nil passivekwlist)
225   (font-lock-remove-keywords nil weaselkwlist)
226   (font-lock-remove-keywords nil artbollockskwlist))
227
228 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
229 ;; Text metrics
230 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
231
232 (defun count-letters ()
233   (how-many "\\w" (point-min) (point-max)))
234
235 (defun count-syllables ()
236   ;; Naively count vowel runs as syllable markers
237   (how-many "[aeiouy]+" (point-min) (point-max)))
238
239 (defun count-words ()
240   (how-many "\\w+" (point-min) (point-max)))
241
242 (defun count-sentences()
243   ;; Avoid 8.8 but count this... as a sentence break
244   (how-many "\\w[!?.]" (point-min) (point-max)))
245
246 ;; FIXME: Avoid divide by zero where document is empty or small
247
248 (defun automated-readability-index ()
249   (let ((words (count-words)))
250     (- (+ (* 4.71 (/ (count-letters) words))
251           (* 0.5 (/ words (count-sentences))))
252        21.43)))
253
254 (defun flesch-reading-ease ()
255   (let ((words (count-words)))
256     (- 206.834
257        (* 1.015 (/ words (count-sentences)))
258        (* 84.6 (/ (count-syllables) words)))))
259
260 (defun flesch-kinkaid-grade-level ()
261   (let ((words (count-words)))
262     (- (+ (* 11.8 (/ (count-syllables) words))
263           (* 0.39 (/ words (count-sentences))))
264        15.59)))
265
266 (defun word-count ()
267   "count the number of words in the buffer"
268   (interactive)
269   (message "Word count: %s" (count-words)))
270
271 (defun sentence-count ()
272   "count the number of sentences in the buffer"
273   (interactive)
274   (message "Sentence count: %s" (count-sentences)))
275
276 (defun readability-index ()
277   "determine the automated readability index of the buffer"
278   (interactive)
279   (message "Readability index: %s" (automated-readability-index)))
280
281 (defun reading-ease ()
282   "determine the Flesch reading ease of the buffer"
283   (interactive)
284   (message "Reading ease: %s" (flesch-reading-ease)))
285
286 (defun grade-level ()
287   "determine the Flesch-Kinkaid grade level of the buffer"
288   (interactive)
289   (message "Grade level: %s" (flesch-kinkaid-grade-level)))
290
291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
292 ;; The mode
293 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
294
295 (defconst artbollocks-mode-keymap (make-keymap))
296 (define-key artbollocks-mode-keymap (kbd "C-c [") 'word-count)
297 (define-key artbollocks-mode-keymap (kbd "C-c ]") 'sentence-count)
298 (define-key artbollocks-mode-keymap (kbd "C-c \\") 'readability-index)
299 (define-key artbollocks-mode-keymap (kbd "C-c /") 'reading-ease)
300 (define-key artbollocks-mode-keymap (kbd "C-c =") 'grade-level)
301
302 ;;;###autoload
303 (define-minor-mode artbollocks-mode "highlight passive voice, weasel words and artbollocks in text, provide useful text metrics"
304   :lighter " AB"
305   :keymap artbollocks-mode-keymap
306   :group 'artbollocks-mode
307   (if artbollocks-mode
308       (add-artbollocks-keywords)
309     (remove-artbollocks-keywords)))
310
311 (defun turn-on-artbollocks-mode ()
312   "turn artbollocks-mode on"
313   (interactive)
314   (artbollocks-mode 1))
315
316 (provide 'artbollocks-mode)
317
318 ;; TODO
319 ;; Toggle adding word/sentence count to status bar
320 ;; Pluralization
321 ;; Incorporate diction commands if available (and advise on installation if not)
322 ;; Split general writing back out