summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Isidoro <denisidoro@users.noreply.github.com>2020-08-23 20:09:27 -0300
committerGitHub <noreply@github.com>2020-08-23 20:09:27 -0300
commit00fe27ba79ed3f01d52347c39f0a55561aa5473e (patch)
tree72ed82d95ff095a84e0844293bf91aa4e22096d0
parente64d1cc8aa81d72ab91f2e10f41ad290bc186fb1 (diff)
Add ability to extend cheatsheets (#382)v2.9.0
Fixes #377
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.md35
-rw-r--r--src/flows/alfred.rs2
-rw-r--r--src/flows/core.rs2
-rw-r--r--src/parser.rs14
-rw-r--r--src/structures/cheat.rs44
-rw-r--r--tests/no_prompt_cheats/cases.cheat31
8 files changed, 106 insertions, 26 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ca7bf1b..8ce2c91 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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)",
diff --git a/Cargo.toml b/Cargo.toml
index c055a26..8f381b6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/README.md b/README.md
index 8b69e49..53ec659 100644
--- a/README.md
+++ b/README.md
@@ -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