summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Tay <sam.chong.tay@gmail.com>2020-06-11 00:50:41 -0700
committerSam Tay <sam.chong.tay@gmail.com>2020-06-11 00:50:41 -0700
commit37ab4c85f9d8bf1e113c4b1b419c0d17ec72b84e (patch)
tree694e9e3ad3303f8213432d6fd44a6455ba982a21
parent7ac11eee2b38522f22fa44227ffb6411b4f758e4 (diff)
...Use cursive instead
-rw-r--r--Cargo.lock359
-rw-r--r--Cargo.toml19
-rw-r--r--TODO.md17
-rw-r--r--src/config.rs1
-rw-r--r--src/error.rs4
-rw-r--r--src/main.rs38
-rw-r--r--src/stackexchange.rs2
-rw-r--r--src/tui/app.rs192
-rw-r--r--src/tui/list.rs60
-rw-r--r--src/tui/markdown.hs3
-rw-r--r--src/tui/markdown.rs330
-rw-r--r--src/tui/mod.rs2
12 files changed, 660 insertions, 367 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 765562d..c5913ec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index bfd9fb9..84aa6f0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"] }
diff --git a/TODO.md b/TODO.md
index 26c87fd..a442a30 100644
--- a/TODO.md
+++ b/TODO.md
@@ -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,