Add option to list all query results

This commit is contained in:
Ajeet D'Souza 2020-05-30 01:06:03 +05:30
parent e8eb685e58
commit 19bac0b31a
7 changed files with 121 additions and 80 deletions

View File

@ -3,8 +3,8 @@ use crate::db::Rank;
use anyhow::{bail, Context, Result};
use std::env;
use std::fs;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
pub fn zo_data_dir() -> Result<PathBuf> {

View File

@ -1,4 +1,5 @@
use anyhow::{bail, Context, Result};
use float_ord::FloatOrd;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -149,6 +150,10 @@ impl Db {
Ok(())
}
pub fn matches<'a>(&'a mut self, now: Epoch, keywords: &[String]) -> DbMatches<'a> {
DbMatches::new(self, now, keywords)
}
fn get_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
data_dir.as_ref().join("db.zo")
}
@ -167,6 +172,49 @@ impl Drop for Db {
}
}
/// Streaming iterator for matching entries
pub struct DbMatches<'a> {
db: &'a mut Db,
idxs: std::iter::Rev<std::ops::Range<usize>>,
keywords: Vec<String>,
}
impl<'a> DbMatches<'a> {
pub fn new(db: &'a mut Db, now: Epoch, keywords: &[String]) -> DbMatches<'a> {
db.dirs
.sort_unstable_by_key(|dir| FloatOrd(dir.get_frecency(now)));
let idxs = (0..db.dirs.len()).rev();
let keywords = keywords
.iter()
.map(|keyword| keyword.to_lowercase())
.collect();
DbMatches { db, idxs, keywords }
}
pub fn next(&mut self) -> Option<&Dir> {
for idx in &mut self.idxs {
let dir = &self.db.dirs[idx];
if !dir.is_match(&self.keywords) {
continue;
}
if !dir.is_valid() {
self.db.dirs.swap_remove(idx);
self.db.modified = true;
continue;
}
let dir = &self.db.dirs[idx];
return Some(dir);
}
None
}
}
pub type Rank = f64;
pub type Epoch = i64; // use a signed integer so subtraction can be performed on it

View File

@ -8,8 +8,9 @@ use structopt::StructOpt;
use std::env;
use std::path::{Path, PathBuf};
/// Add a new directory or increment its rank
#[derive(Debug, StructOpt)]
#[structopt(about = "Add a new directory or increment its rank")]
#[structopt()]
pub struct Add {
path: Option<PathBuf>,
}

View File

@ -7,12 +7,14 @@ use structopt::StructOpt;
use std::fs;
use std::path::{Path, PathBuf};
/// Import from z database
#[derive(Debug, StructOpt)]
#[structopt(about = "Import from z database")]
#[structopt()]
pub struct Import {
path: PathBuf,
#[structopt(long, help = "Merge entries into existing database")]
/// Merge entries into existing database
#[structopt(long)]
merge: bool,
}

View File

@ -6,30 +6,24 @@ use structopt::StructOpt;
use std::io::{self, Write};
/// Generates shell configuration
#[derive(Debug, StructOpt)]
#[structopt(about = "Generates shell configuration")]
#[structopt()]
pub struct Init {
#[structopt(possible_values = &Shell::variants(), case_insensitive = true)]
shell: Shell,
#[structopt(
long,
help = "Renames the 'z' command and corresponding aliases",
alias = "z-cmd",
default_value = "z"
)]
/// Renames the 'z' command and corresponding aliases
#[structopt(long, alias = "z-cmd", default_value = "z")]
cmd: String,
#[structopt(
long,
alias = "no-define-aliases",
help = "Prevents zoxide from defining any commands other than 'z'"
)]
/// Prevents zoxide from defining any commands other than 'z'
#[structopt(long, alias = "no-define-aliases")]
no_aliases: bool,
/// Chooses event on which an entry is added to the database
#[structopt(
long,
help = "Chooses event on which an entry is added to the database",
possible_values = &Hook::variants(),
default_value = "pwd",
case_insensitive = true

