diff options
author | Sam Tay <sam.chong.tay@gmail.com> | 2020-06-11 00:50:41 -0700 |
---|---|---|
committer | Sam Tay <sam.chong.tay@gmail.com> | 2020-06-11 00:50:41 -0700 |
commit | 37ab4c85f9d8bf1e113c4b1b419c0d17ec72b84e (patch) | |
tree | 694e9e3ad3303f8213432d6fd44a6455ba982a21 | |
parent | 7ac11eee2b38522f22fa44227ffb6411b4f758e4 (diff) |
...Use cursive instead
-rw-r--r-- | Cargo.lock | 359 | ||||
-rw-r--r-- | Cargo.toml | 19 | ||||
-rw-r--r-- | TODO.md | 17 | ||||
-rw-r--r-- | src/config.rs | 1 | ||||
-rw-r--r-- | src/error.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 38 | ||||
-rw-r--r-- | src/stackexchange.rs | 2 | ||||
-rw-r--r-- | src/tui/app.rs | 192 | ||||
-rw-r--r-- | src/tui/list.rs | 60 | ||||
-rw-r--r-- | src/tui/markdown.hs | 3 | ||||
-rw-r--r-- | src/tui/markdown.rs | 330 | ||||
-rw-r--r-- | src/tui/mod.rs | 2 |
12 files changed, 660 insertions, 367 deletions
@@ -7,6 +7,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + +[[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -22,6 +31,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] +name = "array-macro" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e97b4e522f9e55523001238ac59d13a8603af57f69980de5d8de4bbbe8ada6" + +[[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -92,12 +107,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] name = "cc" version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -110,6 +119,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] name = "clap" version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -118,7 +138,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -134,6 +154,26 @@ dependencies = [ ] [[package]] +name = "const-random" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" +dependencies = [ + "getrandom", + "proc-macro-hack", +] + +[[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -262,6 +302,86 @@ dependencies = [ ] [[package]] +name = "cursive" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9f12332ab2bca26979ef00cfef9a1c2e287db03b787a83d892ad9961f81374" +dependencies = [ + "ahash", + "cfg-if", + "crossbeam-channel", + "cursive_core", + "enumset", + "lazy_static", + "libc", + "log", + "maplit", + "ncurses", + "signal-hook", + "term_size", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "cursive_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe232694c965c211d5fbd8111ae89a5939b7b169cefcac4f8b6f83dde889e10" +dependencies = [ + "ahash", + "chrono", + "crossbeam-channel", + "enum-map", + "enumset", + "lazy_static", + "libc", + "log", + "num", + "owning_ref", + "signal-hook", + "toml", + "unicode-segmentation", + "unicode-width", + "xi-unicode", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] name = "directories" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -290,12 +410,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] -name = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" - -[[package]] name = "encoding_rs" version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -305,6 +419,49 @@ dependencies = [ ] [[package]] +name = "enum-map" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a375f899a53b9848ad9fb459b5bf90e4851ae5d9fea89134b062dc1828b26e" +dependencies = [ + "array-macro", + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57001dfb2532f5a103ff869656887fae9a8defa7d236f3e39d2ee86ed629ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumset" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3691ce759534316ad900d57dd8e688e2c4263f9750c0f7c1e9b9a4516d4ca241" +dependencies = [ + "enumset_derive", + "num-traits", +] + +[[package]] +name = "enumset_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74bef436ac71820c5cf768d7af9ba33121246b09a00e09a55d94ef8095a875ac" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "flate2" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -552,6 +709,12 @@ dependencies = [ ] [[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -581,15 +744,6 @@ dependencies = [ ] [[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -651,6 +805,12 @@ dependencies = [ ] [[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -761,6 +921,17 @@ dependencies = [ ] [[package]] +name = "ncurses" +version = "5.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] name = "net2" version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -772,6 +943,70 @@ dependencies = [ ] [[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + +[[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -821,6 +1056,15 @@ dependencies = [ ] [[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + +[[package]] name = "parking_lot" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -916,6 +1160,17 @@ dependencies = [ ] [[package]] +name = "pulldown-cmark" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] name = "quote" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1127,9 +1382,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" +checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" dependencies = [ "dtoa", "linked-hash-map", @@ -1176,17 +1431,19 @@ version = "0.1.0" dependencies = [ "clap", "crossterm", + "cursive", "directories", "flate2", "lazy_static", "minimad", + "pulldown-cmark", "reqwest", "serde", "serde_json", "serde_yaml", "termimad", "thiserror", - "tui", + "unicode-width", ] [[package]] @@ -1202,12 +1459,24 @@ dependencies = [ ] [[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" + +[[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] name = "syn" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1233,6 +1502,16 @@ dependencies = [ ] [[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] name = "termimad" version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1327,6 +1606,15 @@ dependencies = [ ] [[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] name = "tower-service" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1339,21 +1627,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] -name = "tui" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9533d39bef0ae8f510e8a99d78702e68d1bbf0b98a78ec9740509d287010ae1e" -dependencies = [ - "bitflags", - "cassowary", - "crossterm", - "either", - "itertools", - "unicode-segmentation", - "unicode-width", -] - -[[package]] name = "unicase" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1575,6 +1848,12 @@ dependencies = [ ] [[package]] +name = "xi-unicode" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7395cdb9d0a6219fa0ea77d08c946adf9c1984c72fcd443ace30365f3daadef7" + +[[package]] name = "yaml-rust" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -7,16 +7,21 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -crossterm = { version = "0.17", features = ["event-stream"] } +thiserror = "1.0" clap = "2.33" directories = "2.0" -flate2 = "1.0" -reqwest = { version = "0.10", features = ["blocking"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.8" -termimad = "0.8" -minimad = "0.6" -tui = { version = "0.9", default-features = false, features = ['crossterm'] } + +reqwest = { version = "0.10", features = ["blocking"] } +flate2 = "1.0" + lazy_static = "1.4" -thiserror = "1.0" +minimad = "0.6" +termimad = "0.8" + +crossterm = { version = "0.17", features = ["event-stream"] } +pulldown-cmark = { version = "0.7", default-features = false } +unicode-width = "0.1.5" +cursive = { version = "0.15", features = ["toml"] } @@ -1,12 +1,25 @@ # TODO -1. maybe <query> is optional, and leaving blank starts up TUI? + +## TUI considerations +Going with cursive because it is way more flexible than tui-rs. +**Important note** Tables are not currently allowed in stackexchange... so the +benefit of incorporating termimad features will not be felt. But, this is +changing [soon](https://meta.stackexchange.com/q/348746). + ### v0.2.0 -0. Termimad interface for viewing questions and answers +0. remove cruft from playing with tui-rs +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. 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 diff --git a/src/config.rs b/src/config.rs index af51de3..58b51f6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -45,6 +45,7 @@ fn write_config(config: &Config) -> Result<()> { Ok(serde_yaml::to_writer(file, config)?) } +// TODO consider switching to .toml to be consistent with colors.toml fn config_file_name() -> Result<PathBuf> { Ok(project_dir()?.config_dir().join("config.yml")) } diff --git a/src/error.rs b/src/error.rs index 86fb55f..35847c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -26,8 +26,8 @@ pub enum Error { ProjectDir, #[error("Empty sites file in cache")] EmptySites, - #[error("Sorry, couldn't find any answers for your query")] - NoResults, + //#[error("Sorry, couldn't find any answers for your query")] + //NoResults, } #[derive(Debug)] diff --git a/src/main.rs b/src/main.rs index f66e192..e099d21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_imports, unused_mut, unused_variables)] mod cli; mod config; mod error; @@ -65,40 +64,13 @@ fn main() { } if let Some(q) = opts.query { - //let se = StackExchange::new(config); - // TODO async - //let qs = se.search(&q)?; - //let ans = que.first().ok_or(Error::NoResults)?.answers.first().expect( - //"StackExchange returned a question with no answers; \ - //this shouldn't be possible!", - //); - // TODO eventually do this in the right place, e.g. abstract out md parser, write benches, & do within threads - // TODO do the below for --lucky with option to continue - //let md = ans.body.replace("<kbd>", "**[").replace("</kbd>", "]**"); + let se = StackExchange::new(config); + //TODO async + let qs = se.search(&q)?; + //TODO do the print_text below for --lucky with option to continue //skin.print_text(&md); - use crate::stackexchange::{Answer, Question}; - tui::run(vec![Question { - id: 42, - score: 323, - title: "How do I exit Vim?".to_string(), - body: "yo this be my problem dawg but don't say **do** `this`".to_string(), - answers: vec![ - Answer { - id: 422, - score: -4, - body: "# bad\nthis is my bad answer".to_string(), - is_accepted: false, - }, - Answer { - id: 423, - score: 23, - body: "this is a *good* answer tho".to_string(), - is_accepted: true, - }, - ], - }])?; + tui::run(qs)?; } - Ok(()) })() .or_else(|e: Error| { diff --git a/src/stackexchange.rs b/src/stackexchange.rs index 8c65942..183e64f 100644 --- a/src/stackexchange.rs +++ b/src/stackexchange.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_imports, unused_mut, unused_variables)] use flate2::read::GzDecoder; use reqwest::blocking::Client; use reqwest::Url; @@ -25,6 +24,7 @@ const SE_SITES_PAGESIZE: u16 = 10000; /// This structure allows interacting with parts of the StackExchange /// API, using the `Config` struct to determine certain API settings and options. +// TODO should my se structs have &str instead of String? pub struct StackExchange { client: Client, config: Config, diff --git a/src/tui/app.rs b/src/tui/app.rs index b02da8c..c079828 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -1,24 +1,22 @@ -#![allow(dead_code, unused_imports, unused_mut, unused_variables)] -use crossterm::execute; -use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen}; -use std::io; -use std::io::Write; -use tui::backend::CrosstermBackend; -use tui::buffer::Buffer; -use tui::layout::{Alignment, Constraint, Direction, Layout as TuiLayout, Rect}; -use tui::style::Style; -use tui::widgets::{Block, Borders, Paragraph, Text, Widget}; -use tui::Terminal; +use cursive::views::TextView; +use super::markdown; use crate::error::Result; -use crate::stackexchange::{Answer, Question}; -use crate::tui::enumerable::Enum; -use crate::tui::list; +use crate::stackexchange::Question; + // ----------------------------------------- // |question title list|answer preview list| 1/3 // ----------------------------------------- // |question body |answer body | 2/3 // ----------------------------------------- +// TODO <shift+HJKL> moves layout boundaries +// TODO <hjkl> to move focus? at least for lists.. +// TODO <space> to cycle layout +// TODO <?> to bring up key mappings +// TODO query initial term size to choose initial layout + +// TODO Circular Focus handles layout & focus & stuff +// TODO these might be "layers" ? pub enum Layout { BothColumns, SingleColumn, @@ -26,6 +24,7 @@ pub enum Layout { } // Tab to cycle focus +// TODO use NamedView pub enum Focus { QuestionList, AnswerList, @@ -55,106 +54,81 @@ pub enum Mode { //} // TODO maybe a struct like Tui::new(stackexchange) creates App::new and impls tui.run()? - +// TODO views::SelectView? // TODO take async questions // TODO take the entire SE struct for future questions pub fn run(qs: Vec<Question>) -> Result<()> { - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen)?; - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - terminal.hide_cursor()?; - - //terminal.draw(|mut f| ui::draw(&mut f, &mut app))?; - terminal.draw(|mut f| { - let chunks = TuiLayout::default() - .direction(Direction::Horizontal) - .constraints( - [ - // TODO this depends on app.ratio and app.layout - Constraint::Ratio(1, 2), - Constraint::Ratio(1, 2), - ] - .as_ref(), - ) - .split(f.size()); - // TODO this depends on app.layout - let question_pane = TuiLayout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)].as_ref()) - .split(chunks[0]); - let answer_pane = TuiLayout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)].as_ref()) - .split(chunks[1]); - let block = Block::default().title("Questions").borders(Borders::ALL); - f.render_widget(block, question_pane[0]); - let block = Block::default().title("Answers").borders(Borders::ALL); - f.render_widget(block, answer_pane[0]); - // for now, just text - let t = qs[0].body.clone(); - let text = [Text::raw(t)]; - let p = Paragraph::new(text.iter()) - .block(Block::default().borders(Borders::ALL)) - .alignment(Alignment::Left) - .wrap(true); - f.render_widget(p, question_pane[1]); - let t = qs[0].answers[0].body.clone(); - let text = [Text::raw(t)]; - let p = Paragraph::new(text.iter()) - .block(Block::default().borders(Borders::ALL)) - .alignment(Alignment::Left) - .wrap(true); - f.render_widget(p, answer_pane[1]); - })?; - //disable_raw_mode()?; - std::thread::sleep(std::time::Duration::from_millis(10000)); - execute!(terminal.backend_mut(), LeaveAlternateScreen,)?; - terminal.show_cursor()?; + let mut siv = cursive::default(); + + //TODO eventually do this in the right place, e.g. abstract out md + //parser, write benches, & do within threads + let md = markdown::parse( + qs[0].answers[0] + .body + .clone() + .replace("<kbd>", "**[") + .replace("</kbd>", "]**"), + ); + siv.add_layer(TextView::new(md)); + + siv.run(); Ok(()) } -//fmttext = termimad::FmtText::from(madskin, md, mwidth) -//// whose dispaly instance just calls -//for line in &self.lines { -//self.skin.write_fmt_line(f, line, self.width, false)?; -//writeln!(f)?; -//} - -////OR directly with madskin -//skin.write_in_area_on(w: Writer, md: &str, area: &Area) - -// lowest level -//skin.write_fmt_composite: actually applies styles to pieces of md text - -// little higher -//skin.write_fmt_line: also handles lines such as table borders - -// higher -//text.write_on: actually queues stuff up, cycling through its self.text.lines() and -//handling scrollbar - -// TODO shift HJKL moves layout boundaries -// TODO hjkl to move focus? at least for lists.. - -// TODO should my se structs have &str instead of String? - -// Space to cycle layout -// TODO query initial term size to init layout - -impl Enum for Layout { - fn to_enum(&self) -> u8 { - match self { - Layout::BothColumns => 0, - Layout::SingleColumn => 1, - Layout::FullScreen => 2, - } - } - fn from_enum(i: u8) -> Self { - match i % 3 { - 0 => Layout::BothColumns, - 1 => Layout::SingleColumn, - _ => Layout::FullScreen, - } +// TODO prettier and more valuable tests +#[cfg(test)] +mod tests { + use super::*; + use crate::stackexchange::{Answer, Question}; + #[test] + fn test_app() { + let ans_body = r#" +Also try the iter: +1. asdf +2. asfd +0. asdfa sfsdf + +but + + cargo build --example stderr + +and then you run it with + + cd "$(target/debug/examples/stderr)" + cd `(target/debug/examples/stderr)` + +what the application prints on stdout is used as argument to `cd`. + +Try it out. + +Hit any key to quit this screen: + +* **1** will print `..` +* **2** will print `/` +* **3** will print `~` +* or anything else to print this text (so that you may copy-paste) +"#; + let qs = vec![Question { + id: 42, + score: 323, + title: "How do I exit Vim?".to_string(), + body: "yo this be my problem dawg but don't say **do** `this`".to_string(), + answers: vec![ + Answer { + id: 422, + score: -4, + body: ans_body.to_string(), + is_accepted: false, + }, + Answer { + id: 423, + score: 23, + body: "this is a *good* answer tho".to_string(), + is_accepted: true, + }, + ], + }]; + + assert_eq!(run(qs).unwrap(), ()); } } diff --git a/src/tui/list.rs b/src/tui/list.rs deleted file mode 100644 index 81d992d..0000000 --- a/src/tui/list.rs +++ /dev/null @@ -1,60 +0,0 @@ -use tui::widgets::ListState; - -pub struct StatefulList<T> { - pub state: ListState, - pub items: Vec<T>, -} - -impl<T> StatefulList<T> { - pub fn new() -> StatefulList<T> { - StatefulList { - state: ListState::default(), - items: Vec::new(), - } - } - - pub fn with_items(items: Vec<T>) -> StatefulList<T> { - StatefulList { - state: ListState::default(), - items, - } - } - - pub fn next(&mut self) { - let i = match self.state.selected() { - Some(i) => { - if i >= self.items.len() - 1 { - 0 - } else { - i + 1 - } - } - None => 0, - }; - self.state.select(Some(i)); - } - - pub fn previous(&mut self) { - let i = match self.state.selected() { - Some(i) => { - if i == 0 { - self.items.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - self.state.select(Some(i)); - } - - pub fn unselect(&mut self) { - self.state.select(None); - } -} - -impl<T> From<Vec<T>> for StatefulList<T> { - fn from(v: Vec<T>) -> Self { - StatefulList::with_items(v) - } -} diff --git a/src/tui/markdown.hs b/src/tui/markdown.hs deleted file mode 100644 index 3627d23..0000000 --- a/src/tui/markdown.hs +++ /dev/null @@ -1,3 +0,0 @@ -pub struct Markdown { - - diff --git a/src/tui/markdown.rs b/src/tui/markdown.rs index c3dbfc9..64d3141 100644 --- a/src/tui/markdown.rs +++ b/src/tui/markdown.rs @@ -1,135 +1,247 @@ -// possibly make this a separate package for others -pub struct Markdown {}; +//! Parse markdown text. +//! +//! Extended from cursive::utils::markup::markdown to add code styles +//! TODO: Bring in the full power of termimad (e.g. md tables) in a View; +//! implementation of those features (.e.g automatic wrapping within each table +//! cell) might be easier in this setting anyway. -// Below is Paragraph; adjust to Markdown! +use std::borrow::Cow; -// style <-> termimad::MadSkin +use cursive::theme::{Effect, PaletteColor, Style}; +use cursive::utils::markup::{StyledIndexedSpan, StyledString}; +use cursive::utils::span::IndexedCow; -// Wrap/Truncate <-> termimad::DisplayableLine -// and the stuff in termimad::composite{fit_width, extend_width} +use pulldown_cmark::{self, CowStr, Event, Tag}; +use unicode_width::UnicodeWidthStr; -// Looping over line_composer.next_line() <-> write_fmt_line -// (see also text_view.write_on) - -#[derive(Debug, Clone)] -pub struct Paragraph<'a, 't, T> +/// Parses the given string as markdown text. +pub fn parse<S>(input: S) -> StyledString where - T: Iterator<Item = &'t Text<'t>>, + S: Into<String>, { - /// A block to wrap the widget in - block: Option<Block<'a>>, - /// Widget style - style: Style, |