Last active
May 13, 2025 01:49
-
-
Save RyanFleck/3195387af3ebd302152ec3397457689a to your computer and use it in GitHub Desktop.
Offline Scripture Text Insertion Functions - rcf-sword.el
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; rcf-sword.el --- Use a local .bblx file to insert and read scripture. | |
;;; Commentary: | |
;; For scriptural study. | |
;; Eventually you should publish this: https://spin.atomicobject.com/write-emacs-package/ | |
;; ===================================================================== | |
;; Changelog - Make a quick note when you update this file. | |
;; ===================================================================== | |
;; 2024-08-01 -> Wrote first version of this program, including | |
;; checking the sqlite db, reading the user input string, and | |
;; inserting an org-mode quote block into the buffer. | |
;; 2024-08-02 -> Cleaned up the code. | |
;; 2024-08-02 -> Added 'insert random verse' functionality. | |
;; 2024-09-10 -> Change position of verse-version insertion | |
;; 2024-09-23 -> Fixed a bug where 'string includes?' would pick | |
;; the wrong book during casual insertion of scripture. | |
;; 2024-11-11 -> Added Markdown and plaintext insertion support. | |
;; 2024-11-23 -> Pull translation title/abbvrev from database. | |
;; 2025-05-12 -> Write new fuctions to show entire books. | |
;;; Code: | |
;; We need string handling and common lisp's MAP magic. | |
(require 'cl-lib) | |
(require 'markdown-mode) | |
(require 'olivetti) | |
(require 'org) | |
(require 's) | |
;; Options | |
(defvar rcf-sword-md-link-reference nil) | |
(defvar rcf-sword-md-use-footnotes nil) | |
;; Simple Helper Functions | |
(defun get-first-char (str) | |
(char-to-string (string-to-char str))) | |
(defun get-first-word (str) | |
(cl-first (s-split " " str))) | |
;; "Ensures the prerequisites are available." | |
(setq rcf-sword-ok (and (sqlite-available-p) (not (version< emacs-version "29")))) | |
;; If you can read this, compile your emacs: https://ryanfleck.ca/2024/compiling-emacs/ | |
;; This sexp will freak out if the prerequisites for this module aren't met. | |
(when (not rcf-sword-ok) | |
(error | |
"Module rcf-sword.el requires Emacs 29 (with native sqlite support) to function, please upgrade")) | |
;; All functionality within this module depends on having a working .bblx database | |
;; Inspect with SQLITE3: .tables, .headers on | |
(setq sword-bblx-file "~/emacs/sword/new.king.james.version.bblx") | |
;; Alternatively - "~/emacs/sword/english.standard.version.bblx" | |
;; Set up DB connection and cache frequently used DB properties. | |
(setq sword-db | |
;; "Holds the database connection to the bible database on disk." | |
(sqlite-open (expand-file-name sword-bblx-file))) | |
(defun set-scripture-db-info (db) | |
"Check the scripture DB for simple properties." | |
(message "Fetching scripture version/abbreviation from sqlite database.") | |
(let* ((query-result (sqlite-execute db "select Description, Abbreviation from Details;")) | |
(description (caar query-result)) | |
(abbreviation (cadar query-result))) | |
;; Set global variables (prevent sqlite3 use when Emacs is started.) | |
(setq scripture-version description) | |
(setq scripture-abbreviation abbreviation) | |
(message (s-concat "[rcf-sword.el] Loaded with '" description "' translation.")) | |
(list description abbreviation))) | |
(defun get-scripture-version (db) | |
"Return the DB's translation name like 'New King James Version' as a string." | |
(if (boundp 'scripture-version) | |
scripture-version | |
(car (set-scripture-db-info db)))) | |
(defun get-scripture-abbreviation (db) | |
"Return the DB's translation abbreviation like 'NKJV' as a string." | |
(if (boundp 'scripture-abbreviation) | |
scripture-abbreviation | |
(cadr (set-scripture-db-info db)))) | |
;; The database must have the following format: | |
;; Typically shipped with '.bblx' extension. | |
;; Table: 'Bible' (can be viewed with sqlite3) | |
;; Book Chapter Verse Scripture | |
;; ---------------------------------------------------------------------------------------------------- | |
;; 1 1 1 In the beginning God created the heavens and the earth. | |
;; 1 1 2 The earth was without form, and void; and darkness was {\cf2\super [1]} on ... | |
;; 1 1 3 Then God said, "Let there be light"; and there was light. | |
;; 1 1 4 And God saw the light, that it was good; and God divided the light from the... | |
;; Sorry to those who use more than 66 books - you'll have to hack this for yourself. | |
(defvar sword-book-data | |
'((1 1 "Genesis") | |
(1 2 "Exodus") | |
(1 3 "Leviticus") | |
(1 4 "Numbers") | |
(1 5 "Deuteronomy") | |
(1 6 "Joshua") | |
(1 7 "Judges") | |
(1 8 "Ruth") | |
(1 9 "1 Samuel") | |
(1 10 "2 Samuel") | |
(1 11 "1 Kings") | |
(1 12 "2 Kings") | |
(1 13 "1 Chronicles") | |
(1 14 "2 Chronicles") | |
(1 15 "Ezra") | |
(1 16 "Nehemiah") | |
(1 17 "Esther") | |
(1 18 "Job") | |
(1 19 "Psalms") | |
(1 20 "Proverbs") | |
(1 21 "Ecclesiastes") | |
(1 22 "Song of Solomon") | |
(1 23 "Isaiah") | |
(1 24 "Jeremiah") | |
(1 24 "Lamentations") | |
(1 26 "Ezekiel") | |
(1 27 "Daniel") | |
(1 28 "Hosea") | |
(1 29 "Joel") | |
(1 30 "Amos") | |
(1 31 "Obadiah") | |
(1 32 "Jonah") | |
(1 33 "Micah") | |
(1 34 "Nahum") | |
(1 35 "Habakkuk") | |
(1 36 "Zephaniah") | |
(1 37 "Haggai") | |
(1 38 "Zechariah") | |
(1 39 "Malachi") | |
(2 40 "Matthew") | |
(2 41 "Mark") | |
(2 42 "Luke") | |
(2 43 "John") | |
(2 44 "Acts") | |
(2 45 "Romans") | |
(2 46 "1 Corinthians") | |
(2 47 "2 Corinthians") | |
(2 48 "Galatians") | |
(2 49 "Ephesians") | |
(2 50 "Philippians") | |
(2 51 "Colossians") | |
(2 52 "1 Thessalonians") | |
(2 53 "2 Thessalonians") | |
(2 54 "1 Timothy") | |
(2 55 "2 Timothy") | |
(2 56 "Titus") | |
(2 57 "Philemon") | |
(2 58 "Hebrews") | |
(2 59 "James") | |
(2 60 "1 Peter") | |
(2 61 "2 Peter") | |
(2 62 "1 John") | |
(2 63 "2 John") | |
(2 64 "3 John") | |
(2 65 "Jude") | |
(2 66 "Revelation")) | |
"Enables a book number to be found from its name.") | |
(defvar sword-book-names (cl-mapcar #'caddr sword-book-data)) | |
;; ;; (car sword-book-data) => (1 1 "Genesis") | |
;; ;; (caddr (car sword-book-data)) => "Genesis" | |
;; ;; (completing-read PROMPT COLLECTION &optional PREDICATE REQUIRE-MATCH INITIAL-INPUT HIST DEF INHERIT-INPUT-METHOD) | |
;; (completing-read-default "Select a book: " sword-book-names ) | |
;; Above this line are experiments | |
;;; ============================================================================ | |
;; Below this line is active code | |
;; What is a BOOK TRIPLE? It is the data above | |
;; Genesis chapter 1 is (1 1 "Genesis") | |
(defun book-title-contains? (book-triple str) | |
"Take the book data BOOK-TRIPLE and check if STR matches the title." | |
;; https://github.com/magnars/s.el?tab=readme-ov-file#s-contains-needle-s-optional-ignore-case | |
(s-contains? str (caddr book-triple) t)) | |
(defun book-title-starts-with? (book-triple str) | |
"Take the book data BOOK-TRIPLE and check if STR matches the title." | |
;; https://github.com/magnars/s.el?tab=readme-ov-file#s-contains-needle-s-optional-ignore-case | |
(s-starts-with? str (caddr book-triple) t)) | |
;; Example: | |
;; (check-book-title (car sword-book-data) "gen") | |
(defun get-book-data (bible-sword-book-data partial-book-str) | |
"Given a PARTIAL-BOOK-STR return the matching book in BIBLE-SWORD-BOOK-DATA." | |
(if-let ((book-data | |
(seq-find | |
#'(lambda (book-triple) | |
(book-title-starts-with? book-triple partial-book-str)) | |
bible-sword-book-data))) | |
book-data ;; If 'starts-with' finds the data return it, otherwise search with 'contains'. | |
(seq-find | |
#'(lambda (book-triple) (book-title-contains? book-triple partial-book-str)) | |
bible-sword-book-data))) | |
;; (get-book-data sword-book-data "Tim") | |
;; (get-book-data sword-book-data "Job") | |
(defun get-book-number (bible-sword-book-data str) | |
"Given a STR return the matching book in BIBLE-SWORD-BOOK-DATA." | |
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html | |
(nth 1 (get-book-data bible-sword-book-data str))) | |
;; Last verse of last chapter: | |
;; (sqlite-execute sword-db "select MAX(chapter), MAX(verse) from bible where book=? and chapter=(SELECT MAX(chapter) FROM Bible WHERE book=?);" (list 2 2)) | |
;; (sqlite-execute sword-db "SELECT MAX(chapter) FROM Bible WHERE book=?;" (list 2)) | |
(defun get-random-book (bible-sword-data) | |
"Return a random book number using BIBLE-SWORD-DATA." | |
(+ 1 (random (length bible-sword-data)))) | |
(defun get-random-chapter (bible-sword-data book) | |
"Return a BOOK number using BIBLE-SWORD-DATA." | |
(let ((max-chapter | |
(or (caar | |
(sqlite-execute sword-db "SELECT MAX(chapter) FROM Bible WHERE book=?;" (list book))) | |
0))) | |
(+ 1 (random max-chapter)))) | |
(defun get-random-verse (bible-sword-data book chapter) | |
"Return a book number using BIBLE-SWORD-DATA in BOOK and CHAPTER." | |
(let ((max-verse | |
(or (caar | |
(sqlite-execute sword-db "SELECT MAX(verse) FROM Bible WHERE book=? AND chapter=?;" | |
(list book chapter))) | |
0))) | |
(+ 1 (random max-verse)))) | |
;; (get-random-book sword-book-data) | |
;; (get-random-chapter sword-book-data 2) | |
;; (get-random-chapter sword-book-data 200) | |
;; (get-random-verse sword-book-data 2 20) | |
(defun get-random-scripture-data (bible-books) | |
"Return a random verse as (book chapter verse) using BIBLE-BOOKS." | |
(let* ((book (get-random-book bible-books)) | |
(chapter (get-random-chapter bible-books book)) | |
(verse (get-random-verse bible-books book chapter))) | |
(list book chapter verse))) | |
(defun get-random-scripture (bible-books) | |
"Return a random verse using BIBLE-BOOKS." | |
(let* ((data (get-random-scripture-data bible-books)) | |
(result | |
(sqlite-execute sword-db "select * from bible where book=? and chapter=? and verse=?;" | |
data))) | |
(if (or (not result) (equal result 0)) | |
(message "I goofed up, data was %s" data) | |
result))) | |
(defun get-random-proverb-data (bible-books) | |
"Return a random verse as (book chapter verse) using BIBLE-BOOKS." | |
(let* ((book (get-book-number bible-books "Proverbs")) | |
(chapter (get-random-chapter bible-books book)) | |
(verse (get-random-verse bible-books book chapter))) | |
(list book chapter verse))) | |
(defun get-random-proverb (bible-books) | |
"Return a random verse using BIBLE-BOOKS." | |
(let* ((data (get-random-proverb-data bible-books)) | |
(result | |
(sqlite-execute sword-db "select * from bible where book=? and chapter=? and verse=?;" | |
data))) | |
(if (or (not result) (equal result 0)) | |
(message "I goofed up, data was %s" data) | |
result))) | |
;; (get-random-scripture sword-book-data) | |
(defun get-book-name (bible-sword-book-data str) | |
"Given a STR return the matching book in BIBLE-SWORD-BOOK-DATA." | |
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html | |
(nth 2 (get-book-data bible-sword-book-data str))) | |
(defun get-book-name-from-num (bible-sword-book-data num) | |
"Given a NUM return the matching book in BIBLE-SWORD-BOOK-DATA." | |
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html | |
(nth 2 (seq-find #'(lambda (x) (equal (nth 1 x) num)) bible-sword-book-data))) | |
;; (get-book-name-from-num sword-book-data 4) | |
;; (get-book-name sword-book-data "Num") | |
;; Example: | |
;; (get-book-number sword-book-data "Mat") => (2 40 "Matthew") | |
(defun get-bible-data-book (book-number) | |
"Retrieves a full book from the bible as indicated by BOOK-NUMBER." | |
(sqlite-execute sword-db "select * from bible where book=?" (list book-number))) | |
;; (get-bible-data-book-chapter 46) | |
(defun get-bible-data-book-chapter (book-number book-chapter) | |
"With BOOK-NUMBER and BOOK-CHAPTER return data for one book of the bible." | |
(sqlite-execute sword-db "select * from bible where book=? and chapter=?" | |
(list book-number book-chapter))) | |
;; (get-bible-data-book-chapter 46 5) | |
;; (get-bible-data-book-chapter 1 6) | |
(defun get-bible-data-book-chapter-verses (book-number book-chapter verse-min verse-max) | |
"Return scripture from BOOK-NUMBER, BOOK-CHAPTER, VERSE-MIN to VERSE-MAX." | |
(sqlite-execute | |
sword-db "select * from bible where book=? and chapter=? and verse>=? and verse<=?;" | |
(list book-number book-chapter verse-min verse-max))) | |
;; (get-bible-data-book-chapter-verses 1 6 1 4) | |
(defun split-input-data (str) | |
"Produces a list with (list book chapter verses) from input STR." | |
(let* ((full-string (s-split " " str)) | |
(chapter-verse (car (last full-string))) | |
(book (s-join " " (butlast full-string))) | |
(chapter-verse-split (s-split ":" chapter-verse)) | |
(verses? (length> chapter-verse-split 1)) | |
(chapter (car chapter-verse-split)) | |
(verses (s-split "-" (car (last chapter-verse-split))))) | |
(if verses? | |
(list book (string-to-number chapter) (cl-mapcar #'string-to-number verses)) | |
(list book (string-to-number chapter) nil)))) | |
;; (split-input-data "Genisis 6") | |
;; (split-input-data "Genisis 6:1") | |
;; (split-input-data "Genisis 6:1-2") | |
(defun get-bible-verses (str bible-sword-book-data) | |
"Interpret STR and return the verses. Use BIBLE-SWORD-BOOK-DATA." | |
(let* ((location-data (split-input-data str)) | |
(book-str (nth 0 location-data)) | |
(book-num (get-book-number bible-sword-book-data book-str)) | |
(chapter (nth 1 location-data)) | |
(verses (nth 2 location-data)) | |
(any-verses? (> (length verses) 0)) | |
(multi-verses? (> (length verses) 1))) | |
;; (list any-verses? multi-verses?) | |
;; (list book-str book-num chapter verses any-verses? multi-verses?) | |
(cond | |
(multi-verses? ; if starting and ending verse | |
(get-bible-data-book-chapter-verses book-num chapter (car verses) (cadr verses))) | |
(any-verses? ; if just one verse | |
(get-bible-data-book-chapter-verses book-num chapter (car verses) (car verses))) | |
(t | |
(get-bible-data-book-chapter book-num chapter))))) | |
;; (get-bible-verses "Gen 3:1-2" sword-book-data) | |
;; (get-bible-verses "Gen 3:1" sword-book-data) | |
;; (get-bible-verses "Gen 3" sword-book-data) | |
;; TODO: Text has pop up notes like this? {\cf2\super [18]} | |
;; http://www.biblesupport.com/topic/11841-adding-pop-up-notes-to-e-sword-bible-modules/page-2 | |
(setq conjunctions | |
'("and" | |
"that" | |
"but" | |
"or" | |
"as" | |
"if" | |
"when" | |
"than" | |
"because" | |
"while" | |
"where" | |
"after" | |
"so" | |
"though" | |
"since" | |
"until" | |
"whether" | |
"before" | |
"although" | |
"nor" | |
"like" | |
"once" | |
"unless" | |
"now" | |
"except")) | |
;; (defun start-of-verse-is-continuation (verse-text) | |
;; "Check if TEXT begins with certain conjunctions." | |
;; (let* ((first-word (get-first-word verse-text)) | |
;; (lower-text (s-downcase verse-text)) | |
;; (conjunctions '("and" "so" "but" "for" "after" "these" "this" "therefore" "then" "that"))) | |
;; (or | |
;; (cl-some (lambda (conj) (s-starts-with? conj lower-text t)) conjunctions) | |
;; (s-lowercase? (first-char verse-text))))) | |
;; I really ought to use a proper 'versification' table. Maybe this is something that I can scrape | |
;; from a KJV or other standard edition of scripture formatted in plaintext or RTF. | |
(defun start-of-verse-is-continuation (verse-text) | |
"Check if TEXT begins with certain conjunctions or a lowercase letter." | |
(let* ((first-word (get-first-word verse-text)) | |
(first-char (get-first-char first-word)) | |
(lower-word (s-downcase first-word))) | |
(or (member lower-word conjunctions) (s-lowercase? first-char)))) | |
(defun end-of-verse-is-continuation (text) | |
"Check if TEXT ends with continuation punctuation." | |
(or (s-ends-with? ";" text t) (s-ends-with? ":" text t) (s-ends-with? "," text t))) | |
;; (4 19 1 " Now the Lord spoke to Moses and Aaron, saying,") | |
(defun num-char? (c) | |
(and (>= c 48) (<= c 57))) | |
(defun int-to-superscript-string (i) | |
(let* ((str (int-to-string i)) | |
(chrs (string-to-list str))) | |
(apply #'concat | |
(mapcar | |
(lambda (x) | |
(cond | |
((= x ?0) | |
"⁰") | |
((= x ?1) | |
"¹") | |
((= x ?2) | |
"²") | |
((= x ?3) | |
"³") | |
;; This works because superscript digits after 3 start at char number 8308 for 4 | |
((num-char? x) | |
(char-to-string (+ 8256 x))) | |
(t | |
""))) | |
chrs)))) | |
(defun insert-scripture-lines (insert-point scripture-data show-verses) | |
(let ((in-org-buffer (derived-mode-p 'org-mode)) | |
(in-markdown-buffer (derived-mode-p 'markdown-mode)) | |
(first-verse (car scripture-data)) | |
(last-verse (last scripture-data))) | |
;; For every verse, write to the buffer. | |
(dolist (line scripture-data) | |
(let* ((text (s-trim (nth 3 line))) | |
(first-verse? (equal line first-verse)) | |
(last-verse? (equal line last-verse)) | |
(start-is-continuation? (start-of-verse-is-continuation text)) | |
(end-is-continuation? (end-of-verse-is-continuation text))) | |
;; If it's not the first line or a continuation, prepend a newline before inserting the | |
;; verse to the buffer. This should be replaced with a real versification system. | |
(when (and (not start-is-continuation?) (not first-verse?)) | |
(insert ?\n) | |
(when in-markdown-buffer | |
(insert "> "))) | |
;; If 'show verses' is true: | |
(when show-verses | |
(insert (concat (int-to-superscript-string (nth 2 line)) ""))) | |
;; Add the scripture into the buffer | |
(insert text) | |
(when end-is-continuation? | |
(insert " ")) | |
;; If it's not a continuation or the last verse in the | |
;; block requested, add a newline. | |
(when (and (not end-is-continuation?) (not last-verse?)) | |
(insert ?\n) | |
(when in-markdown-buffer | |
(insert "> ")))))) | |
;; For now, just remove the footnotes | |
(replace-regexp-in-region "{.*}" "" insert-point (point)) | |
(fill-region insert-point (point))) | |
(defun insert-scripture-in-buffer (data book-data db) | |
"Add the scripture DATA to the current buffer using BOOK-DATA." | |
(if (not (or (null data) (equal data 0))) | |
;; If scripture data was returned, insert it into the buffer | |
(let* ((starting-point (point)) | |
(in-org-buffer (derived-mode-p 'org-mode)) | |
(in-markdown-buffer (derived-mode-p 'markdown-mode)) | |
(book-name (get-book-name-from-num sword-book-data (caar data))) | |
(book-chapter (cadar data)) | |
(first-verse (car data)) | |
(last-verse (car (last data))) | |
(first-verse-num (caddr first-verse)) | |
(last-verse-num (caddr last-verse)) | |
(one-verse? (equal first-verse last-verse)) | |
;; "Galatians 1:1-24" | |
(verse-range | |
(s-concat | |
book-name " " (number-to-string book-chapter) ":" | |
(if one-verse? | |
(number-to-string first-verse-num) | |
(s-concat | |
(number-to-string first-verse-num) "-" (number-to-string last-verse-num))))) | |
;; "NKJV" | |
(verse-abbreviation (get-scripture-abbreviation db)) | |
;; "Galatians 1:1-24 NKJV" | |
(verse-info (s-concat verse-range " " verse-abbreviation))) | |
;; Nicely inserts the book and verse numbers in the quote data. | |
;; Update: quotes are not formatted nicely if info is in +quote: data. | |
(when in-org-buffer | |
(insert | |
(s-concat | |
"#+verse: " verse-info | |
"" ; End of 'verse info' line. | |
"\n#+begin_quote\n"))) | |
(when in-markdown-buffer | |
(insert "> ")) | |
(let ((insert-point (point))) | |
(insert-scripture-lines insert-point data nil)) | |
;; Close out quote block | |
(when in-org-buffer | |
(let ((insert-point (point))) | |
(insert "\n#+end_quote\n") | |
(goto-char insert-point) | |
(delete-blank-lines))) | |
(when in-markdown-buffer | |
(if rcf-sword-md-link-reference | |
(insert | |
(s-concat | |
"\n> \n> -- [" | |
verse-info | |
"](https://www.biblegateway.com/passage/?search=" | |
(url-encode-url verse-range) | |
"&version=" | |
(url-encode-url verse-abbreviation) | |
")")) | |
(insert (s-concat "\n> \n> -- " verse-info))) | |
;; If this option is set, add a markdown footnote with a link to the scripture. | |
(when rcf-sword-md-use-footnotes | |
(markdown-insert-footnote) | |
;; Insert the link to read in multiple translations | |
(insert | |
(s-concat | |
"[" | |
verse-info | |
"](https://www.biblegateway.com/verse/en/" | |
(url-encode-url verse-range) | |
")")))) | |
(when (not (or in-markdown-buffer in-org-buffer)) | |
(insert (s-concat "\n\n -- " verse-info))) | |
(goto-char starting-point)) | |
;; If no data is returned | |
(message "Couldn't find that verse."))) | |
(defun r/bible-insert-verse () | |
"Insert the specified bible verse in org format into the current buffer." | |
(interactive) | |
;; First, grab the data from the sqlite database | |
(let* ((verse (read-string "Enter verse: ")) | |
(data (get-bible-verses verse sword-book-data))) | |
(insert-scripture-in-buffer data sword-book-data sword-db))) | |
(defun r/bible-random-verse () | |
"Insert the specified bible verse in org format into the current buffer." | |
(interactive) | |
;; First, grab the data from the sqlite database | |
(let* ((data (get-random-scripture sword-book-data))) | |
(insert-scripture-in-buffer data sword-book-data sword-db))) | |
(defun r/bible-random-proverb () | |
"Insert the specified bible verse in org format into the current buffer." | |
(interactive) | |
;; First, grab the data from the sqlite database | |
(let* ((data (get-random-proverb sword-book-data))) | |
(insert-scripture-in-buffer data sword-book-data sword-db))) | |
(defun bible-insert () | |
"Call r/insert-bible-verse." | |
(interactive) | |
(r/bible-insert-verse)) | |
;; Book Reader | |
(defun max-chapter (verses) | |
(apply #'max (mapcar (lambda (x) (nth 1 x)) verses))) | |
(defun sword-trigger-writeroom () | |
(interactive) | |
(setq | |
writeroom-global-effects nil | |
writeroom-maximize-window nil | |
writeroom-fullscreen-effect nil | |
writeroom-mode-line t | |
writeroom-width 70) | |
(if writeroom-mode | |
(progn | |
(writeroom-mode 0) | |
;; (message "Writeroom disabled.") | |
) | |
(progn | |
;; (redraw-display) | |
(writeroom-mode 1) | |
(visual-fill-column-adjust) | |
;; (message "Writeroom enabled.") | |
))) | |
(defun sword-trigger-margins () | |
(interactive) | |
(setq olivetti-minimum-body-width 70) | |
(if olivetti-mode | |
(olivetti-mode 0) | |
(olivetti-mode 1))) | |
(defun r/bible-read-book () | |
"Opens a book of the Bible in a temporary buffer." | |
(interactive) | |
(let* ((input (read-string "Enter book: ")) | |
(data (get-book-data sword-book-data input)) | |
(book-number (nth 1 data)) | |
(book-name (nth 2 data)) | |
(buffer-name (concat "Scripture: " book-name))) | |
(when (not data) | |
(error (concat "Couldn't find a book starting with or including '" input "' - try again?"))) | |
(if (get-buffer buffer-name) | |
(switch-to-buffer buffer-name) | |
;; If it does not already exist, open it! | |
(let* ((buf (get-buffer-create buffer-name)) | |
(verses | |
(sqlite-execute | |
sword-db "select * from bible where book=? order by chapter ASC, verse ASC" | |
(list book-number))) | |
(chapters (max-chapter verses))) | |
(switch-to-buffer buf) | |
(erase-buffer) | |
;; Insert Book Name | |
(let ((insert-point (point))) | |
(insert (concat "\n\n\n" book-name "\n")) | |
(insert (make-string (+ 4 (length book-name)) ?—)) | |
(insert "\n\n") | |
(center-region insert-point (point))) | |
(mapcar | |
(lambda (chap) | |
;; Insert a Chapter heading (except for chapter 1) | |
(when (not (= chap 1)) | |
(let ((insert-point (point)) | |
(chapter-title (concat book-name " Ch. " (int-to-string chap)))) | |
(insert (concat "\n" chapter-title "\n")) | |
(insert (make-string (length chapter-title) ?—)) | |
(insert "\n") | |
(center-region insert-point (point)))) | |
(let ((insert-point (point)) | |
(filtered-verses (cl-remove-if-not (lambda (x) (= (nth 1 x) chap)) verses))) | |
(insert-scripture-lines insert-point filtered-verses t))) | |
;; This is the number-sequence for each chapter | |
(number-sequence 1 chapters)) | |
;; Each verse is in the following format: | |
;; BOOK CHAPTER VERSE CONTENT | |
;; (22 1 1 " The song of songs, which is Solomon's.") | |
;; (let ((insert-point (point))) | |
;; (insert-scripture-lines insert-point verses t)) | |
;; Lock down buffer and set to readonly with 'q' to close. | |
(read-only-mode) | |
(setq buffer-read-only t) | |
(local-set-key (kbd "q") 'kill-current-buffer) | |
(beginning-of-buffer) | |
;; Buffer should now be visible and complete. | |
;; Hitting 'C'enter or 'W'riteroom activates the feature | |
(local-set-key (kbd "w") 'sword-trigger-margins) | |
(local-set-key (kbd "c") 'sword-trigger-margins) | |
;; VIM never dies - scroll direction is inverse of movement | |
(local-set-key (kbd "j") 'scroll-up-command) | |
(local-set-key (kbd "k") 'scroll-down-command) | |
(local-set-key (kbd "l") 'recenter-top-bottom) | |
;; Nicely center things | |
(advice-add 'text-scale-adjust :after #'visual-fill-column-adjust) | |
(sword-trigger-margins) | |
;; User should now see centered verse. | |
)))) | |
(provide 'rcf-sword) | |
;;; rcf-sword.el ends here |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment