bible.el 58 KB

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