Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@
: (global-set-key "\C-c\C-gg" 'writegood-grade-level)
: (global-set-key "\C-c\C-ge" 'writegood-reading-ease)

** On the fly

There is support for on-the-fly calculation of the reading score.
This means that while you are writing you will see your sentences
receive a score in the modeline.

There are three events that triggers the on-the-fly calculation:

1. closing a sentence with a '.' character.
2. filling a paragraph (typically done in Emacs with =M-q=)
3. saving the buffer

These events will trigger the score calculation for the sentence,
paragraph and buffer at point respectively.

This feature can be toggled on and off with:

: (writegood-on-the-fly-toggle)

Or by pressing =M-x writegood-on-the-fly-toggle=.

* Customization

The user is free to customize three main portions of the mode.
Expand All @@ -57,6 +78,21 @@ The user is free to customize three main portions of the mode.
the verbs that do not end in 'ed' to signify past tense. This
variable allow the user to modify the list as needed.

** Modes For On The Fly Scoring

One can customize the modes in which the on-the-fly feature should
run (for instance may not make sense to check readability scores in
a programming mode). The variable to customize is:
=writegood-on-the-fly-modes=.

An example of customization can be:

: (add-to-list 'writegood-on-the-fly-modes "some-cool-mode")

By default this is set to:

: '("text-mode" "fundamental-mode" "org-mode" "latex-mode" "rst-mode" "markdown-mode" "mu4e-compose-mode")

* Alternatives

[[https://github.com/sachac/artbollocks-mode][Artbollocks]] looks to be an alternative mode to this one.
104 changes: 99 additions & 5 deletions writegood-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@
:group 'writegood
:type '(repeat character))

(defcustom writegood-on-the-fly-modes

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm no sure I understand why we would want to restrict the modes here?

If I turn on writegood in any mode and then toggle on the on-the-fly feature, it should work. Is there a reason to add this filtering/checking step?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far this mode acts globally when you toggle it. This means that if you are programming, writegood-mode would try to calculate the score of your code (which does not seem to make sense). For this reason I restricted to only those modes I found this feature useful.

A sensible alternative would be to run the feature only for the current buffer.

I just like it activated by default in multiple modes because I do not need to think about activating useful stuff while I am focusing on writing. Sometimes I just think: "this sentence does not look good" and then I find confirmation in the sentence score in the modeline :)

'("text-mode" "org-mode" "latex-mode" "rst-mode" "markdown-mode" "mu4e-compose-mode")
"The modes in which on-the-fly writegood is active"
:group 'writegood
:type '(repeat string))

(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\\|"
Expand Down Expand Up @@ -272,17 +278,105 @@ From Wikipedia URL `https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_t
((and (<= 80.0 score) (< score 90.0)) "Easy (6th grade)")
((<= 90.0 score) "Very easy (5th grade)")))

(defun writegood-calculate-reading-ease (&optional start end)
"Calculate score of Flesch-Kincaid reading ease test in the region bounded by START and END.

Scores roughly between 0 and 100."
(let* ((params (writegood-fk-parameters start end))
(sentences (nth 0 params))
(words (nth 1 params))
(syllables (nth 2 params)))
(- 206.835 (* 1.015 (/ words sentences)) (* 84.6 (/ syllables words)))))

(defun writegoodmode-reading-ease-thing-at-point (thing)
"Calculate score for thing at point."
(let* ((bounds (bounds-of-thing-at-point thing))
(b (car bounds))
(e (cdr bounds)))
(if (and
(not (null b))
(not (null e))
;; this is a guess: when the interval between boundaries is
;; huge, the paragraph is too big to be validated.
(< (- e b) 100000))
(let ((score (writegood-calculate-reading-ease b e)))
(message "%s reading ease score: %.2f %s" (symbol-name thing) score
(writegood-reading-ease-score->comment score))))))

;;;###autoload
(defun writegoodmode-reading-ease-sentence ()
"Calculate score for the sentence at point."
(interactive)
(writegoodmode-reading-ease-thing-at-point 'sentence))

;;;###autoload
(defun writegoodmode-reading-ease-paragraph ()
"Calculate score for the paragraph at point."
(interactive)
(writegoodmode-reading-ease-thing-at-point 'paragraph))

;;;###autoload
(defun writegoodmode-reading-ease-page ()
"Calculate score for the page at point."
(interactive)
(writegoodmode-reading-ease-thing-at-point 'page))

(defun writegood-after-sentence ()
"Calculate reading ease after a sentence is completed. Here we
consider the sentence completion the addition of a dot."
(if (string-match-p sentence-end-base (make-string 1 last-command-event))
(writegoodmode-reading-ease-sentence)))

(defun writegood-after-paragraph ()
"Calculate reading ease after a paragraph is completed. Here we
consider the paragraph completion to be the call of a
,*-fill-paragraph command."
(if (string-match-p (regexp-quote "fill-paragraph") (symbol-name real-this-command)) (writegoodmode-reading-ease-paragraph)))

(defun apply-only-in-text-major-modes (fn)
(if (member (symbol-name major-mode) writegood-on-the-fly-modes)
(funcall fn)))

(defun writegood-after-sentence-hook ()
(save-excursion
(goto-char (- (point) 2)) ; go back a couple of char to make thing-at-point work on the right sentence
(apply-only-in-text-major-modes 'writegood-after-sentence)))
(defun writegood-after-paragraph-hook () (apply-only-in-text-major-modes 'writegood-after-paragraph))
(defun writegood-after-page-hook () (apply-only-in-text-major-modes 'writegoodmode-reading-ease-page))

(defun writegood-on-the-fly-turn-on ()
"Add hooks to enable on-the-fly scoring."
(add-hook 'post-self-insert-hook 'writegood-after-sentence-hook)
(add-hook 'post-command-hook 'writegood-after-paragraph-hook)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can use fill-paragraph-function here to avoid checking the last command and better plug into the fill architecture.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a very cool comment: I will both improve the code and learn more about Emacs fill architecture :D
I will give a look into this, and propose an alternative.

(add-hook 'after-save-hook 'writegood-after-page-hook)
(message "Writegood-mode-on-the-fly turned on."))

(defun writegood-on-the-fly-turn-off ()
"Remove hooks to disable on-the-fly scoring."
(remove-hook 'post-self-insert-hook 'writegood-after-sentence-hook)
(remove-hook 'post-command-hook 'writegood-after-paragraph-hook)
(remove-hook 'after-save-hook 'writegood-after-page-hook)
(message "Writegood-mode-on-the-fly turned off."))


;;;###autoload
(defun writegood-on-the-fly-toggle ()
"Toggle on-the-fly writegood mode. Now every time a dot is typed to close a sentence, every time a paragraph is filled,
and every time the buffer is saved the easy score is calculated
for a sentence, paragraph, whole buffer respectively. The result is shown as a message."
(interactive)
(let ((are-hook-set (member 'writegood-after-page-hook (with-temp-buffer after-save-hook))))
(if are-hook-set
(writegood-on-the-fly-turn-off)
(writegood-on-the-fly-turn-on))))

;;;###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)))))
(let ((score (writegood-calculate-reading-ease start end)))
(message "Flesch-Kincaid reading ease score: %.2f %s" score
(writegood-reading-ease-score->comment score))))

Expand Down