mirror of
https://github.com/denisidoro/navi
synced 2025-02-16 20:48:28 +00:00
Add error info to fzf.
This commit is contained in:
parent
a4e2311263
commit
1c22e11e09
3 changed files with 68 additions and 51 deletions
|
@ -110,14 +110,15 @@ fn prompt_with_suggestions(
|
|||
let (output, _) = fzf::call(opts, |stdin| {
|
||||
stdin
|
||||
.write_all(suggestions.as_bytes())
|
||||
.expect("Could not write to fzf's stdin");
|
||||
None
|
||||
});
|
||||
.context("Could not write to fzf's stdin")?;
|
||||
Ok(None)
|
||||
})
|
||||
.context("fzf was unable to prompt with suggestions")?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn prompt_without_suggestions(variable_name: &str) -> String {
|
||||
fn prompt_without_suggestions(variable_name: &str) -> Result<String, Error> {
|
||||
let opts = FzfOpts {
|
||||
autoselect: false,
|
||||
prompt: Some(display::variable_prompt(variable_name)),
|
||||
|
@ -125,9 +126,10 @@ fn prompt_without_suggestions(variable_name: &str) -> String {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let (output, _) = fzf::call(opts, |_stdin| None);
|
||||
let (output, _) = fzf::call(opts, |_stdin| Ok(None))
|
||||
.context("fzf was unable to prompt without suggestions")?;
|
||||
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn replace_variables_from_snippet(
|
||||
|
@ -135,7 +137,7 @@ fn replace_variables_from_snippet(
|
|||
tags: &str,
|
||||
variables: VariableMap,
|
||||
config: &Config,
|
||||
) -> String {
|
||||
) -> Result<String, Error> {
|
||||
let mut interpolated_snippet = String::from(snippet);
|
||||
let mut values: HashMap<String, String> = HashMap::new();
|
||||
|
||||
|
@ -146,15 +148,16 @@ fn replace_variables_from_snippet(
|
|||
let value = values
|
||||
.get(variable_name)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| {
|
||||
.ok_or_else(|| anyhow!(format!("No value for variable {}", variable_name)))
|
||||
.or_else(|_| {
|
||||
variables
|
||||
.get(&tags, &variable_name)
|
||||
.ok_or_else(|| anyhow!("No suggestions"))
|
||||
.and_then(|suggestion| {
|
||||
prompt_with_suggestions(variable_name, &config, suggestion, &values)
|
||||
})
|
||||
.unwrap_or_else(|_| prompt_without_suggestions(variable_name))
|
||||
});
|
||||
.or_else(|_| prompt_without_suggestions(variable_name))
|
||||
})?;
|
||||
|
||||
values.insert(variable_name.to_string(), value.clone());
|
||||
|
||||
|
@ -162,7 +165,7 @@ fn replace_variables_from_snippet(
|
|||
interpolated_snippet.replacen(bracketed_variable_name, value.as_str(), 1);
|
||||
}
|
||||
|
||||
interpolated_snippet
|
||||
Ok(interpolated_snippet)
|
||||
}
|
||||
|
||||
fn with_new_lines(txt: String) -> String {
|
||||
|
@ -174,20 +177,24 @@ pub fn main(variant: Variant, config: Config, contains_key: bool) -> Result<(),
|
|||
|
||||
let opts = gen_core_fzf_opts(variant, &config).context("Failed to generate fzf options")?;
|
||||
let (raw_selection, variables) = fzf::call(opts, |stdin| {
|
||||
Some(
|
||||
Ok(Some(
|
||||
parser::read_all(&config, stdin)
|
||||
.expect("Failed to read all variables intended for fzf"),
|
||||
)
|
||||
});
|
||||
.context("Failed to parse variables intended for fzf")?,
|
||||
))
|
||||
})
|
||||
.context("Failed getting selection and variables from fzf")?;
|
||||
|
||||
let (key, tags, snippet) = extract_from_selections(&raw_selection[..], contains_key);
|
||||
|
||||
let interpolated_snippet = with_new_lines(replace_variables_from_snippet(
|
||||
snippet,
|
||||
tags,
|
||||
variables.expect("No variables received from fzf"),
|
||||
&config,
|
||||
));
|
||||
let interpolated_snippet = with_new_lines(
|
||||
replace_variables_from_snippet(
|
||||
snippet,
|
||||
tags,
|
||||
variables.expect("No variables received from fzf"),
|
||||
&config,
|
||||
)
|
||||
.context("Failed to replace variables from snippet")?,
|
||||
);
|
||||
|
||||
// copy to clipboard
|
||||
if key == "ctrl-y" {
|
||||
|
|
|
@ -30,9 +30,10 @@ pub fn browse() -> Result<(), Error> {
|
|||
let (repo, _) = fzf::call(opts, |stdin| {
|
||||
stdin
|
||||
.write_all(repos.as_bytes())
|
||||
.expect("Unable to prompt featured repositories");
|
||||
None
|
||||
});
|
||||
.context("Unable to prompt featured repositories")?;
|
||||
Ok(None)
|
||||
})
|
||||
.context("Failed to get repo URL from fzf")?;
|
||||
|
||||
filesystem::remove_dir(&repo_path_str)?;
|
||||
|
||||
|
@ -76,9 +77,10 @@ pub fn add(uri: String) -> Result<(), Error> {
|
|||
let (files, _) = fzf::call(opts, |stdin| {
|
||||
stdin
|
||||
.write_all(all_files.as_bytes())
|
||||
.expect("Unable to prompt cheats to import");
|
||||
None
|
||||
});
|
||||
.context("Unable to prompt cheats to import")?;
|
||||
Ok(None)
|
||||
})
|
||||
.context("Failed to get cheatsheet files from fzf")?;
|
||||
|
||||
for f in files.split('\n') {
|
||||
let from = format!("{}/{}", tmp_path_str, f).replace("./", "");
|
||||
|
|
56
src/fzf.rs
56
src/fzf.rs
|
@ -1,21 +1,20 @@
|
|||
use crate::display;
|
||||
use crate::structures::cheat::VariableMap;
|
||||
use crate::structures::fzf::{Opts, SuggestionType};
|
||||
use anyhow::Context;
|
||||
use anyhow::Error;
|
||||
use std::process;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> String {
|
||||
if let Some(c) = column {
|
||||
let mut result = String::from("");
|
||||
let re = regex::Regex::new(delimiter.unwrap_or(r"\s\s+")).unwrap();
|
||||
let re = regex::Regex::new(delimiter.unwrap_or(r"\s\s+")).expect("Invalid regex");
|
||||
for line in text.split('\n') {
|
||||
if (&line).is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut parts = re.split(line);
|
||||
for _ in 0..(c - 1) {
|
||||
parts.next().unwrap();
|
||||
}
|
||||
let mut parts = re.split(line).skip((c - 1) as usize);
|
||||
if !result.is_empty() {
|
||||
result.push('\n');
|
||||
}
|
||||
|
@ -27,9 +26,9 @@ fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> Stri
|
|||
}
|
||||
}
|
||||
|
||||
pub fn call<F>(opts: Opts, stdin_fn: F) -> (String, Option<VariableMap>)
|
||||
pub fn call<F>(opts: Opts, stdin_fn: F) -> Result<(String, Option<VariableMap>), Error>
|
||||
where
|
||||
F: Fn(&mut process::ChildStdin) -> Option<VariableMap>,
|
||||
F: Fn(&mut process::ChildStdin) -> Result<Option<VariableMap>, Error>,
|
||||
{
|
||||
let mut fzf_command = Command::new("fzf");
|
||||
|
||||
|
@ -117,13 +116,18 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
let stdin = child.stdin.as_mut().unwrap();
|
||||
let result_map = stdin_fn(stdin);
|
||||
let stdin = child
|
||||
.stdin
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("Unable to acquire stdin of fzf"))?;
|
||||
let result_map = stdin_fn(stdin).context("Failed to pass data to fzf")?;
|
||||
|
||||
let out = child.wait_with_output().unwrap();
|
||||
let out = child.wait_with_output().context("Failed to wait for fzf")?;
|
||||
|
||||
let text = match out.status.code() {
|
||||
Some(0) | Some(1) | Some(2) => String::from_utf8(out.stdout).unwrap(),
|
||||
Some(0) | Some(1) | Some(2) => {
|
||||
String::from_utf8(out.stdout).context("Invalid utf8 received from fzf")?
|
||||
}
|
||||
Some(130) => process::exit(130),
|
||||
_ => {
|
||||
let err = String::from_utf8(out.stderr)
|
||||
|
@ -133,17 +137,21 @@ where
|
|||
};
|
||||
|
||||
let out = get_column(
|
||||
parse_output_single(text, opts.suggestion_type),
|
||||
parse_output_single(text, opts.suggestion_type)?,
|
||||
opts.column,
|
||||
opts.delimiter.as_deref(),
|
||||
);
|
||||
|
||||
(out, result_map)
|
||||
Ok((out, result_map))
|
||||
}
|
||||
|
||||
fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> String {
|
||||
match suggestion_type {
|
||||
SuggestionType::SingleSelection => text.lines().next().unwrap().to_string(),
|
||||
fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> Result<String, Error> {
|
||||
Ok(match suggestion_type {
|
||||
SuggestionType::SingleSelection => text
|
||||
.lines()
|
||||
.next()
|
||||
.context("Not sufficient data for single selection")?
|
||||
.to_string(),
|
||||
SuggestionType::MultipleSelections
|
||||
| SuggestionType::Disabled
|
||||
| SuggestionType::SnippetSelection => {
|
||||
|
@ -175,7 +183,7 @@ fn parse_output_single(mut text: String, suggestion_type: SuggestionType) -> Str
|
|||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -185,49 +193,49 @@ mod tests {
|
|||
#[test]
|
||||
fn test_parse_output1() {
|
||||
let text = "palo\n".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleSelection);
|
||||
let output = parse_output_single(text, SuggestionType::SingleSelection).unwrap();
|
||||
assert_eq!(output, "palo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_output2() {
|
||||
let text = "\nenter\npalo".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation);
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
|
||||
assert_eq!(output, "palo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_recommendation_output_1() {
|
||||
let text = "\nenter\npalo".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation);
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
|
||||
assert_eq!(output, "palo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_recommendation_output_2() {
|
||||
let text = "p\nenter\npalo".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation);
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
|
||||
assert_eq!(output, "palo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_recommendation_output_3() {
|
||||
let text = "peter\nenter\n".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation);
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
|
||||
assert_eq!(output, "peter");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_output3() {
|
||||
let text = "p\ntab\npalo".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation);
|
||||
let output = parse_output_single(text, SuggestionType::SingleRecommendation).unwrap();
|
||||
assert_eq!(output, "p");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_snippet_request() {
|
||||
let text = "enter\nssh ⠀login to a server and forward to ssh key (d… ⠀ssh -A <user>@<server> ⠀ssh ⠀login to a server and forward to ssh key (dangerous but usefull for bastion hosts) ⠀ssh -A <user>@<server> ⠀\n".to_string();
|
||||
let output = parse_output_single(text, SuggestionType::SnippetSelection);
|
||||
let output = parse_output_single(text, SuggestionType::SnippetSelection).unwrap();
|
||||
assert_eq!(output, "enter\nssh ⠀login to a server and forward to ssh key (d… ⠀ssh -A <user>@<server> ⠀ssh ⠀login to a server and forward to ssh key (dangerous but usefull for bastion hosts) ⠀ssh -A <user>@<server> ⠀");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue