bible-mode.el 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495
  1. ;;;; -*- mode: EMACS-LISP; lexical-binding: t -*-
  2. ;;
  3. ;; bible-mode.el --- A browsing interface for the SWORD Project's Diatheke CLI
  4. ;; Author: Zacalot
  5. ;; Substantial fixes and modifications by Fred Gilham
  6. ;; Url: https://gitbot.homedns.org/fred/bible-mode
  7. ;; Forked from
  8. ;; Url: https://github.com/Zacalot/bible-mode
  9. ;; Version: 1.0.0
  10. ;; Package-Requires: ((emacs "24.1"))
  11. ;; Keywords: diatheke, sword, research, bible
  12. ;;; Commentary:
  13. ;; This package uses the `diatheke' program to browse and search
  14. ;; Biblical texts provided by the Sword project.
  15. ;; Word study is also supported.
  16. ;;; Usage:
  17. ;; First install `diatheke'. On Debian/Ubuntu it's in the `diatheke'
  18. ;; package. In other distributions it might be in the sword package.
  19. ;; For Windows I found that you can simply install the Xiphos package.
  20. ;; It includes the Sword library and its utilities including diatheke,
  21. ;; installmgr and mkfastmod. Add the "Program Files\Xiphos\bin" path
  22. ;; to your execution path.
  23. ;; Use M-x `bible-open' to open a Bible buffer.
  24. ;; Use C-h f `bible-mode' to see available keybindings.
  25. ;; You may customize `bible-mode-module' to set a default browsing
  26. ;; module, as well as `bible-mode-word-study-enabled' to enable word
  27. ;; study by default.
  28. ;;; Design:
  29. ;; The idea here is to use the diatheke program to insert code from
  30. ;; modules into buffers. The main bible display uses an "internal" XML
  31. ;; format. The whole buffer gets parsed by libxml-parse-html-region to
  32. ;; create a dom tree. This gets parsed by
  33. ;; bible-mode--insert-domnode-recursive to render the text into
  34. ;; reading format.
  35. ;; The text is then decorated using information from the dom format as
  36. ;; necessary along with regular expressions to identify the verse
  37. ;; references. This is for red letters, purple highlighting of the
  38. ;; verse numbers, bold face of the divine name in the OT and so on.
  39. ;; If strongs tags and/or morphological tags are present, they are
  40. ;; looked up in appropriate lexical and morphological modules and used
  41. ;; to add tooltips to the text so that mousing over words will bring
  42. ;; up a tooltip with information about the word. Clicking on a word
  43. ;; with lexical information will display that informatio in a "term"
  44. ;; buffer.
  45. ;;; Code:
  46. ;;;; Requirements
  47. ;;; XXX FMG there are just a few constructs that use this; use elisp versions instead.
  48. ;;(require 'cl-lib)
  49. (require 'dom)
  50. (require 'shr)
  51. ;; Turn off tool bar mode because we want the pixels....
  52. (tool-bar-mode -1)
  53. ;;;; Variables
  54. (defgroup bible-mode nil
  55. "Settings for `bible-mode'."
  56. :group 'tools
  57. :link '(url-link "https://gitbot.homedns.org/fred/bible-mode"))
  58. (defcustom bible-mode-module
  59. "KJV"
  60. "Book module for Diatheke to query."
  61. :type '(choice (const :tag "None" nil)
  62. (string :tag "Module abbreviation (e.g. \"KJV\")"))
  63. ;; :local t
  64. :group 'bible-mode)
  65. ;;;
  66. ;;; XXX Not implememted yet
  67. (defcustom bible-mode-font
  68. "Ezra SIL"
  69. "Default font for bible-mode."
  70. :type '(string :tag "Font family name (e.g. \"Ezra SIL\")")
  71. :local t
  72. :group 'bible-mode)
  73. (defcustom bible-mode-greek-lexicon
  74. "AbbottSmithStrongs"
  75. "Lexicon used for displaying definitions of Greek words using Strong's codes."
  76. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  77. :local nil
  78. :group 'bible-mode)
  79. (defcustom bible-mode-use-index-for-lexicon nil
  80. "Some lexicons are accessed by lemmas rather than Strong's numbers.
  81. Use an index to look up lemmas from Strong's numbers so these lexicons can
  82. be used. Examples of this type of lexicon are AbbottSmith and
  83. LiddellScott. XXX LiddellScott currently doesn't work. XXX AbbottSmithStrongs
  84. now has the complete entries instead of just links."
  85. :type 'boolean
  86. :local nil
  87. :group 'bible-mode)
  88. (defcustom bible-mode-lexicon-index "AbbottSmithStrongs"
  89. "A module that consists of an index mapping Strong's numbers to Greek lemmas.
  90. The code is written to use the entries in AbbottSmithStrongs
  91. which are of the form
  92. <strong's number>: @LINK <greek lemma>"
  93. :type '(string :tag "Lexicon index.")
  94. :local nil
  95. :group 'bible-mode)
  96. (defcustom bible-mode-greek-lexicon-short
  97. "StrongsRealGreek"
  98. "Lexicon used for displaying definitions of Greek words in tooltips."
  99. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  100. :local nil
  101. :group 'bible-mode)
  102. (defcustom bible-mode-hebrew-lexicon
  103. "StrongsRealHebrew"
  104. "Lexicon used for displaying definitions of Hebrew words using Strong's codes."
  105. :type '(string :tag "Lexicon module (e.g. \"StrongsRealHebrew\")")
  106. :local nil
  107. :group 'bible-mode)
  108. (defcustom bible-mode-hebrew-lexicon-short
  109. "BDBGlosses_Strongs" ; This seems to work
  110. "Lexicon used for displaying definitions of Hebrew words in tooltips."
  111. :type '(string :tag "Lexicon module (e.g. \"StrongsRealHebrew\")")
  112. :local nil
  113. :group 'bible-mode)
  114. (defcustom bible-mode-word-study-enabled
  115. nil
  116. "Display Strong's Hebrew, Strong's Greek, and Lemma words for study."
  117. :type 'boolean
  118. :local t
  119. :group 'bible-mode)
  120. (defcustom bible-mode-red-letter-enabled
  121. t
  122. "Display words of Jesus in red when module has that information."
  123. :type 'boolean
  124. :local t
  125. :group 'bible-mode)
  126. (defcustom bible-mode-show-diatheke-exec
  127. t
  128. "Show the arguments by which diatheke is executed."
  129. :type 'boolean
  130. :local nil
  131. :group 'bible-mod)
  132. ;;; variable defs
  133. ;;(defvar bible-mode-verse-regexp "([\d ]*[a-zA-Z]+( \d*:\d*)?)(( - )| )?(((\d* )?[a-zA-Z]+ )?\d*([:-]+\d*)?)")
  134. ;; (defvar bible-mode-verse-regexp "/(\d*)\s*([a-z]+)\s*(\d+)(?::(\d+))?(\s*-\s*(\d+)(?:\s*([a-z]+)\s*(\d+))?(?::(\d+))?)?/i")
  135. (defconst bible-mode-verse-regexp "\\(I \\|II \\|III \\)??[a-zA-Z]+?[ \t\n][0-9]+[:][0-9]+")
  136. (defvar bible-mode-modules (lazy-completion-table bible-mode-modules bible-mode--list-biblical-modules))
  137. ;; XXX I believe these chapter counts aren't the same for all modules, e.g. JPS.
  138. (defvar bible-mode-books
  139. '(;; Old Testament
  140. ("Genesis" . 50) ("Exodus" . 40) ("Leviticus" . 27) ("Numbers" . 36)
  141. ("Deuteronomy" . 34) ("Joshua" . 24) ("Judges" . 21) ("Ruth" . 4)
  142. ("I Samuel" . 31) ("II Samuel" . 24) ("I Kings" . 22) ("II Kings" . 25)
  143. ("I Chronicles" . 29) ("II Chronicles" . 36) ("Ezra" . 10) ("Nehemiah" . 13)
  144. ("Esther" . 10) ("Job" . 42) ("Psalms" . 150) ("Proverbs" . 31)
  145. ("Ecclesiastes" . 12) ("Song of Solomon" . 8) ("Isaiah" . 66) ("Jeremiah" . 52)
  146. ("Lamentations" . 5) ("Ezekiel" . 48) ("Daniel" . 12) ("Hosea" . 14)
  147. ("Joel" . 3) ("Amos" . 9) ("Obadiah" . 1) ("Jonah" . 4)
  148. ("Micah" . 7) ("Nahum" . 3) ("Habakkuk" . 3) ("Zephaniah" . 3)
  149. ("Haggai" . 2) ("Zechariah" . 14) ("Malachi" . 4)
  150. ;; New Testament
  151. ("Matthew" . 28) ("Mark" . 16) ("Luke" . 24) ("John" . 21)
  152. ("Acts" . 28) ("Romans" . 16) ("I Corinthians" . 16) ("II Corinthians" . 13)
  153. ("Galatians" . 6) ("Ephesians" . 6) ("Philippians" . 4) ("Colossians" . 4)
  154. ("I Thessalonians" . 5) ("II Thessalonians" . 3) ("I Timothy" . 6) ("II Timothy" . 4)
  155. ("Titus" . 3) ("Philemon" . 1) ("Hebrews" . 13) ("James" . 5)
  156. ("I Peter" . 5) ("II Peter" . 3) ("I John" . 5) ("II John" . 1)
  157. ("III John" . 1) ("Jude" . 1) ("Revelation of John" . 22))
  158. "A-list of name / chapter count for Bible books.")
  159. (defvar bible-mode-book-name-abbreviations-alist ;; XXX not currently used.
  160. '(;; Old Testament
  161. ("Ge" . "Genesis") ("Ex" . "Exodus") ("Le" . "Leviticus") ("Nu" . "Numbers")
  162. ("De" . "Deuteronomy") ("Js" . "Joshua") ("Jg" . "Judges") ("Judg" . "Judges")
  163. ("Ru" . "Ruth") ("1 Samuel" . "I Samuel") ("I Sa" . "I Samuel") ("1 Sa" . "I Samuel")
  164. ("2 Samuel" . "II Samuel") ("II Sa" . "II Samuel") ("2 Sa" . "II Samuel") ("1 Kings" . "I Kings")
  165. ("I Ki" . "I Kings") ("1 Ki" . "I Kings") ("2 Kings" . "II Kings") ("II Ki" . "II Kings")
  166. ("2 Ki" . "II Kings") ("1 Chronicles" . "I Chronicles") ("I Ch" . "I Chronicles") ("1 Ch" . "I Chronicles")
  167. ("2 Chronicles" . "II Chronicles") ("II Ch" . "II Chronicles") ("2 Ch" . "II Chronicles")
  168. ("Ezr" . "Ezra") ("Ne" . "Nehemiah")
  169. ("Es" . "Esther") ("Jb" . "Job") ("Ps" . "Psalms") ("Pr" . "Proverbs")
  170. ("Ec" . "Ecclesiastes") ("So" . "Song of Solomon") ("Is" . "Isaiah") ("Je" . "Jeremiah")
  171. ("La" . "Lamentations") ("Ez" . "Ezekiel") ("Da" . "Daniel") ("Ho" . "Hosea")
  172. ("Joe" . "Joel") ("Am" . "Amos") ("Ob" . "Obadiah") ("Jon" . "Jonah")
  173. ("Mi" . "Micah") ("Na" . "Nahum") ("Ha" . "Habakkuk") ("Zep" . "Zephaniah")
  174. ("Hag" . "Haggai") ("Ze" . "Zechariah") ("Mal" . "Malachi")
  175. ;; New Testament
  176. ;; Added AbbottSmith lexicon abbreviations to allow proper following of cross references in lexicon buffers.
  177. ("Mt" . "Matthew") ("Matt" . "Matthew")
  178. ("Mk" . "Mark") ("Lk" . "Luke") ("Jo" . "John") ("Ac" . "Acts")
  179. ("Ro" . "Romans") ("Rom" . "Romans")
  180. ("1 Corintihans" . "I Corinthians") ("I Co" . "I Corinthians") ("1 Co" . "I Corinthians") ("ICor" . "I Corinthians")
  181. ("2 Corinthians" . "II Corinthians") ("II Co" . "II Corinthians") ("2 Co" . "II Corinthians") ("IICor" . "II Corinthians")
  182. ("Ga" . "Galatians") ("Gal" . "Galatians")
  183. ("Eph" . "Ephesians")
  184. ("Phl" . "Philippians") ("Phil" . "Philippians")
  185. ("Col" . "Colossians")
  186. ("1 Thessalonians" . "I Thessalonians") ("I Th" . "I Thessalonians") ("1 Th" . "I Thessalonians") ("IThess" . "I Thessalonians")
  187. ("2 Thessalonians" . "II Thessalonians") ("II Th" . "II Thessalonians") ("2 Th" . "II Thessalonians") ("IIThess" . "II Thessalonians")
  188. ("1 Timothy" . "I Timothy") ("I Ti" . "I Timothy") ("1 Ti" . "I Timothy") ("ITim" . "I Timothy")
  189. ("2 Timothy" . "II Timothy") ("II Ti" . "II Timothy") ("2 Ti" . "II Timothy") ("IITim" . "II Timothy")
  190. ("Tit" . "Titus")
  191. ("Phm" . "Philemon") ("Phlm" . "Philemon")
  192. ("He" . "Hebrews") ("Heb" . "Hebrews")
  193. ("Ja" . "James") ("Jas" . "James")
  194. ("1 Peter" . "I Peter") ("I Pe" . "I Peter") ("1 Pe" . "I Peter")
  195. ("2 Peter" . "II Peter") ("II Pe" . "II Peter") ("2 Pe" . "II Peter") ("IIPet" . "II Peter")
  196. ("1 John" . "I John") ("I Jo" . "I John") ("1 Jo" . "I John") ("IJohn" . "I John")
  197. ("2 John" . "II John") ("II Jo" . "II John") ("2 Jo" . "II John") ("IIJohn" . "II John")
  198. ("3 John" . "III John") ("III Jo" . "III John") ("3 Jo" . "III John") ("IIIJohn" . "III John")
  199. ("Ju" . "Jude")
  200. ("Re" . "Revelation of John") ("Rev" . "Revelation of John"))
  201. "A-list of abbreviations for Bible books.")
  202. ;;;; Book / chapter
  203. (defvar-local bible-mode-current-book (assoc "Genesis" bible-mode-books)
  204. "Current book data (name . chapter).")
  205. (defvar-local bible-mode-current-book-name "Genesis"
  206. "Current book name.")
  207. (defvar-local bible-mode-current-chapter 1
  208. "Current book chapter number.")
  209. (defvar-local bible-mode-query nil
  210. "Search query associated with the buffer.")
  211. (defvar-local bible-mode-search-mode "phrase"
  212. "Search mode: either `lucene', `phrase', `regex' or `multiword'.")
  213. (defvar bible-mode-search-range nil)
  214. (defvar-local bible-mode-has-strongs nil
  215. "Set if the module being displayed has strongs numbers availabile.")
  216. (defvar-local bible-mode-has-morphology nil
  217. "Set if the module being displayed has morphology availabile.")
  218. ;; (defvar bible-mode-current-module nil)
  219. ;;;; Keymaps
  220. ;;;; N.B. Bible Mode menu items appear in reverse order of their
  221. ;;;; definition below
  222. (defconst bible-mode-map (make-sparse-keymap)
  223. "Keymap for bible-mode.")
  224. (define-key bible-mode-map [menu-bar bible-mode]
  225. (cons "Bible Mode" (make-sparse-keymap "Bible Mode")))
  226. (define-key bible-mode-map
  227. [menu-bar bible-mode toggle-debug]
  228. '("Toggle debug-on-error" . toggle-debug-on-error))
  229. (defun bible-mode-toggle-display-diatheke ()
  230. "Toggle diatheke args display."
  231. (interactive)
  232. (setq bible-mode-show-diatheke-exec (not bible-mode-show-diatheke-exec)))
  233. (define-key bible-mode-map
  234. [menu-bar bible-mode display-diatheke]
  235. '("Toggle diatheke display" . bible-mode-toggle-display-diatheke))
  236. (defun bible-mode-toggle-display-xml ()
  237. "Toggle XML display."
  238. (interactive)
  239. (setq-local bible-mode-debugme (not bible-mode-debugme))
  240. (bible-mode--display))
  241. (define-key bible-mode-map "d" 'bible-mode-toggle-display-xml)
  242. (define-key bible-mode-map
  243. [menu-bar bible-mode display-xml]
  244. '("Toggle XML Display" . bible-mode-toggle-display-xml))
  245. (define-key bible-mode-map
  246. [menu-bar bible-mode sep]
  247. '(menu-item '"--"))
  248. ;;;;; Misc
  249. (define-key bible-mode-map "m" 'bible-mode-select-module)
  250. (define-key bible-mode-map "w" 'bible-mode-toggle-word-study)
  251. (define-key bible-mode-map "l" 'bible-mode-toggle-red-letter)
  252. (define-key bible-mode-map "x" 'bible-mode-split-display)
  253. (define-key bible-mode-map
  254. [menu-bar bible-mode split-display]
  255. '("Split Display" . bible-mode-split-display))
  256. ;;;;; Search
  257. (define-key bible-mode-map "/" 'bible-search)
  258. (define-key bible-mode-map "s" 'bible-search)
  259. (define-key bible-mode-map
  260. [menu-bar bible-mode search]
  261. '("Search" . bible-search))
  262. (define-key bible-mode-map "r" 'bible-mode-set-search-range)
  263. (define-key bible-mode-map
  264. [menu-bar bible-mode range]
  265. '("Set Search Range" . bible-mode-set-search-range))
  266. ;;;;; Navigation
  267. (define-key bible-mode-map "p" 'bible-mode-previous-chapter)
  268. (define-key bible-mode-map
  269. [menu-bar bible-mode previous-chapter]
  270. '("Previous Chapter" . bible-mode-previous-chapter))
  271. (define-key bible-mode-map "n" 'bible-mode-next-chapter)
  272. (define-key bible-mode-map
  273. [menu-bar bible-mode next-chapter]
  274. '("Next Chapter" . bible-mode-next-chapter))
  275. (define-key bible-mode-map (kbd "TAB") 'bible-mode-next-word)
  276. (define-key bible-mode-map (kbd "M-<tab>") 'bible-mode-previous-word)
  277. ;;;;; Direct jump
  278. (define-key bible-mode-map "c" 'bible-mode-select-chapter)
  279. (define-key bible-mode-map
  280. [menu-bar bible-mode select-chapter]
  281. '("Select Chapter" . bible-mode-select-chapter))
  282. (define-key bible-mode-map "b" 'bible-mode-select-book)
  283. (define-key bible-mode-map
  284. [menu-bar bible-mode select-book]
  285. '("Select Book" . bible-mode-select-book))
  286. (define-key bible-mode-map
  287. [menu-bar bible-mode sep]
  288. '(menu-item '"--"))
  289. ;;;;; Deal with visual-line-mode
  290. (define-key bible-mode-map "\C-n" 'next-logical-line)
  291. (define-key bible-mode-map "\C-p" 'previous-logical-line)
  292. (defun bible-mode-next-search-item ()
  293. (interactive)
  294. (search-forward-regexp bible-mode-verse-regexp))
  295. (defun bible-mode-previous-search-item ()
  296. (interactive)
  297. (search-backward-regexp bible-mode-verse-regexp))
  298. (defconst bible-search-mode-map (make-keymap))
  299. (define-key bible-search-mode-map "s" 'bible-search)
  300. (define-key bible-search-mode-map "w" 'bible-mode-toggle-word-study)
  301. (define-key bible-search-mode-map "n" 'bible-mode-next-search-item)
  302. (define-key bible-search-mode-map "p" 'bible-mode-previous-search-item)
  303. (define-key bible-search-mode-map (kbd "RET") 'bible-search-mode-follow-verse)
  304. (defconst bible-term-hebrew-mode-map (make-sparse-keymap))
  305. (defconst bible-term-greek-mode-map (make-sparse-keymap))
  306. (define-key bible-term-greek-mode-map [mouse-1] 'bible-search-mode-follow-xref)
  307. ;;;
  308. ;;; Menu bar items
  309. ;;;
  310. (defvar-local bible-mode-text-direction 'left-to-right)
  311. (defun bible-mode-toggle-text-direction ()
  312. (interactive)
  313. (if (eq bible-mode-text-direction 'left-to-right)
  314. (setq-local bible-mode-text-direction 'right-to-left)
  315. (setq-local bible-mode-text-direction 'left-to-right))
  316. (setq-local bidi-paragraph-direction bible-mode-text-direction))
  317. (defvar-local bible-mode-debugme nil
  318. "Make text show up as XML when set.")
  319. (defvar-local bible-mode-search-query nil
  320. "Query used in toggles (word study and red letter).")
  321. (defvar use-tooltips t)
  322. (defun bible-mode-toggle-tooltips ()
  323. "Toggle use of tooltips to display lexical/morphological items."
  324. (interactive)
  325. (setq use-tooltips (not use-tooltips))
  326. (tooltip-mode 'toggle)
  327. (setq tooltip-resize-echo-area use-tooltips))
  328. (define-key bible-mode-map
  329. [menu-bar bible-mode sepp]
  330. '(menu-item '"--"))
  331. (define-key bible-mode-map
  332. [menu-bar bible-mode toggle-text-direction]
  333. '("Toggle text direction (for Hebrew display)" . bible-mode-toggle-text-direction))
  334. (define-key bible-mode-map
  335. [menu-bar bible-mode toggle-tooltip-display]
  336. '("Toggle Tooltip Display" . bible-mode-toggle-tooltips))
  337. (define-key bible-mode-map
  338. [menu-bar bible-mode sepp]
  339. '(menu-item '"--"))
  340. (define-key bible-mode-map
  341. [menu-bar bible-mode select-biblical-text]
  342. '("Select Module" . bible-mode-display-available-modules))
  343. (defun bible-mode--display-greek ()
  344. "This command is run by clicking on text, not directly by the user."
  345. (interactive)
  346. (let ((item (car (split-string (get-text-property (point) 'strong)))))
  347. ;; Remove "strong:G" prefix
  348. (bible-term-greek (replace-regexp-in-string "strong:G" "" item))))
  349. (defconst bible-mode-greek-keymap (make-sparse-keymap))
  350. (define-key bible-mode-greek-keymap (kbd "RET") 'bible-mode--display-greek)
  351. (define-key bible-mode-greek-keymap [mouse-1] 'bible-mode--display-greek)
  352. (defun bible-mode--display-hebrew ()
  353. "This command is run by clicking on text, not directly by the user."
  354. (interactive)
  355. (let ((item (car (split-string (get-text-property (point) 'strong)))))
  356. ;; Remove "strong:H" prefix and any alphabetic suffixes.
  357. (bible-term-hebrew (replace-regexp-in-string "strong:H" "" item))))
  358. (defconst bible-mode-hebrew-keymap (make-sparse-keymap))
  359. (define-key bible-mode-hebrew-keymap (kbd "RET") 'bible-mode--display-hebrew)
  360. (define-key bible-mode-hebrew-keymap [mouse-1] 'bible-mode--display-hebrew)
  361. (defconst bible-mode-lemma-keymap (make-sparse-keymap))
  362. (define-key bible-mode-lemma-keymap (kbd "RET")
  363. (lambda ()
  364. (interactive)
  365. ))
  366. (defconst bible-mode-morph-keymap (make-sparse-keymap))
  367. (define-key bible-mode-morph-keymap (kbd "RET")
  368. (lambda ()
  369. (interactive)
  370. ;;; (let ((thing (thing-at-point 'word)))
  371. ;;; (message "thing at point: %s" thing)
  372. ;;; (message "morph property %s" (get-text-property 0 'field thing))
  373. ))
  374. ;;;; Modes
  375. (define-derived-mode bible-mode special-mode "Bible"
  376. "Mode for reading the Bible.
  377. \\{bible-mode-map}"
  378. (buffer-disable-undo)
  379. (font-lock-mode t)
  380. (use-local-map bible-mode-map)
  381. (setq buffer-read-only t)
  382. (visual-line-mode t))
  383. (define-derived-mode bible-search-mode special-mode "Bible Search"
  384. "Mode for performing Bible searches.
  385. \\{bible-search-mode-map}"
  386. (buffer-disable-undo)
  387. (font-lock-mode t)
  388. (use-local-map bible-search-mode-map)
  389. (setq buffer-read-only t)
  390. (visual-line-mode t)
  391. )
  392. (define-derived-mode bible-term-hebrew-mode special-mode "Bible Term (Hebrew)"
  393. "Mode for researching Hebrew terms in the Bible.
  394. \\{bible-term-hebrew-mode-map}"
  395. (buffer-disable-undo)
  396. (font-lock-mode t)
  397. (use-local-map bible-term-hebrew-mode-map)
  398. (setq buffer-read-only t)
  399. (visual-line-mode t))
  400. (define-derived-mode bible-term-greek-mode special-mode "Bible Term (Greek)"
  401. "Mode for researching Greek terms in the Bible.
  402. \\{bible-term-greek-mode-map}"
  403. (buffer-disable-undo)
  404. (font-lock-mode t)
  405. (use-local-map bible-term-greek-mode-map)
  406. (setq buffer-read-only t)
  407. (visual-line-mode t)
  408. )
  409. (define-derived-mode module-select-mode special-mode "Select Text Module"
  410. (buffer-disable-undo)
  411. (font-lock-mode t)
  412. (setq buffer-read-only t))
  413. ;;;; Functions
  414. ;;;;; Commands
  415. ;;;###autoload
  416. (defun bible-open (&optional book-name chapter verse)
  417. "Create and open a `bible-mode' buffer.
  418. Arguments BOOK-NAME, CHAPTER and VERSE, when supplied, give the starting
  419. verse reference for the buffer. If no optional arguments are supplied,
  420. Genesis 1:1 is used."
  421. (interactive)
  422. (let ((buf (get-buffer-create (generate-new-buffer-name (concat "*bible*")))))
  423. (set-buffer buf)
  424. (bible-mode)
  425. (bible-mode--set-location
  426. (or (assoc (or book-name "Genesis") bible-mode-books) (list book-name))
  427. (or chapter 1)
  428. verse)
  429. (set-window-buffer (get-buffer-window (current-buffer)) buf)))
  430. ;;;###autoload
  431. (defun bible-mode-next-chapter ()
  432. "Page to the next chapter for the active `bible-mode' buffer."
  433. (interactive)
  434. (let* ((book-chapters (cdr bible-mode-current-book))
  435. (chapter (min book-chapters (+ bible-mode-current-chapter 1))))
  436. (bible-mode--set-location bible-mode-current-book chapter)))
  437. ;;;###autoload
  438. (defun bible-mode-previous-chapter ()
  439. "Page to the previous chapter for the active `bible-mode' buffer."
  440. (interactive)
  441. (bible-mode--set-location bible-mode-current-book (max 1 (- bible-mode-current-chapter 1))))
  442. (defun bible-mode-next-word ()
  443. "Move forward a word, taking into account the relevant text properties."
  444. (interactive)
  445. (unless (eobp)
  446. (let ((next-change (text-property-search-forward 'strong nil nil t)))
  447. (when next-change
  448. (goto-char (1- (prop-match-end next-change)))))))
  449. (defun bible-mode-previous-word ()
  450. "Move back a word, taking into account the relevant text properties."
  451. (interactive)
  452. (unless (bobp)
  453. (let ((previous-change (text-property-search-backward 'strong)))
  454. (when previous-change
  455. (goto-char (prop-match-beginning previous-change))))))
  456. ;;;###autoload
  457. (defun bible-mode-select-book ()
  458. "Ask user for a new book and chapter for the current `bible-mode' buffer."
  459. (interactive)
  460. (let* ((completion-ignore-case t)
  461. (book-data (assoc (completing-read "Book: " bible-mode-books nil t) bible-mode-books))
  462. (chapter (string-to-number (completing-read "Chapter: " (bible-mode--list-number-range 1 (cdr book-data)) nil t))))
  463. (setq-local bible-mode-current-book book-data)
  464. (setq-local bible-mode-current-book-name (car book-data))
  465. (setq-local bible-mode-current-chapter chapter)
  466. (bible-mode--display)))
  467. ;;;###autoload
  468. (defun bible-mode-select-chapter ()
  469. "Ask user for a new chapter for the current `bible-mode' buffer."
  470. (interactive)
  471. (let* ((book-chapters (cdr bible-mode-current-book))
  472. (chapter (string-to-number (completing-read "Chapter: " (bible-mode--list-number-range 1 book-chapters) nil t))))
  473. (when chapter
  474. (bible-mode--set-location bible-mode-current-book chapter))))
  475. (defun bible-mode-set-search-range ()
  476. "Ask user for a new text module for the current `bible-mode' buffer."
  477. (interactive)
  478. (let ((range (read-string "Range (<return> to clear): ")))
  479. (if (string-equal range "")
  480. (setq bible-mode-search-range nil)
  481. (setq bible-mode-search-range range))
  482. ;; (bible-mode--display)
  483. ))
  484. ;;;###autoload
  485. (defun bible-mode-select-module ()
  486. "Ask user for a new text module for the current `bible-mode' buffer."
  487. (interactive)
  488. ;; (let* ((bible-mode-module (completing-read "Module: " bible-mode-modules)))
  489. ;; (bible-mode--display))
  490. (setq-default bible-mode-module (completing-read "Module: " bible-mode-modules))
  491. (bible-mode--display))
  492. ;;;###autoload
  493. (defun bible-mode-toggle-word-study ()
  494. "Toggle the inclusion of word study for the active `bible-mode' buffer."
  495. (interactive)
  496. (setq bible-mode-word-study-enabled (not bible-mode-word-study-enabled))
  497. (if (equal major-mode 'bible-search-mode)
  498. (bible-mode--display-search bible-mode-search-query bible-mode-search-mode)
  499. (bible-mode--display)))
  500. (defun bible-mode-toggle-red-letter ()
  501. "Toggle red letter mode for the active `bible-mode' buffer."
  502. (interactive)
  503. (setq bible-mode-red-letter-enabled (not bible-mode-red-letter-enabled))
  504. (if (equal major-mode 'bible-search-mode)
  505. (bible-mode--display-search bible-mode-search-query bible-mode-search-mode)
  506. (bible-mode--display)))
  507. ;;;###autoload
  508. (defun bible-mode-split-display ()
  509. "Copy the active `bible-mode' buffer into a new buffer in another window."
  510. (interactive)
  511. (split-window-right)
  512. (balance-windows)
  513. (other-window 1)
  514. (bible-open bible-mode-current-book-name bible-mode-current-chapter))
  515. ;;;###autoload
  516. (defun bible-search (query)
  517. "Prompts the user for a Bible search query: word or phrase and type of
  518. search: either `lucene', `phrase', `regex' or `multiword'. `lucene' mode
  519. requires an index to be built using the `mkfastmod' program. `lucene' is
  520. the default search."
  521. (interactive "sBible Search: ")
  522. (when (> (length query) 0)
  523. (let* ((bible-mode-module (buffer-local-value 'bible-mode-module (current-buffer)))
  524. (searchmode (completing-read "Search Mode: " '("lucene" "phrase" "regex" "multiword") nil t "lucene")))
  525. (bible-mode--open-search query searchmode))))
  526. ;;;###autoload
  527. (defun bible-search-mode-follow-verse ()
  528. "Follow the hovered verse in a `bible-search-mode' buffer.
  529. Create a new `bible-mode' buffer positioned at the selected verse."
  530. (interactive)
  531. (let* ((text (thing-at-point 'line t))
  532. book
  533. chapter
  534. verse)
  535. (string-match ".+ [0-9]?[0-9]?[0-9]?:[0-9]?[0-9]?[0-9]?:" text)
  536. (setq text (match-string 0 text))
  537. (string-match " [0-9]?[0-9]?[0-9]?:" text)
  538. (setq chapter (replace-regexp-in-string "[^0-9]" "" (match-string 0 text)))
  539. (string-match ":[0-9]?[0-9]?[0-9]?" text)
  540. (setq verse (replace-regexp-in-string "[^0-9]" "" (match-string 0 text)))
  541. (setq book (replace-regexp-in-string "[ ][0-9]?[0-9]?[0-9]?:[0-9]?[0-9]?[0-9]?:$" "" text))
  542. (bible-open (string-trim book) (string-to-number chapter) (string-to-number verse))))
  543. (defun bible-search-mode-follow-xref ()
  544. "Follow the hovered verse in a bible term buffer.
  545. Create a new `bible-mode' buffer positioned at the specified verse.
  546. XXX We use the current module to avoid opening cans of worms regarding
  547. OT/NT etc. If that module doesn't have that verse...???
  548. Handle abbreviations from lexicon module (AbbottSmith)."
  549. (interactive)
  550. (let* ((xref (get-text-property (point) 'xref))
  551. (verse-ref (string-split xref))
  552. book-abbrev
  553. book
  554. chapter-verse
  555. chapter
  556. verse)
  557. (if (= (length verse-ref) 3) ; II Cor 3:17 or the like
  558. (setq book-abbrev (concat (car verse-ref) (cadr verse-ref))
  559. chapter-verse (split-string (caddr verse-ref) ":"))
  560. ;; Mat 5 or the like
  561. (setq book-abbrev (car verse-ref)
  562. chapter-verse (split-string (cadr verse-ref) ":")))
  563. (setq book (cdr (assoc book-abbrev bible-mode-book-name-abbreviations-alist)))
  564. (unless book (setq book (car verse-ref))) ; Didn't find abbreviation
  565. (setq chapter (car chapter-verse)
  566. verse (cadr chapter-verse))
  567. (bible-open (string-trim book) (string-to-number chapter) (string-to-number verse))))
  568. ;;;###autoload
  569. (defun bible-term-hebrew (term)
  570. "Query user for a Strong's Hebrew Lexicon TERM."
  571. (interactive "sTerm: ")
  572. (bible-mode--open-term-hebrew term))
  573. ;;;###autoload
  574. (defun bible-term-greek (term)
  575. "Query user for a Strong's Greek Lexicon TERM."
  576. (interactive "sTerm: ")
  577. (bible-mode--open-term-greek term))
  578. ;;;###autoload
  579. (defun bible-insert ()
  580. "Query user to select a verse for insertion into the current buffer."
  581. (interactive)
  582. (let* ((completion-ignore-case t)
  583. (book-data (assoc (completing-read "Book: " bible-mode-books nil t) bible-mode-books))
  584. (chapter (when book-data (completing-read "Chapter: " (bible-mode--list-number-range 1 (cdr book-data)) nil t)))
  585. (verse (when chapter (read-from-minibuffer "Verse: "))))
  586. (when verse
  587. (insert (string-trim
  588. (replace-regexp-in-string
  589. (regexp-opt `(,(concat "(" bible-mode-module ")")))
  590. ""
  591. (bible-mode--exec-diatheke (concat (car book-data) " " chapter ":" verse) nil "plain")))))))
  592. ;;;;; Support
  593. (defconst diatheke-filter-options " avlnmw")
  594. ;;;
  595. ;;; XXX I've mangled this in an ad-hoc manner. It needs to be
  596. ;;; re-written so it is clearer (and correct, for that matter).
  597. (defun bible-mode--exec-diatheke (query &optional filter format searchtype module)
  598. "Execute `diatheke' with specified QUERY options, returning the output."
  599. (let ((module (or module bible-mode-module)))
  600. (with-temp-buffer
  601. (let ((args (list "diatheke" nil (current-buffer) t "-b" module)))
  602. (if searchtype
  603. (progn
  604. (setq filter nil) ; search is not compatible with filters XXX
  605. (setq args (append args (list "-s" (pcase searchtype
  606. ("lucene" "lucene")
  607. ("phrase" "phrase")
  608. ("regex" "regex")
  609. ("multiword" "multiword")))))
  610. (when bible-mode-search-range (setq args (append args (list "-r" bible-mode-search-range)))))
  611. (progn
  612. (if filter
  613. (setq filter (concat filter diatheke-filter-options))
  614. (setq filter diatheke-filter-options))
  615. (setq args (append args (list "-o" filter)))))
  616. (setq args (append args (list "-f" (pcase format ("plain" "plain") (_ "internal")) "-k" query)))
  617. (when bible-mode-show-diatheke-exec
  618. (message "%s" args))
  619. (apply 'call-process args))
  620. (buffer-string))))
  621. (defvar-local bible-mode-chapter-title nil
  622. "Text preceding start of chapter.
  623. Mostly in Psalms, like `Of David' or the like.")
  624. ;;;
  625. ;;; Greek and Hebrew lexicon and morphology tooltip rendering.
  626. ;;;
  627. ;;; Hash tables for Lexical definitions.
  628. (defvar greek-hash (make-hash-table :test 'equal))
  629. (defvar greek-hash-short (make-hash-table :test 'equal))
  630. (defvar hebrew-hash (make-hash-table :test 'equal))
  631. (defvar hebrew-hash-short (make-hash-table :test 'equal))
  632. ;; Do lookups using index to lexicon with lookups by lemma.
  633. (defvar lemma-index-hash (make-hash-table :test 'equal))
  634. (defvar lemma-lex-hash (make-hash-table :test 'equal))
  635. ;;; Hash tables for Morphologies. Three at present.
  636. (defvar robinson-hash (make-hash-table :test 'equal))
  637. (defvar packard-hash (make-hash-table :test 'equal))
  638. (defvar oshm-hash (make-hash-table :test 'equal))
  639. ;;; Use HTMLHREF format with diatheke, post-process to render html.
  640. (defun bible-mode--morph-query (query module)
  641. "Execute `diatheke' to do morph QUERY, using MODULE.
  642. Render HTML, return string. Do some tweaking specific to morphology."
  643. (with-temp-buffer
  644. (let ((args (list "diatheke" nil (current-buffer) t "-b" module "-o" "m" "-f" "HTMLHREF" "-k" query)))
  645. (when bible-mode-show-diatheke-exec
  646. (message "%s" args))
  647. (apply 'call-process args)
  648. (shr-render-region (point-min) (point-max))
  649. (format-replace-strings
  650. '(("\n:" . "") ; This makes the Packard morphology display look better.
  651. ("Part of Speech" . "")) ; This helps the Robinson display look better.
  652. nil (point-min) (point-max))
  653. (substring (buffer-string) (+ (length query) 1))) ; This tries to get rid of unnecessary query identifier.
  654. ))
  655. ;;; Use "plain" format with diatheke.
  656. (defun bible-mode--lex-query (query module)
  657. "Execute `diatheke' for QUERY, using MODULE.
  658. Plain format, returns string."
  659. ;; Get rid of query ID at front of string: ?????:
  660. (bible-mode--exec-diatheke query nil "plain" nil module))
  661. (defun bible-mode--lookup-lemma-index (key)
  662. "Given a strong's number KEY, return the Greek lemma from lemma index."
  663. (or (gethash key lemma-index-hash)
  664. (puthash key
  665. (string-trim
  666. (replace-regexp-in-string
  667. (concat "(" bible-mode-lexicon-index ")") ""
  668. (bible-mode--lex-query key bible-mode-lexicon-index)))
  669. lemma-index-hash)))
  670. ;;;
  671. ;;; The Greek lexical definitions are done using the HTMLHREF output
  672. ;;; format so they come out looking nice and having clickable
  673. ;;; cross-references and/or Strong's references.
  674. (defun bible-mode--process-href ()
  675. "Fix the XML so cross-references are in the right format.
  676. These cross-references get processed later when the term is displayed.
  677. First, find the links put in by diatheke's HTMLHREF output format.
  678. Replace the links with verse references that get changed to clickable
  679. cross-references when the term is displayed.
  680. The verse refs look like this: <bookname>.<chapter>.<verse>. We convert
  681. them to the <bookname> <chapter>:<verse> format."
  682. (goto-char (point-min))
  683. (while (re-search-forward "<a href=\"passagestudy.*?</a>" nil t) ; HTMLHREF cross references.
  684. (let ((match-text (match-string 0)))
  685. ;; Delete original link.
  686. (replace-match "" nil nil)
  687. ;; Get the verse reference from the string we saved. Put it in
  688. ;; good format, then insert it into buffer where href was.
  689. (when (string-match "value=.*?&" match-text)
  690. (let* ((value-string (match-string 0 match-text))
  691. ;; Strip off value= and trailing &.
  692. (verse-ref-string (substring value-string 6 (1- (length value-string))))
  693. (verse-ref-length (length verse-ref-string))
  694. period)
  695. ;; Convert periods
  696. ;; Substitute first period with space
  697. (when (setq period (cl-search "." verse-ref-string))
  698. (aset verse-ref-string period ? ))
  699. ;; Substitute second period with colon
  700. (when (setq period (cl-search "." verse-ref-string))
  701. (aset verse-ref-string period ?:))
  702. ;; Replace numbers (1, 2 or 3) with roman numerals (I, II, III) XXX Fix?
  703. (pcase (aref verse-ref-string 0)
  704. (?1 (setq verse-ref-string (concat "I" (substring verse-ref-string 1))))
  705. (?2 (setq verse-ref-string (concat "II" (substring verse-ref-string 1))))
  706. (?3 (setq verse-ref-string (concat "III" (substring verse-ref-string 1)))))
  707. (set-text-properties 0 verse-ref-length nil verse-ref-string) ; Clear unwanted properties (if any)
  708. (insert verse-ref-string))))))
  709. (defun bible-mode--lookup-greek-def (key)
  710. "Execute `diatheke' to do query on KEY.
  711. Massage output so verse cross references are usable. Returns string.
  712. We use HTMLHREF format output because it may have verse references
  713. as HTML links, depending on the lexicon module."
  714. (with-temp-buffer
  715. (let ((args (list "diatheke" nil (current-buffer) t "-b" bible-mode-greek-lexicon "-o" "m" "-f" "HTMLHREF" "-k" key)))
  716. ;; XXX Change to OSIS? Need to parse OSIS-style references.
  717. ;;; (let ((args (list "diatheke" nil (current-buffer) t "-b" bible-mode-greek-lexicon "-o" "m" "-f" "OSIS" "-k" key)))
  718. (when bible-mode-show-diatheke-exec
  719. (message "%s" args))
  720. (apply 'call-process args)
  721. (bible-mode--process-href) ; Clean up XML so xrefs can work after rendering.
  722. (shr-render-region (point-min) (point-max))
  723. (buffer-string))))
  724. (defun bible-mode--lookup-lex-greek-indexed (key)
  725. "Get Greek lemma using Strong's number KEY.
  726. Then look up the definition of that lemma. Used when two-stage
  727. lexical definition is set for a particular lexicon."
  728. (let ((lemma-entry (bible-mode--lookup-lemma-index key))) ; Get lemma from Strong's number
  729. (when lemma-entry
  730. (let ((lemma (caddr (split-string lemma-entry " "))))
  731. (bible-mode--lookup-greek-def lemma)))))
  732. (defun bible-mode--lookup-lex-greek (key)
  733. "Lookup lexical definition using Strong's number KEY.
  734. 1. Check hash table first. If entry found, return.
  735. 2. Otherwise, if a lexicon is accessed by lemmas, do lookup using index method.
  736. 3. Otherwise just use the Strong's number method."
  737. (or (gethash key greek-hash)
  738. (puthash key
  739. (if bible-mode-use-index-for-lexicon
  740. (bible-mode--lookup-lex-greek-indexed key)
  741. (bible-mode--lookup-greek-def key))
  742. greek-hash)))
  743. (defun bible-mode--lookup-strongs-greek (_window object pos)
  744. "Look up Greek lexical entry of OBJECT clicked on in WINDOW at POS.
  745. If not found in hash table, get it from sword database. stash in hash
  746. table, and return string."
  747. (let ((query (get-text-property pos 'strong object))) ; Get Strong's number from text property
  748. (when (string-match "[0-9]+" query)
  749. (bible-mode--lookup-lex-greek (match-string 0 query)))))
  750. (defun bible-mode--lookup-strongs-greek-short (_window object pos)
  751. "Look up shorter Greek lexical entry of OBJECT clicked on in WINDOW at POS.
  752. If not found in hash table, get it from sword database, stash in hash table,
  753. and return string."
  754. (let ((query (get-text-property pos 'strong object))) ; Get Strong's number from text property
  755. (when (string-match "[0-9]+" query)
  756. (let ((lookup-key (match-string 0 query)))
  757. (or (gethash lookup-key greek-hash-short)
  758. (let ((raw-text (bible-mode--lex-query lookup-key bible-mode-greek-lexicon-short)))
  759. (puthash lookup-key raw-text greek-hash-short)))))))
  760. (defun bible-mode--lookup-strongs-hebrew (_window object pos)
  761. "Look up Hebrew lexical string for object at point.
  762. If not found in hash table, get it from sword database,
  763. stash in hash table, and return string."
  764. (let ((query (get-text-property pos 'strong object)))
  765. (when (string-match "[0-9]+" query)
  766. (let ((lookup-key (concat "H" (match-string 0 query))))
  767. (or (gethash lookup-key hebrew-hash)
  768. (let ((raw-text (bible-mode--lex-query lookup-key bible-mode-hebrew-lexicon)))
  769. (puthash lookup-key raw-text hebrew-hash)))))))
  770. (defun bible-mode--lookup-strongs-hebrew-short (_window object pos)
  771. "Look up Hebrew lexical string from short Hebrew lexicon for object
  772. at point. If not found in hash table, get it from sword database,
  773. stash in hash table, and return string.
  774. Note: compiler warns about unused `window' argument."
  775. (let ((query (get-text-property pos 'strong object)))
  776. (when (string-match "[0-9]+" query)
  777. (let ((lookup-key (concat "H" (match-string 0 query))))
  778. (or (gethash lookup-key hebrew-hash-short)
  779. (let ((raw-text (bible-mode--lex-query lookup-key bible-mode-hebrew-lexicon-short)))
  780. (puthash lookup-key raw-text hebrew-hash-short)))))))
  781. (defun bible-mode--morph-database-lookup (query database hash)
  782. (or (gethash query hash)
  783. (puthash query (bible-mode--morph-query query database) hash)))
  784. (defvar bible-mode-outline-strings
  785. '((" I." . "\n I.")
  786. (" II." . "\n II.")
  787. (" III." . "\n III.")
  788. (" IV." . "\n IV.")
  789. (" V." . "\n V.")
  790. (" 1." . "\n 1.")
  791. (" 2." . "\n 2.")
  792. (" 3." . "\n 3.")
  793. (" 4." . "\n 4.")
  794. (" 5." . "\n 5.")
  795. (" 6." . "\n 6.")
  796. (" 7." . "\n 7.")
  797. (" 8." . "\n 8.")
  798. (" 9." . "\n 9.")
  799. (" a." . "\n a.")
  800. (" b." . "\n b.")
  801. (" c." . "\n c.")
  802. (". ." . ".")
  803. (" . " . ". ")))
  804. (defun bible-mode-cleanup-tooltip-text (lex-text)
  805. (dolist (outline-string bible-mode-outline-strings)
  806. (setq lex-text (string-replace (car outline-string) (cdr outline-string) lex-text)))
  807. lex-text)
  808. ;;;
  809. ;;; Get string for tooltip display
  810. ;;;
  811. (defun bible-mode--show-lex-morph (window object pos)
  812. "Get text for tooltip display. Includes both lex and morph
  813. definitions if text module has both tags, otherwise just get
  814. lex definition."
  815. (let* ((lex-morph-text "")
  816. (lex (get-text-property pos 'strong object))
  817. (lex-module nil)
  818. (lex-text
  819. (cond ((string-match "strong:G" lex)
  820. (bible-mode--lookup-strongs-greek-short window object pos))
  821. ((string-match "strong:H" lex)
  822. (bible-mode--lookup-strongs-hebrew-short window object pos)))))
  823. (setq lex-text (string-replace (concat "(" lex-module ")") "" lex-text))
  824. (let* ((morph (get-text-property pos 'morph object))
  825. (morph-module nil)
  826. (morph-text
  827. (cond ((null morph) nil)
  828. ((string-match "robinson:" morph)
  829. (setq morph-module "Robinson")
  830. (bible-mode--morph-database-lookup (replace-regexp-in-string "robinson:" "" morph) morph-module robinson-hash))
  831. ((string-match "packard:" morph)
  832. (setq morph-module "Packard")
  833. (bible-mode--morph-database-lookup (replace-regexp-in-string "packard:" "" morph) morph-module packard-hash))
  834. ((string-match "oshm:" morph)
  835. (setq morph-module "OSHM")
  836. (bible-mode--morph-database-lookup (replace-regexp-in-string "oshm:" "" morph) morph-module oshm-hash)))))
  837. (when lex-text
  838. (setq lex-morph-text (string-trim (bible-mode-cleanup-tooltip-text (string-fill lex-text 75)))))
  839. (when morph-text
  840. (setq lex-morph-text
  841. (concat lex-morph-text "\n\n"
  842. (string-trim (string-replace (concat "(" morph-module ")") "" morph-text)))))
  843. ;; This prevents bogus command substitutions in the tooltip by
  844. ;; removing backslashes. XXX I couldn't figure out a better way
  845. ;; to bypass command substitution in the tooltips.
  846. (setq lex-morph-text (replace-regexp-in-string "\\\\" "" lex-morph-text)))))
  847. (defun bible-mode-handle-divine-name (item)
  848. (insert "LORD")
  849. (let* ((refstart (- (point) (length "LORD")))
  850. (refend (point))
  851. (strongs (dom-attr item 'savlm)))
  852. (add-face-text-property refstart refend 'bold)
  853. (put-text-property refstart refend 'keymap bible-mode-hebrew-keymap)
  854. (when (and strongs (string-match "strong:H.*" strongs))
  855. (put-text-property refstart refend 'help-echo 'bible-mode--show-lex-morph)
  856. (put-text-property refstart refend 'strong (match-string 0 strongs)))))
  857. (defun bible-mode--process-word (item iproperties)
  858. "Handle <w ....> fubar </w> tag. Add tooltips for definitions and morphology.
  859. Also insert lemmas in buffer (must be done after item is inserted in
  860. buffer)."
  861. (let ((word (string-trim (dom-text item)))
  862. (morph (dom-attr item 'morph))
  863. (savlm (dom-attr item 'savlm))
  864. (lemma (dom-attr item 'lemma))
  865. (divinename (dom-by-tag item 'divinename)))
  866. (insert word)
  867. (let ((refstart (- (point) (length word)))
  868. (refend (point)))
  869. ;; Red letter (Yuck, some modules need this below)
  870. (when (plist-get iproperties 'jesus)
  871. (add-face-text-property refstart refend '(:foreground "red")))
  872. ;; Special case this. XXX Some modules do this differently.
  873. (when divinename
  874. (insert " ")
  875. (bible-mode-handle-divine-name item))
  876. ;; lexical definitions
  877. (when (or savlm lemma)
  878. (let ((matched nil)
  879. (item (or savlm lemma)))
  880. (cond ((string-match "strong:G.*" item) ; Greek
  881. (setq matched (match-string 0 item))
  882. (put-text-property refstart refend 'keymap bible-mode-greek-keymap))
  883. ((string-match "strong:H.*" item) ; Hebrew
  884. (setq matched (match-string 0 item))
  885. (put-text-property refstart refend 'keymap bible-mode-hebrew-keymap)))
  886. ;; Add help-echo, strongs reference for tooltips if match.
  887. (when matched
  888. (setq-local bible-mode-has-strongs t)
  889. (put-text-property refstart refend 'help-echo 'bible-mode--show-lex-morph)
  890. (put-text-property refstart refend 'strong matched))))
  891. ;; morphology
  892. (when morph
  893. (let ((matched nil))
  894. (cond ((string-match "robinson:.*" morph) ; Robinson Greek morphology
  895. (setq matched (match-string 0 morph)))
  896. ((string-match "packard:.*" morph) ; Packard Greek morphology --- LXX seems to use this
  897. (setq matched (match-string 0 morph)))
  898. ((string-match "oshm:.*" morph) ; OSHM Hebrew morphology
  899. (setq matched (match-string 0 morph)))
  900. (t nil
  901. ;;(message "Unknown morphology %s" morph)
  902. ))
  903. (when matched
  904. (setq-local bible-mode-has-morphology t)
  905. (put-text-property refstart refend 'morph matched)
  906. (put-text-property refstart refend 'help-echo 'bible-mode--show-lex-morph))))
  907. ;; Insert lemma into buffer. Lemma tag will be part of savlm item.
  908. ;; XXX Should I do lexicon lookups on these lemmas? I don't use
  909. ;; this anyway....
  910. (when (and bible-mode-word-study-enabled savlm (string-match "lemma.*:.*" savlm))
  911. (dolist (word (split-string (match-string 0 savlm) " "))
  912. (setq word (replace-regexp-in-string "[.:a-zA-Z0-9]+" "" word))
  913. (insert " " word)
  914. (let ((refstart (- (point) 1 (length word)))
  915. (refend (point)))
  916. (add-face-text-property refstart refend '(:foreground "blue"))
  917. (put-text-property refstart refend 'keymap bible-mode-lemma-keymap)))))))
  918. (defun bible-mode-new-line ()
  919. "Ensure beginning of line. Try to avoid redundant blank lines."
  920. (unless (= (current-column) 0)
  921. (insert "\n")))
  922. (defun bible-mode--insert-domnode-recursive (node &optional iproperties notitle)
  923. "Recursively parses a domnode from `libxml-parse-html-region's usage on text
  924. produced by `bible-mode--exec-diatheke'. INSERTS text into active buffer with
  925. properties.
  926. XXX In processing subnodes, each case will prepend a space if it needs it."
  927. (when (and bible-mode-red-letter-enabled (equal (dom-attr node 'who) "Jesus"))
  928. ;; For red-letter display.
  929. (setq iproperties (plist-put iproperties 'jesus t)))
  930. (dolist (subnode (dom-children node))
  931. (cond ((null subnode) nil)
  932. ((stringp subnode)
  933. (let ((search-string (concat (car bible-mode-current-book) " " (number-to-string bible-mode-current-chapter) ":"))
  934. (start 0))
  935. ;; Delete <Book Ch:> (may be more than one)
  936. (setq subnode (string-replace search-string "" subnode))
  937. ;; Highlight verse number(s)
  938. (while (string-match "^ ?[0-9]+:" subnode start)
  939. (setq start (match-end 0))
  940. (add-face-text-property (match-beginning 0) (match-end 0) '(:foreground "purple") nil subnode)))
  941. ;; Red letter
  942. (when (plist-get iproperties 'jesus)
  943. (add-face-text-property 0 (length subnode) '(:foreground "red") nil subnode))
  944. (insert subnode))
  945. ;;;; Maybe process these at some point? Include footnotes etc.
  946. ;;; ((eq (dom-tag subnode) 'node) nil)
  947. ;;; ((eq (dom-tag subnode) 'lb) nil)
  948. ;; These tags appear in ESV modules (and maybe others?) XXX still not right
  949. ((eq (dom-tag subnode) 'l)
  950. (let ((attributes (dom-attributes subnode)))
  951. (cond ((equal (cdr (assoc 'type attributes)) "x-br")
  952. (bible-mode-new-line))
  953. ((equal (cdr (assoc 'type attributes)) "x-indent")
  954. (insert "\t"))
  955. ((assoc 'level attributes)
  956. (let ((indent (string-to-number (cdr (assoc 'level attributes)))))
  957. (when (= indent 2) (insert "\n\t")))))))
  958. ((eq (dom-tag subnode) 'title) (when (not notitle) (setq bible-mode-chapter-title subnode) (bible-mode-new-line)))
  959. ((eq (dom-tag subnode) 'body) (bible-mode--insert-domnode-recursive subnode iproperties notitle))
  960. ;; NASB Module uses this to indicate OT quotations (and others?).
  961. ((eq (dom-tag subnode) 'seg) (bible-mode--insert-domnode-recursive subnode iproperties notitle))
  962. ((eq (dom-tag subnode) 'divinename) (bible-mode-handle-divine-name subnode))
  963. ;; This tag is used for red letter.
  964. ((eq (dom-tag subnode) 'q) (bible-mode--insert-domnode-recursive subnode iproperties notitle))
  965. ((eq (dom-tag subnode) 'p) (bible-mode--insert-domnode-recursive subnode iproperties notitle))
  966. ((eq (dom-tag subnode) 'w) (insert " ") (bible-mode--process-word subnode iproperties))
  967. ;; Some modules use this for line breaks and such.
  968. ((or (and (eq (dom-tag subnode) 'milestone) (equal (cdr (assoc 'type (dom-attributes subnode))) "line"))
  969. (and (eq (dom-tag subnode) 'div) (equal (cdr (assoc 'type (dom-attributes subnode))) "paragraph")))
  970. (bible-mode-new-line))
  971. ((or (eq (dom-tag subnode) 'transchange) (eq (dom-tag subnode) 'hi))
  972. ;; Word inserted by translation, not in original, give visual indication.
  973. (let ((word (dom-text subnode)))
  974. (insert " " word)
  975. (if (plist-get iproperties 'jesus)
  976. (add-face-text-property (- (point) (length word)) (point) '(:foreground "salmon"))
  977. (add-face-text-property (- (point) (length word)) (point) '(:foreground "gray50"))))))))
  978. ;;(defvar bible-mode-debugme nil)
  979. (defun bible-mode--display (&optional verse)
  980. "Render text for `bible-mode'.
  981. If VERSE is supplied, set cursor at verse."
  982. ;; Display buffers can have different modules.
  983. (setq-local bible-mode-module (default-value 'bible-mode-module))
  984. ;; Clear buffer and insert the result of calling bible-mode--exec-diatheke.
  985. (setq buffer-read-only nil)
  986. (erase-buffer)
  987. (setq bible-mode-chapter-title nil
  988. bible-mode-has-strongs nil
  989. bible-mode-has-morphology nil)
  990. (insert (bible-mode--exec-diatheke (concat bible-mode-current-book-name ":" (number-to-string bible-mode-current-chapter))))
  991. ;; Parse the xml in the buffer into a DOM tree.
  992. (let ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  993. ;; Render the DOM tree into the buffer.
  994. (unless bible-mode-debugme
  995. (erase-buffer)
  996. ;; Looking for the "body" tag in the DOM node.
  997. (bible-mode--insert-domnode-recursive (dom-by-tag html-dom-tree 'body) nil nil)
  998. (goto-char (point-min))))
  999. ;; Remove the module name from the buffer.
  1000. (while (search-forward (concat "(" bible-mode-module ")") nil t)
  1001. (replace-match ""))
  1002. ;; Set the mode line of the biffer.
  1003. (setq mode-name (concat "Bible (" bible-mode-current-book-name " " (number-to-string bible-mode-current-chapter) ") "
  1004. bible-mode-module
  1005. (when bible-mode-has-strongs " Lex")
  1006. (when bible-mode-has-morphology " Morph")
  1007. ")"))
  1008. ;; Deal with chapter titles (i.e. in Psalms)
  1009. ;; N.B. This won't change a title inside a chapter, and so it
  1010. ;; doesn't work with Psalm 119 where the acrostic letters get
  1011. ;; printed as "titles".
  1012. (when bible-mode-chapter-title ; This gets set in bible-mode-insert-domnode-recursive.
  1013. (goto-char (point-min))
  1014. (let ((title-text (dom-texts bible-mode-chapter-title))
  1015. (refstart (point-min))
  1016. refend)
  1017. ;; Insert and make bold the title.
  1018. (when (string-or-null-p title-text)
  1019. (insert title-text "\n")
  1020. (setq refend (point))
  1021. (put-text-property refstart refend 'face 'bold))))
  1022. ;; Get rid of spurious spaces.
  1023. (format-replace-strings '(("." . ". ")
  1024. ("," . ", ")
  1025. (";" . "; ")
  1026. (":" . ": ")
  1027. ("?" . "? ")
  1028. ("!" . "! ")
  1029. (" ." . ". ")
  1030. (" ," . ", ")
  1031. (" ;" . "; ")
  1032. (" :" . ": ")
  1033. (" ?" . "? ")
  1034. (" !" . "! ")
  1035. ("“ " . "“")
  1036. ("‘ " . "‘")
  1037. ;;; (" ”" . "”")
  1038. ;;; (" ’" . "’")
  1039. (" " . " ")
  1040. (" " . " ")
  1041. )
  1042. nil (point-min) (point-max))
  1043. (setq buffer-read-only t)
  1044. (goto-char (point-min))
  1045. ;; If optional verse specification go to that verse.
  1046. (when verse
  1047. (goto-char (+ 1 (string-match (regexp-opt `(,(concat (number-to-string verse) ":"))) (buffer-string))))
  1048. ;; (beginning-of-line)
  1049. ))
  1050. (defun bible-mode--list-biblical-modules ()
  1051. "Return a list of accessible Biblical Text modules."
  1052. (let ((text (bible-mode--exec-diatheke "modulelist" nil nil nil "system"))
  1053. modules)
  1054. (catch 'done
  1055. (dolist (line (split-string text "[\n\r]+"))
  1056. (when (equal line "Commentaries:")
  1057. (throw 'done nil))
  1058. (when (not (equal "Biblical Texts:" line))
  1059. (push (split-string line " : ") modules))))
  1060. modules))
  1061. (defun bible-mode-pick-module ()
  1062. "Keymap action function---select module user chooses."
  1063. (interactive)
  1064. (let ((item (get-text-property (point) 'module)))
  1065. (setq-default bible-mode-module item)
  1066. (bible-open)))
  1067. (defconst bible-mode-module-map (make-keymap))
  1068. (define-key bible-mode-module-map [mouse-1] 'bible-mode-pick-module)
  1069. (define-key bible-mode-module-map (kbd "RET") 'bible-mode-pick-module)
  1070. (defun bible-mode-display-available-modules ()
  1071. "Display available modules, allow user to select."
  1072. (interactive)
  1073. (let ((buf (get-buffer-create "Modules"))
  1074. (mods (bible-mode--list-biblical-modules)))
  1075. (set-buffer buf)
  1076. (module-select-mode)
  1077. (setq buffer-read-only nil)
  1078. (erase-buffer)
  1079. (setq-local tab-stop-list '(25))
  1080. (dolist (mod mods)
  1081. (let ((name (string-trim (car mod)))
  1082. (description (string-trim-left (cadr mod))))
  1083. (insert
  1084. (propertize (string-trim name)
  1085. 'face 'bold
  1086. 'module name
  1087. 'help-echo (concat "Select " name)
  1088. 'keymap bible-mode-module-map))
  1089. (move-to-tab-stop)
  1090. (insert (format "%s\n" description))))
  1091. (setq buffer-read-only t)
  1092. (goto-char (point-min))
  1093. (pop-to-buffer buf nil t)))
  1094. ;;;;; Bible Searching
  1095. (defun bible-mode--open-search (query searchmode)
  1096. "Open a search buffer of QUERY using SEARCHMODE in module MOD."
  1097. (let ((buf (get-buffer-create (concat "*bible-search-" (downcase bible-mode-module) "-" query "*"))))
  1098. (set-buffer buf)
  1099. (bible-search-mode)
  1100. (bible-mode--display-search query searchmode)
  1101. (pop-to-buffer buf nil t)))
  1102. (defun bible-mode--display-search (query searchmode)
  1103. "Render results of search QUERY from SEARCHMODE."
  1104. (setq buffer-read-only nil)
  1105. (erase-buffer)
  1106. (let* ((result (string-trim (replace-regexp-in-string
  1107. "Entries .+?--" ""
  1108. (bible-mode--exec-diatheke query nil "plain" searchmode bible-mode-module))))
  1109. (match 0)
  1110. (matchstr "")
  1111. (verses nil)
  1112. (query-verses "")
  1113. fullverses)
  1114. (if (equal result (concat "none (" bible-mode-module ")"))
  1115. (insert "No results found."
  1116. (if (equal searchmode "lucene")
  1117. " Verify index has been build with mkfastmod."
  1118. ""))
  1119. (progn
  1120. (while match
  1121. (setq match (string-match ".+?:[0-9]?[0-9]?" result (+ match (length matchstr)))
  1122. matchstr (match-string 0 result))
  1123. (when match
  1124. (push
  1125. ;; Massage match to make it more sortable, get rid of some characters.
  1126. (replace-regexp-in-string
  1127. "I " "1"
  1128. (replace-regexp-in-string
  1129. "II " "2"
  1130. (replace-regexp-in-string
  1131. "III " "3"
  1132. (replace-regexp-in-string ".+; " "" matchstr))))
  1133. verses)))
  1134. (setq match 0)
  1135. (setq verses (sort verses))
  1136. (dolist (verse verses)
  1137. (if query-verses
  1138. (setq query-verses (concat query-verses ";" verse))
  1139. (setq query-verses verse)))
  1140. (setq fullverses (let ((bible-mode-show-diatheke-exec nil))
  1141. (bible-mode--exec-diatheke query-verses nil nil nil bible-mode-module)))
  1142. (insert fullverses)
  1143. (let* ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  1144. (erase-buffer)
  1145. (bible-mode--insert-domnode-recursive (dom-by-tag html-dom-tree 'body) nil nil)
  1146. (goto-char (point-min))
  1147. (while (search-forward (concat "(" bible-mode-module ")") nil t)
  1148. (replace-match "")))))
  1149. (setq mode-name (concat "Bible Search (" bible-mode-module))
  1150. (when bible-mode-search-range
  1151. (setq mode-name (concat mode-name " [" bible-mode-search-range "]")))
  1152. (setq mode-name (concat mode-name ")"))
  1153. (setq buffer-read-only t)
  1154. (setq-local bible-mode-search-query query)
  1155. (setq-local bible-mode-search-mode searchmode)
  1156. (goto-char (point-min))))
  1157. ;;;;; Terms
  1158. ;;(defun bible-mode-display-morphology (morph)
  1159. ;; ;; xxx Do something here?
  1160. ;; )
  1161. (defun bible-mode--display-term (termtype)
  1162. (setq buffer-read-only nil)
  1163. (goto-char (point-min))
  1164. ;; This enables clicking on Strong's numbers in some lexicon definitions.
  1165. (while (search-forward-regexp "[0-9]+" nil t)
  1166. (cond ((eq termtype 'hebrew)
  1167. (put-text-property (match-beginning 0) (match-end 0) 'strong (concat "strong:H" (match-string 0)))
  1168. (put-text-property (match-beginning 0) (match-end 0) 'keymap bible-mode-hebrew-keymap)
  1169. (add-face-text-property (match-beginning 0) (match-end 0) `(:foreground "blue")))
  1170. ((eq termtype 'greek)
  1171. (put-text-property (match-beginning 0) (match-end 0) 'strong (concat "strong:G" (match-string 0)))
  1172. (put-text-property (match-beginning 0) (match-end 0) 'keymap bible-mode-greek-keymap)
  1173. (add-face-text-property (match-beginning 0) (match-end 0) `(:foreground "blue")))))
  1174. (goto-char (point-min))
  1175. ;; This enables clicking on verse references.
  1176. (while (search-forward-regexp bible-mode-verse-regexp nil t)
  1177. (put-text-property (match-beginning 0) (match-end 0) 'xref (match-string 0))
  1178. (put-text-property (match-beginning 0) (match-end 0) 'keymap bible-search-mode-map)
  1179. (put-text-property (match-beginning 0) (match-end 0) 'help-echo (concat "Go to " (match-string 0)))
  1180. (add-face-text-property (match-beginning 0) (match-end 0) '(:foreground "blue")))
  1181. (goto-char (point-min))
  1182. (while (search-forward "()" nil t)
  1183. (replace-match ""))
  1184. (goto-char (point-min))
  1185. (setq buffer-read-only t))
  1186. (defun bible-mode--open-term-hebrew (term)
  1187. "Open a buffer of the Strong's Hebrew TERM's definition."
  1188. (let ((buf (get-buffer-create (concat "*bible-term-hebrew-" term "*"))))
  1189. (set-buffer buf)
  1190. (bible-term-hebrew-mode)
  1191. (bible-mode--display-term-hebrew term)
  1192. (pop-to-buffer buf nil t)
  1193. (fit-window-to-buffer)))
  1194. (defun bible-mode--open-term-greek (term)
  1195. "Open a buffer of the Strong's Greek TERM definition."
  1196. (let ((buf (get-buffer-create (concat "*bible-term-greek-" term "*"))))
  1197. (set-buffer buf)
  1198. (bible-term-greek-mode)
  1199. (bible-mode--display-term-greek term)
  1200. (pop-to-buffer buf nil t)
  1201. (fit-window-to-buffer)))
  1202. ;;;
  1203. ;;; Note: Hebrew display of terms is backwards; set bidi direction to
  1204. ;;; 'left-to-right.
  1205. (defun bible-mode--display-term-hebrew (term)
  1206. "Render the definition of the Strong's Hebrew TERM.
  1207. Sets bidi-paragraph-direction so the English text will render
  1208. left-to-right. XXX Why doesn't this work for the tooltips?"
  1209. (setq buffer-read-only nil)
  1210. (erase-buffer)
  1211. (insert (replace-regexp-in-string
  1212. (regexp-opt `(,bible-mode-hebrew-lexicon))
  1213. ""
  1214. (bible-mode--exec-diatheke term nil "plain" nil bible-mode-hebrew-lexicon)
  1215. nil nil nil 7
  1216. ))
  1217. (bible-mode--display-term 'hebrew)
  1218. (setq bidi-paragraph-direction 'left-to-right))
  1219. (defun bible-mode--display-term-greek (term)
  1220. "Render the definition of the Strong's Greek TERM."
  1221. (setq buffer-read-only nil)
  1222. (erase-buffer)
  1223. (insert (replace-regexp-in-string
  1224. (regexp-opt `(,bible-mode-greek-lexicon))
  1225. ""
  1226. (bible-mode--lookup-lex-greek term)))
  1227. (bible-mode--display-term 'greek))
  1228. (defun bible-mode--set-location (book chapter &optional verse)
  1229. "Set the BOOK, CHAPTER and optionally VERSE of the active `bible-mode' buffer."
  1230. (setq-local bible-mode-module (default-value 'bible-mode-module))
  1231. (setq-local bible-mode-current-book book)
  1232. (setq-local bible-mode-current-book-name (car book))
  1233. (setq-local bible-mode-current-chapter chapter)
  1234. (bible-mode--display verse))
  1235. ;;;;; Utilities
  1236. (defun bible-mode--list-number-range (min max &optional prefix)
  1237. "Returns a list containing entries for each integer between MIN and MAX.
  1238. If PREFIX is supplied, prepend PREFIX to the entries.
  1239. Used in tandem with `completing-read' for chapter selection."
  1240. (let ((range-list nil))
  1241. (dotimes (num (1+ max))
  1242. (when (>= num min)
  1243. (push (cons (concat prefix (number-to-string num)) num) range-list)))
  1244. (nreverse range-list)))
  1245. ;;; Provides
  1246. (provide 'bible-mode)
  1247. ;; Local Variables:
  1248. ;; End: