aboutsummaryrefslogtreecommitdiff
path: root/emacs/.emacs.d/lisp/my/my-nov.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/.emacs.d/lisp/my/my-nov.el')
-rw-r--r--emacs/.emacs.d/lisp/my/my-nov.el136
1 files changed, 127 insertions, 9 deletions
diff --git a/emacs/.emacs.d/lisp/my/my-nov.el b/emacs/.emacs.d/lisp/my/my-nov.el
index 0246be2..21df675 100644
--- a/emacs/.emacs.d/lisp/my/my-nov.el
+++ b/emacs/.emacs.d/lisp/my/my-nov.el
@@ -28,27 +28,52 @@
(require 'nov)
+(defvar my-nov-mode-line-format "%p%% %t: %c")
+(defvar-local my-nov-title nil)
+(defvar-local my-nov-chapter-title nil)
+(defvar-local my-nov-position-percent nil)
+
;; override nov-render-title
;; this is because header line does not work with follow mode
(defun my-nov-render-title (dom)
"Custom <title> rendering function for DOM.
Sets `header-line-format' to a combination of the EPUB title and
chapter title."
- (let ((title (cdr (assq 'title nov-metadata)))
- (chapter-title (car (esxml-node-children dom))))
- (when (not chapter-title)
- (setq chapter-title "No title"))
- ;; this shouldn't happen for properly authored EPUBs
- (when (not title)
- (setq title "No title"))
+ (setq my-nov-title (cdr (assq 'title nov-metadata))
+ my-nov-chapter-title (car (esxml-node-children dom))))
+
+(defun my-nov-update-mode-line ()
+ (setq my-nov-position-percent
+ (/ (* 100 (my-nov-word-position)) my-nov-total-word-count))
+ (let ((title (or my-nov-title (propertize "No title" 'face 'italic)))
+ (chapter-title (or my-nov-chapter-title
+ (propertize "No title" 'face 'italic))))
(setq mode-line-buffer-identification
- (concat title ": " chapter-title))
- ))
+ (format-spec
+ my-nov-mode-line-format
+ `((?c . ,chapter-title)
+ (?t . ,title)
+ (?p . ,my-nov-position-percent))))))
(defun my-nov-render-span (dom)
(unless (equal (dom-attr dom 'epub:type) "pagebreak")
(shr-generic dom)))
+;;; TODO: perhaps no indentation?
+(defun my-nov-render-ol (dom)
+ (shr-ensure-paragraph)
+ (let* ((attrs (dom-attributes dom))
+ (start-attr (alist-get 'start attrs))
+ ;; Start at 1 if there is no start attribute
+ ;; or if start can't be parsed as an integer.
+ (start-index (condition-case _
+ (cl-parse-integer start-attr)
+ (t nil)))
+ (shr-list-mode (or start-index 'ul))
+ (shr-internal-bullet `(" " . ,(shr-string-pixel-width " "))))
+ (shr-generic dom))
+ (shr-ensure-paragraph))
+
(defun my-nov-find-file-with-ipath (file-name ipath)
"Find epub file and goto IPATH.
@@ -88,5 +113,98 @@ Useful for recoll."
(visual-line-mode)
)
+(defvar-local my-nov-document-word-counts nil
+ "Word count of each nov document.")
+
+(defvar-local my-nov-total-word-count nil
+ "Total word count of the epub.")
+
+(defun my-nov-count-words ()
+ (interactive)
+ (unless my-nov-document-word-counts
+ (message "Counting words...")
+ (setq my-nov-document-word-counts
+ (apply
+ 'vector
+ (seq-map
+ (lambda (doc)
+ (with-temp-buffer
+ (pcase-let ((`(,name . ,file) doc))
+ (insert-file-contents file)
+ (nov-render-html)
+ (cons name (count-words (point-min) (point-max))))))
+ nov-documents)))
+ (setq my-nov-total-word-count
+ (seq-reduce
+ (lambda (sum pair)
+ (+ sum (cdr pair)))
+ my-nov-document-word-counts
+ 0))
+ (message "Counting words...done")))
+
+(defun my-nov-stats ()
+ (interactive)
+ (message "%d words; %d standard pages"
+ my-nov-total-word-count
+ (ceiling (/ my-nov-total-word-count 300.0))))
+
+;;; TODO: also show current percentage in the total book in the mode
+;;; line
+(defun my-nov-goto-nth-word (n)
+ "Go to the nth word of the current epub."
+ (my-nov-count-words)
+ (setq nov-documents-index -1)
+ (let ((found
+ (seq-find
+ (lambda (pair)
+ (setq n (- n (cdr pair)))
+ (setq nov-documents-index (1+ nov-documents-index))
+ (<= n 0))
+ my-nov-document-word-counts)))
+ (nov-render-document)
+ (if (> n 0)
+ (end-of-buffer)
+ (forward-word (+ n (cdr found)))))
+ )
+
+(defun my-nov-word-position ()
+ "Where are we in terms of word position?
+
+Return n, such that nth word of the epub is at point."
+ (my-nov-count-words)
+ (let ((result 0))
+ (dotimes (i nov-documents-index)
+ (setq result (+ result (cdr (aref my-nov-document-word-counts i)))))
+ (setq result (+ result (count-words (point-min) (point))))))
+
+(defun my-nov-skim-forward ()
+ "Forward by 3-10% of the book."
+ (interactive)
+ (let ((pc (+ 3 (random 8))))
+ (my-nov-goto-nth-word
+ (+ (my-nov-word-position)
+ (/ (* my-nov-total-word-count pc) 100)))
+ (message "Skimmed forward by %d%% of the book" pc)))
+
+(defun my-nov-skim-backward ()
+ "Backward by 3-10% of the book."
+ (interactive)
+ (let ((pc (+ 3 (random 8))))
+ (my-nov-goto-nth-word
+ (max
+ 0
+ (- (my-nov-word-position)
+ (/ (* my-nov-total-word-count pc) 100))))
+ (message "Skimmed backward by %d%% of the book" pc)))
+
+(defun my-nov-goto-random-position ()
+ "Goto a random position in the epub."
+ (interactive)
+ (my-nov-count-words)
+ (let ((n (random my-nov-total-word-count)))
+ (my-nov-goto-nth-word n)
+ (message "Went to the %dth word (%d%% of the book)."
+ n (/ (* n 100) my-nov-total-word-count))))
+
(provide 'my-nov)
;;; my-nov.el ends here