summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Tay <sam.chong.tay@gmail.com>2020-06-12 20:25:43 -0700
committerSam Tay <sam.chong.tay@gmail.com>2020-06-12 20:25:43 -0700
commit2d845e3dbe856265155e28c19afc741f28a29fe2 (patch)
treea2dfeb3fde9f629fecddf59300a6471ef58284ca
parent0a22b2493552b6d83e3880aab06271ede9321c3f (diff)
Fix a lot of deeper issues
This thing is back on track.
-rw-r--r--README.md6
-rw-r--r--TODO.md25
-rw-r--r--roadmap.md5
-rw-r--r--src/tui/app.rs91
-rw-r--r--src/tui/markdown.rs4
5 files changed, 92 insertions, 39 deletions
diff --git a/README.md b/README.md
index 4661553..6986419 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/TODO.md b/TODO.md
index bb66b4c..ffbcf3d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -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
diff --git a/roadmap.md b/roadmap.md
index 17da35a..7744145 100644
--- a/roadmap.md
+++ b/roadmap.md
@@ -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 {