View File

@ -2,100 +2,95 @@ use crate::fzf::Fzf;
use crate::util;
use anyhow::{bail, Result};
use float_ord::FloatOrd;
use structopt::StructOpt;
use std::io::{self, Write};
use std::path::Path;
/// Search for a directory
#[derive(Debug, StructOpt)]
#[structopt(about = "Search for a directory")]
#[structopt()]
pub struct Query {
keywords: Vec<String>,
#[structopt(short, long, help = "Opens an interactive selection menu using fzf")]
/// Opens an interactive selection menu using fzf
#[structopt(short, long, conflicts_with = "list")]
interactive: bool,
/// List all matching directories
#[structopt(short, long, conflicts_with = "interactive")]
list: bool,
}
impl Query {
pub fn run(&self) -> Result<()> {
let path_opt = if self.interactive {
query_interactive(&self.keywords)?
} else {
query(&self.keywords)?
};
if self.list {
return query_list(&self.keywords);
}
match path_opt {
Some(path) => println!("{}", path),
None => bail!("no match found"),
};
if self.interactive {
return query_interactive(&self.keywords);
}
Ok(())
// if the input is already a valid path, simply return it
if let [path] = self.keywords.as_slice() {
if Path::new(path).is_dir() {
println!("{}", path);
return Ok(());
}
}
query(&self.keywords)
}
}
fn query(keywords: &[String]) -> Result<Option<String>> {
// if the input is already a valid path, simply return it
if let [path] = keywords {
if Path::new(path).is_dir() {
return Ok(Some(path.to_string()));
}
}
fn query(keywords: &[String]) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let keywords = keywords
.iter()
.map(|keyword| keyword.to_lowercase())
.collect::<Vec<_>>();
let mut matches = db.matches(now, keywords);
db.dirs
.sort_unstable_by_key(|dir| FloatOrd(dir.get_frecency(now)));
for idx in (0..db.dirs.len()).rev() {
let dir = &db.dirs[idx];
if !dir.is_match(&keywords) {
continue;
}
if !dir.is_valid() {
db.dirs.swap_remove(idx);
db.modified = true;
continue;
}
let path = &dir.path;
return Ok(Some(path.to_string()));
match matches.next() {
Some(dir) => println!("{}", dir.path),
None => bail!("no match found"),
}
Ok(None)
Ok(())
}
fn query_interactive(keywords: &[String]) -> Result<Option<String>> {
let keywords = keywords
.iter()
.map(|keyword| keyword.to_lowercase())
.collect::<Vec<_>>();
fn query_interactive(keywords: &[String]) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let mut fzf = Fzf::new()?;
for idx in (0..db.dirs.len()).rev() {
let dir = &db.dirs[idx];
if !dir.is_match(&keywords) {
continue;
}
if !dir.is_valid() {
db.dirs.swap_remove(idx);
db.modified = true;
continue;
}
let mut matches = db.matches(now, keywords);
while let Some(dir) = matches.next() {
fzf.write_dir(dir, now);
}
fzf.wait_selection()
match fzf.wait_selection()? {
Some(path) => println!("{}", path),
None => bail!("no match found"),
};
Ok(())
}
fn query_list(keywords: &[String]) -> Result<()> {
let mut db = util::get_db()?;
let now = util::get_current_time()?;
let mut matches = db.matches(now, keywords);
let stdout = io::stdout();
let mut handle = stdout.lock();
while let Some(dir) = matches.next() {
let path = &dir.path;
writeln!(handle, "{}", path).unwrap();
}
Ok(())
}

View File

@ -3,8 +3,9 @@ use crate::util::{canonicalize, get_db, path_to_str};
use anyhow::{bail, Result};
use structopt::StructOpt;
/// Remove a directory
#[derive(Debug, StructOpt)]
#[structopt(about = "Remove a directory")]
#[structopt()]
pub struct Remove {
path: String,
}