aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs68
1 files changed, 37 insertions, 31 deletions
diff --git a/src/main.rs b/src/main.rs
index ee50d78..b1586c3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,7 +30,7 @@ fn wrap(text: &str, width: usize) -> Vec<(usize, String)> {
}
'-' | '—' => {
if len > width {
- // `end = i + 1` will extend over the margin
+ // `end = i + len` is a hack that breaks here
word += 1;
} else {
end = i + c.len_utf8();
@@ -63,11 +63,6 @@ fn wrap(text: &str, width: usize) -> Vec<(usize, String)> {
struct Position(String, usize, usize);
-enum Direction {
- Forward,
- Backward,
-}
-
trait View {
fn run(&self, bk: &mut Bk, kc: KeyCode);
fn render(&self, bk: &Bk) -> Vec<String>;
@@ -109,36 +104,35 @@ impl View for Nav {
fn run(&self, bk: &mut Bk, kc: KeyCode) {
match kc {
KeyCode::Esc | KeyCode::Left | KeyCode::Char('h') | KeyCode::Char('q') => {
+ bk.chapter = bk.jump.0;
bk.view = Some(&Page);
}
KeyCode::Enter | KeyCode::Tab | KeyCode::Right | KeyCode::Char('l') => {
- bk.jump = (bk.chapter, bk.line);
- bk.chapter = bk.nav_idx;
bk.line = 0;
bk.view = Some(&Page);
}
KeyCode::Down | KeyCode::Char('j') => {
- if bk.nav_idx < bk.toc.len() - 1 {
- bk.nav_idx += 1;
- if bk.nav_idx == bk.nav_top + bk.rows {
+ if bk.chapter < bk.toc.len() - 1 {
+ bk.chapter += 1;
+ if bk.chapter == bk.nav_top + bk.rows {
bk.nav_top += 1;
}
}
}
KeyCode::Up | KeyCode::Char('k') => {
- if bk.nav_idx > 0 {
- if bk.nav_idx == bk.nav_top {
+ if bk.chapter > 0 {
+ if bk.chapter == bk.nav_top {
bk.nav_top -= 1;
}
- bk.nav_idx -= 1;
+ bk.chapter -= 1;
}
}
KeyCode::Home | KeyCode::Char('g') => {
- bk.nav_idx = 0;
+ bk.chapter = 0;
bk.nav_top = 0;
}
KeyCode::End | KeyCode::Char('G') => {
- bk.nav_idx = bk.toc.len() - 1;
+ bk.chapter = bk.toc.len() - 1;
bk.nav_top = bk.toc.len().saturating_sub(bk.rows);
}
_ => (),
@@ -151,7 +145,7 @@ impl View for Nav {
.iter()
.enumerate()
.map(|(i, label)| {
- if bk.nav_idx == bk.nav_top + i {
+ if bk.chapter == bk.nav_top + i {
format!("{}{}{}", Attribute::Reverse, label, Attribute::Reset)
} else {
label.to_string()
@@ -167,8 +161,8 @@ impl View for Page {
match kc {
KeyCode::Esc | KeyCode::Char('q') => bk.view = None,
KeyCode::Tab => {
- bk.nav_idx = bk.chapter;
- bk.nav_top = bk.nav_idx.saturating_sub(bk.rows - 1);
+ bk.nav_top = bk.chapter.saturating_sub(bk.rows - 1);
+ bk.jump = (bk.chapter, bk.line);
bk.view = Some(&Nav);
}
KeyCode::F(1) | KeyCode::Char('?') => bk.view = Some(&Help),
@@ -227,6 +221,11 @@ impl View for Page {
}
}
+enum Direction {
+ Forward,
+ Backward,
+}
+
struct Search;
impl View for Search {
fn run(&self, bk: &mut Bk, kc: KeyCode) {
@@ -277,25 +276,33 @@ impl View for Search {
}
}
+// ideally we could use string slices as pointers, but self referential structs are hard
struct Chapter {
+ // chapter text as a single string, used for search
text: String,
+ // line wrapped text
lines: Vec<String>,
+ // map a line to its position in the chapter
bytes: Vec<usize>,
}
struct Bk<'a> {
- view: Option<&'a dyn View>,
- chapter: usize,
- cols: u16,
- // ideally we could use string slices as pointers, but self referential structs are hard
+ // the book
chapters: Vec<Chapter>,
- nav_idx: usize,
- nav_top: usize,
+ toc: Vec<String>,
+ // position in the book
+ chapter: usize,
line: usize,
jump: (usize, usize),
+ // the terminal
+ cols: u16,
rows: usize,
- toc: Vec<String>,
+ // user config
max_width: u16,
+ // views don't have any internal state, which is great except for this single use nav_top
+ // search is also single use, but probably page view should use it
+ view: Option<&'a dyn View>,
+ nav_top: usize,
search: String,
}
@@ -321,7 +328,6 @@ impl Bk<'_> {
jump: (0, 0),
view: Some(&Page),
chapter: line.1,
- nav_idx: 0,
nav_top: 0,
toc: epub.nav,
chapters,
@@ -399,8 +405,8 @@ impl Bk<'_> {
let head = (self.chapter, self.chapters[self.chapter].bytes[self.line]);
match dir {
Direction::Forward => {
- let rest = (self.chapter + 1..self.chapters.len() - 1).map(|n| (n, 0));
- for (c, byte) in std::iter::once(head).chain(rest) {
+ let tail = (self.chapter + 1..self.chapters.len() - 1).map(|n| (n, 0));
+ for (c, byte) in std::iter::once(head).chain(tail) {
if let Some(index) = self.chapters[c].text[byte..].find(&self.search) {
self.line = match self.chapters[c].bytes.binary_search(&(byte + index)) {
Ok(n) => n,
@@ -413,10 +419,10 @@ impl Bk<'_> {
self.jump();
}
Direction::Backward => {
- let rest = (0..self.chapter - 1)
+ let tail = (0..self.chapter - 1)
.rev()
.map(|c| (c, self.chapters[c].text.len()));
- for (c, byte) in std::iter::once(head).chain(rest) {
+ for (c, byte) in std::iter::once(head).chain(tail) {
if let Some(index) = self.chapters[c].text[..byte].rfind(&self.search) {
self.line = match self.chapters[c].bytes.binary_search(&index) {
Ok(n) => n,