bible-mode.el 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. ;;;; -*- mode: EMACS-LISP; lexical-binding: t -*-
  2. ;;
  3. ;; bible-mode.el --- A browsing interface for the SWORD Project's Diatheke CLI
  4. ;; Time-stamp: <2024-05-22 09:00:30 fred>
  5. ;; Author: Zacalot
  6. ;; Fixes and modifications by Fred Gilham
  7. ;; Url: https://gitbot.homedns.org/fred/bible-mode
  8. ;; Forked from
  9. ;; Url: https://github.com/Zacalot/bible-mode
  10. ;; Version: 1.0.0
  11. ;; Package-Requires: ((emacs "24.1"))
  12. ;; Keywords: diatheke, sword, research, bible
  13. ;;; Commentary:
  14. ;; This package uses the `diatheke' program to browse and search
  15. ;; Biblical texts provided by the Sword project.
  16. ;; Word study is also supported.
  17. ;;; Usage:
  18. ;; First install `diatheke'. On Debian/Ubuntu it's in the `diatheke'
  19. ;; package. In other distributions it might be in the sword package.
  20. ;; For Windows I found that you can simply install the Xiphos package.
  21. ;; It includes the Sword library and its utilities including diatheke,
  22. ;; installmgr and mkfastmod. Add the "Program Files\Xiphos\bin" path
  23. ;; to your execution path.
  24. ;; Use M-x `bible-open' to open a Bible buffer.
  25. ;; Use C-h f `bible-mode' to see available keybindings.
  26. ;; You may customize `bible-mode-module' to set a default browsing
  27. ;; module, as well as `bible-mode-word-study-enabled' to enable word
  28. ;; study by default.
  29. ;;; Design:
  30. ;; The idea here is to use the diatheke program to insert code from
  31. ;; modules into buffers. The main bible display uses an "internal" XML
  32. ;; format. The whole buffer gets parsed by libxml-parse-html-region to
  33. ;; create a dom tree. This gets parsed by
  34. ;; bible-mode--insert-domnode-recursive to render the text into
  35. ;; reading format.
  36. ;; The text is then decorated using information from the dom format as
  37. ;; necessary along with regular expressions to identify the verse
  38. ;; references. This is for red letters, purple highlighting of the
  39. ;; verse numbers, bold face of the divine name in the OT and so on.
  40. ;; If strongs tags and/or morphological tags are present, they are
  41. ;; looked up in appropriate lexical and morphological modules and used
  42. ;; to add tooltips to the text so that mousing over words will bring
  43. ;; up a tooltip with information about the word. Clicking on a word
  44. ;; with lexical information will display that informatio in a "term"
  45. ;; buffer.
  46. ;;;
  47. ;;; bm- is used as shorthand (see Local Variables) for bible-mode-
  48. ;;; Code:
  49. ;;;; Requirements
  50. (require 'cl-lib) ; XXX FMG there are just a few constructs that use this; use elisp versions instead.
  51. ;; (require 'bidi)
  52. (require 'dom)
  53. (require 'shr)
  54. ;; Turn off tool bar mode because we want the pixels....
  55. (tool-bar-mode -1)
  56. ;;;; Variables
  57. (defgroup bible-mode nil
  58. "Settings for `bible-mode'."
  59. :group 'tools
  60. :link '(url-link "https://gitbot.homedns.org/fred/bible-mode"))
  61. (defcustom bm-module
  62. "KJV"
  63. "Book module for Diatheke to query."
  64. :type '(choice (const :tag "None" nil)
  65. (string :tag "Module abbreviation (e.g. \"KJV\")"))
  66. :local t
  67. :group 'bible-mode)
  68. ;;;
  69. ;;; XXX Not implememted yet
  70. (defcustom bm-font
  71. "Ezra SIL"
  72. "Default font for bible-mode."
  73. :type '(string :tag "Font family name (e.g. \"Ezra SIL\")")
  74. :local t
  75. :group 'bible-mode)
  76. (defcustom bm-greek-lexicon
  77. "MLStrong"
  78. "Lexicon used for displaying definitions of Greek words using Strong's codes."
  79. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  80. :local nil
  81. :group 'bible-mode)
  82. ;; This determines whether or not to use the Abbott Smith lexicon.
  83. ;; There is special-case code for this.
  84. (defcustom bm-use-abbott
  85. t
  86. "Use the Abbott Smith `Manual Greek Lexicon' for Greek definitions."
  87. :type 'boolean
  88. :local nil
  89. :group 'bible-mode)
  90. (defcustom bm-short-greek-lexicon
  91. "StrongsRealGreek"
  92. "Lexicon used for displaying definitions of Greek words in tooltips."
  93. :type '(string :tag "Lexicon module (e.g. \"StrongsRealGreek\").")
  94. :local nil
  95. :group 'bible-mode)
  96. (defcustom bm-hebrew-lexicon
  97. "StrongsRealHebrew"
  98. "Lexicon used for displaying definitions of Hebrew words using Strong's codes."
  99. :type '(string :tag "Lexicon module (e.g. \"StrongsRealHebrew\")")
  100. :local nil
  101. :group 'bible-mode)
  102. (defcustom bm-short-hebrew-lexicon
  103. "BDBGlosses_Strongs" ; This seems to work
  104. "Lexicon used for displaying definitions of Hebrew words in tooltips."
  105. :type '(string :tag "Lexicon module (e.g. \"StrongsRealHebrew\")")
  106. :local nil
  107. :group 'bible-mode)
  108. (defcustom bm-word-study-enabled
  109. nil
  110. "Display Strong's Hebrew, Strong's Greek, and Lemma words for study."
  111. :type 'boolean
  112. :local t
  113. :group 'bible-mode)
  114. (defcustom bm-red-letter-enabled
  115. t
  116. "Display words of Jesus in red when module has that information."
  117. :type 'boolean
  118. :local t
  119. :group 'bible-mode)
  120. ;;; defvars
  121. ;;(defvar bm-verse-regexp "([\d ]*[a-zA-Z]+( \d*:\d*)?)(( - )| )?(((\d* )?[a-zA-Z]+ )?\d*([:-]+\d*)?)")
  122. ;; (defvar bm-verse-regexp "/(\d*)\s*([a-z]+)\s*(\d+)(?::(\d+))?(\s*-\s*(\d+)(?:\s*([a-z]+)\s*(\d+))?(?::(\d+))?)?/i")
  123. (defvar bm-verse-regexp "\\(I?I \\)*[a-zA-Z]* [0-9]*:[0-9]*")
  124. (setq bm-verse-regexp "\\(I?I \\)*[a-zA-Z]* [0-9]*:[0-9]*")
  125. (defvar bm-modules (lazy-completion-table bm-modules bm--list-biblical-modules))
  126. ;; XXX I believe these chapter counts aren't the same for all modules, e.g. JPS.
  127. (defvar bm-books
  128. '(;; Old Testament
  129. ("Genesis" . 50) ("Exodus" . 40) ("Leviticus" . 27) ("Numbers" . 36)
  130. ("Deuteronomy" . 34) ("Joshua" . 24) ("Judges" . 21) ("Ruth" . 4)
  131. ("I Samuel" . 31) ("II Samuel" . 24) ("I Kings" . 22) ("II Kings" . 25)
  132. ("I Chronicles" . 29) ("II Chronicles" . 36) ("Ezra" . 10) ("Nehemiah" . 13)
  133. ("Esther" . 10) ("Job" . 42) ("Psalms" . 150) ("Proverbs" . 31)
  134. ("Ecclesiastes" . 12) ("Song of Solomon" . 8) ("Isaiah" . 66) ("Jeremiah" . 52)
  135. ("Lamentations" . 5) ("Ezekiel" . 48) ("Daniel" . 12) ("Hosea" . 14)
  136. ("Joel" . 3) ("Amos" . 9) ("Obadiah" . 1) ("Jonah" . 4)
  137. ("Micah" . 7) ("Nahum" . 3) ("Habakkuk" . 3) ("Zephaniah" . 3)
  138. ("Haggai" . 2) ("Zechariah" . 14) ("Malachi" . 4)
  139. ;; New Testament
  140. ("Matthew" . 28) ("Mark" . 16) ("Luke" . 24) ("John" . 21)
  141. ("Acts" . 28) ("Romans" . 16) ("I Corinthians" . 16) ("II Corinthians" . 13)
  142. ("Galatians" . 6) ("Ephesians" . 6) ("Philippians" . 4) ("Colossians" . 4)
  143. ("I Thessalonians" . 5) ("II Thessalonians" . 3) ("I Timothy" . 6) ("II Timothy" . 4)
  144. ("Titus" . 3) ("Philemon" . 1) ("Hebrews" . 13) ("James" . 5)
  145. ("I Peter" . 5) ("II Peter" . 3) ("I John" . 5) ("II John" . 1)
  146. ("III John" . 1) ("Jude" . 1) ("Revelation of John" . 22))
  147. "A-list of name / chapter count for Bible books.")
  148. (defvar bm-book-name-abbreviations-alist
  149. '(;; Old Testament
  150. ("Ge" . "Genesis") ("Ex" . "Exodus") ("Le" . "Leviticus") ("Nu" . "Numbers")
  151. ("De" . "Deuteronomy") ("Js" . "Joshua") ("Jg" . "Judges") ("Ru" . "Ruth")
  152. ("I Sa" . "I Samuel") ("II Sa" . "II Samuel") ("I Ki" . "I Kings") ("II Ki" . "II Kings")
  153. ("I Ch" . "I Chronicles") ("II Ch" . "II Chronicles") ("Ezr" . "Ezra") ("Ne" . "Nehemiah")
  154. ("Es" . "Esther") ("Jb" . "Job") ("Ps" . "Psalms") ("Pr" . "Proverbs")
  155. ("Ec" . "Ecclesiastes") ("So" . "Song of Solomon") ("Is" . "Isaiah") ("Je" . "Jeremiah")
  156. ("La" . "Lamentations") ("Ez" . "Ezekiel") ("Da" . "Daniel") ("Ho" . "Hosea")
  157. ("Joe" . "Joel") ("Am" . "Amos") ("Ob" . "Obadiah") ("Jon" . "Jonah")
  158. ("Mi" . "Micah") ("Na" . "Nahum") ("Ha" . "Habakkuk") ("Zep" . "Zephaniah")
  159. ("Hag" . "Haggai") ("Ze" . "Zechariah") ("Mal" . "Malachi")
  160. ;; New Testament
  161. ("Mt" . "Matthew") ("Mk" . "Mark") ("Lk" . "Luke") ("Jo" . "John")
  162. ("Ac" . "Acts") ("Ro" . "Romans") ("I Co" . "I Corinthians") ("II Co" . "II Corinthians")
  163. ("Ga" . "Galatians") ("Eph" . "Ephesians") ("Phl" . "Philippians") ("Col" . "Colossians")
  164. ("I Th" . "I Thessalonians") ("II Th" . "II Thessalonians") ("I Ti" . "I Timothy") ("II Ti" . "II Timothy")
  165. ("Tit" . "Titus") ("Phm" . "Philemon") ("He" . "Hebrews") ("Ja" . "James")
  166. ("I Pe" . "I Peter") ("II Pe" . "II Peter") ("I Jo" . "I John") ("II Jo" . "II John")
  167. ("III Jo" . "III John") ("Ju" . "Jude") ("Re" . "Revelation of John"))
  168. "A-list of abbreviations for Bible books.")
  169. ;;;; Book / chapter
  170. (defvar-local bm-current-book (assoc "Genesis" bm-books)
  171. "Current book data (name . chapter).")
  172. (defvar-local bm-current-book-name "Genesis"
  173. "Current book name.")
  174. (defvar-local bm-current-chapter 1
  175. "Current book chapter number.")
  176. (defvar-local bm-search-query nil
  177. "Search query associated with the buffer.")
  178. (defvar-local bm-search-mode "phrase"
  179. "Search mode: either `lucene' or `phrase'.")
  180. (defvar-local bm-has-strongs nil
  181. "Set if the module being displayed has strongs numbers availabile.")
  182. (defvar-local bm-has-morphology nil
  183. "Set if the module being displayed has morphology availabile.")
  184. ;; (defvar bm-current-module nil)
  185. ;;;; Keymaps
  186. (defconst bm-map (make-keymap))
  187. ;;;;; Navigation
  188. (define-key bm-map "n" 'bm-next-chapter)
  189. (define-key bm-map "p" 'bm-previous-chapter)
  190. (define-key bm-map (kbd "TAB") 'bm-forward-word) ; TODO: bm-forward-word
  191. ;;;;; Direct jump
  192. (define-key bm-map "b" 'bm-select-book)
  193. (define-key bm-map "c" 'bm-select-chapter)
  194. ;;;;; Search
  195. (define-key bm-map "s" 'bible-search)
  196. (define-key bm-map "/" 'bible-search)
  197. ;;;; Not yet
  198. ;;(define-key bm-map "" 'bm-set-search-range)
  199. ;;;;; Misc
  200. (define-key bm-map "m" 'bm-select-module)
  201. (define-key bm-map "w" 'bm-toggle-word-study)
  202. (define-key bm-map "x" 'bm-split-display)
  203. ;;;;; Deal with visual-line-mode
  204. (define-key bm-map "\C-n" 'next-logical-line)
  205. (define-key bm-map "\C-p" 'previous-logical-line)
  206. (defconst bible-search-mode-map (make-keymap))
  207. (define-key bible-search-mode-map "s" 'bible-search)
  208. (define-key bible-search-mode-map "w" 'bm-toggle-word-study)
  209. (define-key bible-search-mode-map (kbd "RET") 'bible-search-mode-follow-verse)
  210. (define-key bible-search-mode-map [mouse-1] 'bible-search-mode-follow-xref)
  211. (defconst bible-term-hebrew-mode-map (make-keymap))
  212. (defconst bible-term-greek-mode-map (make-keymap))
  213. ;; (defconst bible-term-morph-mode-map (make-keymap))
  214. ;;;
  215. ;;; Menu bar items
  216. ;;;
  217. (define-key global-map [menu-bar bible-mode]
  218. (cons "Bible Mode" (make-sparse-keymap "Bible Mode")))
  219. (defun bible-set-left-to-right ()
  220. (interactive)
  221. (setq-local bidi-paragraph-direction 'left-to-right))
  222. (defun bible-set-right-to-left ()
  223. (interactive)
  224. (setq-local bidi-paragraph-direction 'right-to-left))
  225. (define-key global-map
  226. [menu-bar bible-mode left-to-right]
  227. '("Left-to-right" . bible-set-left-to-right))
  228. (define-key global-map
  229. [menu-bar bible-mode right-to-left]
  230. '("Right-to-left" . bible-set-right-to-left))
  231. (defvar-local bm-debugme nil
  232. "Make text show up as XML when set.")
  233. (defun bible-set-display-xml ()
  234. "Turn on XML display."
  235. (interactive)
  236. (setq-local bm-debugme t)
  237. (bm--display))
  238. (defun bm-set-display-text ()
  239. "Turn off XML display."
  240. (interactive)
  241. (setq-local bm-debugme nil)
  242. (bm--display))
  243. (define-key global-map
  244. [menu-bar bible-mode display-xml]
  245. '("Display XML" . bm-set-display-xml))
  246. (define-key global-map
  247. [menu-bar bible-mode display-text]
  248. '("Display Text" . bm-set-display-text))
  249. (define-key global-map
  250. [menu-bar bible-mode select-biblical-text]
  251. '("Select Module" . bm--display-available-modules))
  252. (define-key global-map
  253. [menu-bar bible-mode select-biblical-text]
  254. '("Toggle debug-on-error" . toggle-debug-on-error))
  255. (defun bm-display-greek ()
  256. "This command is run by clicking on text, not directly by the user."
  257. (interactive)
  258. (let ((item (car (split-string (get-text-property (point) 'strong)))))
  259. ;; Remove "strong:G" prefix
  260. (bible-term-greek (replace-regexp-in-string "strong:G" "" item))))
  261. (defconst bm-greek-keymap (make-sparse-keymap))
  262. (define-key bm-greek-keymap (kbd "RET") 'bm-display-greek)
  263. (define-key bm-greek-keymap [mouse-1] 'bm-display-greek)
  264. (defun bm-display-hebrew ()
  265. "This command is run by clicking on text, not directly by the user."
  266. (interactive)
  267. (let ((item (car (split-string (get-text-property (point) 'strong)))))
  268. ;; Remove "strong:H" prefix and any alphabetic suffixes.
  269. (bible-term-hebrew (replace-regexp-in-string "strong:H" "" item))))
  270. (defconst bm-hebrew-keymap (make-sparse-keymap))
  271. (define-key bm-hebrew-keymap (kbd "RET") 'bm-display-hebrew)
  272. (define-key bm-hebrew-keymap [mouse-1] 'bm-display-hebrew)
  273. (defconst bm-lemma-keymap (make-sparse-keymap))
  274. (define-key bm-lemma-keymap (kbd "RET")
  275. (lambda ()
  276. (interactive)
  277. ))
  278. (defconst bm-morph-keymap (make-sparse-keymap))
  279. (define-key bm-morph-keymap (kbd "RET")
  280. (lambda ()
  281. (interactive)
  282. ;;; (let ((thing (thing-at-point 'word)))
  283. ;;; (message "thing at point: %s" thing)
  284. ;;; (message "morph property %s" (get-text-property 0 'field thing))
  285. ))
  286. ;;;; Modes
  287. (define-derived-mode bible-mode special-mode "Bible"
  288. "Mode for reading the Bible.
  289. \\{bm-map}"
  290. (buffer-disable-undo)
  291. (font-lock-mode t)
  292. (use-local-map bm-map)
  293. (setq buffer-read-only t)
  294. (visual-line-mode t))
  295. (define-derived-mode bible-search-mode special-mode "Bible Search"
  296. "Mode for performing Bible searches.
  297. \\{bible-search-mode-map}"
  298. (buffer-disable-undo)
  299. (font-lock-mode t)
  300. (use-local-map bible-search-mode-map)
  301. (setq buffer-read-only t)
  302. (visual-line-mode t)
  303. )
  304. (define-derived-mode bible-term-hebrew-mode special-mode "Bible Term (Hebrew)"
  305. "Mode for researching Hebrew terms in the Bible.
  306. \\{bible-term-hebrew-mode-map}"
  307. (buffer-disable-undo)
  308. (font-lock-mode t)
  309. (use-local-map bible-term-hebrew-mode-map)
  310. (setq buffer-read-only t)
  311. (visual-line-mode t))
  312. (define-derived-mode bible-term-greek-mode special-mode "Bible Term (Greek)"
  313. "Mode for researching Greek terms in the Bible.
  314. \\{bible-term-greek-mode-map}"
  315. (buffer-disable-undo)
  316. (font-lock-mode t)
  317. ;; (use-local-map bible-term-greek-mode-map)
  318. (setq buffer-read-only t)
  319. (visual-line-mode t))
  320. (define-derived-mode module-select-mode special-mode "Select Text Module"
  321. (buffer-disable-undo)
  322. (font-lock-mode t)
  323. (setq buffer-read-only t))
  324. ;;;; Functions
  325. ;;;;; Commands
  326. ;;;###autoload
  327. (defun bible-open (&optional book-name chapter verse)
  328. "Creates and opens a `bible-mode' buffer"
  329. (interactive)
  330. (let ((buf (get-buffer-create (generate-new-buffer-name (concat "*bible*")))))
  331. (set-buffer buf)
  332. (bible-mode)
  333. (bm--set-location (assoc (or book-name "Genesis") bm-books) (or chapter 1) verse)
  334. (set-window-buffer (get-buffer-window (current-buffer)) buf)))
  335. ;;;###autoload
  336. (defun bm-next-chapter ()
  337. "Pages to the next chapter for the active `bible-mode' buffer."
  338. (interactive)
  339. (let* ((book-chapters (cdr bm-current-book))
  340. (chapter (min book-chapters (+ bm-current-chapter 1))))
  341. (bm--set-location bm-current-book chapter)))
  342. ;;;###autoload
  343. (defun bm-previous-chapter ()
  344. "Pages to the previous chapter for the active `bible-mode' buffer."
  345. (interactive)
  346. (bm--set-location bm-current-book (max 1 (- bm-current-chapter 1))))
  347. (defun bm-forward-word ()
  348. "Moves forward a word, taking into account the relevant text properties.
  349. XXX Doesn't work yet."
  350. (interactive)
  351. (field-end))
  352. ;;;###autoload
  353. (defun bm-select-book ()
  354. "Queries user to select a new book and chapter for the current
  355. `bible-mode' buffer."
  356. (interactive)
  357. (let* ((completion-ignore-case t)
  358. (book-data (assoc (completing-read "Book: " bm-books nil t) bm-books))
  359. (chapter (string-to-number (completing-read "Chapter: " (bm--list-number-range 1 (cdr book-data)) nil t))))
  360. (setq-local bm-current-book book-data)
  361. (setq-local bm-current-book-name (car book-data))
  362. (setq-local bm-current-chapter chapter)
  363. (bm--display)))
  364. ;;;###autoload
  365. (defun bm-select-chapter ()
  366. "Queries user to select a new chapter for the current `bible-mode' buffer."
  367. (interactive)
  368. (let* ((book-chapters (cdr bm-current-book))
  369. (chapter (string-to-number (completing-read "Chapter: " (bm--list-number-range 1 book-chapters) nil t))))
  370. (when chapter
  371. (bm--set-location bm-current-book chapter))))
  372. ;;;###autoload
  373. (defun bm-select-module ()
  374. "Queries user to select a new reading module for the current `bible-mode' buffer."
  375. (interactive)
  376. (let ((module (completing-read "Module: " bm-modules)))
  377. (setq-local bm-module module)
  378. (bm--display)))
  379. ;;;###autoload
  380. (defun bm-toggle-word-study()
  381. "Toggles the inclusion of word study for the active `bible-mode' buffer."
  382. (interactive)
  383. (setq bm-word-study-enabled (not bm-word-study-enabled))
  384. (if (equal major-mode 'bible-search-mode)
  385. (bm--display-search bm-search-query bm-search-mode bm-module)
  386. (bm--display)))
  387. ;;;###autoload
  388. (defun bm-split-display ()
  389. "Copies the active `bible-mode' buffer into a new buffer in another window."
  390. (interactive)
  391. (split-window-right)
  392. (balance-windows)
  393. (other-window 1)
  394. (bible-open bm-current-book-name bm-current-chapter))
  395. ;;;###autoload
  396. (defun bible-search (query)
  397. "Prompts the user for a Bible search query: word or phrase and type of
  398. search: either `lucene' or `phrase'. `lucene' mode requires an index
  399. to be built using the `mkfastmod' program. `lucene' is the default
  400. search."
  401. (interactive "sBible Search: ")
  402. (when (> (length query) 0)
  403. (let* ((searchmode (completing-read "Search Mode: " '("lucene" "phrase") nil t "lucene")))
  404. (bm--open-search query searchmode))))
  405. ;;;###autoload
  406. (defun bible-search-mode-follow-verse ()
  407. "Follows the hovered verse in a `bible-search-mode' buffer,
  408. creating a new `bible-mode' buffer positioned at the specified verse."
  409. (interactive)
  410. (let* ((text (thing-at-point 'line t))
  411. book
  412. chapter
  413. verse)
  414. (string-match ".+ [0-9]?[0-9]?[0-9]?:[0-9]?[0-9]?[0-9]?:" text)
  415. (setq text (match-string 0 text))
  416. (string-match " [0-9]?[0-9]?[0-9]?:" text)
  417. (setq chapter (replace-regexp-in-string "[^0-9]" "" (match-string 0 text)))
  418. (string-match ":[0-9]?[0-9]?[0-9]?" text)
  419. (setq verse (replace-regexp-in-string "[^0-9]" "" (match-string 0 text)))
  420. (setq book (replace-regexp-in-string "[ ][0-9]?[0-9]?[0-9]?:[0-9]?[0-9]?[0-9]?:$" "" text))
  421. (bible-open (string-trim book) (string-to-number chapter) (string-to-number verse))))
  422. (defun bible-search-mode-follow-xref ()
  423. "Follows the hovered verse in a `bible-search-mode' buffer,
  424. creating a new `bible-mode' buffer positioned at the specified verse.
  425. N.B. We use the default module to avoid opening cans of worms regarding
  426. OT/NT etc."
  427. (interactive)
  428. (let* ((xref (get-text-property (point) 'xref))
  429. (verse-ref (string-split xref))
  430. book-abbrev
  431. book
  432. chapter-verse
  433. chapter
  434. verse)
  435. (if (= (length verse-ref) 3) ; II Cor 3:17 or the like
  436. (progn
  437. (setq book-abbrev (concat (car verse-ref) " " (cadr verse-ref)))
  438. (setq chapter-verse (split-string (caddr verse-ref) ":")))
  439. (progn ; Mat 5 or the like
  440. (setq book-abbrev (car verse-ref))
  441. (setq chapter-verse (split-string (cadr verse-ref) ":"))))
  442. (setq book (cdr (assoc book-abbrev bm-book-name-abbreviations-alist)))
  443. (setq chapter (car chapter-verse)
  444. verse (cadr chapter-verse))
  445. (bible-open (string-trim book) (string-to-number chapter) (string-to-number verse))))
  446. ;;;###autoload
  447. (defun bible-term-hebrew (term)
  448. "Queries user for a Strong's Hebrew Lexicon term."
  449. (interactive "sTerm: ")
  450. (bm--open-term-hebrew term))
  451. ;;;###autoload
  452. (defun bible-term-greek (term)
  453. "Queries user for a Strong's Greek Lexicon term."
  454. (interactive "sTerm: ")
  455. (bm--open-term-greek term))
  456. ;;;###autoload
  457. (defun bible-insert ()
  458. "Queries user to select a verse for insertion into the current buffer."
  459. (interactive)
  460. (let* ((completion-ignore-case t)
  461. (book-data (assoc (completing-read "Book: " bm-books nil t) bm-books))
  462. (chapter (when book-data (completing-read "Chapter: " (bm--list-number-range 1 (cdr book-data)) nil t)))
  463. (verse (when chapter (read-from-minibuffer "Verse: "))))
  464. (when verse
  465. (insert (string-trim
  466. (replace-regexp-in-string
  467. (regexp-opt `(,(concat "(" bm-module ")")))
  468. ""
  469. (bm--exec-diatheke (concat (car book-data) " " chapter ":" verse) nil "plain")))))))
  470. ;;;;; Support
  471. ;;;
  472. ;;; XXX I've magled this in an ad-hoc manner. It needs to be
  473. ;;; re-written so it is clearer (and correct, for that matter).
  474. (defun bm--exec-diatheke (query &optional filter format searchtype module)
  475. "Executes `diatheke' with specified query options, returning the output."
  476. (let ((module (or module bm-module)))
  477. (with-temp-buffer
  478. (let ((args (list "diatheke" nil (current-buffer) t "-b" module)))
  479. (if filter
  480. (setq filter (concat filter " avmws"))
  481. (setq filter "avmws"))
  482. (when filter (setq args (append args (list "-o" filter))))
  483. (when searchtype
  484. (setq args (append args (list "-s" (pcase searchtype ("lucene" "lucene") ("phrase" "phrase"))))))
  485. (setq args (append args (list "-f" (pcase format ("plain" "plain") (_ "internal")) "-k" query)))
  486. (message "%s" args)
  487. (apply 'call-process args))
  488. (buffer-string))))
  489. (defvar-local bm-chapter-title nil
  490. "Document text at start of chapter, mostly in Psalms,
  491. like `Of David' or the like.")
  492. ;;;
  493. ;;; Greek and Hebrew lexicon and morphology tooltip rendering.
  494. ;;;
  495. ;;; Hash tables for Lexical definitions.
  496. (defvar greek-hash (make-hash-table :test 'equal))
  497. (defvar greek-short-hash (make-hash-table :test 'equal)) ; Hash table for ``short'' lexical lookup
  498. (defvar hebrew-hash (make-hash-table :test 'equal))
  499. (defvar hebrew-short-hash (make-hash-table :test 'equal))
  500. ;; Do lookups using AbbottSmith_Strongs as index to AbbottSmith lexicon.
  501. (defvar abbott-index-hash (make-hash-table :test 'equal))
  502. (defvar abbott-lex-hash (make-hash-table :test 'equal))
  503. ;;; Hash tables for Morphologies. Three at present.
  504. (defvar robinson-hash (make-hash-table :test 'equal))
  505. (defvar packard-hash (make-hash-table :test 'equal))
  506. (defvar oshm-hash (make-hash-table :test 'equal))
  507. ;;; Use HTMLHREF format with diatheke, post-process to render html.
  508. (defun bm--morph-query (query module)
  509. "Executes `diatheke' to do morph query, renders HTML, returns string.
  510. Does some tweaking specific to morphology."
  511. (with-temp-buffer
  512. (let ((args (list "diatheke" nil (current-buffer) t "-b" module "-o" "m" "-f" "HTMLHREF" "-k" query)))
  513. (apply 'call-process args)
  514. (shr-render-region (point-min) (point-max))
  515. (format-replace-strings
  516. '(("\n:" . "") ; This makes the Packard morphology display look better.
  517. ("Part of Speech" . "")) ; This helps the Robinson display look better.
  518. nil (point-min) (point-max))
  519. (substring (buffer-string) (+ (length query) 1))) ; This tries to get rid of unnecessary query identifier.
  520. ))
  521. ;;; Use "plain" format with diatheke.
  522. (defun bm--lex-query (query module)
  523. "Executes `diatheke' for query, plain format, returns string."
  524. ;; Get rid of query ID at front of string: ?????:
  525. (bm--exec-diatheke query nil "plain" nil module))
  526. (defun bm--lookup-strongs-greek (window object pos)
  527. "Look up Greek lexical data for object at point. If not found in hash table,
  528. get it from sword database, stash in hash table, and return data."
  529. (let* ((query (get-text-property pos 'strong object))
  530. (match (string-match "[0-9]+" query))
  531. (lookup-key (match-string 0 query)))
  532. (and lookup-key
  533. (or (gethash lookup-key greek-hash)
  534. (puthash lookup-key (bm--lex-query lookup-key bible-mode-short-greek-lexicon) greek-hash)))))
  535. (defun bm--lookup-lemma-abbott (key)
  536. "Given a strong's number, return the Greek lemma from AbbottSmithStrongs."
  537. (or (gethash key abbott-index-hash)
  538. (puthash key
  539. (string-trim
  540. (replace-regexp-in-string
  541. "(AbbottSmithStrongs)" ""
  542. (bm--lex-query key "AbbottSmithStrongs")))
  543. abbott-index-hash)))
  544. (defun bm--lookup-def-abbott (lemma)
  545. "Executes `diatheke' to do abbott query, renders HTML, sets text
  546. properties to allow verse cross references. Returns string."
  547. (with-temp-buffer
  548. (let ((args (list "diatheke" nil (current-buffer) t "-b" "AbbottSmith" "-o" "m" "-f" "plain" "-k" lemma)))
  549. (apply 'call-process args)
  550. (format-replace-strings
  551. '((" I." . "\n I.")
  552. (" 1." . "\n 1.")
  553. (" (a)" . "\n (a)")
  554. (". ." . ".")
  555. (" . " . ". ")))
  556. (goto-char (point-min))
  557. (while (search-forward-regexp bm-verse-regexp nil t)
  558. (put-text-property (match-beginning 0) (match-end 0) 'xref (match-string 0))
  559. (put-text-property (match-beginning 0) (match-end 0) 'keymap bible-search-mode-map)
  560. (add-face-text-property (match-beginning 0) (match-end 0) '(:foreground "blue"))
  561. ))
  562. (buffer-string)))
  563. (defun bm--lookup-lex-def-abbott (key)
  564. (let* ((abbott-lemma (bm--lookup-lemma-abbott key))
  565. ;; Get the lemma which is after the `@LINK' string.
  566. (lemma (caddr (split-string abbott-lemma)))
  567. ;; Use the lemma to lookup the definition.
  568. (lex-def (bm--lookup-def-abbott lemma)))
  569. lex-def))
  570. (defun bm--lookup-strongs-greek-abbott (window object pos)
  571. "To use Abbott's Lexicon we extract the Strong's key from the text in the
  572. buffer. Given the Strong's number, get the lemma for that number. Use
  573. that lemma to lookup the definition in the AbbottStrongs lexicon.
  574. Compiler warns about unused Window argument."
  575. (let* ((query (get-text-property pos 'strong object))
  576. (match (string-match "[0-9]+" query)) ; Compiler warns about unused match variable.
  577. (lookup-key (match-string 0 query)))
  578. (when lookup-key
  579. (bm--lookup-lex-def-abbott lookup-key))))
  580. ;;; Not used.
  581. (defun bm--hebrew-lex-query (query module)
  582. "Executes `diatheke' to do hebrew query, renders HTML, returns string.
  583. XXX directionality problems."
  584. (with-temp-buffer
  585. (let ((args (list "diatheke" nil (current-buffer) t "-b" module "-o" "m" "-f" "HTMLHREF" "-k" query)))
  586. (apply 'call-process args)
  587. (shr-render-region (point-min) (point-max)))))
  588. (defun bm--lookup-strongs-hebrew (window object pos)
  589. "Look up Hebrew lexical data for object at point. If not found in hash table,
  590. get it from sword database, stash in hash table, and return data.
  591. Note: compiler warns about unused `window' argument."
  592. (let* ((query (get-text-property pos 'strong object))
  593. (match (string-match "[0-9]+" query)) ; Compiler warns about match.
  594. (match-string (match-string 0 query)))
  595. (when match-string
  596. (let ((lookup-key (concat "H" (match-string 0 query))))
  597. (or (gethash lookup-key hebrew-hash)
  598. ;; Use PLAIN format for lookup. XXX directionality problems.
  599. (let ((raw-text (bm--lex-query lookup-key bm-short-hebrew-lexicon)))
  600. ;; XXX massage this text to handle outline formatting a bit better.
  601. (puthash lookup-key raw-text hebrew-hash)))))))
  602. (defun bm--morph-database-lookup (query database hash)
  603. (or (gethash query hash)
  604. (puthash query (bm--morph-query query database) hash)))
  605. ;;;
  606. ;;; Get string for tooltip display
  607. ;;;
  608. (defun bm--show-lex-morph (window object pos)
  609. (let* ((lex-morph-text "")
  610. (lex (get-text-property pos 'strong object))
  611. (lex-module nil)
  612. (lex-text
  613. (cond ((string-match "strong:G" lex)
  614. (setq lex-module bm-short-greek-lexicon)
  615. (bm--lookup-strongs-greek window object pos))
  616. ((string-match "strong:H" lex)
  617. (setq lex-module bm-short-hebrew-lexicon)
  618. (bm--lookup-strongs-hebrew window object pos)))))
  619. (setq lex-text (string-replace (concat "(" lex-module ")") "" lex-text))
  620. (let* ((morph (get-text-property pos 'morph object))
  621. (morph-module nil)
  622. (morph-text
  623. (cond ((null morph) nil)
  624. ((string-match "robinson:" morph)
  625. (setq morph-module "Robinson")
  626. (bm--morph-database-lookup (replace-regexp-in-string "robinson:" "" morph) morph-module robinson-hash))
  627. ((string-match "packard:" morph)
  628. (setq morph-module "Packard")
  629. (bm--morph-database-lookup (replace-regexp-in-string "packard:" "" morph) morph-module packard-hash))
  630. ((string-match "oshm:" morph)
  631. (setq morph-module "OSHM")
  632. (bm--morph-database-lookup (replace-regexp-in-string "oshm:" "" morph) morph-module oshm-hash)))))
  633. (when lex-text
  634. (setq lex-morph-text (string-trim (string-fill lex-text 75))))
  635. (when morph-text
  636. (setq lex-morph-text
  637. (concat lex-morph-text "\n\n"
  638. (string-trim (string-replace (concat "(" morph-module ")") "" morph-text)))))
  639. ;; This prevents bogus command substitutions in the tooltip by
  640. ;; removing backslashes. XXX I couldn't figure out a better way
  641. ;; to bypass command substitution in the tooltips.
  642. (setq lex-morph-text (replace-regexp-in-string "\\\\" "" lex-morph-text)))))
  643. (defun bm--process-word (item iproperties)
  644. "Word study. Add tooltips for definitions and morphologyl.
  645. Insert lemmas in buffer. Must be done after item is inserted in buffer."
  646. (let ((word (dom-text item))
  647. (morph (dom-attr item 'morph))
  648. (savlm (dom-attr item 'savlm))
  649. (divinename (dom-by-tag item 'divinename)))
  650. (insert word)
  651. (let ((refstart (- (point) (length word)))
  652. (refend (point)))
  653. ;; Red letter
  654. (when (plist-get iproperties 'jesus)
  655. (add-face-text-property refstart refend '(:foreground "red")))
  656. ;; Special case this. XXX Some modules do this differently.
  657. (when divinename
  658. (insert "LORD")
  659. (let* ((refstart (- (point) (length "LORD")))
  660. (refend (point))
  661. (strongs (dom-attr item 'savlm)))
  662. (string-match "strong:H.*" strongs)
  663. (let ((strongs-ref (match-string 0 strongs)))
  664. (add-face-text-property refstart refend 'bold)
  665. (put-text-property refstart refend 'keymap bm-hebrew-keymap)
  666. (put-text-property refstart refend 'help-echo 'bm--show-lex-morph)
  667. (put-text-property refstart refend 'strong strongs-ref))))
  668. ;; lexical definitions
  669. (when savlm
  670. (let ((matched nil))
  671. (cond ((string-match "strong:G.*" savlm) ; Greek
  672. (setq matched (match-string 0 savlm))
  673. (put-text-property refstart refend 'keymap bm-greek-keymap))
  674. ((string-match "strong:H.*" savlm) ; Hebrew
  675. (setq matched (match-string 0 savlm))
  676. (put-text-property refstart refend 'keymap bm-hebrew-keymap)))
  677. ;; Add help-echo, strongs reference for tooltips if match.
  678. (when matched
  679. (setq-local bm-has-strongs t)
  680. (put-text-property refstart refend 'help-echo 'bm--show-lex-morph)
  681. (put-text-property refstart refend 'strong matched))))
  682. ;; morphology
  683. (when morph
  684. (let ((matched nil))
  685. (cond ((string-match "robinson:.*" morph) ; Robinson Greek morphology
  686. (setq matched (match-string 0 morph)))
  687. ((string-match "packard:.*" morph) ; Packard Greek morphology --- LXX seems to use this
  688. (setq matched (match-string 0 morph)))
  689. ((string-match "oshm:.*" morph) ; OSHM Hebrew morphology
  690. (setq matched (match-string 0 morph)))
  691. (t nil
  692. ;;(message "Unknown morphology %s" morph)
  693. ))
  694. (when matched
  695. (setq-local bm-has-morphology t)
  696. (put-text-property refstart refend 'morph matched)
  697. (put-text-property refstart refend 'help-echo 'bm--show-lex-morph))))
  698. ;; Insert lemma into buffer. Lemma tag will be part of savlm item.
  699. (when (and bm-word-study-enabled savlm (string-match "lemma.*:.*" savlm))
  700. (dolist (word (split-string (match-string 0 savlm) " "))
  701. (setq word (replace-regexp-in-string "[.:a-zA-Z0-9]+" "" word))
  702. (insert " " word)
  703. (let ((refstart (- (point) 1 (length word)))
  704. (refend (point)))
  705. (add-face-text-property refstart refend '(:foreground "blue"))
  706. (put-text-property refstart refend 'keymap bm-lemma-keymap)))))))
  707. (defun bm--insert-domnode-recursive (node &optional iproperties notitle)
  708. "Recursively parses a domnode from `libxml-parse-html-region's usage on text
  709. produced by `bm--exec-diatheke'. Outputs text to active buffer
  710. with properties.
  711. In processing subnodes, each case will prepend a space if it needs it."
  712. (if (and bm-red-letter-enabled (equal (dom-attr node 'who) "Jesus"))
  713. ;; For red-letter display.
  714. (setq iproperties (plist-put iproperties 'jesus t))
  715. (setq iproperties nil))
  716. ;; (when (equal (dom-tag node) 'title)
  717. ;; ;; Space one line down so there's room for the title at the beginning.
  718. ;; (insert "\n"))
  719. (dolist (subnode (dom-children node))
  720. (cond ((null subnode) nil)
  721. ((stringp subnode)
  722. ;; Insert the subnode. Highlight the verse references.
  723. (insert subnode)
  724. ;; XXX this is still not quite right
  725. (let ((verse-start (string-match bm-verse-regexp subnode)))
  726. (when verse-start
  727. (let* ((verse-match (string-trim (match-string 0 subnode)))
  728. (verse-start-text (string-trim-left (substring subnode verse-start (length subnode))))
  729. (start (- (point) 1 (length (string-trim-right verse-start-text)))))
  730. (add-face-text-property start (+ start (length (string-trim-right verse-match))) '(:foreground "purple"))))))
  731. ((eq (dom-tag subnode) 'title)
  732. (if notitle nil
  733. (progn
  734. (setq bm-chapter-title subnode))))
  735. ((eq (dom-tag subnode) 'body) (bm--insert-domnode-recursive subnode iproperties notitle))
  736. ((eq (dom-tag subnode) 'seg) ; NASB Module uses this to indicate OT quotations (and others?).
  737. (bm--insert-domnode-recursive subnode iproperties notitle))
  738. ((eq (dom-tag subnode) 'q) (bm--insert-domnode-recursive subnode iproperties notitle))
  739. ((eq (dom-tag subnode) 'p) (bm--insert-domnode-recursive subnode iproperties notitle))
  740. ((eq (dom-tag subnode) 'w) (insert " ") (bm--process-word subnode iproperties))
  741. ((eq (dom-tag subnode) 'milestone) (insert "\n"))
  742. ((eq (dom-tag subnode) 'transchange) ; Word inserted by translation, not in original, give visual indication.
  743. (let ((word (dom-text subnode)))
  744. (insert " " word)
  745. (add-face-text-property (- (point) (length word)) (point) '(:foreground "gray50")))))))
  746. (defvar bm-debugme nil)
  747. (setf bm-debugme nil)
  748. (defun bm--display (&optional verse)
  749. "Renders text for `bible-mode'"
  750. ;; Clear buffer and insert the result of calling bm--exec-diatheke.
  751. (setq buffer-read-only nil)
  752. (erase-buffer)
  753. (setq bm-chapter-title nil
  754. bm-has-strongs nil
  755. bm-has-morphology nil)
  756. (insert (bm--exec-diatheke (concat bm-current-book-name ":" (number-to-string bm-current-chapter))))
  757. ;; Parse the xml in the buffer into a DOM tree.
  758. (let ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  759. ;; Render the DOM tree into the buffer.
  760. (if (not bm-debugme)
  761. (progn
  762. (erase-buffer)
  763. ;; Looking for the "body" tag in the DOM node.
  764. (bm--insert-domnode-recursive (dom-by-tag html-dom-tree 'body) nil nil)
  765. (goto-char (point-min)))
  766. ;;; (shr-render-region (point-min) (point-max))
  767. ))
  768. ;; Remove the module name from the buffer.
  769. (while (search-forward (concat "(" bm-module ")") nil t)
  770. (replace-match ""))
  771. ;; Set the mode line of the biffer.
  772. (setq mode-name (concat "Bible ("
  773. bm-module
  774. (when bm-has-strongs " Lex")
  775. (when bm-has-morphology " Morph")
  776. ")"))
  777. ;; Deal with chapter titles (i.e. in Psalms)
  778. ;; N.B. This won't change a title inside a chapter, and so it
  779. ;; doesn't work with Psalm 119 where the acrostic letters get
  780. ;; printed as "titles".
  781. (when bm-chapter-title ; This gets set in bm-insert-domnode-recursive.
  782. (goto-char (point-min))
  783. (let ((title-text (dom-texts bm-chapter-title))
  784. (refstart (point-min))
  785. refend)
  786. ;; Insert and make bold the title.
  787. (when (string-or-null-p title-text)
  788. (insert title-text "\n")
  789. (setq refend (point))
  790. (put-text-property refstart refend 'face 'bold))))
  791. (setq buffer-read-only t)
  792. (goto-char (point-min))
  793. ;; If optional verse specification go to that verse.
  794. (when verse
  795. (goto-char (string-match (regexp-opt `(,(concat ":" (number-to-string verse) ":"))) (buffer-string)))
  796. (beginning-of-line)))
  797. (defun bm--list-biblical-modules ()
  798. "Returns a list of accessible Biblical Text modules."
  799. (let ((text (bm--exec-diatheke "modulelist" nil nil nil "system"))
  800. modules)
  801. (catch 'done
  802. (dolist (line (split-string text "\n"))
  803. (when (equal line "Commentaries:")
  804. (throw 'done nil))
  805. (when (not (equal "Biblical Texts:" line))
  806. (push (split-string line " : ") modules))))
  807. modules))
  808. (defun bm-pick-module ()
  809. (interactive)
  810. (let ((item (get-text-property (point) 'module)))
  811. (setq-default bm-module item)
  812. (bible-open)))
  813. (defconst bm-module-map (make-keymap))
  814. (define-key bm-module-map [mouse-1] 'bm-pick-module)
  815. (defun bm-display-available-modules ()
  816. (interactive)
  817. (let ((buf (get-buffer-create "Modules"))
  818. (mods (bm--list-biblical-modules)))
  819. (set-buffer buf)
  820. (module-select-mode)
  821. (setq buffer-read-only nil)
  822. (erase-buffer)
  823. (dolist (mod mods)
  824. (insert
  825. (propertize (car mod)
  826. 'face 'bold
  827. 'module (car mod)
  828. 'help-echo (concat "Select " (car mod))
  829. 'keymap bm-module-map)
  830. "\t\t"
  831. (format "%s\n" (cadr mod))))
  832. (setq buffer-read-only t)
  833. (goto-char (point-min))
  834. (pop-to-buffer buf nil t)))
  835. ;;;;; Bible Searching
  836. (defun bm--open-search (query searchmode)
  837. "Opens a search buffer of QUERY using SEARCHMODE."
  838. (let ((buf (get-buffer-create (concat "*bible-search-" (downcase bm-module) "-" query "*"))))
  839. (set-buffer buf)
  840. (bible-search-mode)
  841. (bm--display-search query searchmode bm-module)
  842. (pop-to-buffer buf nil t)))
  843. (defun bm--display-search (query searchmode mod)
  844. "Renders results of search QUERY from SEARHCMODE"
  845. (setq buffer-read-only nil)
  846. (erase-buffer)
  847. (let* ((result (string-trim (replace-regexp-in-string
  848. "Entries .+?--" ""
  849. (bm--exec-diatheke query nil "plain" searchmode mod))))
  850. (match 0)
  851. (matchstr "")
  852. (verses nil)
  853. (query-verses "")
  854. fullverses)
  855. (if (equal result (concat "none (" bm-module ")"))
  856. (insert "No results found." (when (equal searchmode "lucene") " Verify index has been build with mkfastmod."))
  857. (progn
  858. (while match
  859. (setq match (string-match ".+?:[0-9]?[0-9]?" result (+ match (length matchstr)))
  860. matchstr (match-string 0 result))
  861. (when match
  862. (push
  863. ;; Massage match to make it more sortable, get rid of
  864. ;; some characters.
  865. (replace-regexp-in-string
  866. "I " "1"
  867. (replace-regexp-in-string
  868. "II " "2"
  869. (replace-regexp-in-string ".+; " "" matchstr)))
  870. verses)))
  871. (setq match 0)
  872. (setq verses (sort verses))
  873. (dolist (verse verses)
  874. (if query-verses
  875. (setq query-verses (concat query-verses ";" verse))
  876. (setq query-verses verse)))
  877. (setq fullverses (bm--exec-diatheke query-verses))
  878. (insert fullverses)
  879. (let* ((html-dom-tree (libxml-parse-html-region (point-min) (point-max))))
  880. (erase-buffer)
  881. (bm--insert-domnode-recursive (dom-by-tag html-dom-tree 'body) nil nil)
  882. (goto-char (point-min))
  883. (while (search-forward (concat "(" bm-module ")") nil t)
  884. (replace-match "")))))
  885. (setq mode-name (concat "Bible Search (" bm-module ")"))
  886. (setq buffer-read-only t)
  887. (setq-local bm-search-query query)
  888. (setq-local bm-search-mode searchmode)
  889. (goto-char (point-min))))
  890. ;;;;; Terms
  891. ;;(defun bm-display-morphology (morph)
  892. ;; ;; xxx Do something here?
  893. ;; )
  894. (defun bm--display-term (termtype)
  895. (setq buffer-read-only nil)
  896. (cl-do* ((text (buffer-string))
  897. (match (string-match "[0-9]+" text) (string-match "[0-9]+" text (match-end 0))))
  898. ((not match))
  899. (let* ((matchstr (match-string 0 text))
  900. (matchstrlen (length matchstr))
  901. (refstart (+ match 1))
  902. (refend (+ match 1 matchstrlen)))
  903. ;; This enables clicking on the Strong's numbers inside the term display.
  904. (cond ((eq termtype 'hebrew)
  905. (put-text-property refstart refend 'strong (concat "strong:H" matchstr))
  906. (put-text-property refstart refend 'keymap bm-hebrew-keymap)
  907. (add-face-text-property refstart refend `(:foreground "blue")))
  908. ((and (not bm-use-abbott) (eq termtype 'greek)) ; Abbott entries don't have Strong's numbers
  909. (put-text-property refstart refend 'strong (concat "strong:G" matchstr))
  910. (put-text-property refstart refend 'keymap bm-greek-keymap)
  911. (add-face-text-property refstart refend `(:foreground "blue"))))))
  912. (goto-char (point-min))
  913. ;; (while (search-forward (concat "(" bm-module ")") nil t)
  914. ;; (replace-match ""))
  915. (while (search-forward "()" nil t)
  916. (replace-match ""))
  917. (goto-char (point-min))
  918. (setq buffer-read-only t))
  919. (defun bm--open-term-hebrew (term)
  920. "Opens a buffer of the Strong's Hebrew TERM's definition"
  921. (let ((buf (get-buffer-create (concat "*bible-term-hebrew-" term "*"))))
  922. (set-buffer buf)
  923. (bible-term-hebrew-mode)
  924. (bm--display-term-hebrew term)
  925. (pop-to-buffer buf nil t)
  926. (fit-window-to-buffer)))
  927. (defun bm--open-term-greek (term)
  928. "Opens a buffer of the Strong's Greek TERM's definition"
  929. (let ((buf (get-buffer-create (concat "*bible-term-greek-" term "*"))))
  930. (set-buffer buf)
  931. (bible-term-greek-mode)
  932. (bm--display-term-greek term)
  933. (pop-to-buffer buf nil t)
  934. (fit-window-to-buffer)))
  935. ;;;
  936. ;;; Note: Hebrew display of terms is backwards; set bidi direction to
  937. ;;; 'left-to-right.
  938. (defun bm--display-term-hebrew (term)
  939. "Render the definition of the Strong's Hebrew TERM. Use
  940. bidi-paragraph-direction so the English text will render
  941. left-to-right. XXX Why doesn't this work for the tooltips?"
  942. (setq buffer-read-only nil)
  943. (erase-buffer)
  944. (insert (replace-regexp-in-string
  945. (regexp-opt `(,bm-hebrew-lexicon))
  946. ""
  947. (bm--exec-diatheke term nil "plain" nil bm-hebrew-lexicon)
  948. nil nil nil 7
  949. ))
  950. (bm--display-term 'hebrew)
  951. (setq bidi-paragraph-direction 'left-to-right))
  952. (defun bm--display-term-greek (term)
  953. "Render the definition of the Strong's Greek TERM."
  954. (setq buffer-read-only nil)
  955. (erase-buffer)
  956. (if bm-use-abbott
  957. (insert (replace-regexp-in-string "\(AbbottSmith\)" "" (bm--lookup-lex-def-abbott term)))
  958. (insert (replace-regexp-in-string
  959. (regexp-opt `(,bm-greek-lexicon))
  960. ""
  961. (bm--exec-diatheke term nil "plain" nil bm-greek-lexicon)
  962. nil nil nil 7
  963. )))
  964. (bm--display-term 'greek))
  965. (defun bm--set-location (book chapter &optional verse)
  966. "Sets the global chapter of the active `bible-mode' buffer."
  967. (setq-local bm-current-book book)
  968. (setq-local bm-current-book-name (car book))
  969. (setq-local bm-current-chapter chapter)
  970. (bm--display verse))
  971. ;;;;; Utilities
  972. (defun bm--list-number-range (min max &optional prefix)
  973. "Returns a list containing entries for each integer between min and max.
  974. Used in tandem with `completing-read' for chapter selection."
  975. (let ((range-list nil))
  976. (dotimes (num (1+ max))
  977. (when (>= num min)
  978. (push (cons (concat prefix (number-to-string num)) num) range-list)))
  979. (nreverse range-list)))
  980. ;;; Provides
  981. (provide 'bible-mode)
  982. ;; Local Variables:
  983. ;; read-symbol-shorthands: (("bm-" . "bible-mode-"))
  984. ;; End: