diff options
author | Sam Tay <sam.chong.tay@gmail.com> | 2020-06-12 20:25:43 -0700 |
---|---|---|
committer | Sam Tay <sam.chong.tay@gmail.com> | 2020-06-12 20:25:43 -0700 |
commit | 2d845e3dbe856265155e28c19afc741f28a29fe2 (patch) | |
tree | a2dfeb3fde9f629fecddf59300a6471ef58284ca | |
parent | 0a22b2493552b6d83e3880aab06271ede9321c3f (diff) |
Fix a lot of deeper issues
This thing is back on track.
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | TODO.md | 25 | ||||
-rw-r--r-- | roadmap.md | 5 | ||||
-rw-r--r-- | src/tui/app.rs | 91 | ||||
-rw-r--r-- | src/tui/markdown.rs | 4 |
5 files changed, 92 insertions, 39 deletions
@@ -2,6 +2,12 @@ **Note:** under development, not ready for prime time. +### to troubleshoot +``` +export RUST_BACKTRACE=full +cargo run -- how do I exit Vim > test.txt 2>&1 +``` + ### api keys According to the [StackExchange docs](https://api.stackexchange.com/docs/throttle), most users should be fine @@ -7,22 +7,27 @@ benefit of incorporating termimad features will not be felt. But, this is changing [soon](https://meta.stackexchange.com/q/348746). ### v0.2.0 -0. Cursive interface for viewing questions and answers -1. Focus in one of four areas -3. arrows and vim bindings to manipulate lists and scroll, depending on focus -2. `/` to bring up new search prompt -1. For sure: Use custom code palette nodes in theme.toml to style markdown -1. Layout cycling? -0. use [par_iter](https://github.com/rayon-rs/rayon) for parsing markdown and manual <kbd> removers, etc. + +#### Cursive interface for viewing questions and answers +0. Split up `run` function with many little helper functions defining callbacks + and stull general to both the question and answer panes +1. Make each pane scrollable +2. Handle focus with tab and h,l +4. Allow cycling layouts +5. Init with smaller layout if terminal size smaller +6. Small text at bottom with '?' to bring up key mapping dialog +7. `/` to bring up new search prompt + +#### other +1. Use [par_iter](https://github.com/rayon-rs/rayon) for text preprocess & parsing? + +### v0.2.1 1. Add `lucky: bool` to config, but 2. add --lucky and --no-lucky conflicting flags to cli 3. If --lucky, async get 1 result while getting limit results 4. Display with [space] to see more, any other key to exit. 1. maybe <query> is optional, and leaving blank starts up TUI? -### v0.2.1 -1. Add colors.yml configuration - ### v0.2.2 1. Site can be multiple 2. do tokio async on SE api @@ -11,12 +11,11 @@ [x] Finishing touches on cli opts like --set-api-key, etc. ### v0.2.0 -[ ] Add --no-lucky option -[ ] For --lucky, async parsing first q/a, then the rest [ ] Termimad interface for viewing multiple results ### v0.2.1 -[ ] Add colors.yml configuration +[ ] Add --no-lucky option +[ ] For --lucky, async parsing first q/a, then the rest ### v0.2.2 [ ] Support multiple --site args & searches diff --git a/src/tui/app.rs b/src/tui/app.rs index 0d355e0..200c2b1 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1,9 +1,12 @@ use cursive::event::EventResult; +use cursive::theme::{BaseColor, Color, Effect, Style}; use cursive::traits::Nameable; -use cursive::view::{View, ViewWrapper}; +use cursive::utils::span::SpannedString; use cursive::views::{ - LinearLayout, NamedView, OnEventView, ResizedView, SelectView, TextContent, TextView, + LinearLayout, NamedView, OnEventView, Panel, ResizedView, SelectView, TextContent, TextView, }; +use cursive::XY; +use std::cmp; use std::collections::HashMap; use std::sync::Arc; @@ -50,6 +53,8 @@ pub enum Mode { } // TODO make my own views for lists, md, etc, and use cursive::inner_getters! +// (or at least type synonyms) +// and abstract out the common builder funcs //pub struct App<'a> { //pub stackexchange: StackExchange, @@ -71,6 +76,7 @@ pub enum Mode { pub fn run(qs: Vec<Question>) -> Result<()> { let mut siv = cursive::default(); siv.load_theme_file(config::theme_file_name()?).unwrap(); // TODO dont unwrap + let XY { x, y } = siv.screen_size(); //app state //put this in siv.set_user_data? hmm @@ -90,35 +96,62 @@ pub fn run(qs: Vec<Question>) -> Result<()> { let current_question = TextContent::new(""); // init would be great let question_view: NamedView<TextView> = TextView::new_with_content(current_question.clone()).with_name("question"); + let question_view = Panel::new(question_view); // answer view let current_answer = TextContent::new(""); // init would be great let answer_view: NamedView<TextView> = TextView::new_with_content(current_answer.clone()).with_name("answer"); + let answer_view = Panel::new(answer_view); // question list view //let question_map_ = question_map.clone(); //let current_question_ = current_question.clone(); - let question_list_view: NamedView<SelectView<u32>> = SelectView::new() + // TODO fuck select view has indexing capabilities :facepalm: + let mut question_list_view: NamedView<SelectView<u32>> = SelectView::new() .with_all(qs.into_iter().map(|q| (q.title, q.id))) - .on_select(move |s, qid| { + .on_select(move |mut s, qid| { let q = question_map.get(qid).unwrap(); + let XY { x, y: _y } = s.screen_size(); current_question.set_content(markdown::parse(&q.body)); - s.call_on_name("answer_list", move |v: &mut SelectView<u32>| { + let cb = s.call_on_name("answer_list", move |v: &mut SelectView<u32>| { v.clear(); v.add_all(q.answers.iter().map(|a| { - // TODO dedup newlines, split newlines, join with spaces - // add ellipses - // set const for cutoff + // TODO make a damn func for this // add score & accepted checkmark - let mut a_body = a.body.clone(); - a_body.truncate(50); - (markdown::parse(a_body), a.id) + let width = cmp::min(a.body.len(), x / 2); + let a_body = a.body[..width].to_owned(); + let md = markdown::preview(x, a_body); + let color = if a.score > 0 { + Color::Light(BaseColor::Green) + } else { + Color::Light(BaseColor::Red) + }; + let mut preview = SpannedString::styled( + format!("({}) ", a.score), + Style::merge(&[Style::from(color), Style::from(Effect::Bold)]), + ); + if a.is_accepted { + preview.append_styled( + "\u{2713} ", // "✔ " + Style::merge(&[ + Style::from(Color::Light(BaseColor::Green)), + Style::from(Effect::Bold), + ]), + ); + } + preview.append(md); + (preview, a.id) })); - }); // TODO select initial answer - }) // TODO select initial question + v.set_selection(0) + }); + if let Some(cb) = cb { + cb(&mut s) + } + }) .with_name("question_list"); let question_list_view = make_select_scrollable(question_list_view); + let question_list_view = Panel::new(question_list_view).title("Questions"); // answer list view //let answer_map_ = answer_map.clone(); @@ -130,24 +163,33 @@ pub fn run(qs: Vec<Question>) -> Result<()> { }) .with_name("answer_list"); let answer_list_view = make_select_scrollable(answer_list_view); + let answer_list_view = Panel::new(answer_list_view).title("Answers"); //TODO eventually do this in the right place, e.g. abstract out md //parser, write benches, & do within threads siv.add_layer( LinearLayout::horizontal() - .child(ResizedView::with_min_width( - 30, + .child(ResizedView::with_fixed_width( + x / 2, LinearLayout::vertical() - .child(ResizedView::with_min_height(15, question_list_view)) - .child(ResizedView::with_min_height(20, question_view)), + .child(ResizedView::with_fixed_height(y / 3, question_list_view)) + .child(ResizedView::with_fixed_height(2 * y / 3, question_view)), )) - .child(ResizedView::with_min_width( - 30, + .child(ResizedView::with_fixed_width( + x / 2, LinearLayout::vertical() - .child(ResizedView::with_min_height(15, answer_list_view)) - .child(ResizedView::with_min_height(20, answer_view)), + .child(ResizedView::with_fixed_height(y / 3, answer_list_view)) + .child(ResizedView::with_fixed_height(2 * y / 3, answer_view)), )), ); + let cb = siv.call_on_name("question_list", |v: &mut SelectView<u32>| { + v.set_selection(0) + }); + if let Some(cb) = cb { + cb(&mut siv) + } + cursive::logger::init(); + siv.add_global_callback('?', cursive::Cursive::toggle_debug_console); siv.run(); Ok(()) } @@ -158,14 +200,13 @@ pub fn run(qs: Vec<Question>) -> Result<()> { fn make_select_scrollable( view: NamedView<SelectView<u32>>, ) -> OnEventView<NamedView<SelectView<u32>>> { + // Clobber existing functionality: OnEventView::new(view) .on_pre_event_inner('k', |s, _| { - s.get_mut().select_up(1); - Some(EventResult::Consumed(None)) + Some(EventResult::Consumed(Some(s.get_mut().select_up(1)))) }) .on_pre_event_inner('j', |s, _| { - s.get_mut().select_down(1); - Some(EventResult::Consumed(None)) + Some(EventResult::Consumed(Some(s.get_mut().select_down(1)))) }) } diff --git a/src/tui/markdown.rs b/src/tui/markdown.rs index 123fe3c..c784c63 100644 --- a/src/tui/markdown.rs +++ b/src/tui/markdown.rs @@ -51,7 +51,9 @@ where }) .collect(); - StyledString::with_spans(input, spans) + let mut prev = StyledString::with_spans(input, spans); + prev.append_plain("..."); + prev } fn preprocess(input: String) -> String { |