bible-tui 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. bible_tui-0.6.0/LICENSE +21 -0
  2. bible_tui-0.6.0/PKG-INFO +154 -0
  3. bible_tui-0.6.0/README.md +123 -0
  4. bible_tui-0.6.0/bible_tui/__init__.py +3 -0
  5. bible_tui-0.6.0/bible_tui/__main__.py +3 -0
  6. bible_tui-0.6.0/bible_tui/bibles/bible_asv.sqlite +0 -0
  7. bible_tui-0.6.0/bible_tui/bibles/bible_kjv.sqlite +0 -0
  8. bible_tui-0.6.0/bible_tui/cli.py +339 -0
  9. bible_tui-0.6.0/bible_tui/config.py +168 -0
  10. bible_tui-0.6.0/bible_tui/highlights.py +126 -0
  11. bible_tui-0.6.0/bible_tui/marks.py +108 -0
  12. bible_tui-0.6.0/bible_tui/paths.py +66 -0
  13. bible_tui-0.6.0/bible_tui/store.py +186 -0
  14. bible_tui-0.6.0/bible_tui/tui.py +3933 -0
  15. bible_tui-0.6.0/bible_tui/update.py +196 -0
  16. bible_tui-0.6.0/bible_tui/versions.py +135 -0
  17. bible_tui-0.6.0/bible_tui.egg-info/PKG-INFO +154 -0
  18. bible_tui-0.6.0/bible_tui.egg-info/SOURCES.txt +32 -0
  19. bible_tui-0.6.0/bible_tui.egg-info/dependency_links.txt +1 -0
  20. bible_tui-0.6.0/bible_tui.egg-info/entry_points.txt +2 -0
  21. bible_tui-0.6.0/bible_tui.egg-info/top_level.txt +1 -0
  22. bible_tui-0.6.0/pyproject.toml +68 -0
  23. bible_tui-0.6.0/setup.cfg +4 -0
  24. bible_tui-0.6.0/tests/test_about.py +93 -0
  25. bible_tui-0.6.0/tests/test_cli.py +96 -0
  26. bible_tui-0.6.0/tests/test_cmdline.py +122 -0
  27. bible_tui-0.6.0/tests/test_glyphs_and_messages.py +77 -0
  28. bible_tui-0.6.0/tests/test_highlights.py +206 -0
  29. bible_tui-0.6.0/tests/test_marks.py +219 -0
  30. bible_tui-0.6.0/tests/test_paths_and_config.py +109 -0
  31. bible_tui-0.6.0/tests/test_split.py +266 -0
  32. bible_tui-0.6.0/tests/test_tui.py +290 -0
  33. bible_tui-0.6.0/tests/test_update.py +298 -0
  34. bible_tui-0.6.0/tests/test_versions.py +95 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cody Sechelski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: bible-tui
