mirror of
https://github.com/denisidoro/navi
synced 2024-11-10 14:04:17 +00:00
Forward many more parameters to fzf after ---
(#294)
This commit is contained in:
parent
64045e32ca
commit
10f0219792
10 changed files with 105 additions and 95 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -245,13 +245,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "navi"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
dependencies = [
|
||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"raw_tty 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"shellwords 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"terminal_size 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -443,6 +444,15 @@ name = "semver-parser"
|
|||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "shellwords"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.2.0"
|
||||
|
@ -698,6 +708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum shellwords 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "685f0e9b0efe23d26e60a780d8dcd3ac95e90975814de9bc6f48e5d609b5d0f5"
|
||||
"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum structopt 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe43617218c0805c6eb37160119dc3c548110a67786da7218d1c6555212f073"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "navi"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
authors = ["Denis Isidoro <denis_isidoro@live.com>"]
|
||||
edition = "2018"
|
||||
description = "An interactive cheatsheet tool for the command-line"
|
||||
|
@ -23,6 +23,7 @@ lazy_static = "1.4.0"
|
|||
dirs = "2.0.0"
|
||||
terminal_size = "0.1.10"
|
||||
walkdir = "2"
|
||||
shellwords = "1.0.0"
|
||||
|
||||
[dependencies.git2]
|
||||
version = "0.10.0"
|
||||
|
|
12
README.md
12
README.md
|
@ -202,9 +202,15 @@ $ image_id: docker images --- --column 3 --header-lines 1 --delimiter '\s\s+'
|
|||
The supported parameters are:
|
||||
- `--prevent-extra` *(experimental)*: limits the user to select one of the suggestions;
|
||||
- `--column <number>`: extracts a single column from the selected result;
|
||||
- `--delimiter <regex>`: delimits columns + forwarded option to `fzf`;
|
||||
- `--multi`: forwarded option to `fzf`;
|
||||
- `--header-lines <number>`: forwarded option to `fzf`;
|
||||
|
||||
In addition, it's possible to forward the following parameters to `fzf`:
|
||||
- `--header-lines <number>`;
|
||||
- `--delimiter <regex>`;
|
||||
- `--query <text>`;
|
||||
- `--filter <text>`;
|
||||
- `--header <text>`;
|
||||
- `--preview <code>`;
|
||||
- `--preview-window <text>`.
|
||||
|
||||
### Variable dependency
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ use crate::flows;
|
|||
use crate::fzf;
|
||||
use crate::handler;
|
||||
use crate::parser;
|
||||
use crate::structures::cheat::{Suggestion, SuggestionType, VariableMap};
|
||||
use crate::structures::fzf::Opts as FzfOpts;
|
||||
use crate::structures::cheat::{Suggestion, VariableMap};
|
||||
use crate::structures::fzf::{Opts as FzfOpts, SuggestionType};
|
||||
use crate::structures::option;
|
||||
use crate::structures::option::Config;
|
||||
use regex::Regex;
|
||||
|
@ -73,7 +73,7 @@ fn prompt_with_suggestions(
|
|||
for (key, value) in values.iter() {
|
||||
vars_cmd.push_str(format!("{}=\"{}\"; ", key, value).as_str());
|
||||
}
|
||||
let (suggestion_command, suggestion_options) = &suggestion;
|
||||
let (suggestion_command, suggestion_opts) = suggestion;
|
||||
let command = format!("{} {}", vars_cmd, suggestion_command);
|
||||
|
||||
let child = Command::new("bash")
|
||||
|
@ -85,18 +85,12 @@ fn prompt_with_suggestions(
|
|||
|
||||
let suggestions = String::from_utf8(child.wait_with_output().unwrap().stdout).unwrap();
|
||||
|
||||
let mut opts = FzfOpts {
|
||||
let opts = suggestion_opts.clone().unwrap_or_default();
|
||||
let opts = FzfOpts {
|
||||
autoselect: !config.no_autoselect,
|
||||
overrides: config.fzf_overrides_var.clone(),
|
||||
prompt: Some(display::variable_prompt(varname)),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(o) = &suggestion_options {
|
||||
opts.suggestion_type = o.suggestion_type;
|
||||
opts.header_lines = o.header_lines;
|
||||
opts.column = o.column;
|
||||
opts.delimiter = o.delimiter.clone();
|
||||
..opts
|
||||
};
|
||||
|
||||
let (output, _) = fzf::call(opts, |stdin| {
|
||||
|
@ -162,9 +156,9 @@ fn with_new_lines(txt: String) -> String {
|
|||
pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(), Box<dyn Error>> {
|
||||
let _ = display::WIDTHS;
|
||||
|
||||
let (raw_selection, variables) = fzf::call(gen_core_fzf_opts(variant, &config), |stdin| {
|
||||
Some(parser::read_all(&config, stdin))
|
||||
});
|
||||
let opts = gen_core_fzf_opts(variant, &config);
|
||||
let (raw_selection, variables) =
|
||||
fzf::call(opts, |stdin| Some(parser::read_all(&config, stdin)));
|
||||
|
||||
let (key, tags, snippet) = extract_from_selections(&raw_selection[..], contains_key);
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::filesystem;
|
||||
use crate::fzf;
|
||||
use crate::git;
|
||||
use crate::structures::cheat::SuggestionType;
|
||||
use crate::structures::fzf::Opts as FzfOpts;
|
||||
use crate::structures::fzf::{Opts as FzfOpts, SuggestionType};
|
||||
use git2::Repository;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
|
@ -66,14 +65,13 @@ pub fn add(uri: String) -> Result<(), Box<dyn Error>> {
|
|||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
let overrides = "--preview-window right:30%".to_string();
|
||||
let opts = FzfOpts {
|
||||
suggestion_type: SuggestionType::MultipleSelections,
|
||||
preview: Some(format!("cat '{}/{{}}'", tmp_path_str)),
|
||||
header: Some(
|
||||
"Select the cheatsheets you want to import with <TAB> then hit <Enter>".to_string(),
|
||||
),
|
||||
overrides: Some(overrides),
|
||||
preview_window: Some("--preview-window right:30%".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
|
27
src/fzf.rs
27
src/fzf.rs
|
@ -1,6 +1,6 @@
|
|||
use crate::display;
|
||||
use crate::structures::cheat::{SuggestionType, VariableMap};
|
||||
use crate::structures::fzf::Opts;
|
||||
use crate::structures::cheat::VariableMap;
|
||||
use crate::structures::fzf::{Opts, SuggestionType};
|
||||
use std::process;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
|
@ -86,6 +86,10 @@ where
|
|||
fzf_command.args(&["--prompt", &p]);
|
||||
}
|
||||
|
||||
if let Some(pw) = opts.preview_window {
|
||||
fzf_command.args(&["--preview-window", &pw]);
|
||||
}
|
||||
|
||||
if opts.header_lines > 0 {
|
||||
fzf_command.args(&["--header-lines", format!("{}", opts.header_lines).as_str()]);
|
||||
}
|
||||
|
@ -114,12 +118,12 @@ where
|
|||
};
|
||||
|
||||
let stdin = child.stdin.as_mut().unwrap();
|
||||
let result = stdin_fn(stdin);
|
||||
let result_map = stdin_fn(stdin);
|
||||
|
||||
let out = child.wait_with_output().unwrap();
|
||||
|
||||
let text = match out.status.code() {
|
||||
Some(0) | Some(1) => String::from_utf8(out.stdout).unwrap(),
|
||||
Some(0) | Some(1) | Some(2) => String::from_utf8(out.stdout).unwrap(),
|
||||
Some(130) => process::exit(130),
|
||||
_ => {
|
||||
let err = String::from_utf8(out.stderr)
|
||||
|
@ -128,14 +132,13 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
(
|
||||
get_column(
|
||||
parse_output_single(text, opts.suggestion_type),
|
||||
opts.column,
|
||||
opts.delimiter.as_deref(),
|
||||
),
|
||||
result,
|
||||
)
|
||||
let out = get_column(
|
||||
parse_output_single(text, opts.suggestion_type),
|
||||
opts.column,
|
||||
opts.delimiter.as_deref(),
|
||||
);
|
||||
|
||||
(out, result_map)
|
||||
}
|
||||
|
||||
fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> String {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::display;
|
||||
use crate::filesystem;
|
||||
use crate::structures::cheat::{SuggestionOpts, SuggestionType, VariableMap};
|
||||
use crate::structures::cheat::VariableMap;
|
||||
use crate::structures::fnv::HashLine;
|
||||
use crate::structures::fzf::{Opts as FzfOpts, SuggestionType};
|
||||
use crate::structures::option::Config;
|
||||
use crate::welcome;
|
||||
use regex::Regex;
|
||||
|
@ -9,47 +10,43 @@ use std::collections::HashSet;
|
|||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
fn remove_quotes(txt: &str) -> String {
|
||||
txt.replace('"', "").replace('\'', "")
|
||||
}
|
||||
|
||||
fn parse_opts(text: &str) -> SuggestionOpts {
|
||||
let mut header_lines: u8 = 0;
|
||||
let mut column: Option<u8> = None;
|
||||
fn parse_opts(text: &str) -> FzfOpts {
|
||||
let mut multi = false;
|
||||
let mut prevent_extra = false;
|
||||
let mut delimiter: Option<String> = None;
|
||||
|
||||
let mut parts = text.split(' ');
|
||||
let mut opts = FzfOpts::default();
|
||||
let parts_vec = shellwords::split(text).unwrap();
|
||||
let mut parts = parts_vec.into_iter();
|
||||
|
||||
while let Some(p) = parts.next() {
|
||||
match p {
|
||||
match p.as_str() {
|
||||
"--multi" => multi = true,
|
||||
"--prevent-extra" => prevent_extra = true,
|
||||
"--header" | "--headers" | "--header-lines" => {
|
||||
header_lines = remove_quotes(parts.next().unwrap()).parse::<u8>().unwrap()
|
||||
"--headers" | "--header-lines" => {
|
||||
opts.header_lines = parts.next().unwrap().parse::<u8>().unwrap()
|
||||
}
|
||||
"--column" => {
|
||||
column = Some(remove_quotes(parts.next().unwrap()).parse::<u8>().unwrap())
|
||||
}
|
||||
"--delimiter" => delimiter = Some(remove_quotes(parts.next().unwrap()).to_string()),
|
||||
"--column" => opts.column = Some(parts.next().unwrap().parse::<u8>().unwrap()),
|
||||
"--delimiter" => opts.delimiter = Some(parts.next().unwrap().to_string()),
|
||||
"--query" => opts.query = Some(parts.next().unwrap().to_string()),
|
||||
"--filter" => opts.filter = Some(parts.next().unwrap().to_string()),
|
||||
"--preview" => opts.preview = Some(parts.next().unwrap().to_string()),
|
||||
"--preview-window" => opts.preview_window = Some(parts.next().unwrap().to_string()),
|
||||
"--header" => opts.header = Some(parts.next().unwrap().to_string()),
|
||||
"--overrides" => opts.overrides = Some(parts.next().unwrap().to_string()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
SuggestionOpts {
|
||||
header_lines,
|
||||
column,
|
||||
delimiter,
|
||||
suggestion_type: match (multi, prevent_extra) {
|
||||
(true, _) => SuggestionType::MultipleSelections, // multi wins over allow-extra
|
||||
(false, false) => SuggestionType::SingleRecommendation,
|
||||
(false, true) => SuggestionType::SingleSelection,
|
||||
},
|
||||
}
|
||||
let suggestion_type = match (multi, prevent_extra) {
|
||||
(true, _) => SuggestionType::MultipleSelections, // multi wins over allow-extra
|
||||
(false, false) => SuggestionType::SingleRecommendation,
|
||||
(false, true) => SuggestionType::SingleSelection,
|
||||
};
|
||||
opts.suggestion_type = suggestion_type;
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
fn parse_variable_line(line: &str) -> (&str, &str, Option<SuggestionOpts>) {
|
||||
fn parse_variable_line(line: &str) -> (&str, &str, Option<FzfOpts>) {
|
||||
let re = Regex::new(r"^\$\s*([^:]+):(.*)").unwrap();
|
||||
let caps = re.captures(line).unwrap();
|
||||
let variable = caps.get(1).unwrap().as_str().trim();
|
||||
|
@ -200,11 +197,12 @@ mod tests {
|
|||
assert_eq!(variable, "user");
|
||||
assert_eq!(
|
||||
command_options,
|
||||
Some(SuggestionOpts {
|
||||
Some(FzfOpts {
|
||||
header_lines: 0,
|
||||
column: None,
|
||||
delimiter: None,
|
||||
suggestion_type: SuggestionType::SingleRecommendation
|
||||
suggestion_type: SuggestionType::SingleRecommendation,
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -220,11 +218,12 @@ mod tests {
|
|||
read_file(path, &mut variables, &mut visited_lines, child_stdin);
|
||||
let expected_suggestion = (
|
||||
r#" echo -e "$(whoami)\nroot" "#.to_string(),
|
||||
Some(SuggestionOpts {
|
||||
Some(FzfOpts {
|
||||
header_lines: 0,
|
||||
column: None,
|
||||
delimiter: None,
|
||||
suggestion_type: SuggestionType::SingleRecommendation,
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
let actual_suggestion = variables.get("ssh", "user");
|
||||
|
|
|
@ -1,29 +1,8 @@
|
|||
use crate::structures::fnv::HashLine;
|
||||
use crate::structures::fzf::Opts;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SuggestionOpts {
|
||||
pub header_lines: u8,
|
||||
pub column: Option<u8>,
|
||||
pub delimiter: Option<String>,
|
||||
pub suggestion_type: SuggestionType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum SuggestionType {
|
||||
/// fzf will not print any suggestions
|
||||
Disabled,
|
||||
/// fzf will only select one of the suggestions
|
||||
SingleSelection,
|
||||
/// fzf will select multiple suggestions
|
||||
MultipleSelections,
|
||||
/// fzf will select one of the suggestions or use the query
|
||||
SingleRecommendation,
|
||||
/// initial snippet selection
|
||||
SnippetSelection,
|
||||
}
|
||||
|
||||
pub type Suggestion = (String, Option<SuggestionOpts>);
|
||||
pub type Suggestion = (String, Option<Opts>);
|
||||
|
||||
fn gen_key(tags: &str, variable: &str) -> u64 {
|
||||
format!("{};{}", tags, variable).hash_line()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::structures::cheat::SuggestionType;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Opts {
|
||||
pub query: Option<String>,
|
||||
pub filter: Option<String>,
|
||||
pub prompt: Option<String>,
|
||||
pub preview: Option<String>,
|
||||
pub preview_window: Option<String>,
|
||||
pub autoselect: bool,
|
||||
pub overrides: Option<String>,
|
||||
pub header_lines: u8,
|
||||
|
@ -21,6 +21,7 @@ impl Default for Opts {
|
|||
filter: None,
|
||||
autoselect: true,
|
||||
preview: None,
|
||||
preview_window: None,
|
||||
overrides: None,
|
||||
header_lines: 0,
|
||||
header: None,
|
||||
|
@ -31,3 +32,17 @@ impl Default for Opts {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum SuggestionType {
|
||||
/// fzf will not print any suggestions
|
||||
Disabled,
|
||||
/// fzf will only select one of the suggestions
|
||||
SingleSelection,
|
||||
/// fzf will select multiple suggestions
|
||||
MultipleSelections,
|
||||
/// fzf will select one of the suggestions or use the query
|
||||
SingleRecommendation,
|
||||
/// initial snippet selection
|
||||
SnippetSelection,
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ echo "I like these examples: "$(printf '%s' "<examples>" | sed 's/^..*$/"&"/' |
|
|||
# multiple replacements -> "foo"
|
||||
echo "<x> <y> <x> <z>"
|
||||
|
||||
# with preview
|
||||
cat "<file>"
|
||||
|
||||
$ x: echo '1 2 3' | tr ' ' '\n'
|
||||
$ y: echo 'a b c' | tr ' ' '\n'
|
||||
$ z: echo 'foo bar' | tr ' ' '\n'
|
||||
|
@ -39,8 +42,9 @@ $ table_elem: echo -e '0 rust rust-lang.org\n1 clojure clojure.org' ---
|
|||
$ table_elem2: echo -e '0;rust;rust-lang.org\n1;clojure;clojure.org' --- --column 2 --delimiter ';'
|
||||
$ multi_col: ls -la | awk '{print $1, $9}' --- --column 2 --delimiter '\s' --multi
|
||||
$ langs: echo 'clojure rust javascript' | tr ' ' '\n' --- --multi
|
||||
$ examples: echo -e 'foo bar\nlorem ipsum\ndolor sit' --- --multi
|
||||
$ multiword: echo -e 'foo bar\nlorem ipsum\ndolor sit\nbaz'
|
||||
$ examples: echo -e 'foo bar\nlorem ipsum\ndolor sit' --- --mult
|
||||
$ multiword: echo -e 'foo bar\nlorem ipsum\ndolor sit\nbaz'i
|
||||
$ file: ls . --- --preview 'cat {}' --preview-window '50%'
|
||||
|
||||
# this should be displayed
|
||||
echo hi
|
Loading…
Reference in a new issue