emacs/lisp/+org-wc.el

113 lines
3.5 KiB
EmacsLisp

;;; +org-wc.el --- org-wc in the modeline -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(require 'org-wc)
(require '+modeline)
(require 'cl-lib)
(defgroup +org-wc nil
"Extra fast word-counting in `org-mode'"
:group 'org-wc
:group 'org)
(defvar-local +org-wc-word-count nil
"Running total of words in this buffer.")
(defcustom +org-wc-update-after-funcs '(org-narrow-to-subtree
org-narrow-to-block
org-narrow-to-element
org-capture-narrow)
"Functions after which to update the word count."
:type '(repeat function))
(defcustom +org-wc-deletion-idle-timer 0.25
"Length of time, in seconds, to wait before updating word-count."
:type 'number)
(defcustom +org-wc-huge-change 5000
"Number of characters that constitute a \"huge\" insertion."
:type 'number)
(defcustom +org-wc-huge-buffer 10000
"Number of words past which we're not going to try to count."
:type 'number)
(defvar +org-wc-correction -5
"Number to add to `+org-wc-word-count', for some reason?
`+org-wc-word-count' seems to consistently be off by 5. Thus
this correction. (At some point I should correct the underlying
code... probably).")
(defvar-local +org-wc-update-timer nil)
(defun +org-wc-delayed-update (&rest _)
(if +org-wc-update-timer
(setq +org-wc-update-timer nil)
(setq +org-wc-update-timer
(run-with-idle-timer +org-wc-deletion-idle-timer nil #'+org-wc-update))))
(defun +org-wc-force-update ()
(interactive)
(message "Counting words...")
(when (timerp +org-wc-update-timer)
(cancel-timer +org-wc-update-timer))
(+org-wc-update)
(message "Counting words...done"))
(defun +org-wc-update (&rest _) ; Needs variadic parameters, since it's advice
(dlet ((+org-wc-counting t))
(+org-wc-buffer)
(force-mode-line-update)
(setq +org-wc-update-timer nil)))
(defun +org-wc-changed (start end length)
(+org-wc-delayed-update))
(defun +org-wc-buffer ()
"Count the words in the buffer."
(when (and (derived-mode-p 'org-mode)
(not (eq +org-wc-word-count 'huge)))
(setq +org-wc-word-count
(cond
((> (count-words (point-min) (point-max))
+org-wc-huge-buffer)
'huge)
(t (org-word-count-aux (point-min) (point-max)))))))
(defvar +org-wc-counting nil
"Are we currently counting?")
(defun +org-wc-recount-widen (&rest _)
(when (and (not +org-wc-counting))
(+org-wc-update)))
(defun +org-wc-modeline ()
(cond
((eq +org-wc-word-count 'huge) "huge")
(+org-wc-word-count (format "%sw" (max 0 (+ +org-wc-word-count +org-wc-correction))))))
(define-minor-mode +org-wc-mode
"Count words in `org-mode' buffers in the mode-line."
:lighter ""
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-.") #'+org-wc-force-update)
map)
(if +org-wc-mode
(progn ; turn on
(+org-wc-buffer)
(add-hook 'after-change-functions #'+org-wc-delayed-update nil t)
(setq-local +modeline-position-function #'+org-wc-modeline)
(dolist (fn +org-wc-update-after-funcs)
(advice-add fn :after #'+org-wc-update)))
(progn ; turn off
(remove-hook 'after-change-functions #'+org-wc-delayed-update t)
(kill-local-variable '+modeline-position-function)
(dolist (fn +org-wc-update-after-funcs)
(advice-remove fn #'+org-wc-update)))))
(provide '+org-wc)
;;; +org-wc.el ends here