bible.el 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044
  1. ;;; bible.el --- A Bible browsing application -*- lexical-binding: t; mode: EMACS-LISP; indent-tabs-mode: nil -*-
  2. ;; Copyright (c) 2025-2026 Fred Gilham
  3. ;; Author: Fred Gilham <fmgilham@gmail.com>
  4. ;; Version: 1.2.1
  5. ;; Keywords: files, text, hypermedia
  6. ;; Package-Requires: ((emacs "29.1") cl-lib dom shr)
  7. ;; URL: https://gitbot.homedns.org/fred/bible
  8. ;; This file is not part of GNU Emacs.
  9. ;; bible.el is free software; you can redistribute it and/or modify it
  10. ;; under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation; either version 3, or (at your option)
  12. ;; any later version.
  13. ;; bible.el is distributed in the hope that it will be useful, but
  14. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. ;; General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this file; see the file LICENSE. If not, see
  19. ;; <https://www.gnu.org/licenses/>.
  20. ;;; Commentary:
  21. ;; Forked and extensively modified from package by Zacalot
  22. ;; Url: https://github.com/Zacalot/bible-mode
  23. ;; This package uses the `diatheke' program to browse and search
  24. ;; Biblical texts provided by the Sword project (https://crosswire.org).
  25. ;; Word study is also supported.
  26. ;;;; Usage
  27. ;; Use M-x `bible-open' to open a Bible buffer.
  28. ;; Use C-h f `bible' to see available keybindings.
  29. ;; The program also installs a Bible menu with keybindings and other
  30. ;; commands.
  31. ;; You may customize `bible-text' to set a default browsing module, as
  32. ;; well as `bible-word-study-enabled' to enable word study by default.
  33. ;; NB: Currently this just shows the lemmas in the original language
  34. ;; if present. Tooltips will display whenever there are strongs
  35. ;; numbers in the module.
  36. ;;;; Design
  37. ;; The idea here is to use the diatheke program to lookup text from
  38. ;; modules (biblical texts), then insert this text into buffers. The
  39. ;; main bible display uses diatheke's internal XML format. The whole
  40. ;; buffer gets parsed by `libxml-parse-html-region' to create a dom
  41. ;; tree. This gets parsed by `bible--insert-domnode-recursive' to render
  42. ;; the text into reading format.
  43. ;; The text is then decorated using information from the dom format as
  44. ;; necessary along with regular expressions to identify the verse
  45. ;; references. This is for red letters, purple highlighting of the
  46. ;; verse numbers, bold face of the divine name in the OT and so on.
  47. ;; If Strongs tags and/or morphological tags are present, they are
  48. ;; looked up in appropriate lexical and morphological modules and used
  49. ;; to add tooltips to the text so that hovering over words will bring
  50. ;; up a tooltip with information about the word. Clicking on a word
  51. ;; with lexical information will display that information in a "term"
  52. ;; buffer.
  53. ;;; Code:
  54. ;;;; Environment stuff
  55. ;; Turn off modes because we are greedy for pixels....
  56. (tool-bar-mode -1)
  57. (scroll-bar-mode -1)
  58. ;; eldoc isn't meaningful in this program, and this saves space in the
  59. ;; mode line.
  60. (global-eldoc-mode -1)
  61. ;;;; Requirements
  62. (require 'cl-lib) ; Required by `dom' and `shr', might as well use it.
  63. (require 'dom)
  64. (require 'shr)
  65. (require 'menu+ nil t) ; If you have it, it looks nice.
  66. ;;;; Aliases for obsolete functions
  67. ;; dom-text and dom-texts declared obsolescent in Emacs 31. Check for
  68. ;; new function, retain backward compatibility.
  69. ;; Note that the following is the simplest way I found to avoid compile warnings.
  70. (defalias 'bible-dom-text (if (fboundp 'dom-inner-text) 'dom-inner-text 'dom-text))
  71. (defalias 'bible-dom-texts (if (fboundp 'dom-inner-texts) 'dom-inner-text 'dom-texts))
  72. ;;;; Customization Variables
  73. (defgroup bible nil
  74. "Settings for `bible'."
  75. :group 'tools
  76. :link '(url-link "https://gitbot.homedns.org/fred/bible-mode"))
  77. (defcustom bible-text
  78. "KJV"
  79. "Customize default bible text module for Diatheke to query.
  80. \(For full list of installed modules, run `diatheke -b system -l bibliography'\)"
  81. :type '(choice (const :tag "None" nil)
  82. (string :tag "Bible text (e.g. \"KJV\")"))
  83. :local nil
  84. :group 'bible)
  85. (defcustom bible-commentary
  86. "Clarke"
  87. "Customize default commentary module for Diatheke to query.
  88. \(For full list of installed modules, run `diatheke -b system -l bibliography'\)"
  89. :type '(choice (const :tag "None" nil)
  90. (string :tag "Commentary (e.g. \"Clarke\")"))
  91. :local nil
  92. :group 'bible)
  93. ;; TODO: Not implememted yet (FMG 5-Mar-2026)
  94. (defcustom bible-font
  95. "Ezra SIL"
  96. "Default font for bible (not yet implemented)."
  97. :type '(string :tag "Font family name (e.g. \"Ezra SIL\")")
  98. :local t
  99. :group 'bible)
  100. (defcustom bible-sword-query
  101. "diatheke"
  102. "Specify program used to query sword modules.
  103. Must be some version of the sword library's diatheke program."
  104. :type '(string :tag "Sword library query executable (e.g. \"/usr/local/bin/diatheke\").")
  105. :local nil
  106. :group 'bible)
  107. ;;; (defcustom bible-greek-lexicon
  108. ;;; ;; AbbottSmithStrongs now has both links to lemmas and definitions
  109. ;;; ;; keyed by lemma. So we only need the AbbottSmithStrongs lexicon
  110. ;;; ;; and not the AbbottSmith lexicon.
  111. ;;; "AbbottSmithStrongs"
  112. ;;; "Lexicon used for displaying definitions of Greek words using Strong's codes."
  113. ;;; :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  114. ;;; :local nil
  115. ;;; :group 'bible)
  116. (defcustom bible-greek-lexicon
  117. ;; AbbottSmithStrongs now has both links to lemmas and definitions
  118. ;; keyed by lemma. So we only need the AbbottSmithStrongs lexicon
  119. ;; and not the AbbottSmith lexicon.
  120. "AbbottSmithStrongs"
  121. "Lexicon used for displaying definitions of Greek words using Strong's codes."
  122. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  123. :local nil
  124. :group 'bible)
  125. (defcustom bible-use-index-for-lexicon t
  126. "Some lexicons are accessed by lemmas rather than Strong's numbers.
  127. Use an index to look up lemmas from Strong's numbers so these lexicons
  128. can be used."
  129. :type 'boolean
  130. :local nil
  131. :group 'bible)
  132. (defcustom bible-lexicon-index "AbbottSmithStrongs"
  133. "A module that consists of an index mapping Strong's numbers to Greek lemmas.
  134. The code is written to use the entries in AbbottSmithStrongs
  135. which are of the form
  136. <strong's number>: @LINK <greek lemma>"
  137. :type '(string :tag "Lexicon index.")
  138. :local nil
  139. :group 'bible)
  140. (defcustom bible-greek-lexicon-short
  141. "StrongsRealGreek"
  142. "Lexicon used for displaying definitions of Greek words in tooltips."
  143. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  144. :local nil
  145. :group 'bible)
  146. ;; HACK: The Hebrew lexicons differ on whether they accept keys of the
  147. ;; form `Hnnnn' or `nnnn'. The code does not yet handle this
  148. ;; correctly, so stick with the following. (FMG 5-Mar-2026)
  149. (defcustom bible-hebrew-lexicon
  150. "BDBGlosses_Strongs" ; This seems to work
  151. "Specify Lexicon used to display definitions of Hebrew words.
  152. Note that changing this may require changing some code.
  153. See `bible--display-lemma-hebrew'."
  154. :type '(string :tag "Lexicon module (e.g. \"BDBGlosses_Strongs\")")
  155. :local nil
  156. :group 'bible)
  157. (defcustom bible-hebrew-lexicon-short
  158. "StrongsRealHebrew"
  159. "Lexicon used for displaying definitions of Hebrew words in tooltips."
  160. :type '(string :tag "Lexicon module (e.g. \"StrongsRealHebrew\")")
  161. :local nil
  162. :group 'bible)
  163. (defcustom bible-word-study-enabled
  164. nil
  165. "Display original language Lemma words if present in module.
  166. \(KJV New Testament has this.\)"
  167. :type 'boolean
  168. :local t
  169. :group 'bible)
  170. (defcustom bible-red-letter-enabled
  171. t
  172. "Display words of Jesus in red when module has that information."
  173. :type 'boolean
  174. :local t
  175. :group 'bible)
  176. (defcustom bible-show-diatheke-exec
  177. nil
  178. "Show the arguments by which diatheke is executed (mostly for debugging)."
  179. :type 'boolean
  180. :local nil
  181. :group 'bible)
  182. ;;;; Mode line formats for different kinds of buffers.
  183. (defvar bible-mode-line-format
  184. '("%e" mode-line-front-space
  185. mode-line-frame-identification mode-line-buffer-identification " "
  186. bible--current-book-name
  187. " " (:eval (number-to-string bible--current-chapter))
  188. " " (:eval (if bible--synced-p "Sync" ""))
  189. (:eval (when bible-search-range (concat " <" bible-search-range ">")))
  190. " " mode-line-modes bible-has-lexemes bible-has-morphemes mode-line-misc-info
  191. mode-line-end-spaces)
  192. "Mode line format for bible buffers.")
  193. (defvar bible-search-mode-line-format
  194. '("%e" mode-line-front-space
  195. mode-line-frame-identification mode-line-buffer-identification " "
  196. bible-search-text-this-query " " bible-search-word-this-query " "
  197. (:eval (when bible-search-range-this-query (concat "<" bible-search-range-this-query "> ")))
  198. (:eval (number-to-string bible-search-matches)) " matches"
  199. " " mode-line-modes mode-line-misc-info
  200. mode-line-end-spaces)
  201. "Mode line format for bible search buffers.")
  202. (defvar bible-term-mode-line-format
  203. '("%e" mode-line-front-space
  204. mode-line-frame-identification mode-line-buffer-identification " "
  205. mode-line-modes mode-line-misc-info
  206. mode-line-end-spaces)
  207. "Mode line format for bible term buffers.")
  208. ;;;; Modes
  209. (define-derived-mode bible special-mode "Bible"
  210. "Mode for reading the Bible.
  211. \\{bible-map}"
  212. (buffer-disable-undo)
  213. (font-lock-mode t)
  214. (use-local-map bible-map)
  215. (setq-local mode-line-format bible-mode-line-format)
  216. (setq buffer-read-only t)
  217. (visual-line-mode t))
  218. (define-derived-mode bible-search-mode special-mode "Bible Search"
  219. "Mode for performing Bible searches.
  220. \\{bible-search-mode-map}"
  221. (buffer-disable-undo)
  222. (font-lock-mode t)
  223. (use-local-map bible-search-mode-map)
  224. (setq-local mode-line-format bible-search-mode-line-format
  225. bible-buffer-type 'search)
  226. (setq buffer-read-only t)
  227. (visual-line-mode t))
  228. (define-derived-mode bible-term-mode special-mode "Bible Term"
  229. "Mode for researching terms in the Bible.
  230. \\{bible-term-mode-map}"
  231. (buffer-disable-undo)
  232. (font-lock-mode t)
  233. (use-local-map bible-term-mode-map)
  234. (setq-local mode-line-format bible-term-mode-line-format
  235. bible-buffer-type 'term)
  236. (setq buffer-read-only t)
  237. (visual-line-mode t))
  238. (define-derived-mode bible-term-hebrew-mode bible-term-mode "Bible Term"
  239. "Mode for researching Hebrew terms in the Bible.
  240. \\{bible-term-hebrew-mode-map}"
  241. (setq-local bible-term-language "Hebrew"))
  242. (define-derived-mode bible-term-greek-mode bible-term-mode "Bible Term"
  243. "Mode for researching Greek terms in the Bible.
  244. \\{bible-term-greek-mode-map}"
  245. (setq-local bible-term-language "Greek"))
  246. (define-derived-mode bible-text-select-mode special-mode "Select Module"
  247. (buffer-disable-undo)
  248. (font-lock-mode t)
  249. (setq buffer-read-only t))
  250. ;;;; Keymaps
  251. ;; N.B. Bible Menu items appear in reverse order of their definition
  252. ;; below
  253. (defconst bible-map (make-sparse-keymap)
  254. "Keymap for bible.")
  255. (define-key bible-map [menu-bar bible]
  256. (cons "Bible" (make-sparse-keymap "Bible")))
  257. (define-key bible-map
  258. [menu-bar bible toggle-debug]
  259. '("Toggle debug-on-error" . toggle-debug-on-error))
  260. (define-key bible-map
  261. [menu-bar bible display-diatheke]
  262. '("Toggle diatheke display" . bible-toggle-display-diatheke))
  263. (define-key bible-map "d" 'bible-toggle-display-xml)
  264. (define-key bible-map
  265. [menu-bar bible display-xml]
  266. '("Toggle XML Display" . bible-toggle-display-xml))
  267. (define-key bible-map
  268. [menu-bar bible toggle-text-direction]
  269. '("Toggle text direction (for Hebrew display)" . bible-toggle-text-direction))
  270. (define-key bible-map
  271. [menu-bar bible toggle-tooltip-display]
  272. '("Toggle Tooltip Display" . bible-toggle-tooltips))
  273. (define-key bible-map
  274. [menu-bar bible sep]
  275. '(menu-item '"--"))
  276. ;;;;; Misc key bindings
  277. (define-key bible-map "T" 'bible-select-text-module)
  278. (define-key bible-map "C" 'bible-select-commentary-module)
  279. (define-key bible-map "w" 'bible-toggle-word-study)
  280. (define-key bible-map "l" 'bible-toggle-red-letter)
  281. (define-key bible-map "z" 'text-scale-adjust)
  282. (define-key bible-map
  283. [menu-bar bible zoom-text]
  284. '("Zoom Text" . text-scale-adjust))
  285. (define-key bible-map "x" 'bible-split-display)
  286. (define-key bible-map
  287. [menu-bar bible split-display]
  288. '("Split Display" . bible-split-display))
  289. (define-key bible-map "S" 'bible-toggle-buffer-sync)
  290. (define-key bible-map [menu-bar bible sync]
  291. '("Toggle Synchronize Buffer" . bible-toggle-buffer-sync))
  292. ;;;;; Navigation
  293. (define-key bible-map "p" 'bible-previous-chapter)
  294. (define-key bible-map
  295. [menu-bar bible previous-chapter]
  296. '("Previous Chapter" . bible-previous-chapter))
  297. (define-key bible-map "n" 'bible-next-chapter)
  298. (define-key bible-map
  299. [menu-bar bible next-chapter]
  300. '("Next Chapter" . bible-next-chapter))
  301. (define-key bible-map (kbd "TAB") 'bible-next-word)
  302. (define-key bible-map (kbd "M-<tab>") 'bible-previous-word)
  303. ;;;;; Direct jump
  304. (define-key bible-map "c" 'bible-select-chapter)
  305. (define-key bible-map
  306. [menu-bar bible select-chapter]
  307. '("Select Chapter" . bible-select-chapter))
  308. (define-key bible-map "b" 'bible-select-book)
  309. (define-key bible-map
  310. [menu-bar bible select-book]
  311. '("Select Book" . bible-select-book))
  312. (define-key bible-map
  313. [menu-bar bible sep]
  314. '(menu-item '"--"))
  315. ;; Deal with visual-line-mode navigation.
  316. (define-key bible-map "\C-n" 'next-logical-line)
  317. (define-key bible-map "\C-p" 'previous-logical-line)
  318. ;;;;; Search
  319. (define-key bible-map "/" 'bible-search)
  320. (define-key bible-map "s" 'bible-search)
  321. (define-key bible-map
  322. [menu-bar bible search]
  323. '("Search" . bible-search))
  324. (define-key bible-map "r" 'bible-set-search-range)
  325. (define-key bible-map
  326. [menu-bar bible range]
  327. '("Set Search Range" . bible-set-search-range))
  328. (define-key bible-map
  329. [menu-bar bible sepp]
  330. '(menu-item '"--"))
  331. (define-key bible-map
  332. [menu-bar bible sepp]
  333. '(menu-item '"--"))
  334. (define-key bible-map
  335. [menu-bar bible select-biblical-commentary]
  336. '("Select Commentary" . bible-display-available-commentaries))
  337. (define-key bible-map
  338. [menu-bar bible select-biblical-text]
  339. '("Select Text" . bible-display-available-texts))
  340. (defconst bible-search-mode-map (make-keymap))
  341. (define-key bible-search-mode-map "s" 'bible-search)
  342. (define-key bible-search-mode-map "w" 'bible-toggle-word-study)
  343. (define-key bible-search-mode-map "n" 'bible-next-search-item)
  344. (define-key bible-search-mode-map "p" 'bible-previous-search-item)
  345. (define-key bible-search-mode-map (kbd "RET") 'bible-follow-verse)
  346. (define-key bible-search-mode-map [mouse-1] 'bible-follow-verse)
  347. ;;;;; Term mode keymaps
  348. (defconst bible-term-mode-map (make-sparse-keymap))
  349. (define-key bible-term-mode-map "z" 'text-scale-adjust)
  350. (define-key bible-term-mode-map [mouse-1] 'bible-follow-xref)
  351. (defconst bible-greek-keymap (make-sparse-keymap))
  352. (define-key bible-greek-keymap (kbd "RET") 'bible--display-greek)
  353. (define-key bible-greek-keymap [mouse-1] 'bible--display-greek)
  354. (defconst bible-hebrew-keymap (make-sparse-keymap))
  355. (define-key bible-hebrew-keymap (kbd "RET") 'bible--display-hebrew)
  356. (define-key bible-hebrew-keymap [mouse-1] 'bible--display-hebrew)
  357. (defconst bible-lemma-keymap (make-sparse-keymap))
  358. (define-key bible-lemma-keymap (kbd "RET")
  359. (lambda ()
  360. (interactive)))
  361. ;; Not used. Not really sure what to do here or if it's useful to do anything.
  362. (defconst bible-morph-keymap (make-sparse-keymap))
  363. (define-key bible-morph-keymap (kbd "RET")
  364. (lambda ()
  365. (interactive)
  366. ;; (let ((thing (thing-at-point 'word)))
  367. ;; (message "thing at point: %s" thing)
  368. ;; (message "morph property %s" (get-text-property 0 'field thing))
  369. ))
  370. ;;;;; Module choice keymaps.
  371. (defconst bible-text-map (make-keymap))
  372. (define-key bible-text-map [mouse-1] 'bible-pick-text-module)
  373. (define-key bible-text-map (kbd "RET") 'bible-pick-text-module)
  374. (defconst bible-commentary-map (make-keymap))
  375. (define-key bible-commentary-map [mouse-1] 'bible-pick-commentary-module)
  376. (define-key bible-commentary-map (kbd "RET") 'bible-pick-commentary-module)
  377. ;;;; Variable definitions
  378. (defconst bible--verse-regexp "\\(I \\|1 \\|II \\|2 \\|III \\|3 \\)??[a-zA-Z]+?[ \t\n][0-9]+[:][0-9]+")
  379. ;; Don't know how to get footnotes and scripture cross references yet.
  380. ;;(defconst bible-diatheke-filter-options " afilmnsvw")
  381. (defconst bible-diatheke-filter-options " almnvw")
  382. (defvar bible--text-buffers nil
  383. "List of Bible text buffers.")
  384. (defvar bible--commentary-buffers nil
  385. "List of commentary buffers.")
  386. (defvar bible-buffer-type nil
  387. "One of `text', `commentary', `term', `select' or `search'. Identifies
  388. the type of buffer.")
  389. (defvar-local bible-associated-buffer nil
  390. "Buffer associated with a given commentary buffer---used for
  391. chasing cross-references.")
  392. (defvar bible--synced-buffers nil
  393. "List of buffers that are synchronized so that navigation in one applies
  394. to all of them.")
  395. (defvar-local bible--synced-p nil
  396. "Is this buffer syncronized?")
  397. (defvar bible--texts (lazy-completion-table bible--texts bible--list-biblical-texts))
  398. (defvar bible--commentaries (lazy-completion-table bible--commentaries bible--list-biblical-commentaries))
  399. ;; REVIEW: I believe these chapter counts aren't the same for all modules, e.g. JPS. (FMG 5-Mar-2026)
  400. (defvar bible--books
  401. '(;; Old Testament
  402. ("Genesis" . 50) ("Exodus" . 40) ("Leviticus" . 27) ("Numbers" . 36)
  403. ("Deuteronomy" . 34) ("Joshua" . 24) ("Judges" . 21) ("Ruth" . 4)
  404. ("I Samuel" . 31) ("II Samuel" . 24) ("I Kings" . 22) ("II Kings" . 25)
  405. ("1 Samuel" . 31) ("2 Samuel" . 24) ("1 Kings" . 22) ("2 Kings" . 25)
  406. ("I Chronicles" . 29) ("II Chronicles" . 36) ("Ezra" . 10) ("Nehemiah" . 13)
  407. ("1 Chronicles" . 29) ("2 Chronicles" . 36)
  408. ("Ezra" . 10) ("Nehemiah" . 13)
  409. ("Esther" . 10) ("Job" . 42) ("Psalms" . 150) ("Proverbs" . 31)
  410. ("Ecclesiastes" . 12) ("Song of Solomon" . 8) ("Isaiah" . 66) ("Jeremiah" . 52)
  411. ("Lamentations" . 5) ("Ezekiel" . 48) ("Daniel" . 12) ("Hosea" . 14)
  412. ("Joel" . 3) ("Amos" . 9) ("Obadiah" . 1) ("Jonah" . 4)
  413. ("Micah" . 7) ("Nahum" . 3) ("Habakkuk" . 3) ("Zephaniah" . 3)
  414. ("Haggai" . 2) ("Zechariah" . 14) ("Malachi" . 4)
  415. ;; New Testament
  416. ("Matthew" . 28) ("Mark" . 16) ("Luke" . 24) ("John" . 21)
  417. ("Acts" . 28) ("Romans" . 16)
  418. ("I Corinthians" . 16) ("II Corinthians" . 13)
  419. ("1 Corinthians" . 16) ("2 Corinthians" . 13)
  420. ("Galatians" . 6) ("Ephesians" . 6) ("Philippians" . 4) ("Colossians" . 4)
  421. ("I Thessalonians" . 5) ("II Thessalonians" . 3) ("I Timothy" . 6) ("II Timothy" . 4)
  422. ("1 Thessalonians" . 5) ("2 Thessalonians" . 3) ("1 Timothy" . 6) ("2 Timothy" . 4)
  423. ("Titus" . 3) ("Philemon" . 1) ("Hebrews" . 13) ("James" . 5)
  424. ("I Peter" . 5) ("II Peter" . 3) ("I John" . 5) ("II John" . 1)
  425. ("1 Peter" . 5) ("2 Peter" . 3) ("1 John" . 5) ("2 John" . 1)
  426. ("III John" . 1) ("Jude" . 1) ("Revelation of John" . 22)
  427. ("3 John" . 1))
  428. "A-list of name / chapter count for Bible books.")
  429. ;; These abbreviations are used to follow cross-references in commentaries and lexicons.
  430. ;; Abbreviations from NETnote module and Clarke module (commentaries).
  431. ;; Abbreviations from some lexicons.
  432. ;; Standard abbreviations come first.
  433. (defvar bible--book-name-abbreviations
  434. '(;; Old Testament
  435. ("Gen" . "Genesis") ("Ge" . "Genesis")
  436. ("Exod" . "Exodus") ("Ex" . "Exodus") ("Exo" . "Exodus")
  437. ("Lev" . "Leviticus") ("Le" . "Leviticus")
  438. ("Num" . "Numbers") ("Nu" . "Numbers")
  439. ("Deut" . "Deuteronomy") ("De" . "Deuteronomy") ("Deu" . "Deuteronomy")
  440. ("Josh" . "Joshua") ("Js" . "Joshua") ("Jos" . "Joshua")
  441. ("Judg" . "Judges") ("Jg" . "Judges") ("Jdg" . "Judges")
  442. ("Ru" . "Ruth") ("Rut" . "Ruth")
  443. ("1 Sam" . "I Samuel") ("1 Samuel" . "I Samuel") ("I Sa" . "I Samuel")
  444. ("1 Sa" . "I Samuel") ("1Sam" . "I Samuel")
  445. ("2 Sam" . "II Samuel") ("2 Samuel" . "II Samuel") ("II Sa" . "II Samuel")
  446. ("2 Sa" . "II Samuel") ("2Sam" . "II Samuel")
  447. ("1 Kgs" . "I Kings") ("1 Kings" . "I Kings") ("I Ki" . "I Kings")
  448. ("1 Ki" . "I Kings") ("1Ki" . "I Kings")
  449. ("2 Kgs" . "II Kings") ("2 Kings" . "II Kings") ("II Ki" . "II Kings")
  450. ("2 Ki" . "II Kings") ("2Ki" . "II Kings")
  451. ("1 Chr" . "I Chronicles") ("1 Chronicles" . "I Chronicles")
  452. ("I Ch" . "I Chronicles") ("1 Ch" . "I Chronicles")
  453. ("2 Chr" . "II Chronicles") ("2 Chronicles" . "II Chronicles")
  454. ("II Ch" . "II Chronicles") ("2 Ch" . "II Chronicles")
  455. ("Ezr" . "Ezra")
  456. ("Neh" . "Nehemiah") ("Ne" . "Nehemiah")
  457. ("Esth" . "Esther") ("Es" . "Esther") ("Est" . "Esther")
  458. ("Jb" . "Job")
  459. ("Ps" . "Psalms") ("Psa" . "Psalms")
  460. ("Prov" . "Proverbs") ("Pr" . "Proverbs") ("Pro" . "Proverbs")
  461. ("Eccl" . "Ecclesiastes") ("Ec" . "Ecclesiastes")
  462. ("Ecc" . "Ecclesiastes")
  463. ("Song" . "Song of Solomon") ("So" . "Song of Solomon")
  464. ("Sol" . "Song of Solomon")
  465. ("Isa" . "Isaiah") ("Is" . "Isaiah")
  466. ("Jer" . "Jeremiah") ("Je" . "Jeremiah")
  467. ("Lam" . "Lamentations") ("La" . "Lamentations")
  468. ("Ezek" . "Ezekiel") ("Ez" . "Ezekiel") ("Eze" . "Ezekiel")
  469. ("Dan" . "Daniel") ("Da" . "Daniel")
  470. ("Hos" . "Hosea") ("Ho" . "Hosea")
  471. ("Joe" . "Joel")
  472. ("Am" . "Amos") ("Amo" . "Amos")
  473. ("Obad" . "Obadiah") ("Ob" . "Obadiah") ("Oba" . "Obadiah")
  474. ("Jon" . "Jonah")
  475. ("Mic" . "Micah") ("Mi" . "Micah")
  476. ("Nah" . "Nahum") ("Na" . "Nahum")
  477. ("Hab" . "Habakkuk") ("Ha" . "Habakkuk")
  478. ("Zeph" . "Zephaniah") ("Zep" . "Zephaniah")
  479. ("Hag" . "Haggai")
  480. ("Zech" . "Zechariah") ("Ze" . "Zechariah")
  481. ("Zac" . "Zechariah") ; Is this one correct??
  482. ("Mal" . "Malachi")
  483. ;; New Testament
  484. ("Mt" . "Matthew") ("Mat" . "Matthew") ("Matt" . "Matthew")
  485. ("Mk" . "Mark") ("Mar" . "Mark")
  486. ("Lk" . "Luke") ("Luk" . "Luke")
  487. ("Jn" . "John") ("Jo" . "John") ("Joh" . "John")
  488. ("Ac" . "Acts") ("Act" . "Acts")
  489. ("Rom" . "Romans") ("Ro" . "Romans")
  490. ("1 Cor" . "I Corinthians") ("1 Corintihans" . "I Corinthians")
  491. ("I Co" . "I Corinthians") ("1 Co" . "I Corinthians")
  492. ("ICor" . "I Corinthians") ("1Cor" . "I Corinthians")
  493. ("2 Cor" . "II Corinthians") ("2 Corinthians" . "II Corinthians")
  494. ("II Co" . "II Corinthians") ("2 Co" . "II Corinthians")
  495. ("IICor" . "II Corinthians") ("2Cor" . "II Corinthians")
  496. ("Gal" . "Galatians") ("Ga" . "Galatians")
  497. ("Eph" . "Ephesians")
  498. ("Phil" . "Philippians") ("Phl" . "Philippians")
  499. ("Col" . "Colossians")
  500. ("1 Thess" . "I Thessalonians") ("1 Thessalonians" . "I Thessalonians")
  501. ("I Th" . "I Thessalonians") ("1 Th" . "I Thessalonians")
  502. ("IThess" . "I Thessalonians") ("1Thes" . "I Thessalonians")
  503. ("1Thess" . "I Thessalonians")
  504. ("2 Thess" . "II Thessalonians") ("2 Thessalonians" . "II Thessalonians")
  505. ("II Th" . "II Thessalonians") ("2 Th" . "II Thessalonians")
  506. ("IIThess" . "II Thessalonians") ("2Thes" . "II Thessalonians")
  507. ("2Thess" . "II Thessalonians")
  508. ("1 Tim" . "I Timothy") ("1 Timothy" . "I Timothy")
  509. ("I Ti" . "I Timothy") ("1 Ti" . "I Timothy") ("ITim" . "I Timothy")
  510. ("1Tim" . "I Timothy")
  511. ("2 Tim" . "II Timothy") ("2 Timothy" . "II Timothy")
  512. ("II Ti" . "II Timothy") ("2 Ti" . "II Timothy") ("IITim" . "II Timothy")
  513. ("2Tim" . "II Timothy")
  514. ("Tit" . "Titus")
  515. ("Phlm" . "Philemon") ("Phm" . "Philemon") ("Plm" . "Philemon")
  516. ("Heb" . "Hebrews") ("He" . "Hebrews")
  517. ("Jas" . "James") ("Ja" . "James") ("Jam" . "James")
  518. ("1 Pet" . "I Peter") ("1 Peter" . "I Peter") ("I Pe" . "I Peter")
  519. ("1 Pe" . "I Peter") ("IPet" . "I Peter") ("1Pet" . "I Peter")
  520. ("2 Pet" . "II Peter") ("2 Peter" . "II Peter") ("II Pe" . "II Peter")
  521. ("2 Pe" . "II Peter") ("IIPet" . "II Peter") ("2Pet" . "II Peter")
  522. ("1 Jn" . "I John") ("1 John" . "I John") ("I Jo" . "I John")
  523. ("1 Jo" . "I John") ("IJohn" . "I John") ("1Jn" . "I John")
  524. ("2 Jn" . "II John") ("2 John" . "II John") ("II Jo" . "II John")
  525. ("2 Jo" . "II John") ("IIJohn" . "II John") ("2Jn" . "II John")
  526. ("3 Jn" . "III John") ("3 John" . "III John") ("III Jo" . "III John")
  527. ("3 Jo" . "III John") ("IIIJohn" . "III John") ("3Jn" . "III John")
  528. ("Ju" . "Jude") ("Jde" . "Jude")
  529. ("Rev" . "Revelation of John") ("Re" . "Revelation of John"))
  530. "A-list of abbreviations for Bible books.")
  531. ;;;;; Book / chapter
  532. (defvar-local bible--current-book-entry (assoc "Genesis" bible--books)
  533. "Current book data (name . chapter).")
  534. (defvar-local bible--current-book-name "Genesis"
  535. "Current book name.")
  536. (defvar-local bible--current-chapter 1
  537. "Current book chapter number.")
  538. ;;;;; Search / query
  539. (defvar-local bible-query nil
  540. "Search query associated with the buffer.")
  541. (defvar-local bible-search-mode "phrase"
  542. "Search mode: either `lucene', `phrase', `regex' or `multiword'.")
  543. (defvar bible-search-range nil)
  544. ;;;;; Lexemes / morphemes
  545. (defvar-local bible-term-language nil
  546. "Displaying terms of this language.")
  547. (defvar-local bible-term-lemma nil
  548. "Lemma for term mode line.")
  549. (defvar-local bible-has-lexemes nil
  550. "Set if the module being displayed has lexical entries availabile.")
  551. (defvar-local bible-has-morphemes nil
  552. "Set if the module being displayed has morphemes availabile.")
  553. (defvar-local bible-text-direction 'left-to-right)
  554. (defvar-local bible-debugme nil
  555. "Make text show up as XML when set.")
  556. (defvar bible-use-tooltips t)
  557. (setq tooltip-delay 1)
  558. (setq tooltip-short-delay .5)
  559. (setq use-system-tooltips nil)
  560. (defvar-local bible-search-query nil
  561. "Query used in toggles (word study and red letter).")
  562. (defvar-local bible-chapter-title nil
  563. "Text preceding start of chapter.
  564. Mostly in Psalms, like `Of David' or the like.")
  565. (defvar-local bible-level "0"
  566. "Used by some modules for indentation and line breaks.")
  567. ;;;; Greek and Hebrew lexeme and morpheme tooltip rendering.
  568. ;;;;; Hash tables for Lexical definitions.
  569. (defvar bible-hash-greek (make-hash-table :test 'equal))
  570. (defvar bible-hash-hebrew (make-hash-table :test 'equal))
  571. ;;;;; Hash table for short lexical definitions for tooltips.
  572. (defvar lex-hash (make-hash-table :test 'equal))
  573. ;;;;; Hash table for morphological items.
  574. (defvar morph-hash (make-hash-table :test 'equal))
  575. (defvar bible-outline-strings
  576. '(;;(". ." . ".")
  577. (" I. ." . "\nI.")
  578. (" .I. " . "\nI.")
  579. ("1. ." . "\n 1.")
  580. ("a. ." . "\n a.")
  581. ("(a)" . "\n (a)")
  582. ("(α)" . "\n (α)")
  583. (" II. ." . " II.")
  584. (" III. ." . " III.")
  585. (" IV. ." . " IV.")
  586. (" V. ." . " V.")
  587. ("2. ." . "2.")
  588. ("3. ." . "3.")
  589. ("4. ." . "4.")
  590. ("5. ." . "5.")
  591. ("6. ." . "6.")
  592. ("7. ." . "7.")
  593. ("8. ." . "8.")
  594. ("9. ." . "9.")
  595. ("b. ." . " b.")
  596. ("c. ." . " c.")
  597. ("d. ." . " d.")
  598. ("e. ." . " e.")
  599. ("f. ." . " f.")
  600. ("g. ." . " g.")
  601. ("h. ." . " h.")
  602. (" . " . ". ")
  603. (".." . ".")
  604. ("\n\n" . "\n"))
  605. "String replacements to clean up outlines in lexicon entries.")
  606. ;;;;; Variables for mode-line-format for search buffers.
  607. (defvar-local bible-search-word-this-query "")
  608. (defvar-local bible-search-text-this-query "")
  609. (defvar-local bible-search-range-this-query nil)
  610. (defvar-local bible-search-matches 0)
  611. (defvar bible-reference-buffer nil)
  612. ;;;;; Variables used in constructing cross-references.
  613. (defvar-local bible-current-xref-book nil)
  614. (defvar-local bible-current-xref-chapter nil)
  615. ;;;; Functions
  616. ;;;;; Keymap helpers
  617. (defun bible-toggle-display-diatheke ()
  618. "Toggle diatheke args display."
  619. (interactive)
  620. (setq bible-show-diatheke-exec (not bible-show-diatheke-exec))
  621. (message ""))
  622. (defun bible-next-search-item ()
  623. "Go to next item in list of found verses."
  624. (interactive)
  625. (search-forward-regexp bible--verse-regexp nil t))
  626. (defun bible-previous-search-item ()
  627. "Go to previous item in list of found verses."
  628. (interactive)
  629. (when (search-backward-regexp bible--verse-regexp nil t)
  630. (beginning-of-line)))
  631. (defun bible-toggle-display-xml ()
  632. "Toggle XML display."
  633. (interactive)
  634. (setq-local bible-debugme (not bible-debugme))
  635. (bible--display)
  636. (goto-char (point-min)))
  637. (defun bible-toggle-text-direction ()
  638. "Switch between left-to-right and right-to-left text direction."
  639. (interactive)
  640. (if (eq bible-text-direction 'left-to-right)
  641. (setq-local bible-text-direction 'right-to-left)
  642. (setq-local bible-text-direction 'left-to-right))
  643. (setq-local bidi-paragraph-direction bible-text-direction))
  644. (defun bible-toggle-tooltips ()
  645. "Toggle use of tooltips to display lexical/morphological items."
  646. (interactive)
  647. (setq bible-use-tooltips (not bible-use-tooltips))
  648. (tooltip-mode 'toggle)
  649. (setq tooltip-resize-echo-area (not bible-use-tooltips))
  650. (setq bible-show-diatheke-exec (and bible-show-diatheke-exec bible-use-tooltips)) ; Don't conflict with echo area
  651. (message ""))
  652. ;;;;; Commands (interactive)
  653. ;;;###autoload
  654. (defun bible-open (&optional book-name chapter verse module buffer)
  655. "Create and open a `bible' buffer.
  656. Optional arguments BOOK-NAME, CHAPTER and VERSE, when supplied, give the
  657. starting verse reference for the buffer. If no optional location
  658. arguments are supplied, Genesis 1:1 is used. Optional argument MODULE
  659. specifies the module to use. Optional argument BUFFER will use that
  660. buffer instead of creating a new one."
  661. (interactive)
  662. (with-current-buffer (or buffer (get-buffer-create "*bible*"))
  663. (bible)
  664. (when module (setq-local bible-text module))
  665. (setq-local bible-buffer-type 'text
  666. mode-name (concat "Text " bible-text))
  667. (bible--set-location
  668. (assoc (or book-name "Genesis") bible--books)
  669. (or chapter 1)
  670. (or verse 1))
  671. (cl-pushnew (current-buffer) bible--text-buffers)
  672. (set-window-buffer (get-buffer-window (current-buffer)) (current-buffer))))
  673. (defun commentary-open (&optional book-name chapter verse module buffer)
  674. "Create and open a `commentary' buffer.
  675. Optional argument MODULE specifies the commentary module to use.
  676. Optional arguments BOOK-NAME, CHAPTER and VERSE, when supplied, give the
  677. starting verse reference for the buffer. If no optional location
  678. arguments are supplied, Genesis 1:1 is used.
  679. Optional argument BUFFER gives a buffer into which to render the text."
  680. (interactive)
  681. (let ((old-buffer (current-buffer)))
  682. (unless old-buffer (error "No current buffer! How did this happen!"))
  683. (with-current-buffer (or buffer (get-buffer-create "*comm*"))
  684. (bible)
  685. (when module
  686. (setq-local bible-commentary module))
  687. (setq-local bible-buffer-type 'commentary
  688. bible-text bible-commentary
  689. mode-name (concat "Commentary " bible-text)
  690. ;; This is used for chasing cross-references.
  691. bible-associated-buffer (cl-first bible--text-buffers))
  692. (bible--set-location
  693. (assoc (or book-name "Genesis") bible--books)
  694. (or chapter 1)
  695. (or verse 1))
  696. (cl-pushnew (current-buffer) bible--commentary-buffers)
  697. (set-window-buffer (get-buffer-window (current-buffer)) (current-buffer)))))
  698. ;;;;;; Navigation
  699. (defun bible--do-set-location (book chapter &optional verse)
  700. (setq-local bible--current-book-name book)
  701. (setq-local bible--current-chapter chapter)
  702. (bible--display (or verse 1)))
  703. (defun bible--set-location (book chapter &optional verse)
  704. "Set the BOOK, CHAPTER and optionally VERSE of the active `bible' buffer.
  705. If there are synchronized buffers, set their locations as well."
  706. (let ((buffer (current-buffer)))
  707. (setq-local bible--current-book-entry book)
  708. (bible--do-set-location (car book) chapter (or verse 1))
  709. ;; Handle synchronized buffers.
  710. (when (cl-find buffer bible--synced-buffers)
  711. (dolist (buf bible--synced-buffers)
  712. (unless (eq buf buffer)
  713. (with-current-buffer buf
  714. (bible--do-set-location (car book) chapter (or verse 1))))))))
  715. (defun bible-next-chapter ()
  716. "Page to the next chapter for the active `bible' buffer and
  717. for any synchronized buffers."
  718. (interactive)
  719. (let* ((book-chapters (cdr bible--current-book-entry))
  720. (chapter (min book-chapters (1+ bible--current-chapter))))
  721. (bible--set-location bible--current-book-entry chapter)))
  722. (defun bible-previous-chapter ()
  723. "Page to the previous chapter for the active `bible' buffer and
  724. for any synchronized buffers."
  725. (interactive)
  726. (let ((chapter (max 1 (1- bible--current-chapter))))
  727. (bible--set-location bible--current-book-entry chapter)))
  728. (defun bible-next-word (next-word)
  729. "Move forward a word, taking into account the relevant text properties.
  730. Stay on the word so that clicking or hitting return will bring up the
  731. term dispplay for that word."
  732. (interactive (list (unless (eobp) (text-property-search-forward 'strong nil nil t))))
  733. (when next-word (goto-char (1- (prop-match-end next-word)))))
  734. (defun bible-previous-word (previous-word)
  735. "Move back a word, taking into account the relevant text properties.
  736. Stay on the word so that clicking or hitting return will bring up the
  737. term dispplay for that word."
  738. (interactive (list (unless (bobp) (text-property-search-backward 'strong))))
  739. (when previous-word (goto-char (prop-match-beginning previous-word))))
  740. ;;;;;; Select Location
  741. (defun bible-select-book ()
  742. "Ask user for a new book and chapter for the current `bible' buffer."
  743. (interactive)
  744. (let* ((completion-ignore-case t)
  745. (book-data (assoc (completing-read "Book: " bible--books nil t) bible--books))
  746. (book-data-string (car book-data))
  747. (chapter (string-to-number
  748. (completing-read
  749. "Chapter [1]: "
  750. (bible--list-number-range 1 (cdr book-data)) nil t nil nil "1"))))
  751. (pcase (aref book-data-string 0)
  752. (?1 (setq book-data (cons (concat "I" (substring book-data-string 1)) (cdr book-data))))
  753. (?2 (setq book-data (cons (concat "II" (substring book-data-string 1)) (cdr book-data))))
  754. (?3 (setq book-data (cons (concat "III" (substring book-data-string 1)) (cdr book-data)))))
  755. (bible--set-location book-data chapter)))
  756. (defun bible-select-chapter ()
  757. "Ask user for a new chapter for the current `bible' buffer."
  758. (interactive)
  759. (let* ((book-chapters (cdr bible--current-book-entry))
  760. (chapter (string-to-number
  761. (completing-read
  762. "Chapter [1]: "
  763. (bible--list-number-range 1 book-chapters) nil t nil nil "1"))))
  764. (when chapter
  765. (bible--set-location bible--current-book-entry chapter))))
  766. ;;;;;; Choose modules, either by keystroke or by choosing from a list.
  767. (defun bible-pick-text-module ()
  768. "Select a text from a list. Re-use the buffer listing the texts. Called
  769. by the menu item `Select Text'."
  770. (interactive)
  771. (let ((item (get-text-property (point) 'module)))
  772. (rename-buffer "*bible*" t)
  773. (bible-open bible--current-book-name
  774. bible--current-chapter
  775. 1
  776. item
  777. (current-buffer))))
  778. (defun bible-pick-commentary-module ()
  779. "Select a commentary from a list. Re-use the buffer listing the commentaries.
  780. Called by the menu item `Select Commentary'."
  781. (interactive)
  782. (let ((item (get-text-property (point) 'module)))
  783. (rename-buffer "*comm*" t)
  784. (commentary-open bible--current-book-name
  785. bible--current-chapter
  786. 1
  787. item
  788. (current-buffer))))
  789. (defun bible--new-window (name)
  790. "Open a new window and generate a new buffer with NAME.
  791. Return the new buffer."
  792. (split-window-right)
  793. (balance-windows)
  794. (other-window 1)
  795. (generate-new-buffer name))
  796. (defun bible-select-text-module (text)
  797. "Prompt for a new text module for the current `bible' buffer.
  798. Replace current text in buffer. With prefix argument or if called from a
  799. commentary window, use new buffer and new window."
  800. (interactive (list (completing-read "Text: " bible--texts)))
  801. (unless (string= text "")
  802. (with-current-buffer (if (or current-prefix-arg (eq bible-buffer-type 'commentary))
  803. (bible--new-window "*bible*")
  804. (current-buffer))
  805. (setq-local bible-text text)
  806. (bible-open bible--current-book-name
  807. bible--current-chapter
  808. 1
  809. text
  810. (current-buffer)))))
  811. (defun bible-select-commentary-module (commentary)
  812. "Promp for a new commentary module for the current `commentary' buffer.
  813. Replace current text in buffer. With prefix argument or if called from a
  814. text window, split window and put commentary in a new buffer in a new
  815. window."
  816. (interactive (list (completing-read "Commentary: " bible--commentaries)))
  817. (unless (string= commentary "")
  818. (with-current-buffer (if (or current-prefix-arg (eq bible-buffer-type 'text))
  819. (bible--new-window "*comm*")
  820. (current-buffer))
  821. (setq-local bible-commentary commentary)
  822. (commentary-open bible--current-book-name
  823. bible--current-chapter
  824. 1
  825. commentary
  826. (current-buffer)))))
  827. ;;;;;; Toggles
  828. (defun bible-toggle-word-study ()
  829. "Toggle the inclusion of word study for the active `bible' buffer."
  830. (interactive)
  831. (setq bible-word-study-enabled (not bible-word-study-enabled))
  832. (bible--display))
  833. (defun bible-toggle-red-letter ()
  834. "Toggle red letter mode for the active `bible' buffer."
  835. (interactive)
  836. (setq bible-red-letter-enabled (not bible-red-letter-enabled))
  837. (bible--display))
  838. (defun bible-toggle-buffer-sync ()
  839. "Either add or remove the current buffer from the
  840. `bible--synced-buffers' list."
  841. (interactive)
  842. (let ((buffer (current-buffer)))
  843. (if bible--synced-p
  844. (progn
  845. (setq bible--synced-buffers (cl-delete buffer bible--synced-buffers))
  846. (setq-local bible--synced-p nil))
  847. (cl-pushnew buffer bible--synced-buffers)
  848. (setq-local bible--synced-p t))
  849. (force-mode-line-update)))
  850. ;;(defun bible-split-display ()
  851. ;; "Copy the active `bible' buffer into a new buffer in another window."
  852. ;; (interactive)
  853. ;; (split-window-right)
  854. ;; (balance-windows)
  855. ;; (other-window 1)
  856. ;; (bible-open bible-text
  857. ;; bible--current-book-name
  858. ;; bible--current-chapter
  859. ;; 1
  860. ;; (generate-new-buffer "*bible*")))
  861. (defun bible-split-display ()
  862. "Copy the active `bible' buffer into a new buffer in another window."
  863. (interactive)
  864. (bible-open bible--current-book-name
  865. bible--current-chapter
  866. 1
  867. bible-text
  868. (bible--new-window "*bible*")))
  869. ;;;;;; Search helpers
  870. (defun bible-set-search-range ()
  871. "Ask user for a new text module for the current `bible' buffer."
  872. (interactive)
  873. (let ((range (read-string "Range (<return> to clear): ")))
  874. (if (string-equal range "")
  875. (setq bible-search-range nil)
  876. (setq bible-search-range range))))
  877. (defun bible-search (query searchmode)
  878. "Search for a QUERY with search mode SEARCHMODE. Use one of the diatheke
  879. search modes. See diatheke for more information.
  880. First ask for the query and then ask the user for search mode: either
  881. `lucene', `phrase', `regex' or `multiword'. `lucene' is the default search.
  882. `lucene' mode requires an index to be built for the module being searched
  883. using the `mkfastmod' program."
  884. (interactive (let ((query (read-string "Query: ")))
  885. (if (> (length query) 0)
  886. (list query (completing-read "Search Mode: " '("lucene" "phrase" "regex" "multiword") nil t "lucene"))
  887. (list nil nil))))
  888. (when (> (length query) 0)
  889. (setq bible-reference-buffer (current-buffer))
  890. (bible--open-search query searchmode (buffer-local-value 'bible-text (current-buffer)))))
  891. (defun bible--lookup-name (name)
  892. "Find the canonical book entry for NAME in bible--books. First check in
  893. the bible--books list to see if NAME is the canonical name. If not,
  894. check if NAME is an abbreviation. If so, look up the abbreviation and
  895. get the canonical name from there. Use the canonical name to look up the
  896. book entry."
  897. (or (cl-find name
  898. bible--books :key #'car :test #'string-equal-ignore-case)
  899. (cl-find (alist-get name bible--book-name-abbreviations nil nil #'string-equal-ignore-case)
  900. bible--books :key #'car :test #'string-equal-ignore-case)))
  901. (defun bible-follow-verse (verse-ref)
  902. "Follow VERSE-REF obtained from a bible search mode buffer.
  903. Create a new `bible' buffer positioned at the selected verse."
  904. (interactive (list (thing-at-point 'line t)))
  905. (when (string-match bible--verse-regexp verse-ref)
  906. (let* ((ref-string (match-string 0 verse-ref))
  907. (book (when (string-match "I?I?I? ?[A-Z]?[a-z]* " ref-string)
  908. (string-trim (match-string 0 ref-string))))
  909. (chapter (when (string-match "[0-9]?[0-9]?[0-9]?:" ref-string)
  910. (string-to-number
  911. (substring ref-string (match-beginning 0) (1- (match-end 0))))))
  912. (verse (when (string-match ":[0-9]?[0-9]?[0-9]?" ref-string)
  913. (string-to-number (substring (match-string 0 ref-string) 1)))))
  914. (pop-to-buffer bible-reference-buffer)
  915. (bible--set-location (assoc book bible--books) chapter verse))))
  916. (defun bible-follow-xref (verse-ref)
  917. "Follow VERSE-REF in a bible term or commentary buffer.
  918. Create a new `bible' buffer positioned at the specified verse.
  919. Handle abbreviations."
  920. (interactive (list (split-string (get-text-property (point) 'xref))))
  921. (when verse-ref
  922. (let* (book-abbrev
  923. chapter-verse)
  924. (cond ((= (length verse-ref) 2) ; Mat 5 or the like
  925. (setq book-abbrev (cl-first verse-ref)
  926. chapter-verse (split-string (cadr verse-ref) ":")))
  927. ((= (length verse-ref) 3) ; II Cor 3:17 or the like
  928. (setq book-abbrev (concat (cl-first verse-ref) " " (cl-second verse-ref))
  929. chapter-verse (split-string (cl-third verse-ref) ":")))
  930. (t (error "Invalid verse ref %s" verse-ref)))
  931. ;; Use book abbreviation if present or try whatever is in verse-ref.
  932. (let* ((book-entry (bible--lookup-name book-abbrev))
  933. (chapter (string-to-number (cl-first chapter-verse)))
  934. (verse (string-to-number (cl-second chapter-verse))))
  935. (pop-to-buffer (or bible-associated-buffer (cl-first bible--text-buffers)))
  936. (bible--set-location book-entry chapter verse)))))
  937. ;;;;;; User visible actions.
  938. ;; These can be called interactively if you know the Strong's number
  939. ;; you want to look up.
  940. ;;;###autoload
  941. (defun bible-term-hebrew (term)
  942. "Query user for a Strong's Hebrew Lexicon TERM."
  943. (interactive "sTerm: ")
  944. (bible--open-term-hebrew term))
  945. ;;;###autoload
  946. (defun bible-term-greek (term)
  947. "Query user for a Strong's Greek Lexicon TERM."
  948. (interactive "sTerm: ")
  949. (bible--open-term-greek term))
  950. ;; Interactively insert a verse into an arbitrary current buffer.
  951. ;;;###autoload
  952. (defun bible-insert ()
  953. "Query user to select a verse for insertion into the current buffer."
  954. (interactive)
  955. (let* ((completion-ignore-case t)
  956. (book-data (assoc (completing-read "Book: " bible--books nil t) bible--books))
  957. (chapter (when book-data (completing-read "Chapter: " (bible--list-number-range 1 (cdr book-data)) nil t "1" nil "1")))
  958. (verse (when chapter (read-from-minibuffer "Verse: ")))
  959. (query (concat (car book-data) " " chapter ":" verse))
  960. (args (list bible-sword-query nil (current-buffer) t "-b" bible-text "-f" "plain" "-k" query)))
  961. (apply #'call-process args)))
  962. ;;;;;; Support (internal)
  963. ;;;;;;; Diatheke interface
  964. (defun bible--exec-diatheke (query &optional filter format module)
  965. "Execute `diatheke' with specified QUERY options.
  966. FILTER is the Diatheke filter argument. FORMAT is either plain or
  967. the default of internal. MODULE is the text module to use. Returns
  968. string containing query result."
  969. (let ((module (or module bible-text)))
  970. (with-temp-buffer
  971. (let ((args (list bible-sword-query nil (current-buffer) t "-b" module)))
  972. (if filter
  973. (setq filter (concat filter bible-diatheke-filter-options))
  974. (setq filter bible-diatheke-filter-options))
  975. (setq args (append args (list "-o" filter)))
  976. (setq args (append args (list "-f" (pcase format ("plain" "plain") (_ "internal")) "-k" query)))
  977. (when bible-show-diatheke-exec
  978. (message "%s" args))
  979. (apply #'call-process args))
  980. (buffer-string))))
  981. (defun bible--diatheke-search (query searchtype &optional format module)
  982. "Execute `diatheke' on QUERY with SEARCHTYPE.
  983. Optional argument FORMAT is either plain or the default of internal.
  984. MODULE is the text module to use and defaults to the current module."
  985. (with-temp-buffer
  986. (let ((args (list bible-sword-query nil (current-buffer) t "-b" (or module bible-text))))
  987. (setq args (append args (list "-s" (pcase searchtype
  988. ("lucene" "lucene")
  989. ("phrase" "phrase")
  990. ("regex" "regex")
  991. ("multiword" "multiword")))))
  992. (when bible-search-range (setq args (append args (list "-r" bible-search-range))))
  993. (setq args (append args (list "-f" (pcase format ("plain" "plain") (_ "internal")) "-k" query)))
  994. (when bible-show-diatheke-exec
  995. (message "%s" args))
  996. (apply #'call-process args))
  997. (buffer-string)))
  998. ;; Use HTMLHREF format with diatheke, post-process to render html.
  999. (defun bible--morph-query (query module)
  1000. "Execute `diatheke' to do morph QUERY, using MODULE.
  1001. Render html, return string. Do some tweaking specific to morphology."
  1002. (with-temp-buffer
  1003. (let ((args (list bible-sword-query nil (current-buffer) t "-b" module "-o" "m" "-f" "HTMLHREF" "-k" query)))
  1004. (when bible-show-diatheke-exec
  1005. (message "%s" args))
  1006. (apply #'call-process args)
  1007. (shr-render-region (point-min) (point-max))
  1008. (format-replace-strings
  1009. '(("\n:" . "") ; This makes the Packard morphology display look better.
  1010. ("Part of Speech" . "")) ; This helps the Robinson display look better.
  1011. nil (point-min) (point-max))
  1012. (substring (buffer-string) (1+ (length query)))))) ; This tries to get rid of unnecessary query identifier.
  1013. ;; Use "plain" format with diatheke.
  1014. (defun bible--lex-query (query module)
  1015. "Execute `diatheke' for QUERY, using MODULE.
  1016. Plain format, returns string."
  1017. (bible--exec-diatheke query nil "plain" module))
  1018. ;;;;;; Lexicon processing
  1019. ;; The Greek lexical definitions are done using the HTMLHREF output
  1020. ;; format so they come out looking nice and having clickable
  1021. ;; cross-references and/or Strong's references.
  1022. (defun bible--process-href ()
  1023. "Fix the XML so cross-references are in the right format.
  1024. These cross-references get processed later when the term is displayed.
  1025. First, find the links put in by diatheke's HTMLHREF output format.
  1026. Replace the links with verse references that get changed to clickable
  1027. cross-references when the term is displayed.
  1028. The verse refs look like this: <bookname>.<chapter>.<verse>. Convert
  1029. them to the <bookname> <chapter>:<verse> format."
  1030. (goto-char (point-min))
  1031. (while (re-search-forward "<a href=\"passagestudy.*?</a>" nil t) ; HTMLHREF cross references.
  1032. (let ((match-text (match-string 0)))
  1033. ;; Delete original link.
  1034. (replace-match "" nil nil)
  1035. ;; Get the verse reference from the string we saved. Put it in
  1036. ;; good format, then insert it into buffer where href was.
  1037. (when (string-match "value=.*?&" match-text)
  1038. (let* ((value-string (match-string 0 match-text))
  1039. ;; Strip off value= and trailing &.
  1040. (verse-ref-string (substring value-string 6 (1- (length value-string))))
  1041. (verse-ref-length (length verse-ref-string))
  1042. period)
  1043. ;; Convert periods
  1044. ;; Substitute first period with space
  1045. (when (setq period (cl-search "." verse-ref-string))
  1046. (aset verse-ref-string period ? ))
  1047. ;; Substitute second period with colon
  1048. (when (setq period (cl-search "." verse-ref-string))
  1049. (aset verse-ref-string period ?:))
  1050. ;; Replace numbers (1, 2 or 3) with roman numerals (I, II, III).
  1051. (pcase (aref verse-ref-string 0)
  1052. (?1 (setq verse-ref-string (concat "I" (substring verse-ref-string 1))))
  1053. (?2 (setq verse-ref-string (concat "II" (substring verse-ref-string 1))))
  1054. (?3 (setq verse-ref-string (concat "III" (substring verse-ref-string 1)))))
  1055. (set-text-properties 0 verse-ref-length nil verse-ref-string) ; Clear unwanted properties (if any)
  1056. (insert verse-ref-string))))))
  1057. (defun bible--cleanup-lex-text (lex-text)
  1058. "Reformat lexical entry text LEX-TEXT to look nice."
  1059. (let ((return-string (substring lex-text)))
  1060. (dolist (outline-string bible-outline-strings return-string)
  1061. (setq return-string (string-replace (car outline-string) (cdr outline-string) return-string)))))
  1062. (defun bible--lookup-def-greek (key)
  1063. "Execute `diatheke' to do query on KEY.
  1064. Massage output so verse cross references are usable. Returns string."
  1065. (with-temp-buffer
  1066. (let ((args (list bible-sword-query nil (current-buffer) t "-b" bible-greek-lexicon "-f" "plain" "-k" key)))
  1067. (when bible-show-diatheke-exec
  1068. (message "%s" args))
  1069. (apply #'call-process args)
  1070. (bible--cleanup-lex-text
  1071. (bible--remove-module-name
  1072. bible-greek-lexicon
  1073. (buffer-string))))))
  1074. (defun bible--lookup-lemma-index (key)
  1075. "Return the Greek lemma from lemma index with a strong's number as KEY."
  1076. (string-trim
  1077. (bible--remove-module-name bible-lexicon-index (bible--lex-query key bible-lexicon-index))))
  1078. (defun bible--lookup-entry-greek (key)
  1079. "Lookup Greek lexicon entry using Strong's number KEY.
  1080. If the lexicon has a lemma index, first use the Strong's number
  1081. to look up the lemma. Then look up the lexicon entry of that lemma."
  1082. (let ((lex-key key))
  1083. (when bible-use-index-for-lexicon
  1084. (let ((lemma-entry (bible--lookup-lemma-index key))) ; Get lemma from Strong's number
  1085. (when lemma-entry
  1086. ;; Use lemma as key.
  1087. (setq lex-key (caddr (split-string lemma-entry " "))))))
  1088. (bible--lookup-def-greek lex-key)))
  1089. (defun bible--lookup-lemma-greek (key)
  1090. "Lookup lexical definition using Strong's number KEY.
  1091. Check hash table first. If entry found, return it. Otherwise
  1092. do the diatheke lookup."
  1093. (or (gethash key bible-hash-greek)
  1094. (puthash key (bible--lookup-entry-greek key) bible-hash-greek)))
  1095. (defun bible--lookup-def-hebrew (key)
  1096. "Execute `diatheke' to do query on KEY.
  1097. Massage output so various cross references are usable. Return resulting
  1098. string."
  1099. (with-temp-buffer
  1100. (let ((args (list bible-sword-query nil (current-buffer) t "-b" bible-hebrew-lexicon "-f" "plain" "-k" key)))
  1101. (when bible-show-diatheke-exec
  1102. (message "%s" args))
  1103. (apply #'call-process args)
  1104. (bible--process-href)
  1105. (concat (string ?\x200e)
  1106. (bible--remove-module-name bible-hebrew-lexicon (substring (buffer-string) 7))))))
  1107. (defun bible--lookup-lemma-hebrew (key)
  1108. "Lookup lexical definition using Strong's number KEY.
  1109. Check hash table first. If entry found, return it. Otherwisde
  1110. do the diatheke lookup."
  1111. (or (gethash key bible-hash-hebrew)
  1112. (puthash key (bible--lookup-def-hebrew key) bible-hash-hebrew)))
  1113. ;; Use the shorter lexicons for text in tooltips. Cache the lex and
  1114. ;; morph strings, hoping to speed up tooltip rendering.
  1115. (defun bible--lookup-lemma-short (lemma lexicon)
  1116. "Look up lexical entry for LEMMA in `short' LEXICON.
  1117. Return a string that is intended to be displayed in a tooltip."
  1118. (when (string-match "[0-9]+" lemma)
  1119. (let ((string
  1120. (bible--remove-module-name
  1121. lexicon
  1122. ;; Get rid of unnecessary strongs codes at the beginning.
  1123. (substring (bible--lex-query (concat (match-string 0 lemma)) lexicon) 7))))
  1124. string)))
  1125. (defun bible--lookup-lex (lex)
  1126. "Look up lexical item LEX. This is used for tooltips.
  1127. Return hash table entry if present in `lex-hash' cache, else look up in
  1128. database and stash in cache."
  1129. (let* ((key (substring lex 7)) ; strip off "strong:" prefix.
  1130. (entry (gethash key lex-hash)))
  1131. (unless entry
  1132. (setq entry
  1133. (cond ((string-prefix-p "G" key)
  1134. (bible--lookup-lemma-short key bible-greek-lexicon-short))
  1135. ((string-prefix-p "H" key)
  1136. ;; Force left-to-right for tooltips.
  1137. (concat (string ?\x200e)
  1138. (bible--lookup-lemma-short key bible-hebrew-lexicon-short)))))
  1139. (puthash key (string-fill (bible--cleanup-lex-text entry) 75) lex-hash))
  1140. entry))
  1141. (defun bible--lookup-morph (morph)
  1142. "Look up entry for morphological item MORPH.
  1143. Return hash table entry if present in `morph-hash' cache, else look up in
  1144. database and stash in cache."
  1145. (or (gethash morph morph-hash)
  1146. (puthash morph
  1147. (let (morph-module morph-key)
  1148. ;; We know about these modules. (Assume they're installed.)
  1149. (cond ((string-prefix-p "robinson:" morph)
  1150. (setq morph-module "Robinson")
  1151. (setq morph-key (substring morph (length "robinson:"))))
  1152. ((string-prefix-p "packard:" morph)
  1153. (setq morph-module "Packard")
  1154. (setq morph-key (substring morph (length "packard:"))))
  1155. ((string-prefix-p "oshm:" morph)
  1156. (setq morph-module "OSHM")
  1157. (setq morph-key (substring morph (length "oshm:")))))
  1158. (bible--remove-module-name morph-module (bible--morph-query morph-key morph-module)))
  1159. morph-hash)))
  1160. ;; Get string for tooltip display
  1161. (defun bible--show-lex-morph (_window object pos)
  1162. "Get text for tooltip display for OBJECT at POS in WINDOW.
  1163. Include both lex and morph definitions if text module has both tags,
  1164. otherwise just get lex definition."
  1165. (let* ((lex (get-text-property pos 'strong object))
  1166. (lex-text (and lex (bible--lookup-lex lex)))
  1167. (morph (get-text-property pos 'morph object))
  1168. (morph-text (and morph (bible--lookup-morph morph))))
  1169. (when lex-text
  1170. (setq lex-text
  1171. (if morph-text
  1172. (concat (string-trim lex-text) "\n" (string-trim morph-text))
  1173. (string-trim lex-text)))
  1174. ;; Don't try to do command substitution in tooltip.
  1175. (put-text-property 0 1 'help-echo-inhibit-substitution t lex-text)
  1176. lex-text)))
  1177. ;;;; Display module text
  1178. (defun bible-handle-divine-name (item)
  1179. "When ITEM is divine name, display it as such."
  1180. (let ((start (point))
  1181. (strongs (dom-attr item 'savlm)))
  1182. (insert "LORD")
  1183. (let ((end (point)))
  1184. (add-face-text-property start end 'bold)
  1185. (put-text-property start end 'keymap bible-hebrew-keymap)
  1186. (when (and strongs (string-match "strong:H" strongs))
  1187. (put-text-property start end 'help-echo 'bible--show-lex-morph)
  1188. (put-text-property start end 'strong (match-string 0 strongs))))))
  1189. (defun bible--process-word (item iproperties)
  1190. "Handle <w ...> fubar </w> tag in ITEM. Check IPROPERTIES for qualifiers.
  1191. Add tooltips for definitions and morphology. Also insert lemmas in
  1192. buffer if `word study' is turned on (must be done after item is inserted
  1193. in buffer)."
  1194. (let ((word (string-trim (bible-dom-text item)))
  1195. (morph (dom-attr item 'morph))
  1196. (savlm (dom-attr item 'savlm))
  1197. (lemma (dom-attr item 'lemma))
  1198. (divinename (dom-by-tag item 'divinename)))
  1199. (let ((start (point))
  1200. (end (+ (point) (length word))))
  1201. (insert word)
  1202. ;; REVIEW: Special case this. Some modules do this differently.
  1203. ;; (FMG 5-Mar-2026)
  1204. (when divinename
  1205. (just-one-space)
  1206. (bible-handle-divine-name item)
  1207. (just-one-space))
  1208. ;; Red letter.
  1209. (when (plist-get iproperties 'jesus)
  1210. (add-face-text-property start end '(:foreground "red")))
  1211. ;; lexical definitions
  1212. ;; N.B. There are some severe issues with Strongs numbers in some modules.
  1213. (when (or savlm lemma)
  1214. (let* ((matched nil)
  1215. (lexemes (split-string (or savlm lemma)))
  1216. (lexeme
  1217. ;; HACK: Kludge alert. KJV module conflates Greek
  1218. ;; articles with nouns. Deal with this.
  1219. ;; (FMG 5-Mar-2026)
  1220. (let ((lexeme-list
  1221. (if (string= bible-text "KJV")
  1222. (reverse lexemes) ; Use the last `strong:' entry.
  1223. lexemes)))
  1224. (catch 'loop
  1225. (dolist (item lexeme-list)
  1226. (when (string-prefix-p "strong:" item)
  1227. (throw 'loop item)))))))
  1228. (when lexeme
  1229. (cond ((string-match "strong:G.*" lexeme) ; Greek
  1230. (setq matched (match-string 0 lexeme))
  1231. (put-text-property start end 'keymap bible-greek-keymap))
  1232. ((string-match "strong:H.*" lexeme) ; Hebrew
  1233. (setq matched (match-string 0 lexeme))
  1234. (put-text-property start end 'keymap bible-hebrew-keymap)))
  1235. ;; Add help-echo, strongs reference for tooltips if match.
  1236. (when matched
  1237. (setq-local bible-has-lexemes " Lex")
  1238. (put-text-property start end 'help-echo 'bible--show-lex-morph)
  1239. (put-text-property start end 'strong matched))))
  1240. ;; morphology
  1241. (when morph
  1242. (let* ((morphemes (split-string morph))
  1243. (morpheme (car (last morphemes)))) ; KJV kludge as above
  1244. (when (or
  1245. (string-match "robinson:.*" morpheme) ; Robinson Greek morphology
  1246. (string-match "packard:.*" morpheme) ; Packard Greek morphology --- LXX seems to use this
  1247. (string-match "oshm:.*" morpheme)) ; OSHM Hebrew morphology
  1248. (setq-local bible-has-morphemes " Morph")
  1249. (put-text-property start end 'morph (match-string 0 morpheme))
  1250. (put-text-property start end 'help-echo 'bible--show-lex-morph))))
  1251. ;; Insert lemma into buffer. Lemma tag will be part of lemma/savelm item.
  1252. ;; TODO: Should I enable lexicon lookups on these lemmas? I
  1253. ;; don't use this anyway.... (FMG 5-Mar-2026)
  1254. (when (and bible-word-study-enabled lemma (string-match "lemma.*:.*" lemma))
  1255. (dolist (word (split-string (match-string 0 lemma) " "))
  1256. (setq word (replace-regexp-in-string "[.:a-zA-Z0-9]+" "" word))
  1257. (just-one-space)
  1258. (let ((refstart (point)))
  1259. (insert word)
  1260. (add-face-text-property refstart (point) '(:foreground "blue"))
  1261. (put-text-property refstart (point) 'keymap bible-lemma-keymap))))))))
  1262. (defun bible--insert-title (title-node)
  1263. "Insert the text in TITLE-NODE into buffer as a chapter title.
  1264. Since each verse will have a `title' tag, keep track and only emit a
  1265. title when the new title in `title-node' is different from the one
  1266. stored in `bible-chapter-title'."
  1267. (unless (equal bible-chapter-title title-node)
  1268. (setq-local bible-chapter-title title-node)
  1269. (let ((title-text
  1270. (replace-regexp-in-string ; Clear out XML.
  1271. "<.*?>" ""
  1272. (bible-dom-texts bible-chapter-title)))
  1273. (start (point)))
  1274. (bible-new-line)
  1275. ;; Insert the LRM character to make the text render left-to-right.
  1276. ;; This is necessary in the KJV module when displaying psalm 119.
  1277. (insert (string ?\x200e) title-text)
  1278. (put-text-property start (point) 'face 'bold)
  1279. (newline)
  1280. (delete-blank-lines))))
  1281. ;; These tags appear in ESV modules (and maybe others?)
  1282. ;; REVIEW: Is this right? (FMG 5-Mar-2026)
  1283. (defun bible--level-tag (node)
  1284. "Indent or break line as dictated by NODE."
  1285. (let ((type (dom-attr node 'type))
  1286. (level (dom-attr node 'level)))
  1287. (cond ((and type (string-equal-ignore-case type "x-br"))
  1288. (newline))
  1289. ((and type (string-equal-ignore-case type "x-indent"))
  1290. (insert "\t"))
  1291. ;; REVIEW: Some modules use `level' tag but
  1292. ;; not in a consistent way. (FMG 7-Mar-2026)
  1293. ((equal level "1")
  1294. (just-one-space))
  1295. ((equal level "2")
  1296. (newline)
  1297. (delete-blank-lines)))))
  1298. (defun bible--insert-xref (node)
  1299. "Insert a cross reference specified by NODE.
  1300. This format is used by the NETnote module."
  1301. ;; HACK: What a mess! There are still some broken edge cases! (FMG 29-Mar-2026)
  1302. (let* ((refs-text (bible-dom-text node))
  1303. (refs (split-string refs-text "," t " ")))
  1304. ;; Fix: each reference will have a book name. There are three
  1305. ;; different kinds of reference entries:
  1306. ;;
  1307. ;; 1. The reference entry will have a single book with possibly
  1308. ;; multiple references to that book. Make sure the book is carried
  1309. ;; along to all the other references until another book name
  1310. ;; appears. E.g. Mark 2:3 4 8:17 all refer to the book of Mark.
  1311. ;;
  1312. ;; 2. The reference entry has multiple book names. Mark 2:3 8 Luke
  1313. ;; 8:17 is three references, two to Mark and one to Luke. Make
  1314. ;; sure the book names are updated appropriately.
  1315. ;;
  1316. ;; 3. If the reference entry has no book at all, use the current
  1317. ;; module's book name. We set it here as a default; it will get
  1318. ;; replaced if a book name appears in the reference entry.
  1319. (setq-local bible-current-xref-book (concat bible--current-book-name " "))
  1320. (dolist (ref refs)
  1321. (let ((a-ref (split-string ref ";" t " ")))
  1322. (dolist (b-ref a-ref)
  1323. ;; b-ref is an individual reference.
  1324. ;; At this point b-ref will, we hope, look like one of the following:
  1325. ;; <book> <chapter>:<verse> (or maybe <book> <chapter>)
  1326. ;; <chapter>:verse
  1327. ;; <verse> (or maybe <verse>-<verse>)
  1328. ;; Books may look like this: <1 Cor> or <Gal> so we have to deal with the possible space.
  1329. ;; We ignore verse ranges and hope for the best (it seems to do the right thing).
  1330. (let ((chapter-set-p nil))
  1331. ;; See if we've got a book name. Use greedy regexp to make
  1332. ;; sure we get the whole name. If the name is not found,
  1333. ;; there will be a default (for this xref) name in
  1334. ;; bible-current-xref-book.
  1335. (when (string-match ".* " b-ref)
  1336. (setq-local bible-current-xref-book (match-string 0 b-ref)))
  1337. ;; See if we've got a chapter. Note this regexp will skip
  1338. ;; the book name if present.
  1339. (when (string-match "[0-9]*:" b-ref)
  1340. (setq-local bible-current-xref-chapter
  1341. (string-trim
  1342. (substring
  1343. (match-string 0 b-ref)
  1344. 0
  1345. (1- (length (match-string 0 b-ref))))))
  1346. (setq chapter-set-p t))
  1347. (let* ((match (string-match "[0-9]*" b-ref (if chapter-set-p (match-end 0) 0)))
  1348. (verse (string-trim (substring b-ref match (match-end 0)))))
  1349. (just-one-space)
  1350. (let ((start (point))
  1351. (the-ref (concat bible-current-xref-book bible-current-xref-chapter)))
  1352. (when verse (setq the-ref (concat the-ref ":" verse)))
  1353. (insert b-ref)
  1354. (put-text-property start (point) 'xref the-ref)
  1355. (put-text-property start (point) 'keymap bible-term-mode-map)
  1356. (put-text-property start (point) 'help-echo (concat "Go to " the-ref))
  1357. (add-face-text-property start (point) '(:foreground "blue"))))))))))
  1358. (defun bible--insert-osis-xref (node)
  1359. "Insert a cross reference specified by NODE.
  1360. The node should have the `osisref' attribute."
  1361. (let* ((ref-text (bible-dom-text node))
  1362. (ref-ref (dom-attr node 'osisref))
  1363. (ref-split (split-string ref-ref "[.]" t " "))
  1364. (ref-book (cl-first ref-split))
  1365. (ref-chapter (cl-second ref-split))
  1366. (ref-verse (cl-third ref-split))
  1367. (the-ref (concat ref-book " " ref-chapter ":" ref-verse)))
  1368. (just-one-space)
  1369. (let ((start (point)))
  1370. (insert ref-text)
  1371. (put-text-property start (point) 'xref the-ref)
  1372. (put-text-property start (point) 'keymap bible-term-mode-map)
  1373. (put-text-property start (point) 'help-echo (concat "Go to " the-ref))
  1374. (add-face-text-property start (point) '(:foreground "blue")))))
  1375. (defun bible--insert-domnode-recursive (node &optional iproperties)
  1376. "Recursively parse domnode NODE obtained from `libxml-parse-html-region'.
  1377. Inserts resulting text into active buffer with properties specified in
  1378. IPROPERTIES.
  1379. In processing subnodes, each case will prepend a space if it needs it."
  1380. (when (and bible-red-letter-enabled (equal (dom-attr node 'who) "Jesus"))
  1381. ;; For red-letter display.
  1382. (setq iproperties (plist-put iproperties 'jesus t)))
  1383. (dolist (subnode (dom-children node))
  1384. (cond ((null subnode) nil)
  1385. ((stringp subnode)
  1386. ;; Red letter
  1387. (when (plist-get iproperties 'jesus)
  1388. (add-face-text-property 0 (length subnode) '(:foreground "red") nil subnode))
  1389. (insert subnode))
  1390. ((consp subnode)
  1391. (let ((tag (dom-tag subnode)))
  1392. (pcase tag
  1393. ;; TODO: There are lots of tags we don't handle, especially in commentaries.
  1394. ;; Maybe process these at some point? Include footnotes etc.
  1395. ;; (FMG 5-Mar-2026)
  1396. ;; 'w is usual case.
  1397. ('w (insert " ") (bible--process-word subnode iproperties))
  1398. ('title
  1399. ;; This mess is to deal with the possibility that the
  1400. ;; title might change in the middle of the chapter. I'm
  1401. ;; talking about YOU, Psalm 119.
  1402. (if bible-chapter-title
  1403. (bible--insert-title subnode) ; Middle of chapter.
  1404. (save-excursion ; Beginning of chapter.
  1405. (goto-char (point-min))
  1406. (bible--insert-title subnode))))
  1407. ;; Font tag ignored for now, treat as if 'w.
  1408. ('font (insert " ") (bible--process-word subnode iproperties))
  1409. ('hi (when (equal (dom-attr subnode 'type) "bold")
  1410. (just-one-space)
  1411. (let ((word (bible-dom-text subnode))
  1412. (start (point)))
  1413. (insert word)
  1414. (put-text-property start (point) 'face 'bold))))
  1415. ;; Italic face (special case for certain module)
  1416. ('i
  1417. (just-one-space)
  1418. (let ((word (bible-dom-text subnode))
  1419. (start (point)))
  1420. (insert word)
  1421. (put-text-property start (point) 'face 'bold)
  1422. (add-face-text-property start (point) '(:foreground "orange"))))
  1423. ;; 'q is used for quotations (red letter and non-specific).
  1424. ;; NASB Module uses 'seg to indicate OT quotations (and others?).
  1425. ((or 'body 'seg 'p 'q)
  1426. ;; Some modules use `marker' to indicate quotation marks.
  1427. (when (dom-attr subnode 'marker) (insert (dom-attr subnode 'marker)))
  1428. (bible--insert-domnode-recursive subnode iproperties))
  1429. ('l (bible--level-tag subnode))
  1430. ;; REVIEW: divine name handling doesn't seem to work the same
  1431. ;; with all modules. (FMG 26-Mar-2026)
  1432. ('divinename (bible-handle-divine-name subnode))
  1433. ;; Some modules use this for line breaks and such.
  1434. ('milestone
  1435. (pcase (dom-attr subnode 'type)
  1436. ("line" (bible-new-line))
  1437. ;; ("x-PN" (bible-new-line)) ; REVIEW: Don't yet understand this one. (FMG 26-Mar-2026)
  1438. ("x-p" (insert (dom-attr subnode 'marker) " "))))
  1439. ('br (bible-new-line))
  1440. ('lb (when (equal (dom-attr subnode 'type) "x-begin-paragraph") (bible-new-line)))
  1441. ('div (when (or (equal (dom-attr subnode 'type) "paragraph")
  1442. (equal (dom-attr subnode 'type) "x-p"))
  1443. (bible-new-line)))
  1444. ;; For commentaries and the like.
  1445. ;; This is used by the NETnote module.
  1446. ('scripref (bible--insert-xref subnode))
  1447. ;; This is used by many commentaries.
  1448. ('reference (bible--insert-osis-xref subnode))
  1449. ;; Various text properties---ignore for now. REVIEW: (FMG 26-Mar-2026)
  1450. ((or 'b 'u) (bible--insert-domnode-recursive subnode iproperties))
  1451. ;; Word inserted by translation, not in original, give visual indication.
  1452. ('transchange
  1453. (insert " ")
  1454. (let ((word (bible-dom-text subnode))
  1455. (start (point))
  1456. (face (if (plist-get iproperties 'jesus) '(:foreground "salmon") '(:foreground "gray50"))))
  1457. (insert word)
  1458. (add-face-text-property start (point) face)))))))))
  1459. (defun bible--display (&optional verse)
  1460. "Render a page (chapter) of a Bible module.
  1461. Defaults to using `bible-text'.
  1462. If optional argument VERSE is supplied, set cursor at verse."
  1463. (let ((buffer-read-only nil))
  1464. (erase-buffer)
  1465. (insert (bible--exec-diatheke (concat bible--current-book-name ":" (number-to-string bible--current-chapter))))
  1466. ;; Parse the xml in the buffer into a DOM tree.
  1467. (let ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  1468. ;; Render the DOM tree into the buffer.
  1469. (unless bible-debugme ; If this is true, display the XML.
  1470. (erase-buffer)
  1471. (setq-local bible-chapter-title nil)
  1472. ;; Looking for the "body" tag in the DOM node.
  1473. (bible--insert-domnode-recursive (dom-by-tag html-dom-tree 'body))
  1474. (goto-char (point-min))))
  1475. (save-excursion
  1476. (let ((search-string (concat " *" (car bible--current-book-entry) " " (number-to-string bible--current-chapter) ":")))
  1477. ;; Delete <Book Ch:> at beginning of verse, just leave verse number.
  1478. (while (re-search-forward search-string nil t)
  1479. (replace-match "")
  1480. (bible-new-line)
  1481. ;; Highlight verse number
  1482. (when (re-search-forward " *[0-9]+:" nil t 1)
  1483. (add-face-text-property (match-beginning 0) (match-end 0) '(:foreground "purple"))))))
  1484. (save-excursion
  1485. ;; Fix divine name lossage.
  1486. (while (re-search-forward "Lord LORD" nil t)
  1487. (replace-match "LORD")
  1488. (add-face-text-property (point) (- (point) 4) 'bold))
  1489. (while (re-search-forward "Lord.+s LORD" nil t -1)
  1490. (replace-match "LORD's")
  1491. (add-face-text-property (1- (point)) (- (point) 5) 'bold))
  1492. ;; Remove the module name from the buffer.
  1493. (while (re-search-forward (concat "^.*" bible-text ".*$") nil t)
  1494. (replace-match ""))
  1495. (delete-blank-lines))
  1496. (save-excursion
  1497. (format-replace-strings '(("." . ". ")
  1498. ("," . ", ")
  1499. (";" . "; ")
  1500. (":" . ": ")
  1501. ("?" . "? ")
  1502. ("!" . "! ")
  1503. (" ." . ". ")
  1504. (" ," . ", ")
  1505. (" ;" . "; ")
  1506. (" :" . ": ")
  1507. (" ?" . "? ")
  1508. (" !" . "! ")
  1509. ("“ " . "“")
  1510. (" ”" . "”")
  1511. ("‘ " . "‘")
  1512. (" ’" . "’")
  1513. ;; (". ”" . ".”")
  1514. ("? ”" . "?”"))
  1515. nil (point-min) (point-max)))
  1516. ;; Get rid of multiple consecutive spaces.
  1517. (save-excursion
  1518. (while (re-search-forward " *" nil t) ; More than one space in a row
  1519. (just-one-space)))
  1520. (force-mode-line-update)) ; Ensure mode line indicators are correct.
  1521. ;; If optional verse specification go to that verse.
  1522. (when verse
  1523. (re-search-forward (concat " ?" (number-to-string verse)) nil t 1)))
  1524. ;;;; Modules (Bible texts, commentaries)
  1525. (defun compare-module-names (n1 n2)
  1526. "Compare N1 and N2, ignoring case, using collation order."
  1527. (string-collate-lessp n1 n2 nil t))
  1528. (defun bible--get-biblical-modules ()
  1529. "Populate `bible--texts' and `bible--commentaries' lists."
  1530. (let ((lines
  1531. (split-string
  1532. (bible--exec-diatheke "modulelist" nil "plain" "system")
  1533. "[\n\r]+"))
  1534. (texts nil)
  1535. (commentaries nil)
  1536. (doing-texts nil)
  1537. (doing-commentaries nil))
  1538. (setq bible--texts nil)
  1539. (setq bible--commentaries nil)
  1540. (catch 'done
  1541. (dolist (line lines)
  1542. (when doing-texts
  1543. (push (split-string line " : ") texts))
  1544. (when doing-commentaries
  1545. (push (split-string line " : ") commentaries))
  1546. (when (string-equal line "Biblical Texts:")
  1547. (setq doing-texts t))
  1548. (when (string-equal line "Commentaries:")
  1549. (setq doing-texts nil)
  1550. (pop texts) ; Remove `Commentaries:' line from `bible--texts'.
  1551. (setq doing-commentaries t))
  1552. (when (string-equal line "Lexicons / Dictionaries:")
  1553. (pop commentaries) ; Remove `Lexicons / Dictionaries:' line
  1554. ; from bible--commentaries.
  1555. (throw 'done nil))))
  1556. (setq bible--texts (cl-sort texts #'compare-module-names :key #'car))
  1557. (setq bible--commentaries (cl-sort commentaries #'compare-module-names :key #'car)))
  1558. nil)
  1559. (defun bible--list-biblical-texts ()
  1560. "Return a list of accessible Biblical Text modules."
  1561. (bible--get-biblical-modules) ; Make sure the lists are fresh.
  1562. bible--texts)
  1563. (defun bible--list-biblical-commentaries ()
  1564. "Return a list of accessible Biblical Text modules."
  1565. (bible--get-biblical-modules) ; Make sure the lists are fresh.
  1566. bible--commentaries)
  1567. (defun bible-display-available-texts ()
  1568. "Display available modules, allow user to select."
  1569. (interactive)
  1570. (bible--get-biblical-modules) ; Make sure lists are fresh.
  1571. (with-current-buffer (get-buffer-create " Texts")
  1572. (bible-text-select-mode)
  1573. (let ((buffer-read-only nil))
  1574. (erase-buffer)
  1575. (setq-local tab-stop-list '(25))
  1576. (dolist (text bible--texts)
  1577. (let ((name (string-trim (cl-first text)))
  1578. (description (string-trim-left (cl-second text))))
  1579. (insert (propertize (string-trim name)
  1580. 'face 'bold
  1581. 'module name
  1582. 'help-echo (concat "Select " name)
  1583. 'keymap bible-text-map))
  1584. (insert (propertize " " 'display '(space :align-to 20)))
  1585. (insert (format "%s\n" description)))))
  1586. (goto-char (point-min))
  1587. (pop-to-buffer (current-buffer) nil t)))
  1588. (defun bible-display-available-commentaries ()
  1589. "Display available modules, allow user to select."
  1590. (interactive)
  1591. (bible--get-biblical-modules) ; Make sure lists are fresh.
  1592. (with-current-buffer (get-buffer-create " Commentaries")
  1593. (bible-text-select-mode)
  1594. (let ((buffer-read-only nil))
  1595. (erase-buffer)
  1596. (dolist (commentary bible--commentaries)
  1597. (let ((name (string-trim (cl-first commentary)))
  1598. (description (string-trim-left (cl-second commentary))))
  1599. (insert (propertize (string-trim name)
  1600. 'face 'bold
  1601. 'module name
  1602. 'help-echo (concat "Select " name)
  1603. 'keymap bible-commentary-map))
  1604. (insert (propertize " " 'display '(space :align-to 22)))
  1605. (insert (format "%s\n" description)))))
  1606. (goto-char (point-min))
  1607. (pop-to-buffer (current-buffer) nil t)))
  1608. ;;;; Bible Searching
  1609. (defun bible--open-search (query searchmode module)
  1610. "Open a search buffer of QUERY using SEARCHMODE, getting values from BUFFER."
  1611. (let ((results (string-trim (replace-regexp-in-string
  1612. "Entries .+?--" ""
  1613. (bible--diatheke-search query searchmode "plain" module)))))
  1614. (if (equal results (concat "none (" module ")"))
  1615. (message (concat
  1616. "No results found."
  1617. (when (equal searchmode "lucene")
  1618. " Verify index has been build with mkfastmod.")))
  1619. (with-current-buffer (generate-new-buffer "*bible-search*")
  1620. (bible-search-mode)
  1621. (bible--display-search results module)
  1622. (setq-local bible-search-word-this-query query
  1623. bible-search-text-this-query module
  1624. bible-search-range-this-query bible-search-range)
  1625. (pop-to-buffer (current-buffer) nil t)))))
  1626. (defun bible--display-search (results module)
  1627. "Render RESULTS of search query with MODULE."
  1628. (let ((match 0)
  1629. (matchstr "")
  1630. (verses nil)
  1631. (query-verses "")
  1632. (buffer-read-only nil))
  1633. ;; (message "display-search %s" module)
  1634. (setq-default bible-text module)
  1635. (erase-buffer)
  1636. (while match
  1637. (setq match (string-match ".+?:[0-9]?[0-9]?" results (+ match (length matchstr)))
  1638. matchstr (match-string 0 results))
  1639. (when match
  1640. (push
  1641. ;; Massage match to make it more sortable, get rid of some characters.
  1642. (replace-regexp-in-string
  1643. ".+; " ""
  1644. (string-replace
  1645. "I " "1"
  1646. (string-replace
  1647. "II " "2"
  1648. (string-replace
  1649. "III " "3"
  1650. matchstr))))
  1651. verses)))
  1652. (setq verses (cl-sort verses #'string-version-lessp))
  1653. (dolist (verse verses)
  1654. (if query-verses
  1655. (setq query-verses (concat query-verses ";" verse))
  1656. (setq query-verses verse)))
  1657. (let ((bible-show-diatheke-exec nil))
  1658. (insert (bible--exec-diatheke query-verses nil nil module)))
  1659. (let* ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  1660. (erase-buffer)
  1661. (bible--insert-domnode-recursive (dom-by-tag html-dom-tree 'body)))
  1662. (goto-char (point-min))
  1663. (save-excursion
  1664. ;; Highlight verse numbers.
  1665. (while (re-search-forward bible--verse-regexp nil t)
  1666. (add-face-text-property (match-beginning 0) (match-end 0) '(:foreground "purple")))
  1667. ;; Remove module name from buffer.
  1668. (while (re-search-forward (concat "^.*" module ".*$") nil t)
  1669. (replace-match ""))
  1670. (delete-blank-lines))
  1671. (setq mode-name "Bible Search ")
  1672. (setq-local bible-search-matches (length verses))))
  1673. ;;;; Terms (lemmas, morphemes)
  1674. (defun bible--get-lemma (language strongs)
  1675. "Get the lemma from shorter lexicon for LANGUAGE for term STRONGS.
  1676. Used to display lemmas in mode lines. Assumes that StrongsHebrew and
  1677. StrongsGreek lexicons, or lexicons with compatible output, have been
  1678. installed."
  1679. (let ((lemma-entry
  1680. (pcase language
  1681. ('hebrew
  1682. ;; Use shorter Hebrew lexicon to look up Hebrew lemma.
  1683. (bible--lex-query strongs bible-hebrew-lexicon-short))
  1684. ('greek
  1685. ;; Use shorter Greek lexicon to look up Greek lemma.
  1686. (bible--lex-query strongs bible-greek-lexicon-short)))))
  1687. (unless (equal lemma-entry "")
  1688. ;; Entry will look something like
  1689. ;; <num>: <num>. <lemma> <definition>.
  1690. ;; Get rid of everything before and after <lemma>.
  1691. (let* ((lemma-line (split-string lemma-entry)))
  1692. (cl-third lemma-line)))))
  1693. (defun bible--display-greek (term)
  1694. "Display Greek TERM.
  1695. This command is run by clicking on text, not directly by the user."
  1696. (interactive (list (car (split-string (get-text-property (point) 'strong)))))
  1697. ;; Remove "strong:G" prefix
  1698. (bible--open-term-greek (replace-regexp-in-string "strong:G" "" term)))
  1699. (defun bible--display-hebrew (term)
  1700. "Display Hebrew TERM.
  1701. This command is run by clicking on text, not directly by the user."
  1702. (interactive (list (car (split-string (get-text-property (point) 'strong)))))
  1703. ;; Remove "strong:H" prefix and any alphabetic suffixes.
  1704. (bible--open-term-hebrew (replace-regexp-in-string "strong:H" "" term)))
  1705. ;;(defun bible-display-morphology (morph)
  1706. ;; ;; REVIEW: Do something here? (FMG 5-Mar-2026)
  1707. ;; )
  1708. (defun bible--fixup-lexicon-display ()
  1709. "Fixup the display of a lexical entry."
  1710. (let ((buffer-read-only nil))
  1711. (goto-char (point-min))
  1712. ;; This enables clicking on verse references.
  1713. (save-excursion
  1714. (while (search-forward-regexp bible--verse-regexp nil t)
  1715. (let ((match (match-string 0))
  1716. (start (match-beginning 0))
  1717. (end (match-end 0)))
  1718. (put-text-property start end 'xref match)
  1719. (put-text-property start end 'keymap bible-term-mode-map)
  1720. (put-text-property start end 'help-echo (concat "Go to " (substring-no-properties match)))
  1721. (add-face-text-property start end '(:foreground "blue")))))
  1722. (save-excursion
  1723. (while (search-forward "()" nil t)
  1724. (replace-match ""))
  1725. (delete-blank-lines))))
  1726. (defun bible--open-term-hebrew (term)
  1727. "Open a buffer of the Strong's Hebrew TERM's definition."
  1728. (with-current-buffer (generate-new-buffer "*bible-term*")
  1729. (bible-term-hebrew-mode)
  1730. (setq-local bidi-paragraph-direction 'left-to-right)
  1731. (setq-local mode-name (concat (bible--get-lemma 'hebrew term) " Term (Hebrew)"))
  1732. (bible--display-lemma-hebrew term)
  1733. (pop-to-buffer (current-buffer) nil t)
  1734. (fit-window-to-buffer)))
  1735. (defun bible--display-lemma-hebrew (lemma)
  1736. "Render the definition of the Strong's Hebrew LEMMA.
  1737. This code is customized for the BDBGlosses_Strongs lexicon."
  1738. (let ((buffer-read-only nil))
  1739. (erase-buffer)
  1740. ;; BDBGlosses_Strongs needs the prefixed `H'.
  1741. (insert
  1742. (substring
  1743. (bible--cleanup-lex-text
  1744. (bible--lookup-lemma-hebrew (concat "H" lemma)))
  1745. 7))
  1746. (bible--fixup-lexicon-display)))
  1747. (defun bible--open-term-greek (term)
  1748. "Open a buffer of the Strong's Greek TERM definition."
  1749. (with-current-buffer (generate-new-buffer "*bible-term*")
  1750. (bible-term-greek-mode)
  1751. (setq-local mode-name (concat (bible--get-lemma 'greek term) " Term (Greek)"))
  1752. (bible--display-lemma-greek term)
  1753. (pop-to-buffer (current-buffer) nil t)
  1754. (fit-window-to-buffer)))
  1755. (defun bible--display-lemma-greek (lemma)
  1756. "Render the definition of the Strong's Greek LEMMA."
  1757. (let ((buffer-read-only nil))
  1758. (erase-buffer)
  1759. (insert (bible--lookup-lemma-greek lemma))
  1760. (bible--fixup-lexicon-display)))
  1761. ;;;; Utilities
  1762. (defun bible-new-line ()
  1763. "Ensure beginning of line. Try to avoid redundant blank lines."
  1764. (unless (bolp)
  1765. (newline)))
  1766. (defun bible--remove-module-name (module-name string)
  1767. "Remove parenthesized MODULE-NAME from STRING.
  1768. Also deal with bug where some versions of diatheke return string that is
  1769. missing close parenthesis."
  1770. (replace-regexp-in-string (concat "^(" module-name ".*$") "" string))
  1771. (defun bible--list-number-range (min max &optional prefix)
  1772. "Return a list containing entries for each integer between MIN and MAX.
  1773. If PREFIX is supplied, prepend PREFIX to the entries.
  1774. Used in tandem with `completing-read' for chapter selection."
  1775. (let ((range-list nil))
  1776. (dotimes (num (1+ max))
  1777. (when (>= num min)
  1778. (push (cons (concat prefix (number-to-string num)) num) range-list)))
  1779. (nreverse range-list)))
  1780. (provide 'bible)
  1781. ;;; bible.el ends here.