3
+ Version: 0.6.0
4
+ Summary: A dependency-free terminal Bible reader with Vim-style navigation and pluggable versions
5
+ Author: Cody Sechelski
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/codysechelski/bible-tui
8
+ Project-URL: Documentation, https://github.com/codysechelski/bible-tui/blob/main/docs/features/index.md
9
+ Project-URL: Issues, https://github.com/codysechelski/bible-tui/issues
10
+ Project-URL: Changelog, https://github.com/codysechelski/bible-tui/blob/main/CHANGELOG.md
11
+ Keywords: bible,scripture,terminal,tui,curses,vim,kjv,reader
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console :: Curses
14
+ Classifier: Intended Audience :: End Users/Desktop
15
+ Classifier: Natural Language :: English
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Religion
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Dynamic: license-file
31
+
32
+ # bible-tui
33
+
34
+ <p align="center">
35
+ <a href="https://github.com/codysechelski/bible-tui/releases"><img alt="Release" src="https://img.shields.io/github/v/release/codysechelski/bible-tui?sort=semver"></a>
36
+ <a href="https://github.com/codysechelski/bible-tui/actions/workflows/release.yml"><img alt="CI" src="https://github.com/codysechelski/bible-tui/actions/workflows/release.yml/badge.svg"></a>
37
+ <a href="https://github.com/codysechelski/bible-tui/blob/main/pyproject.toml"><img alt="Python 3.9+" src="https://img.shields.io/badge/python-3.9%2B-blue"></a>
38
+ <a href="https://github.com/codysechelski/bible-tui/blob/main/pyproject.toml"><img alt="Dependencies: none" src="https://img.shields.io/badge/dependencies-none-brightgreen"></a>
39
+ </p>
40
+
41
+ A fast, dependency-free terminal Bible reader with Vim-style keys.
42
+
43
+ ![bible-tui showing Genesis 1 with the books and chapters panes](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/main.png)
44
+
45
+ - **[Read and navigate](https://github.com/codysechelski/bible-tui/blob/main/docs/features/navigation.md)** with arrows or Vim keys,
46
+ [jump to `John 3:16`](https://github.com/codysechelski/bible-tui/blob/main/docs/features/go-to-reference.md) with autocomplete, and use a Vim-style
47
+ [`:` command line](https://github.com/codysechelski/bible-tui/blob/main/docs/features/command-line.md).
48
+ - **[Search](https://github.com/codysechelski/bible-tui/blob/main/docs/features/search.md)** within a chapter, or
49
+ [fuzzy-find across the whole Bible](https://github.com/codysechelski/bible-tui/blob/main/docs/features/fuzzy-find.md) with a live preview.
50
+ - **[Compare two versions](https://github.com/codysechelski/bible-tui/blob/main/docs/features/split-view.md)** side by side, with verses lined up.
51
+ - **[Mark](https://github.com/codysechelski/bible-tui/blob/main/docs/features/marks.md) and [highlight](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md)** verses in six colors. They're
52
+ saved and show in every version.
53
+ - **[Bring any version](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md)**, in any language: each one is a SQLite file. The
54
+ public-domain KJV and ASV are included.
55
+
56
+ | | |
57
+ | --- | --- |
58
+ | ![Highlight color picker](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/multi-color-highlighting.png) | ![List of highlights](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/showing-jump-list-of-highlights.png) |
59
+ | [Highlight verses](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md) in six colors | [Jump back](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md) to any highlight or mark |
60
+ | ![Version picker](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/supports-multiple-versions.png) | ![A Portuguese Bible](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/support-for-non-english-bibles.png) |
61
+ | [Switch versions](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md) and keep your place | [Any language](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md#any-language) works |
62
+
63
+ <sub>The Portuguese screenshot shows the Bíblia Livre (BLIVRE), copyright © 2018 Diego Santos, Mario Sérgio, and Marco Teles, from
64
+ [eBible.org](https://ebible.org/porbr2018/copyright.htm), licensed under
65
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Not bundled with bible-tui.</sub>
66
+
67
+ **[See all features →](https://github.com/codysechelski/bible-tui/blob/main/docs/features/index.md)** Each one has a short animation.
68
+
69
+ ## Install
70
+
71
+ Requires Python 3.9+ on macOS or Linux, and nothing else.
72
+
73
+ ```sh
74
+ pipx install git+https://github.com/codysechelski/bible-tui
75
+ bible-tui
76
+ ```
77
+
78
+ Or run it from a clone with `./bible-tui`.
79
+
80
+ bible-tui checks for new releases once a day and tells you in the footer. Run `:update` to install the new
81
+ version and restart where you were. To turn the check off, set `"check_for_updates": false` (see
82
+ [update checks](https://github.com/codysechelski/bible-tui/blob/main/docs/configuration.md#update-checks)).
83
+
84
+ ## Usage
85
+
86
+ ```sh
87
+ bible-tui # open the default version
88
+ bible-tui asv # open a version by abbreviation
89
+ bible-tui ~/my.sqlite # open a database file directly
90
+ bible-tui --bibles-dir ~/Bibles # search an extra folder (repeatable)
91
+ bible-tui --list-versions
92
+ bible-tui --paths # show the config, state, and Bible folders in use
93
+ ```
94
+
95
+ | Action | Keys |
96
+ | --- | --- |
97
+ | Move | arrows, or `h` `j` `k` `l` |
98
+ | Next / previous chapter | `]` / `[` |
99
+ | Go to a reference | `ctrl+g`, or `:John 3:16` |
100
+ | Search the chapter / the whole Bible | `/` / `ctrl+s` |
101
+ | Switch version | `t` |
102
+ | Split to compare versions | `ctrl+w v` |
103
+ | Mark / highlight a verse | `m` / `c` |
104
+ | Copy the verse | `yy` |
105
+ | All commands and keys | `ctrl+p` or `?` |
106
+ | Quit | `q` |
107
+
108
+ Every action also works without Vim keys. The full list is in [docs/keybindings.md](https://github.com/codysechelski/bible-tui/blob/main/docs/keybindings.md).
109
+
110
+ ## Adding versions
111
+
112
+ Each version is one SQLite file. Copy a database into `~/.config/bible-tui/bibles/` and it appears in the
113
+ version picker. To make one from a published Bible (USFM, USX, OSIS, and others), use
114
+ [bible-tui-bible-converter](https://github.com/codysechelski/bible-tui-bible-converter). See
115
+ [docs/adding-bibles.md](https://github.com/codysechelski/bible-tui/blob/main/docs/adding-bibles.md) for the steps, and
116
+ [docs/bible-databases.md](https://github.com/codysechelski/bible-tui/blob/main/docs/bible-databases.md) for the schema.
117
+
118
+ ## Configuration
119
+
120
+ `config.json` lives in the app home: `~/.bible-tui` if that folder exists, otherwise `~/.config/bible-tui` (or
121
+ `$BIBLE_TUI_HOME`).
122
+
123
+ ```json
124
+ {
125
+ "default_version": "KJV",
126
+ "bibles_dirs": ["~/Bibles"],
127
+ "book_order": "bible",
128
+ "nerd_font": false,
129
+ "remember_split": false,
130
+ "yank_format": "{text}"
131
+ }
132
+ ```
133
+
134
+ Every setting is described in [docs/configuration.md](https://github.com/codysechelski/bible-tui/blob/main/docs/configuration.md).
135
+
136
+ ## Development
137
+
138
+ ```sh
139
+ python3 -m unittest discover -s tests -t .
140
+ python3 -m py_compile bible_tui/*.py tests/*.py
141
+ ```
142
+
143
+ Releases are automatic from [Conventional Commits](https://www.conventionalcommits.org/) on `main`: `fix:`
144
+ releases a patch and `feat:` a minor version. See [CONTRIBUTING.md](https://github.com/codysechelski/bible-tui/blob/main/CONTRIBUTING.md) to get involved, and
145
+ [ROADMAP.md](https://github.com/codysechelski/bible-tui/blob/main/ROADMAP.md) for what's planned.
146
+
147
+ ## Bible texts
148
+
149
+ The bundled KJV and ASV come from [eBible.org](https://ebible.org) and are in the public domain. Sources and
150
+ notes are in [docs/bible-databases.md](https://github.com/codysechelski/bible-tui/blob/main/docs/bible-databases.md#bundled-versions).
151
+
152
+ ## Licence
153
+
154
+ bible-tui is released under the [MIT License](https://github.com/codysechelski/bible-tui/blob/main/LICENSE). The bundled Bible texts are in the public domain.
@@ -0,0 +1,123 @@
1
+ # bible-tui
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/codysechelski/bible-tui/releases"><img alt="Release" src="https://img.shields.io/github/v/release/codysechelski/bible-tui?sort=semver"></a>
5
+ <a href="https://github.com/codysechelski/bible-tui/actions/workflows/release.yml"><img alt="CI" src="https://github.com/codysechelski/bible-tui/actions/workflows/release.yml/badge.svg"></a>
6
+ <a href="https://github.com/codysechelski/bible-tui/blob/main/pyproject.toml"><img alt="Python 3.9+" src="https://img.shields.io/badge/python-3.9%2B-blue"></a>
7
+ <a href="https://github.com/codysechelski/bible-tui/blob/main/pyproject.toml"><img alt="Dependencies: none" src="https://img.shields.io/badge/dependencies-none-brightgreen"></a>
8
+ </p>
9
+
10
+ A fast, dependency-free terminal Bible reader with Vim-style keys.
11
+
12
+ ![bible-tui showing Genesis 1 with the books and chapters panes](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/main.png)
13
+
14
+ - **[Read and navigate](https://github.com/codysechelski/bible-tui/blob/main/docs/features/navigation.md)** with arrows or Vim keys,
15
+ [jump to `John 3:16`](https://github.com/codysechelski/bible-tui/blob/main/docs/features/go-to-reference.md) with autocomplete, and use a Vim-style
16
+ [`:` command line](https://github.com/codysechelski/bible-tui/blob/main/docs/features/command-line.md).
17
+ - **[Search](https://github.com/codysechelski/bible-tui/blob/main/docs/features/search.md)** within a chapter, or
18
+ [fuzzy-find across the whole Bible](https://github.com/codysechelski/bible-tui/blob/main/docs/features/fuzzy-find.md) with a live preview.
19
+ - **[Compare two versions](https://github.com/codysechelski/bible-tui/blob/main/docs/features/split-view.md)** side by side, with verses lined up.
20
+ - **[Mark](https://github.com/codysechelski/bible-tui/blob/main/docs/features/marks.md) and [highlight](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md)** verses in six colors. They're
21
+ saved and show in every version.
22
+ - **[Bring any version](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md)**, in any language: each one is a SQLite file. The
23
+ public-domain KJV and ASV are included.
24
+
25
+ | | |
26
+ | --- | --- |
27
+ | ![Highlight color picker](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/multi-color-highlighting.png) | ![List of highlights](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/showing-jump-list-of-highlights.png) |
28
+ | [Highlight verses](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md) in six colors | [Jump back](https://github.com/codysechelski/bible-tui/blob/main/docs/features/highlights.md) to any highlight or mark |
29
+ | ![Version picker](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/supports-multiple-versions.png) | ![A Portuguese Bible](https://raw.githubusercontent.com/codysechelski/bible-tui/main/docs/screenshots/images/support-for-non-english-bibles.png) |
30
+ | [Switch versions](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md) and keep your place | [Any language](https://github.com/codysechelski/bible-tui/blob/main/docs/features/versions.md#any-language) works |
31
+
32
+ <sub>The Portuguese screenshot shows the Bíblia Livre (BLIVRE), copyright © 2018 Diego Santos, Mario Sérgio, and Marco Teles, from
33
+ [eBible.org](https://ebible.org/porbr2018/copyright.htm), licensed under
34
+ [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Not bundled with bible-tui.</sub>
35
+
36
+ **[See all features →](https://github.com/codysechelski/bible-tui/blob/main/docs/features/index.md)** Each one has a short animation.
37
+
38
+ ## Install
39
+
40
+ Requires Python 3.9+ on macOS or Linux, and nothing else.
41
+
42
+ ```sh
43
+ pipx install git+https://github.com/codysechelski/bible-tui
44
+ bible-tui
45
+ ```
46
+
47
+ Or run it from a clone with `./bible-tui`.
48
+
49
+ bible-tui checks for new releases once a day and tells you in the footer. Run `:update` to install the new
50
+ version and restart where you were. To turn the check off, set `"check_for_updates": false` (see
51
+ [update checks](https://github.com/codysechelski/bible-tui/blob/main/docs/configuration.md#update-checks)).
52
+
53
+ ## Usage
54
+
55
+ ```sh
56
+ bible-tui # open the default version
57
+ bible-tui asv # open a version by abbreviation
58
+ bible-tui ~/my.sqlite # open a database file directly
59
+ bible-tui --bibles-dir ~/Bibles # search an extra folder (repeatable)
60
+ bible-tui --list-versions
61
+ bible-tui --paths # show the config, state, and Bible folders in use
62
+ ```
63
+
64
+ | Action | Keys |
65
+ | --- | --- |
66
+ | Move | arrows, or `h` `j` `k` `l` |
67
+ | Next / previous chapter | `]` / `[` |
68
+ | Go to a reference | `ctrl+g`, or `:John 3:16` |
69
+ | Search the chapter / the whole Bible | `/` / `ctrl+s` |
70
+ | Switch version | `t` |
71
+ | Split to compare versions | `ctrl+w v` |
72
+ | Mark / highlight a verse | `m` / `c` |
73
+ | Copy the verse | `yy` |
74
+ | All commands and keys | `ctrl+p` or `?` |
75
+ | Quit | `q` |
76
+
77
+ Every action also works without Vim keys. The full list is in [docs/keybindings.md](https://github.com/codysechelski/bible-tui/blob/main/docs/keybindings.md).
78
+
79
+ ## Adding versions
80
+
81
+ Each version is one SQLite file. Copy a database into `~/.config/bible-tui/bibles/` and it appears in the
82
+ version picker. To make one from a published Bible (USFM, USX, OSIS, and others), use
83
+ [bible-tui-bible-converter](https://github.com/codysechelski/bible-tui-bible-converter). See
84
+ [docs/adding-bibles.md](https://github.com/codysechelski/bible-tui/blob/main/docs/adding-bibles.md) for the steps, and
85
+ [docs/bible-databases.md](https://github.com/codysechelski/bible-tui/blob/main/docs/bible-databases.md) for the schema.
86
+
87
+ ## Configuration
88
+
89
+ `config.json` lives in the app home: `~/.bible-tui` if that folder exists, otherwise `~/.config/bible-tui` (or
90
+ `$BIBLE_TUI_HOME`).
91
+
92
+ ```json
93
+ {
94
+ "default_version": "KJV",
95
+ "bibles_dirs": ["~/Bibles"],
96
+ "book_order": "bible",
97
+ "nerd_font": false,
98
+ "remember_split": false,
99
+ "yank_format": "{text}"
100
+ }
101
+ ```
102
+
103
+ Every setting is described in [docs/configuration.md](https://github.com/codysechelski/bible-tui/blob/main/docs/configuration.md).
104
+
105
+ ## Development
106
+
107
+ ```sh
108
+ python3 -m unittest discover -s tests -t .
109
+ python3 -m py_compile bible_tui/*.py tests/*.py
110
+ ```
111
+
112
+ Releases are automatic from [Conventional Commits](https://www.conventionalcommits.org/) on `main`: `fix:`
113
+ releases a patch and `feat:` a minor version. See [CONTRIBUTING.md](https://github.com/codysechelski/bible-tui/blob/main/CONTRIBUTING.md) to get involved, and
114
+ [ROADMAP.md](https://github.com/codysechelski/bible-tui/blob/main/ROADMAP.md) for what's planned.
115
+
116
+ ## Bible texts
117
+
118
+ The bundled KJV and ASV come from [eBible.org](https://ebible.org) and are in the public domain. Sources and
119
+ notes are in [docs/bible-databases.md](https://github.com/codysechelski/bible-tui/blob/main/docs/bible-databases.md#bundled-versions).
120
+
121
+ ## Licence
122
+
123
+ bible-tui is released under the [MIT License](https://github.com/codysechelski/bible-tui/blob/main/LICENSE). The bundled Bible texts are in the public domain.
@@ -0,0 +1,3 @@
1
+ """A dependency-free terminal Bible reader."""
2
+
3
+ __version__ = "0.6.0"
@@ -0,0 +1,3 @@
1
+ from bible_tui.cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,339 @@
1
+ """Command line entry point: resolve paths and settings, pick a version, run the TUI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import curses
7
+ import locale
8
+ import os
9
+ import sys
10
+ from collections.abc import Mapping, Sequence
11
+ from pathlib import Path
12
+
13
+ from bible_tui import __version__
14
+ from bible_tui.config import ConfigError, Settings, load_settings, load_state, save_state
15
+ from bible_tui.highlights import HighlightStore
16
+ from bible_tui.marks import MarkStore
17
+ from bible_tui.update import (
18
+ RESTORE_POSITION_ENV,
19
+ UPDATED_FROM_ENV,
20
+ UpdateCheck,
21
+ detect_install,
22
+ format_position,
23
+ parse_position,
24
+ restart_command,
25
+ )
26
+ from bible_tui.paths import (
27
+ BIBLES_DIR_NAME,
28
+ BUNDLED_BIBLES_DIR,
29
+ CONFIG_FILE_NAME,
30
+ HIGHLIGHTS_FILE_NAME,
31
+ MARKS_FILE_NAME,
32
+ STATE_FILE_NAME,
33
+ home_candidates,
34
+ resolve_home,
35
+ )
36
+ from bible_tui.versions import (
37
+ BibleVersion,
38
+ Discovery,
39
+ VersionError,
40
+ discover_versions,
41
+ find_version,
42
+ inspect_database,
43
+ )
44
+
45
+
46
+ class CliError(Exception):
47
+ pass
48
+
49
+
50
+ def build_parser() -> argparse.ArgumentParser:
51
+ parser = argparse.ArgumentParser(
52
+ prog="bible-tui",
53
+ description="Browse the Bible in your terminal.",
54
+ )
55
+ parser.add_argument(
56
+ "bible",
57
+ nargs="?",
58
+ metavar="VERSION",
59
+ help="version to open: an abbreviation such as KJV, or a path to a .sqlite file",
60
+ )
61
+ parser.add_argument(
62
+ "-d",
63
+ "--bibles-dir",
64
+ action="append",
65
+ default=[],
66
+ type=Path,
67
+ metavar="DIR",
68
+ help="also load Bible databases from DIR (repeatable; searched before the defaults)",
69
+ )
70
+ parser.add_argument(
71
+ "-c",
72
+ "--config",
73
+ type=Path,
74
+ metavar="FILE",
75
+ help=f"use FILE instead of {CONFIG_FILE_NAME} in the app home",
76
+ )
77
+ parser.add_argument("-l", "--list-versions", action="store_true", help="list available versions and exit")
78
+ parser.add_argument("--paths", action="store_true", help="show where bible-tui looks for files and exit")
79
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
80
+ return parser
81
+
82
+
83
+ def bibles_search_path(home: Path, settings: Settings, extra_dirs: Sequence[Path]) -> list[Path]:
84
+ """Directories scanned for databases, highest priority first."""
85
+ return [
86
+ *(directory.expanduser() for directory in extra_dirs),
87
+ *settings.bibles_dirs,
88
+ home / BIBLES_DIR_NAME,
89
+ BUNDLED_BIBLES_DIR,
90
+ ]
91
+
92
+
93
+ def choose_version(
94
+ discovery: Discovery,
95
+ requested: str | None,
96
+ settings: Settings,
97
+ state: Mapping[str, object],
98
+ ) -> tuple[BibleVersion, list[str]]:
99
+ """Pick the version to open: the command line, then ``default_version``, then the
100
+ last version used, then the first available."""
101
+ warnings: list[str] = []
102
+ versions = discovery.versions
103
+ if requested:
104
+ version = find_version(versions, requested)
105
+ if version is None:
106
+ available = ", ".join(v.abbreviation for v in versions) or "none"
107
+ raise CliError(f"unknown version {requested!r} (available: {available})")
108
+ return version, warnings
109
+ if settings.default_version:
110
+ version = find_version(versions, settings.default_version)
111
+ if version is not None:
112
+ return version, warnings
113
+ warnings.append(f"default_version {settings.default_version!r} not found; ignoring it")
114
+ last = state.get("last_version")
115
+ if isinstance(last, str):
116
+ version = find_version(versions, last)
117
+ if version is not None:
118
+ return version, warnings
119
+ if not versions:
120
+ raise CliError("no Bible databases found (run `bible-tui --paths` to see where to put them)")
121
+ return versions[0], warnings
122
+
123
+
124
+ def remembered_split(
125
+ settings: Settings,
126
+ state: Mapping[str, object],
127
+ versions: Sequence[BibleVersion],
128
+ ) -> tuple[str, BibleVersion] | None:
129
+ """The split to reopen at launch, when ``remember_split`` is on and its version still exists."""
130
+ from bible_tui.tui import SPLITS
131
+
132
+ if not settings.remember_split:
133
+ return None
134
+ split = state.get("split")
135
+ abbreviation = state.get("split_version")
136
+ if split not in SPLITS or not isinstance(abbreviation, str):
137
+ return None
138
+ version = find_version(versions, abbreviation)
139
+ return (split, version) if version is not None else None
140
+
141
+
142
+ def describe(path: Path) -> str:
143
+ home = str(Path.home())
144
+ text = str(path)
145
+ return "~" + text[len(home) :] if text == home or text.startswith(home + os.sep) else text
146
+
147
+
148
+ def print_paths(home: Path, config_path: Path, search_path: Sequence[Path], env: Mapping[str, str]) -> None:
149
+ def status(path: Path) -> str:
150
+ return "" if path.exists() else " (missing)"
151
+
152
+ print("App home lookup (first existing wins):")
153
+ for candidate in home_candidates(env):
154
+ marker = "*" if candidate == home else " "
155
+ print(f" {marker} {describe(candidate)}{status(candidate)}")
156
+ print(f"\nConfig: {describe(config_path)}{status(config_path)}")
157
+ state_path = home / STATE_FILE_NAME
158
+ print(f"State: {describe(state_path)}{status(state_path)}")
159
+ marks_path = home / MARKS_FILE_NAME
160
+ print(f"Marks: {describe(marks_path)}{status(marks_path)}")
161
+ highlights_path = home / HIGHLIGHTS_FILE_NAME
162
+ print(f"Highlights: {describe(highlights_path)}{status(highlights_path)}")
163
+ print("\nBible folders (earlier folders win when versions share a name):")
164
+ for index, directory in enumerate(search_path, 1):
165
+ print(f" {index}. {describe(directory)}{status(directory)}")
166
+
167
+
168
+ def print_versions(discovery: Discovery, current: BibleVersion | None) -> None:
169
+ if not discovery.versions:
170
+ print("No Bible databases found.")
171
+ width = max((len(version.abbreviation) for version in discovery.versions), default=0)
172
+ for version in discovery.versions:
173
+ marker = "*" if version == current else " "
174
+ name = version.name if version.name != version.abbreviation else ""
175
+ print(f"{marker} {version.abbreviation.ljust(width)} {name}".rstrip())
176
+ print(f" {' ' * width} {describe(version.path)}")
177
+ if discovery.skipped:
178
+ print("\nSkipped:")
179
+ for path, reason in discovery.skipped:
180
+ print(f" {describe(path)}: {reason}")
181
+
182
+
183
+ def run_tui(
184
+ version: BibleVersion,
185
+ versions: Sequence[BibleVersion],
186
+ settings: Settings,
187
+ home: Path,
188
+ state: dict[str, object],
189
+ marks: MarkStore,
190
+ highlights: HighlightStore,
191
+ warnings: Sequence[str] = (),
192
+ notice: str = "",
193
+ position: tuple[int, int, int] | None = None,
194
+ restart_args: Sequence[str] = (),
195
+ ) -> None:
196
+ from bible_tui.store import BibleStore
197
+ from bible_tui.tui import MESSAGE_UPDATE, BibleTui
198
+
199
+ def remember(chosen: BibleVersion) -> None:
200
+ state["last_version"] = chosen.abbreviation
201
+ save_state(home, state)
202
+
203
+ def remember_split(split: str | None, split_version: BibleVersion | None) -> None:
204
+ if not settings.remember_split:
205
+ return
206
+ if split is None or split_version is None:
207
+ state.pop("split", None)
208
+ state.pop("split_version", None)
209
+ else:
210
+ state["split"] = split
211
+ state["split_version"] = split_version.abbreviation
212
+ save_state(home, state)
213
+
214
+ update_check = None
215
+ if settings.check_for_updates:
216
+ update_check = UpdateCheck(__version__, state, save=lambda: save_state(home, state))
217
+ update_check.start()
218
+ restart = "" # The position to reopen at, once :update asks for a restart.
219
+
220
+ def run(stdscr: curses.window) -> None:
221
+ nonlocal restart
222
+ app = BibleTui(
223
+ stdscr,
224
+ BibleStore(version.path),
225
+ version,
226
+ versions,
227
+ alphabetical=settings.book_order == "alphabetical",
228
+ on_version_change=remember,
229
+ on_split_change=remember_split,
230
+ marks=marks,
231
+ highlights=highlights,
232
+ nerd_font=settings.nerd_font,
233
+ yank_format=settings.yank_format,
234
+ update_check=update_check,
235
+ install=detect_install(),
236
+ )
237
+ split = remembered_split(settings, state, versions)
238
+ if split is not None:
239
+ app.open_split(*split, restoring=True)
240
+ if position is not None:
241
+ app.jump_to_key(position)
242
+ if warnings:
243
+ app.show_error(f"warning: {warnings[0]}")
244
+ elif notice:
245
+ app.set_message(notice, MESSAGE_UPDATE)
246
+ try:
247
+ app.run()
248
+ finally:
249
+ app.close()
250
+ if app.restart_requested:
251
+ restart = format_position(app.restart_position) or "-"
252
+
253
+ curses.wrapper(run)
254
+ if restart:
255
+ # Replace this process with the updated code. The new one says what changed and reopens here.
256
+ command = restart_command(restart_args)
257
+ sys.stdout.flush()
258
+ os.execve(command[0], command, {**os.environ, UPDATED_FROM_ENV: __version__, RESTORE_POSITION_ENV: restart})
259
+
260
+
261
+ def update_notice(env: Mapping[str, str]) -> str:
262
+ """After ``:update`` restarts the app, say whether the version changed."""
263
+ previous = env.get(UPDATED_FROM_ENV)
264
+ if not previous:
265
+ return ""
266
+ if previous == __version__:
267
+ return f"bible-tui {__version__} is up to date"
268
+ return f"updated bible-tui {previous} to {__version__}"
269
+
270
+
271
+ def main(argv: Sequence[str] | None = None, env: Mapping[str, str] | None = None) -> int:
272
+ env = os.environ if env is None else env
273
+ argv = list(sys.argv[1:] if argv is None else argv)
274
+ args = build_parser().parse_args(argv)
275
+ try:
276
+ home = resolve_home(env)
277
+ if args.config is not None and not args.config.exists():
278
+ raise CliError(f"config file not found: {args.config}")
279
+ config_path = args.config if args.config is not None else home / CONFIG_FILE_NAME
280
+ settings = load_settings(config_path, env)
281
+ for warning in settings.warnings:
282
+ print(f"bible-tui: warning: {warning}", file=sys.stderr)
283
+ search_path = bibles_search_path(home, settings, args.bibles_dir)
284
+
285
+ if args.paths:
286
+ print_paths(home, config_path, search_path, env)
287
+ return 0
288
+
289
+ discovery = discover_versions(search_path)
290
+ if args.bible and Path(args.bible).expanduser().is_file():
291
+ try:
292
+ opened = inspect_database(Path(args.bible).expanduser())
293
+ except VersionError as error:
294
+ raise CliError(f"{args.bible}: not a usable Bible database ({error})") from None
295
+ others = tuple(v for v in discovery.versions if v.abbreviation.casefold() != opened.abbreviation.casefold())
296
+ discovery = Discovery((opened, *others), discovery.skipped)
297
+ args.bible = opened.abbreviation
298
+
299
+ state = load_state(home)
300
+ if args.list_versions:
301
+ current = None
302
+ try:
303
+ current, _ = choose_version(discovery, args.bible, settings, state)
304
+ except CliError:
305
+ pass
306
+ print_versions(discovery, current)
307
+ return 0
308
+
309
+ version, choice_warnings = choose_version(discovery, args.bible, settings, state)
310
+ for warning in choice_warnings:
311
+ print(f"bible-tui: warning: {warning}", file=sys.stderr)
312
+ marks = MarkStore.load(home)
313
+ if marks.load_error:
314
+ choice_warnings.append(f"marks are read-only until this is fixed: {marks.load_error}")
315
+ print(f"bible-tui: warning: {choice_warnings[-1]}", file=sys.stderr)
316
+ highlights = HighlightStore.load(home)
317
+ if highlights.load_error:
318
+ choice_warnings.append(f"highlights are read-only until this is fixed: {highlights.load_error}")
319
+ print(f"bible-tui: warning: {choice_warnings[-1]}", file=sys.stderr)
320
+ warnings = [*settings.warnings, *choice_warnings]
321
+ except (CliError, ConfigError) as error:
322
+ print(f"bible-tui: {error}", file=sys.stderr)
323
+ return 1
324
+
325
+ locale.setlocale(locale.LC_ALL, "")
326
+ run_tui(
327
+ version,
328
+ discovery.versions,
329
+ settings,
330
+ home,
331
+ state,
332
+ marks,
333
+ highlights,
334
+ warnings,
335
+ notice=update_notice(env),
336
+ position=parse_position(env.get(RESTORE_POSITION_ENV)),
337
+ restart_args=argv,
338
+ )
339
+ return 0