mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-11-25 22:17:33 +00:00
Interactive mode for zoxide remove
This commit is contained in:
parent
b21dbefa22
commit
56218f35d3
12
CHANGELOG.md
12
CHANGELOG.md
@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Interactive mode for removing entries (`zoxide remove -i`).
|
||||
- Aliases for interactive `query` and `remove` (`zqi` and `zri` respectively)
|
||||
|
||||
### Changed
|
||||
|
||||
- `zoxide remove` now throws an error if there was no match in the database.
|
||||
- Interactive mode in `zoxide` no longer throws an error if `fzf` exits gracefully.
|
||||
|
||||
## [0.3.1] - 2020-04-03
|
||||
|
||||
### Added
|
||||
|
42
src/db.rs
42
src/db.rs
@ -239,7 +239,13 @@ impl DB {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_all<'a>(&'a mut self, keywords: &'a [String]) -> impl Iterator<Item = &'a Dir> {
|
||||
pub fn query_many<'a>(&'a mut self, keywords: &'a [String]) -> impl Iterator<Item = &'a Dir> {
|
||||
self.query_all()
|
||||
.iter()
|
||||
.filter(move |dir| dir.is_match(keywords))
|
||||
}
|
||||
|
||||
pub fn query_all(&mut self) -> &[Dir] {
|
||||
let orig_len = self.data.dirs.len();
|
||||
self.data.dirs.retain(Dir::is_dir);
|
||||
|
||||
@ -247,28 +253,34 @@ impl DB {
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
self.data
|
||||
.dirs
|
||||
.iter()
|
||||
.filter(move |dir| dir.is_match(keywords))
|
||||
self.data.dirs.as_slice()
|
||||
}
|
||||
|
||||
pub fn remove<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
let path_canonicalized;
|
||||
let path_abs = match path.as_ref().canonicalize() {
|
||||
Ok(path_abs) => {
|
||||
path_canonicalized = path_abs;
|
||||
&path_canonicalized
|
||||
if let Ok(path_abs) = path.as_ref().canonicalize() {
|
||||
self.remove_exact(path_abs)
|
||||
.or_else(|_| self.remove_exact(path))
|
||||
} else {
|
||||
self.remove_exact(path)
|
||||
}
|
||||
}
|
||||
Err(_) => path.as_ref(),
|
||||
};
|
||||
|
||||
if let Some(idx) = self.data.dirs.iter().position(|dir| dir.path == path_abs) {
|
||||
pub fn remove_exact<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
if let Some(idx) = self
|
||||
.data
|
||||
.dirs
|
||||
.iter()
|
||||
.position(|dir| dir.path == path.as_ref())
|
||||
{
|
||||
self.data.dirs.swap_remove(idx);
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
bail!(
|
||||
"could not find path in database: {}",
|
||||
path.as_ref().display()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_tmp(&self) -> PathBuf {
|
||||
|
@ -219,9 +219,14 @@ fn fish_alias(z_cmd: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
abbr -a zi '{} -i'
|
||||
|
||||
abbr -a za 'zoxide add'
|
||||
|
||||
abbr -a zq 'zoxide query'
|
||||
abbr -a zqi 'zoxide query -i'
|
||||
|
||||
abbr -a zr 'zoxide remove'
|
||||
abbr -a zri 'zoxide remove -i'
|
||||
"#,
|
||||
z_cmd
|
||||
)
|
||||
@ -231,9 +236,14 @@ fn posix_alias(z_cmd: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
alias zi='{} -i'
|
||||
|
||||
alias za='zoxide add'
|
||||
|
||||
alias zq='zoxide query'
|
||||
alias zqi='zoxide query -i'
|
||||
|
||||
alias zr='zoxide remove'
|
||||
alias zri='zoxide remove -i'
|
||||
"#,
|
||||
z_cmd
|
||||
)
|
||||
|
@ -70,7 +70,7 @@ impl Query {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut db = util::get_db()?;
|
||||
let dirs = db.query_all(&keywords);
|
||||
let dirs = db.query_many(&keywords);
|
||||
util::fzf_helper(now, dirs)
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,29 @@ use std::path::PathBuf;
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(about = "Remove a directory")]
|
||||
pub struct Remove {
|
||||
path: PathBuf,
|
||||
#[structopt(required_unless("interactive"))]
|
||||
path: Option<PathBuf>,
|
||||
#[structopt(short, long, help = "Opens an interactive selection menu using fzf")]
|
||||
interactive: bool,
|
||||
}
|
||||
|
||||
impl Remove {
|
||||
pub fn run(&self) -> Result<()> {
|
||||
util::get_db()?.remove(&self.path)
|
||||
if self.interactive {
|
||||
let mut db = util::get_db()?;
|
||||
let dirs = db.query_all();
|
||||
let now = util::get_current_time()?;
|
||||
|
||||
if let Some(path_bytes) = util::fzf_helper(now, dirs)? {
|
||||
let path = util::bytes_to_path(&path_bytes)?;
|
||||
db.remove_exact(path)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
// structopt guarantees that unwrap is safe here
|
||||
let path = self.path.as_ref().unwrap();
|
||||
util::get_db()?.remove(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
src/util.rs
30
src/util.rs
@ -12,15 +12,35 @@ use std::process::{Command, Stdio};
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn path_to_bytes<P: AsRef<Path>>(path: &P) -> Option<&[u8]> {
|
||||
pub fn path_to_bytes<P: AsRef<Path>>(path: &P) -> Result<&[u8]> {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
Some(path.as_ref().as_os_str().as_bytes())
|
||||
Ok(path.as_ref().as_os_str().as_bytes())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn path_to_bytes<P: AsRef<Path>>(path: &P) -> Option<&[u8]> {
|
||||
Some(path.as_ref().to_str()?.as_bytes())
|
||||
pub fn path_to_bytes<P: AsRef<Path>>(path: &P) -> Result<&[u8]> {
|
||||
match path.as_ref().to_str() {
|
||||
Some(path_str) => Ok(path_str.as_bytes()),
|
||||
None => bail!("invalid Unicode in path"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn bytes_to_path(bytes: &[u8]) -> Result<&Path> {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
Ok(Path::new(OsStr::from_bytes(bytes)))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
pub fn bytes_to_path(bytes: &[u8]) -> Result<&Path> {
|
||||
use std::str;
|
||||
|
||||
str::from_utf8(bytes)
|
||||
.map(Path::new)
|
||||
.context("invalid Unicode in path")
|
||||
}
|
||||
|
||||
pub fn get_db() -> Result<DB> {
|
||||
@ -75,7 +95,7 @@ where
|
||||
|
||||
for &(dir, frecency) in dir_frecencies.iter() {
|
||||
// ensure that frecency fits in 4 characters
|
||||
if let Some(path_bytes) = path_to_bytes(&dir.path) {
|
||||
if let Ok(path_bytes) = path_to_bytes(&dir.path) {
|
||||
(|| {
|
||||
write!(fzf_stdin, "{:>4} ", frecency)?;
|
||||
fzf_stdin.write_all(path_bytes)?;
|
||||
|
Loading…
Reference in New Issue
Block a user