mirror of
https://github.com/denisidoro/navi
synced 2025-02-16 12:38:28 +00:00
parent
e64d1cc8aa
commit
00fe27ba79
8 changed files with 106 additions and 26 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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"
|
||||
|
|
35
README.md
35
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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(&mut self, tags: &str, variable: &str, value: Suggestion) {
|
||||
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_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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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"
|
Loading…
Add table
Reference in a new issue