mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-09-21 03:19:04 +00:00
Add option to list all query results
This commit is contained in:
parent
e8eb685e58
commit
19bac0b31a
@ -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> {
|
||||
|
48
src/db.rs
48
src/db.rs
@ -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
|
||||
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user