diff options
author | Denis Isidoro <denisidoro@users.noreply.github.com> | 2020-08-23 20:09:27 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-23 20:09:27 -0300 |
commit | 00fe27ba79ed3f01d52347c39f0a55561aa5473e (patch) | |
tree | 72ed82d95ff095a84e0844293bf91aa4e22096d0 | |
parent | e64d1cc8aa81d72ab91f2e10f41ad290bc186fb1 (diff) |
Add ability to extend cheatsheets (#382)v2.9.0
Fixes #377
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | README.md | 35 | ||||
-rw-r--r-- | src/flows/alfred.rs | 2 | ||||
-rw-r--r-- | src/flows/core.rs | 2 | ||||
-rw-r--r-- | src/parser.rs | 14 | ||||
-rw-r--r-- | src/structures/cheat.rs | 44 | ||||
-rw-r--r-- | tests/no_prompt_cheats/cases.cheat | 31 |
8 files changed, 106 insertions, 26 deletions
@@ -174,7 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "navi" -version = "2.8.0" +version = "2.9.0" dependencies = [ "anyhow 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1,6 +1,6 @@ [package] name = "navi" -version = "2.8.0" +version = "2.9.0" authors = ["Denis Isidoro <denis_isidoro@live.com>"] edition = "2018" description = "An interactive cheatsheet tool for the command-line" @@ -185,7 +185,8 @@ $ branch: git branch | awk '{print $NF}' - lines starting with `%` determine the start of a new cheatsheet and should contain tags, useful for searching; - lines starting with `#` should be descriptions of commands; - lines starting with `;` are ignored. You can use them for metacomments; -- lines starting with `$` should contain commands that generate a list of possible values for a given argument; +- lines starting with `$` should contain [commands that generate a list of possible values for a given argument](#variables); +- lines starting with `@` should contain [tags whose associated cheatsheet you want to base on](#extending-cheatsheets); - all the other non-empty lines are considered as executable commands. It's irrelevant how many files are used to store cheatsheets. They can be all in a single file if you wish, as long as you split them accordingly with lines starting with `%`. @@ -232,7 +233,16 @@ In addition, it's possible to forward the following parameters to `fzf`: ### Variable dependency -The command for generating possible inputs can refer previous variables: +The command for generating possible inputs can implicitly refer previous variables by using the `<varname>` syntax: +```sh +# Should print /my/pictures/wallpapers +echo "<wallpaper_folder>" + +$ pictures_folder: echo "/my/pictures" +$ wallpaper_folder: echo "<pictures_folder>/wallpapers" +``` + +If you want to make dependencies explicit, you can use the `$varname` syntax: ```sh # If you select "hello" for <x>, the possible values of <y> will be "hello foo" and "hello bar" echo <x> <y> @@ -244,13 +254,26 @@ $ x: echo "hello hi" | tr ' ' '\n' $ y: echo "$x foo;$x bar" | tr ';' '\n' ``` -If you want to have implicit variable dependency, you can use the `<varname>` syntax inside a variable command: +### Extending cheatsheets + +With the `@ same tags from other cheatsheet` syntax you can reuse the same variable in multiple cheatsheets. + ```sh -# Should print /my/pictures/wallpapers -echo "<wallpaper_folder>" +% dirs, common $ pictures_folder: echo "/my/pictures" -$ wallpaper_folder: echo "<pictures_folder>/wallpapers" + +% wallpapers +@ dirs, common + +# Should print /my/pictures/wallpapers +echo "<pictures_folder>/wallpapers" + +% screenshots +@ dirs, common + +# Should print /my/pictures/screenshots +echo "<pictures_folder>/screenshots" ``` ### Multiline snippets diff --git a/src/flows/alfred.rs b/src/flows/alfred.rs index 4ca1f56..acfa7e3 100644 --- a/src/flows/alfred.rs +++ b/src/flows/alfred.rs @@ -66,7 +66,7 @@ pub fn suggestions(config: Config, dry_run: bool) -> Result<(), Error> { let capture = display::VAR_REGEX.captures_iter(&snippet).next(); let bracketed_varname = &(capture.expect("Invalid capture"))[0]; let varname = &bracketed_varname[1..bracketed_varname.len() - 1]; - let command = variables.get(&tags, &varname); + let command = variables.get_suggestion(&tags, &varname); if dry_run { if command.is_none() { diff --git a/src/flows/core.rs b/src/flows/core.rs index 3b8055a..b5341bc 100644 --- a/src/flows/core.rs +++ b/src/flows/core.rs @@ -148,7 +148,7 @@ fn replace_variables_from_snippet( e } else { variables - .get(&tags, &variable_name) + .get_suggestion(&tags, &variable_name) .ok_or_else(|| anyhow!("No suggestions")) .and_then(|suggestion| { let mut new_suggestion = suggestion.clone(); diff --git a/src/parser.rs b/src/parser.rs index ede15be..de5e7ed 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -160,7 +160,6 @@ fn read_file( // duplicate if !tags.is_empty() && !comment.is_empty() {} - // blank if line.is_empty() { } @@ -176,6 +175,15 @@ fn read_file( String::from("") }; } + // depedency + else if line.starts_with('@') { + let tags_dependency = if line.len() > 2 { + String::from(&line[2..]) + } else { + String::from("") + }; + variables.insert_dependency(&tags, &tags_dependency); + } // metacomment else if line.starts_with(';') { } @@ -204,7 +212,7 @@ fn read_file( path ) })?; - variables.insert(&tags, &variable, (String::from(command), opts)); + variables.insert_suggestion(&tags, &variable, (String::from(command), opts)); } // snippet else { @@ -331,7 +339,7 @@ mod tests { ..Default::default() }), ); - let actual_suggestion = variables.get("ssh", "user"); + let actual_suggestion = variables.get_suggestion("ssh", "user"); assert_eq!(Some(&expected_suggestion), actual_suggestion); } diff --git a/src/structures/cheat.rs b/src/structures/cheat.rs index e695063..3b5a7ce 100644 --- a/src/structures/cheat.rs +++ b/src/structures/cheat.rs @@ -5,26 +5,56 @@ use std::collections::HashMap; pub type Suggestion = (String, Option<Opts>); #[derive(Clone)] -pub struct VariableMap(HashMap<u64, HashMap<String, Suggestion>>); +pub struct VariableMap { + variables: HashMap<u64, HashMap<String, Suggestion>>, + dependencies: HashMap<u64, Vec<u64>>, +} impl VariableMap { pub fn new() -> Self { - Self(HashMap::new()) + Self { + variables: HashMap::new(), + dependencies: HashMap::new(), + } + } + + pub fn insert_dependency(&mut self, tags: &str, tags_dependency: &str) { + let k = tags.hash_line(); + if let Some(v) = self.dependencies.get_mut(&k) { + v.push(tags_dependency.hash_line()); + } else { + let mut v: Vec<u64> = Vec::new(); + v.push(tags_dependency.hash_line()); + self.dependencies.insert(k, v); + } } - pub fn insert(&mut self, tags: &str, variable: &str, value: Suggestion) { + pub fn insert_suggestion(&mut self, tags: &str, variable: &str, value: Suggestion) { let k1 = tags.hash_line(); let k2 = String::from(variable); - if let Some(m) = self.0.get_mut(&k1) { + if let Some(m) = self.variables.get_mut(&k1) { m.insert(k2, value); } else { let mut m = HashMap::new(); m.insert(k2, value); - self.0.insert(k1, m); + self.variables.insert(k1, m); } } - pub fn get(&self, tags: &str, variable: &str) -> Option<&Suggestion> { - self.0.get(&tags.hash_line())?.get(variable) + pub fn get_suggestion(&self, tags: &str, variable: &str) -> Option<&Suggestion> { + let k = tags.hash_line(); + let res = self.variables.get(&k)?.get(variable); + if res.is_some() { + return res; + } + if let Some(dependency_keys) = self.dependencies.get(&k) { + for dependency_key in dependency_keys { + let res = self.variables.get(&dependency_key)?.get(variable); + if res.is_some() { + return res; + } + } + } + None } } diff --git a/tests/no_prompt_cheats/cases.cheat b/tests/no_prompt_cheats/cases.cheat index e3f07dd..d60e859 100644 --- a/tests/no_prompt_cheats/cases.cheat +++ b/tests/no_prompt_cheats/cases.cheat @@ -1,6 +1,6 @@ ; author: CI/CD -% test, ci/cd +% test, first # trivial case -> "foo" echo "foo" @@ -34,9 +34,6 @@ echo "<x> <x2> <y> <x>" # variable dependency, we can ignore intermediate values -> "12" : <x>; echo "<x2>" -# nested used value -> "path: /my/pictures/wallpapers" -echo "path: <wallpaper_folder>" - # nested unused value -> "path: /my/pictures" echo "path: <pictures_folder>" @@ -47,7 +44,29 @@ $ language: echo '0 rust rust-lang.org' --- --column 2 $ language2: echo '1;clojure;clojure.org' --- --column 2 --delimiter ';' $ multiword: echo 'foo bar' $ pictures_folder: echo "/my/pictures" -$ wallpaper_folder: echo "<pictures_folder>/wallpapers" # this should be displayed -> "hi" -echo hi
\ No newline at end of file +echo hi + + +% test, second + +@ test, first +@ test, third + +# nested used value -> "path: /my/pictures/wallpapers" +echo "path: <wallpaper_folder>" + +# same command as before -> "12" +: <x>; echo "<x2>" + +# the order isn't relevant -> "br" +echo "<country>" + +$ wallpaper_folder: echo "<pictures_folder>/wallpapers" + + +% test, third + +; this cheathsheet doesnt have any commands +$ country: echo "br"
\ No newline at end of file |