Add support for exact match

This commit is contained in:
SeeStarz 2025-02-17 19:36:12 +07:00
parent 845fe79b5e
commit 2e7b1d733b
9 changed files with 61 additions and 2 deletions

View File

@ -126,6 +126,8 @@ _arguments "${_arguments_options[@]}" : \
'(-i --interactive)--list[List all matching directories]' \ '(-i --interactive)--list[List all matching directories]' \
'-s[Print score with results]' \ '-s[Print score with results]' \
'--score[Print score with results]' \ '--score[Print score with results]' \
'-e[Only match exact]' \
'--exact[Only match exact]' \
'-h[Print help]' \ '-h[Print help]' \
'--help[Print help]' \ '--help[Print help]' \
'-V[Print version]' \ '-V[Print version]' \

View File

@ -108,6 +108,8 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
[CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'List all matching directories') [CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'List all matching directories')
[CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'Print score with results') [CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'Print score with results')
[CompletionResult]::new('--score', '--score', [CompletionResultType]::ParameterName, 'Print score with results') [CompletionResult]::new('--score', '--score', [CompletionResultType]::ParameterName, 'Print score with results')
[CompletionResult]::new('-e', '-e', [CompletionResultType]::ParameterName, 'Only match exact')
[CompletionResult]::new('--exact', '--exact', [CompletionResultType]::ParameterName, 'Only match exact')
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version') [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Print version')

View File

@ -187,7 +187,7 @@ _zoxide() {
return 0 return 0
;; ;;
zoxide__query) zoxide__query)
opts="-a -i -l -s -h -V --all --interactive --list --score --exclude --help --version [KEYWORDS]..." opts="-a -i -l -s -e -h -V --all --interactive --list --score --exclude --exact --help --version [KEYWORDS]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0 return 0

View File

@ -96,6 +96,8 @@ set edit:completion:arg-completer[zoxide] = {|@words|
cand --list 'List all matching directories' cand --list 'List all matching directories'
cand -s 'Print score with results' cand -s 'Print score with results'
cand --score 'Print score with results' cand --score 'Print score with results'
cand -e 'Only match exact'
cand --exact 'Only match exact'
cand -h 'Print help' cand -h 'Print help'
cand --help 'Print help' cand --help 'Print help'
cand -V 'Print version' cand -V 'Print version'

View File

@ -62,6 +62,7 @@ complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s a -l all -d 'Sho
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s i -l interactive -d 'Use interactive selection' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s i -l interactive -d 'Use interactive selection'
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s l -l list -d 'List all matching directories' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s l -l list -d 'List all matching directories'
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s s -l score -d 'Print score with results' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s s -l score -d 'Print score with results'
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s e -l exact -d 'Only match exact'
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s h -l help -d 'Print help' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s V -l version -d 'Print version' complete -c zoxide -n "__fish_zoxide_using_subcommand query" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_zoxide_using_subcommand remove" -s h -l help -d 'Print help' complete -c zoxide -n "__fish_zoxide_using_subcommand remove" -s h -l help -d 'Print help'

View File

@ -228,6 +228,10 @@ const completion: Fig.Spec = {
name: ["-s", "--score"], name: ["-s", "--score"],
description: "Print score with results", description: "Print score with results",
}, },
{
name: ["-e", "--exact"],
description: "Only match exact",
},
{ {
name: ["-h", "--help"], name: ["-h", "--help"],
description: "Print help", description: "Print help",

View File

@ -180,6 +180,10 @@ pub struct Query {
/// Exclude the current directory /// Exclude the current directory
#[clap(long, value_hint = ValueHint::DirPath, value_name = "path")] #[clap(long, value_hint = ValueHint::DirPath, value_name = "path")]
pub exclude: Option<String>, pub exclude: Option<String>,
/// Only match exact
#[clap(long, short)]
pub exact: bool,
} }
/// Remove a directory from the database /// Remove a directory from the database

View File

@ -79,7 +79,8 @@ impl Query {
fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Result<Stream<'a>> { fn get_stream<'a>(&self, db: &'a mut Database, now: Epoch) -> Result<Stream<'a>> {
let mut options = StreamOptions::new(now) let mut options = StreamOptions::new(now)
.with_keywords(self.keywords.iter().map(|s| s.as_str())) .with_keywords(self.keywords.iter().map(|s| s.as_str()))
.with_exclude(config::exclude_dirs()?); .with_exclude(config::exclude_dirs()?)
.with_exact(self.exact);
if !self.all { if !self.all {
let resolve_symlinks = config::resolve_symlinks(); let resolve_symlinks = config::resolve_symlinks();
options = options.with_exists(true).with_resolve_symlinks(resolve_symlinks); options = options.with_exists(true).with_resolve_symlinks(resolve_symlinks);

View File

@ -1,5 +1,7 @@
use std::ffi::OsString;
use std::iter::Rev; use std::iter::Rev;
use std::ops::Range; use std::ops::Range;
use std::path::{Component, PathBuf};
use std::{fs, path}; use std::{fs, path};
use glob::Pattern; use glob::Pattern;
@ -40,6 +42,10 @@ impl<'a> Stream<'a> {
continue; continue;
} }
if !self.filter_by_exact(&dir.path) {
continue;
}
let dir = &self.db.dirs()[idx]; let dir = &self.db.dirs()[idx];
return Some(dir); return Some(dir);
} }
@ -91,6 +97,34 @@ impl<'a> Stream<'a> {
if self.options.resolve_symlinks { fs::symlink_metadata } else { fs::metadata }; if self.options.resolve_symlinks { fs::symlink_metadata } else { fs::metadata };
resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default() resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default()
} }
fn filter_by_exact(&self, path: &str) -> bool {
if !self.options.exact {
return true;
}
let path = util::to_lowercase(path);
let path = PathBuf::from(path);
let mut components: Vec<Component> = path.components().collect();
for keyword in self.options.keywords.iter().rev().map(OsString::from) {
let idx = components.iter().rposition(|component| {
if let Component::Normal(sub_path) = component {
sub_path == &keyword
} else {
false
}
});
if let Some(idx) = idx {
components = components.drain(0..idx).collect();
} else {
return false;
};
}
true
}
} }
pub struct StreamOptions { pub struct StreamOptions {
@ -109,6 +143,9 @@ pub struct StreamOptions {
/// Whether to resolve symlinks when checking if a directory exists. /// Whether to resolve symlinks when checking if a directory exists.
resolve_symlinks: bool, resolve_symlinks: bool,
// Whether to only allow exact match of directory names.
exact: bool,
/// Directories that do not exist and haven't been accessed since TTL will /// Directories that do not exist and haven't been accessed since TTL will
/// be lazily removed. /// be lazily removed.
ttl: Epoch, ttl: Epoch,
@ -122,6 +159,7 @@ impl StreamOptions {
exclude: Vec::new(), exclude: Vec::new(),
exists: false, exists: false,
resolve_symlinks: false, resolve_symlinks: false,
exact: false,
ttl: now.saturating_sub(3 * MONTH), ttl: now.saturating_sub(3 * MONTH),
} }
} }
@ -149,6 +187,11 @@ impl StreamOptions {
self.resolve_symlinks = resolve_symlinks; self.resolve_symlinks = resolve_symlinks;
self self
} }
pub fn with_exact(mut self, exact: bool) -> Self {
self.exact = exact;
self
}
} }
#[cfg(test)] #[cfg(test)]