;;; +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