diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 85 | ||||
| -rw-r--r-- | src/view.rs | 219 | 
2 files changed, 160 insertions, 144 deletions
| diff --git a/src/main.rs b/src/main.rs index d330bf8..f2ba392 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,13 +12,12 @@ use std::{      collections::HashMap,      env, fs,      io::{stdout, Write}, -    iter,      process::exit,  };  use unicode_width::UnicodeWidthChar;  mod view; -use view::{Page, Search, Toc, View}; +use view::{Page, Toc, View};  mod epub;  use epub::Chapter; @@ -133,7 +132,7 @@ impl Bk<'_> {                      .title                      .chars()                      .take(width - 1) -                    .chain(iter::once('…')) +                    .chain(std::iter::once('…'))                      .collect();              }          } @@ -160,9 +159,6 @@ impl Bk<'_> {          bk      } -    fn pad(&self) -> u16 { -        self.cols.saturating_sub(self.max_width) / 2 -    }      fn run(&mut self) -> crossterm::Result<()> {          let mut stdout = stdout();          queue!( @@ -225,8 +221,8 @@ impl Bk<'_> {          )?;          terminal::disable_raw_mode()      } -    fn mark(&mut self, c: char) { -        self.mark.insert(c, (self.chapter, self.line)); +    fn chap(&self) -> &Chapter { +        &self.chapters[self.chapter]      }      fn jump(&mut self, (c, l): (usize, usize)) {          self.mark('\''); @@ -238,74 +234,11 @@ impl Bk<'_> {          self.chapter = c;          self.line = l;      } -    fn chap(&self) -> &Chapter { -        &self.chapters[self.chapter] -    } -    fn next_chapter(&mut self) { -        if self.chapter < self.chapters.len() - 1 { -            self.chapter += 1; -            self.line = 0; -        } -    } -    fn prev_chapter(&mut self) { -        if self.chapter > 0 { -            self.chapter -= 1; -            self.line = 0; -        } -    } -    fn scroll_down(&mut self, n: usize) { -        if self.line + self.rows < self.chap().lines.len() { -            self.line += n; -        } else { -            self.next_chapter(); -        } -    } -    fn scroll_up(&mut self, n: usize) { -        if self.line > 0 { -            self.line = self.line.saturating_sub(n); -        } else if self.chapter > 0 { -            self.chapter -= 1; -            self.line = self.chap().lines.len().saturating_sub(self.rows); -        } -    } -    fn start_search(&mut self, dir: Direction) { -        self.mark('\''); -        self.query.clear(); -        self.dir = dir; -        self.view = &Search; +    fn mark(&mut self, c: char) { +        self.mark.insert(c, (self.chapter, self.line));      } -    fn search(&mut self, args: SearchArgs) -> bool { -        let (start, end) = self.chap().lines[self.line]; -        match args.dir { -            Direction::Next => { -                let byte = if args.skip { end } else { start }; -                let head = (self.chapter, byte); -                let tail = (self.chapter + 1..self.chapters.len() - 1).map(|n| (n, 0)); -                for (c, byte) in iter::once(head).chain(tail) { -                    if let Some(index) = self.chapters[c].text[byte..].find(&self.query) { -                        self.line = get_line(&self.chapters[c].lines, index + byte); -                        self.chapter = c; -                        return true; -                    } -                } -                false -            } -            Direction::Prev => { -                let byte = if args.skip { start } else { end }; -                let head = (self.chapter, byte); -                let tail = (0..self.chapter) -                    .rev() -                    .map(|c| (c, self.chapters[c].text.len())); -                for (c, byte) in iter::once(head).chain(tail) { -                    if let Some(index) = self.chapters[c].text[..byte].rfind(&self.query) { -                        self.line = get_line(&self.chapters[c].lines, index); -                        self.chapter = c; -                        return true; -                    } -                } -                false -            } -        } +    fn pad(&self) -> u16 { +        self.cols.saturating_sub(self.max_width) / 2      }  } @@ -377,7 +310,7 @@ fn init() -> Result<State> {      }      let (path, chapter, byte) = match (&save, &path) { -        (Err(_), None) => return Err(anyhow::anyhow!("no path arg and no or invalid save file")), +        (Err(_), None) => return Err(anyhow::anyhow!("no path arg and no valid save file")),          (Err(_), Some(p)) => (p, 0, 0),          (Ok(save), None) => {              let &(chapter, byte) = save.files.get(&save.last).unwrap(); diff --git a/src/view.rs b/src/view.rs index 6d4af33..2028fda 100644 --- a/src/view.rs +++ b/src/view.rs @@ -8,7 +8,7 @@ use crossterm::{  use std::cmp::{min, Ordering};  use unicode_width::UnicodeWidthChar; -use crate::{get_line, Bk, Direction, SearchArgs}; +use crate::{Bk, Direction, SearchArgs};  pub trait View {      fn render(&self, bk: &Bk) -> Vec<String>; @@ -148,8 +148,8 @@ impl View for Toc {                  bk.view = &Page;              }              Enter | Right | Char('l') => { -                bk.cursor = 0;                  bk.line = 0; +                bk.cursor = 0;                  bk.view = &Page;              }              Down | Char('j') => self.next(bk, 1), @@ -182,54 +182,90 @@ impl View for Toc {  }  pub struct Page; -impl View for Page { -    fn on_mouse(&self, bk: &mut Bk, e: MouseEvent) { -        match e.kind { -            MouseEventKind::Down(_) => { -                let c = bk.chap(); -                let line = bk.line + e.row as usize; - -                if e.column < bk.pad() || line >= c.lines.len() { -                    return; -                } -                let (start, end) = c.lines[line]; -                let line_col = (e.column - bk.pad()) as usize; +impl Page { +    fn next_chapter(&self, bk: &mut Bk) { +        if bk.chapter < bk.chapters.len() - 1 { +            bk.chapter += 1; +            bk.line = 0; +        } +    } +    fn prev_chapter(&self, bk: &mut Bk) { +        if bk.chapter > 0 { +            bk.chapter -= 1; +            bk.line = 0; +        } +    } +    fn scroll_down(&self, bk: &mut Bk, n: usize) { +        if bk.line + bk.rows < bk.chap().lines.len() { +            bk.line += n; +        } else { +            self.next_chapter(bk); +        } +    } +    fn scroll_up(&self, bk: &mut Bk, n: usize) { +        if bk.line > 0 { +            bk.line = bk.line.saturating_sub(n); +        } else if bk.chapter > 0 { +            bk.chapter -= 1; +            bk.line = bk.chap().lines.len().saturating_sub(bk.rows); +        } +    } +    fn click(&self, bk: &mut Bk, e: MouseEvent) { +        let c = bk.chap(); +        let line = bk.line + e.row as usize; -                let mut cols = 0; -                let mut found = false; -                let mut byte = start; -                for (i, c) in c.text[start..end].char_indices() { -                    cols += c.width().unwrap(); -                    if cols > line_col { -                        byte += i; -                        found = true; -                        break; -                    } -                } +        if e.column < bk.pad() || line >= c.lines.len() { +            return; +        } +        let (start, end) = c.lines[line]; +        let line_col = (e.column - bk.pad()) as usize; -                if !found { -                    return; -                } +        let mut cols = 0; +        let mut found = false; +        let mut byte = start; +        for (i, c) in c.text[start..end].char_indices() { +            cols += c.width().unwrap(); +            if cols > line_col { +                byte += i; +                found = true; +                break; +            } +        } -                let r = c.links.binary_search_by(|&(start, end, _)| { -                    if start > byte { -                        Ordering::Greater -                    } else if end <= byte { -                        Ordering::Less -                    } else { -                        Ordering::Equal -                    } -                }); +        if !found { +            return; +        } -                if let Ok(i) = r { -                    let url = &c.links[i].2; -                    let &(chapter, byte) = bk.links.get(url).unwrap(); -                    let line = get_line(&bk.chapters[chapter].lines, byte); -                    bk.jump((chapter, line)); -                } +        let r = c.links.binary_search_by(|&(start, end, _)| { +            if start > byte { +                Ordering::Greater +            } else if end <= byte { +                Ordering::Less +            } else { +                Ordering::Equal              } -            MouseEventKind::ScrollDown => bk.scroll_down(3), -            MouseEventKind::ScrollUp => bk.scroll_up(3), +        }); + +        if let Ok(i) = r { +            let url = &c.links[i].2; +            let &(chapter, byte) = bk.links.get(url).unwrap(); +            let line = super::get_line(&bk.chapters[chapter].lines, byte); +            bk.jump((chapter, line)); +        } +    } +    fn start_search(&self, bk: &mut Bk, dir: Direction) { +        bk.mark('\''); +        bk.query.clear(); +        bk.dir = dir; +        bk.view = &Search; +    } +} +impl View for Page { +    fn on_mouse(&self, bk: &mut Bk, e: MouseEvent) { +        match e.kind { +            MouseEventKind::Down(_) => self.click(bk, e), +            MouseEventKind::ScrollDown => self.scroll_down(bk, 3), +            MouseEventKind::ScrollUp => self.scroll_up(bk, 3),              _ => (),          }      } @@ -245,19 +281,27 @@ impl View for Page {              Char('m') => bk.view = &Mark,              Char('\'') => bk.view = &Jump,              Char('i') => bk.view = &Metadata, -            Char('?') => bk.start_search(Direction::Prev), -            Char('/') => bk.start_search(Direction::Next), +            Char('?') => self.start_search(bk, Direction::Prev), +            Char('/') => self.start_search(bk, Direction::Next),              Char('N') => { -                bk.search(SearchArgs { -                    dir: Direction::Prev, -                    skip: true, -                }); +                Search::search( +                    &Search, +                    bk, +                    SearchArgs { +                        dir: Direction::Prev, +                        skip: true, +                    }, +                );              }              Char('n') => { -                bk.search(SearchArgs { -                    dir: Direction::Next, -                    skip: true, -                }); +                Search::search( +                    &Search, +                    bk, +                    SearchArgs { +                        dir: Direction::Next, +                        skip: true, +                    }, +                );              }              End | Char('G') => {                  bk.mark('\''); @@ -267,16 +311,16 @@ impl View for Page {                  bk.mark('\'');                  bk.line = 0;              } -            Char('d') => bk.scroll_down(bk.rows / 2), -            Char('u') => bk.scroll_up(bk.rows / 2), -            Up | Char('k') => bk.scroll_up(3), +            Char('d') => self.scroll_down(bk, bk.rows / 2), +            Char('u') => self.scroll_up(bk, bk.rows / 2), +            Up | Char('k') => self.scroll_up(bk, 3),              Left | PageUp | Char('b') | Char('h') => { -                bk.scroll_up(bk.rows); +                self.scroll_up(bk, bk.rows);              } -            Down | Char('j') => bk.scroll_down(3), -            Right | PageDown | Char('f') | Char('l') | Char(' ') => bk.scroll_down(bk.rows), -            Char('[') => bk.prev_chapter(), -            Char(']') => bk.next_chapter(), +            Down | Char('j') => self.scroll_down(bk, 3), +            Right | PageDown | Char('f') | Char('l') | Char(' ') => self.scroll_down(bk, bk.rows), +            Char('[') => self.prev_chapter(bk), +            Char(']') => self.next_chapter(bk),              _ => (),          }      } @@ -374,11 +418,47 @@ impl View for Page {  }  pub struct Search; +impl Search { +    fn search(&self, bk: &mut Bk, args: SearchArgs) -> bool { +        let (start, end) = bk.chap().lines[bk.line]; +        match args.dir { +            Direction::Next => { +                let byte = if args.skip { end } else { start }; +                let head = (bk.chapter, byte); +                let tail = (bk.chapter + 1..bk.chapters.len() - 1).map(|n| (n, 0)); +                for (c, byte) in std::iter::once(head).chain(tail) { +                    if let Some(index) = bk.chapters[c].text[byte..].find(&bk.query) { +                        bk.line = super::get_line(&bk.chapters[c].lines, index + byte); +                        bk.chapter = c; +                        return true; +                    } +                } +                false +            } +            Direction::Prev => { +                let byte = if args.skip { start } else { end }; +                let head = (bk.chapter, byte); +                let tail = (0..bk.chapter) +                    .rev() +                    .map(|c| (c, bk.chapters[c].text.len())); +                for (c, byte) in std::iter::once(head).chain(tail) { +                    if let Some(index) = bk.chapters[c].text[..byte].rfind(&bk.query) { +                        bk.line = super::get_line(&bk.chapters[c].lines, index); +                        bk.chapter = c; +                        return true; +                    } +                } +                false +            } +        } +    } +}  impl View for Search {      fn on_key(&self, bk: &mut Bk, kc: KeyCode) {          match kc {              Esc => {                  bk.jump_reset(); +                bk.query.clear();                  bk.view = &Page;              }              Enter => { @@ -387,10 +467,13 @@ impl View for Search {              Backspace => {                  bk.query.pop();                  bk.jump_reset(); -                bk.search(SearchArgs { -                    dir: bk.dir.clone(), -                    skip: false, -                }); +                self.search( +                    bk, +                    SearchArgs { +                        dir: bk.dir.clone(), +                        skip: false, +                    }, +                );              }              Char(c) => {                  bk.query.push(c); @@ -398,7 +481,7 @@ impl View for Search {                      dir: bk.dir.clone(),                      skip: false,                  }; -                if !bk.search(args) { +                if self.search(bk, args) {                      bk.jump_reset();                  }              } | 
