Skip to content

Commit

Permalink
Add finder argument to choose between fzf and skim
Browse files Browse the repository at this point in the history
  • Loading branch information
shenek committed Mar 28, 2020
1 parent 5ab74c3 commit 229ae72
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 268 deletions.
4 changes: 0 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ license = "Apache-2.0"
[badges]
travis-ci = { repository = "denisidoro/navi", branch = "master" }

[features]
default = []
use_skim = []

[dependencies]
regex = "1.3.4"
structopt = "0.3"
Expand Down
215 changes: 110 additions & 105 deletions src/finder/fzf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{get_column, parse_output_single};
use super::{get_column, parse_output_single, Finder};
use crate::display;
use crate::structures::cheat::VariableMap;
use crate::structures::finder::{Opts, SuggestionType};
Expand All @@ -7,121 +7,126 @@ use anyhow::Error;
use std::process;
use std::process::{Command, Stdio};

pub fn call<F>(opts: Opts, stdin_fn: F) -> Result<(String, Option<VariableMap>), Error>
where
F: Fn(&mut process::ChildStdin) -> Result<Option<VariableMap>, Error>,
{
let mut fzf_command = Command::new("fzf");

fzf_command.args(&[
"--preview-window",
"up:2",
"--with-nth",
"1,2,3",
"--delimiter",
display::DELIMITER.to_string().as_str(),
"--ansi",
"--bind",
"ctrl-j:down,ctrl-k:up",
"--exact",
]);

if opts.autoselect {
fzf_command.arg("--select-1");
}

match opts.suggestion_type {
SuggestionType::MultipleSelections => {
fzf_command.arg("--multi");
}
SuggestionType::Disabled => {
fzf_command.args(&["--print-query", "--no-select-1", "--height", "1"]);
}
SuggestionType::SnippetSelection => {
fzf_command.args(&["--expect", "ctrl-y,enter"]);
#[derive(Debug)]
pub struct FzfFinder;

impl Finder for FzfFinder {
fn call<F>(&self, opts: Opts, stdin_fn: F) -> Result<(String, Option<VariableMap>), Error>
where
F: Fn(&mut process::ChildStdin) -> Result<Option<VariableMap>, Error>,
{
let mut fzf_command = Command::new("fzf");

fzf_command.args(&[
"--preview-window",
"up:2",
"--with-nth",
"1,2,3",
"--delimiter",
display::DELIMITER.to_string().as_str(),
"--ansi",
"--bind",
"ctrl-j:down,ctrl-k:up",
"--exact",
]);

if opts.autoselect {
fzf_command.arg("--select-1");
}
SuggestionType::SingleRecommendation => {
fzf_command.args(&["--print-query", "--expect", "tab,enter"]);
}
_ => {}
}

if let Some(p) = opts.preview {
fzf_command.args(&["--preview", &p]);
}

if let Some(q) = opts.query {
fzf_command.args(&["--query", &q]);
}

if let Some(f) = opts.filter {
fzf_command.args(&["--filter", &f]);
}

if let Some(h) = opts.header {
fzf_command.args(&["--header", &h]);
}

if let Some(p) = opts.prompt {
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()]);
}
match opts.suggestion_type {
SuggestionType::MultipleSelections => {
fzf_command.arg("--multi");
}
SuggestionType::Disabled => {
fzf_command.args(&["--print-query", "--no-select-1", "--height", "1"]);
}
SuggestionType::SnippetSelection => {
fzf_command.args(&["--expect", "ctrl-y,enter"]);
}
SuggestionType::SingleRecommendation => {
fzf_command.args(&["--print-query", "--expect", "tab,enter"]);
}
_ => {}
}

if let Some(o) = opts.overrides {
o.as_str()
.split(' ')
.map(|s| s.to_string())
.filter(|s| !s.is_empty())
.for_each(|s| {
fzf_command.arg(s);
});
}
if let Some(p) = opts.preview {
fzf_command.args(&["--preview", &p]);
}

let child = fzf_command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn();
if let Some(q) = opts.query {
fzf_command.args(&["--query", &q]);
}

let mut child = match child {
Ok(x) => x,
Err(_) => {
eprintln!("navi was unable to call fzf.\nPlease make sure it's correctly installed\nRefer to https://github.com/junegunn/fzf for more info.");
process::exit(33)
if let Some(f) = opts.filter {
fzf_command.args(&["--filter", &f]);
}
};

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")?;
if let Some(h) = opts.header {
fzf_command.args(&["--header", &h]);
}

let out = child.wait_with_output().context("Failed to wait for fzf")?;
if let Some(p) = opts.prompt {
fzf_command.args(&["--prompt", &p]);
}

let text = match out.status.code() {
Some(0) | Some(1) | Some(2) => {
String::from_utf8(out.stdout).context("Invalid utf8 received from fzf")?
if let Some(pw) = opts.preview_window {
fzf_command.args(&["--preview-window", &pw]);
}
Some(130) => process::exit(130),
_ => {
let err = String::from_utf8(out.stderr)
.unwrap_or_else(|_| "<stderr contains invalid UTF-8>".to_owned());
panic!("External command failed:\n {}", err)

if opts.header_lines > 0 {
fzf_command.args(&["--header-lines", format!("{}", opts.header_lines).as_str()]);
}
};

let out = get_column(
parse_output_single(text, opts.suggestion_type)?,
opts.column,
opts.delimiter.as_deref(),
);
if let Some(o) = opts.overrides {
o.as_str()
.split(' ')
.map(|s| s.to_string())
.filter(|s| !s.is_empty())
.for_each(|s| {
fzf_command.arg(s);
});
}

Ok((out, result_map))
let child = fzf_command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn();

let mut child = match child {
Ok(x) => x,
Err(_) => {
eprintln!("navi was unable to call fzf.\nPlease make sure it's correctly installed\nRefer to https://github.com/junegunn/fzf for more info.");
process::exit(33)
}
};

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().context("Failed to wait for fzf")?;

let text = match out.status.code() {
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)
.unwrap_or_else(|_| "<stderr contains invalid UTF-8>".to_owned());
panic!("External command failed:\n {}", err)
}
};

let out = get_column(
parse_output_single(text, opts.suggestion_type)?,
opts.column,
opts.delimiter.as_deref(),
);

Ok((out, result_map))
}
}
36 changes: 29 additions & 7 deletions src/finder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
#[cfg(not(feature = "use_skim"))]
mod fzf;
#[cfg(feature = "use_skim")]
mod skim;

#[cfg(not(feature = "use_skim"))]
pub use fzf::call;

#[cfg(feature = "use_skim")]
pub use skim::call;
pub use fzf::FzfFinder;
pub use skim::SkimFinder;

use crate::structures::cheat::VariableMap;
use crate::structures::finder::Opts;
use crate::structures::finder::SuggestionType;
use anyhow::Context;
use anyhow::Error;
use std::process;

#[derive(Debug)]
pub enum FinderChoice {
Fzf,
Skim,
}

impl Finder for FinderChoice {
fn call<F>(&self, opts: Opts, stdin_fn: F) -> Result<(String, Option<VariableMap>), Error>
where
F: Fn(&mut process::ChildStdin) -> Result<Option<VariableMap>, Error>,
{
match self {
Self::Fzf => FzfFinder.call(opts, stdin_fn),
Self::Skim => SkimFinder.call(opts, stdin_fn),
}
}
}

pub trait Finder {
fn call<F>(&self, opts: Opts, stdin_fn: F) -> Result<(String, Option<VariableMap>), Error>
where
F: Fn(&mut process::ChildStdin) -> Result<Option<VariableMap>, Error>;
}

fn get_column(text: String, column: Option<u8>, delimiter: Option<&str>) -> String {
if let Some(c) = column {
Expand Down
Loading

0 comments on commit 229ae72

Please sign in to comment.