mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-11-10 23:30:57 +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 anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn zo_data_dir() -> Result<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 anyhow::{bail, Context, Result};
|
||||||
|
use float_ord::FloatOrd;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -149,6 +150,10 @@ impl Db {
|
|||||||
Ok(())
|
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 {
|
fn get_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
||||||
data_dir.as_ref().join("db.zo")
|
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 Rank = f64;
|
||||||
pub type Epoch = i64; // use a signed integer so subtraction can be performed on it
|
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::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Add a new directory or increment its rank
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(about = "Add a new directory or increment its rank")]
|
#[structopt()]
|
||||||
pub struct Add {
|
pub struct Add {
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,14 @@ use structopt::StructOpt;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Import from z database
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(about = "Import from z database")]
|
#[structopt()]
|
||||||
pub struct Import {
|
pub struct Import {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
||||||
#[structopt(long, help = "Merge entries into existing database")]
|
/// Merge entries into existing database
|
||||||
|
#[structopt(long)]
|
||||||
merge: bool,
|
merge: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,30 +6,24 @@ use structopt::StructOpt;
|
|||||||
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
/// Generates shell configuration
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(about = "Generates shell configuration")]
|
#[structopt()]
|
||||||
pub struct Init {
|
pub struct Init {
|
||||||
#[structopt(possible_values = &Shell::variants(), case_insensitive = true)]
|
#[structopt(possible_values = &Shell::variants(), case_insensitive = true)]
|
||||||
shell: Shell,
|
shell: Shell,
|
||||||
|
|
||||||
#[structopt(
|
/// Renames the 'z' command and corresponding aliases
|
||||||
long,
|
#[structopt(long, alias = "z-cmd", default_value = "z")]
|
||||||
help = "Renames the 'z' command and corresponding aliases",
|
|
||||||
alias = "z-cmd",
|
|
||||||
default_value = "z"
|
|
||||||
)]
|
|
||||||
cmd: String,
|
cmd: String,
|
||||||
|
|
||||||
#[structopt(
|
/// Prevents zoxide from defining any commands other than 'z'
|
||||||
long,
|
#[structopt(long, alias = "no-define-aliases")]
|
||||||
alias = "no-define-aliases",
|
|
||||||
help = "Prevents zoxide from defining any commands other than 'z'"
|
|
||||||
)]
|
|
||||||
no_aliases: bool,
|
no_aliases: bool,
|
||||||
|
|
||||||
|
/// Chooses event on which an entry is added to the database
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long,
|
long,
|
||||||
help = "Chooses event on which an entry is added to the database",
|
|
||||||
possible_values = &Hook::variants(),
|
possible_values = &Hook::variants(),
|
||||||
default_value = "pwd",
|
default_value = "pwd",
|
||||||
case_insensitive = true
|
case_insensitive = true
|
||||||
|
@ -2,100 +2,95 @@ use crate::fzf::Fzf;
|
|||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use float_ord::FloatOrd;
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// Search for a directory
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(about = "Search for a directory")]
|
#[structopt()]
|
||||||
pub struct Query {
|
pub struct Query {
|
||||||
keywords: Vec<String>,
|
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,
|
interactive: bool,
|
||||||
|
|
||||||
|
/// List all matching directories
|
||||||
|
#[structopt(short, long, conflicts_with = "interactive")]
|
||||||
|
list: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Query {
|
impl Query {
|
||||||
pub fn run(&self) -> Result<()> {
|
pub fn run(&self) -> Result<()> {
|
||||||
let path_opt = if self.interactive {
|
if self.list {
|
||||||
query_interactive(&self.keywords)?
|
return query_list(&self.keywords);
|
||||||
} else {
|
}
|
||||||
query(&self.keywords)?
|
|
||||||
};
|
|
||||||
|
|
||||||
match path_opt {
|
if self.interactive {
|
||||||
Some(path) => println!("{}", path),
|
return query_interactive(&self.keywords);
|
||||||
None => bail!("no match found"),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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>> {
|
fn query(keywords: &[String]) -> Result<()> {
|
||||||
// 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut db = util::get_db()?;
|
let mut db = util::get_db()?;
|
||||||
let now = util::get_current_time()?;
|
let now = util::get_current_time()?;
|
||||||
|
|
||||||
let keywords = keywords
|
let mut matches = db.matches(now, keywords);
|
||||||
.iter()
|
|
||||||
.map(|keyword| keyword.to_lowercase())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
db.dirs
|
match matches.next() {
|
||||||
.sort_unstable_by_key(|dir| FloatOrd(dir.get_frecency(now)));
|
Some(dir) => println!("{}", dir.path),
|
||||||
|
None => bail!("no match found"),
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_interactive(keywords: &[String]) -> Result<Option<String>> {
|
fn query_interactive(keywords: &[String]) -> Result<()> {
|
||||||
let keywords = keywords
|
|
||||||
.iter()
|
|
||||||
.map(|keyword| keyword.to_lowercase())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut db = util::get_db()?;
|
let mut db = util::get_db()?;
|
||||||
let now = util::get_current_time()?;
|
let now = util::get_current_time()?;
|
||||||
|
|
||||||
let mut fzf = Fzf::new()?;
|
let mut fzf = Fzf::new()?;
|
||||||
|
|
||||||
for idx in (0..db.dirs.len()).rev() {
|
let mut matches = db.matches(now, keywords);
|
||||||
let dir = &db.dirs[idx];
|
|
||||||
|
|
||||||
if !dir.is_match(&keywords) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dir.is_valid() {
|
|
||||||
db.dirs.swap_remove(idx);
|
|
||||||
db.modified = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while let Some(dir) = matches.next() {
|
||||||
fzf.write_dir(dir, now);
|
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 anyhow::{bail, Result};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
/// Remove a directory
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
#[structopt(about = "Remove a directory")]
|
#[structopt()]
|
||||||
pub struct Remove {
|
pub struct Remove {
|
||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user