Add manual score adjustment
This commit adds '-i' and '-d' options to the 'zoxide add' command. '-i' increments the base score by a specified amount. '-d' decrements the base score by a specified amount. Also added unit tests covering the new functionality combined with recency scoring. Fixes/Supports #392
This commit is contained in:
parent
31c44f0649
commit
7c6e003ded
|
|
@ -30,6 +30,10 @@ _zoxide() {
|
||||||
case $line[1] in
|
case $line[1] in
|
||||||
(add)
|
(add)
|
||||||
_arguments "${_arguments_options[@]}" \
|
_arguments "${_arguments_options[@]}" \
|
||||||
|
'(-d --decrement)-i+[Increment path(s) score by specified amount]:INCREMENT: ' \
|
||||||
|
'(-d --decrement)--increment=[Increment path(s) score by specified amount]:INCREMENT: ' \
|
||||||
|
'(-i --increment)-d+[Decrement path(s) score by specified amount. Score won'\''t go below 0]:DECREMENT: ' \
|
||||||
|
'(-i --increment)--decrement=[Decrement path(s) score by specified amount. Score won'\''t go below 0]:DECREMENT: ' \
|
||||||
'-h[Print help information]' \
|
'-h[Print help information]' \
|
||||||
'--help[Print help information]' \
|
'--help[Print help information]' \
|
||||||
'-V[Print version information]' \
|
'-V[Print version information]' \
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
'zoxide;add' {
|
'zoxide;add' {
|
||||||
|
[CompletionResult]::new('-i', 'i', [CompletionResultType]::ParameterName, 'Increment path(s) score by specified amount')
|
||||||
|
[CompletionResult]::new('--increment', 'increment', [CompletionResultType]::ParameterName, 'Increment path(s) score by specified amount')
|
||||||
|
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Decrement path(s) score by specified amount. Score won''t go below 0')
|
||||||
|
[CompletionResult]::new('--decrement', 'decrement', [CompletionResultType]::ParameterName, 'Decrement path(s) score by specified amount. Score won''t go below 0')
|
||||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
|
||||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,28 @@ _zoxide() {
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
zoxide__add)
|
zoxide__add)
|
||||||
opts="-h -V --help --version <PATHS>..."
|
opts="-i -d -h -V --increment --decrement --help --version <PATHS>..."
|
||||||
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
|
||||||
fi
|
fi
|
||||||
case "${prev}" in
|
case "${prev}" in
|
||||||
|
--increment)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-i)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--decrement)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-d)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ set edit:completion:arg-completer[zoxide] = {|@words|
|
||||||
cand remove 'Remove a directory from the database'
|
cand remove 'Remove a directory from the database'
|
||||||
}
|
}
|
||||||
&'zoxide;add'= {
|
&'zoxide;add'= {
|
||||||
|
cand -i 'Increment path(s) score by specified amount'
|
||||||
|
cand --increment 'Increment path(s) score by specified amount'
|
||||||
|
cand -d 'Decrement path(s) score by specified amount. Score won''t go below 0'
|
||||||
|
cand --decrement 'Decrement path(s) score by specified amount. Score won''t go below 0'
|
||||||
cand -h 'Print help information'
|
cand -h 'Print help information'
|
||||||
cand --help 'Print help information'
|
cand --help 'Print help information'
|
||||||
cand -V 'Print version information'
|
cand -V 'Print version information'
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ complete -c zoxide -n "__fish_use_subcommand" -f -a "import" -d 'Import entries
|
||||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "init" -d 'Generate shell configuration'
|
complete -c zoxide -n "__fish_use_subcommand" -f -a "init" -d 'Generate shell configuration'
|
||||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "query" -d 'Search for a directory in the database'
|
complete -c zoxide -n "__fish_use_subcommand" -f -a "query" -d 'Search for a directory in the database'
|
||||||
complete -c zoxide -n "__fish_use_subcommand" -f -a "remove" -d 'Remove a directory from the database'
|
complete -c zoxide -n "__fish_use_subcommand" -f -a "remove" -d 'Remove a directory from the database'
|
||||||
|
complete -c zoxide -n "__fish_seen_subcommand_from add" -s i -l increment -d 'Increment path(s) score by specified amount' -r
|
||||||
|
complete -c zoxide -n "__fish_seen_subcommand_from add" -s d -l decrement -d 'Decrement path(s) score by specified amount. Score won\'t go below 0' -r
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s h -l help -d 'Print help information'
|
complete -c zoxide -n "__fish_seen_subcommand_from add" -s h -l help -d 'Print help information'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from add" -s V -l version -d 'Print version information'
|
complete -c zoxide -n "__fish_seen_subcommand_from add" -s V -l version -d 'Print version information'
|
||||||
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z }"
|
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z }"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,30 @@ const completion: Fig.Spec = {
|
||||||
name: "add",
|
name: "add",
|
||||||
description: "Add a new directory or increment its rank",
|
description: "Add a new directory or increment its rank",
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: ["-i", "--increment"],
|
||||||
|
description: "Increment path(s) score by specified amount",
|
||||||
|
exclusiveOn: [
|
||||||
|
"-d",
|
||||||
|
"--decrement",
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
name: "increment",
|
||||||
|
isOptional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ["-d", "--decrement"],
|
||||||
|
description: "Decrement path(s) score by specified amount. Score won't go below 0",
|
||||||
|
exclusiveOn: [
|
||||||
|
"-i",
|
||||||
|
"--increment",
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
name: "decrement",
|
||||||
|
isOptional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: ["-h", "--help"],
|
name: ["-h", "--help"],
|
||||||
description: "Print help information",
|
description: "Print help information",
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,14 @@ pub enum Cmd {
|
||||||
pub struct Add {
|
pub struct Add {
|
||||||
#[clap(min_values = 1, required = true, value_hint = ValueHint::DirPath)]
|
#[clap(min_values = 1, required = true, value_hint = ValueHint::DirPath)]
|
||||||
pub paths: Vec<PathBuf>,
|
pub paths: Vec<PathBuf>,
|
||||||
|
|
||||||
|
/// Increment path(s) score by specified amount.
|
||||||
|
#[clap(long, short, conflicts_with = "decrement")]
|
||||||
|
pub increment: Option<u16>,
|
||||||
|
|
||||||
|
/// Decrement path(s) score by specified amount. Score won't go below 0.
|
||||||
|
#[clap(long, short, conflicts_with = "increment")]
|
||||||
|
pub decrement: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import entries from another application
|
/// Import entries from another application
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,13 @@ impl Run for Add {
|
||||||
let exclude_dirs = config::exclude_dirs()?;
|
let exclude_dirs = config::exclude_dirs()?;
|
||||||
let max_age = config::maxage()?;
|
let max_age = config::maxage()?;
|
||||||
let now = util::current_time()?;
|
let now = util::current_time()?;
|
||||||
|
let increment = if let Some(num) = self.increment {
|
||||||
|
num as f64
|
||||||
|
} else if let Some(num) = self.decrement {
|
||||||
|
-(num as f64)
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
let mut db = DatabaseFile::new(data_dir);
|
||||||
let mut db = db.open()?;
|
let mut db = db.open()?;
|
||||||
|
|
@ -31,7 +38,7 @@ impl Run for Add {
|
||||||
if !Path::new(path).is_dir() {
|
if !Path::new(path).is_dir() {
|
||||||
bail!("not a directory: {}", path);
|
bail!("not a directory: {}", path);
|
||||||
}
|
}
|
||||||
db.add(path, now);
|
db.add(path, now, increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if db.modified {
|
if db.modified {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@ use anyhow::{bail, Context, Result};
|
||||||
use bincode::Options as _;
|
use bincode::Options as _;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const HOUR: Epoch = 60 * 60;
|
||||||
|
pub const DAY: Epoch = 24 * HOUR;
|
||||||
|
pub const WEEK: Epoch = 7 * DAY;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct DirList<'a>(#[serde(borrow)] pub Vec<Dir<'a>>);
|
pub struct DirList<'a>(#[serde(borrow)] pub Vec<Dir<'a>>);
|
||||||
|
|
||||||
|
|
@ -89,10 +93,6 @@ pub struct Dir<'a> {
|
||||||
|
|
||||||
impl Dir<'_> {
|
impl Dir<'_> {
|
||||||
pub fn score(&self, now: Epoch) -> Rank {
|
pub fn score(&self, now: Epoch) -> Rank {
|
||||||
const HOUR: Epoch = 60 * 60;
|
|
||||||
const DAY: Epoch = 24 * HOUR;
|
|
||||||
const WEEK: Epoch = 7 * DAY;
|
|
||||||
|
|
||||||
// The older the entry, the lesser its importance.
|
// The older the entry, the lesser its importance.
|
||||||
let duration = now.saturating_sub(self.last_accessed);
|
let duration = now.saturating_sub(self.last_accessed);
|
||||||
if duration < HOUR {
|
if duration < HOUR {
|
||||||
|
|
|
||||||
|
|
@ -32,16 +32,19 @@ impl<'file> Database<'file> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new directory or increments its rank. Also updates its last accessed time.
|
/// Adds a new directory or increments its rank. Also updates its last accessed time.
|
||||||
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch) {
|
pub fn add<S: AsRef<str>>(&mut self, path: S, now: Epoch, increment: f64) {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
||||||
None => {
|
None => {
|
||||||
self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: 1.0 });
|
self.dirs.push(Dir { path: path.to_string().into(), last_accessed: now, rank: increment });
|
||||||
}
|
}
|
||||||
Some(dir) => {
|
Some(dir) => {
|
||||||
dir.last_accessed = now;
|
dir.last_accessed = now;
|
||||||
dir.rank += 1.0;
|
dir.rank += increment;
|
||||||
|
if dir.rank < 0.0 {
|
||||||
|
dir.rank = 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -148,7 +151,9 @@ fn db_path<P: AsRef<Path>>(data_dir: P) -> PathBuf {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::dir::{DAY, HOUR, WEEK};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
|
|
@ -159,8 +164,8 @@ mod tests {
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = DatabaseFile::new(data_dir.path());
|
||||||
let mut db = db.open().unwrap();
|
let mut db = db.open().unwrap();
|
||||||
db.add(path, now);
|
db.add(path, now, 1.0);
|
||||||
db.add(path, now);
|
db.add(path, now, 1.0);
|
||||||
db.save().unwrap();
|
db.save().unwrap();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +179,53 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
// Nominal case
|
||||||
|
#[case(1.0, None, HOUR - 10, 4.0)]
|
||||||
|
#[case(1.0, None, DAY - 10, 2.0)]
|
||||||
|
#[case(1.0, None, WEEK - 10, 0.5)]
|
||||||
|
#[case(1.0, None, WEEK + 10, 0.25)]
|
||||||
|
// Start with higher priority
|
||||||
|
#[case(10.0, None, HOUR - 10, 40.0)]
|
||||||
|
#[case(10.0, None, DAY - 10, 20.0)]
|
||||||
|
#[case(10.0, None, WEEK - 10, 5.0)]
|
||||||
|
#[case(10.0, None, WEEK + 10, 2.5)]
|
||||||
|
// Increment priority
|
||||||
|
#[case(1.0, Some(9.0), HOUR - 10, 40.0)]
|
||||||
|
#[case(1.0, Some(9.0), DAY - 10, 20.0)]
|
||||||
|
#[case(1.0, Some(9.0), WEEK - 10, 5.0)]
|
||||||
|
#[case(1.0, Some(9.0), WEEK + 10, 2.5)]
|
||||||
|
// Decrement priority
|
||||||
|
#[case(10.0, Some(-5.0), HOUR - 10, 20.0)]
|
||||||
|
#[case(10.0, Some(-5.0), DAY - 10, 10.0)]
|
||||||
|
#[case(10.0, Some(-5.0), WEEK - 10, 2.5)]
|
||||||
|
#[case(10.0, Some(-5.0), WEEK + 10, 1.25)]
|
||||||
|
// Attempt decrement < 0
|
||||||
|
#[case(1.0, Some(-5.0), HOUR - 10, 0.0)]
|
||||||
|
#[case(1.0, Some(-5.0), DAY - 10, 0.0)]
|
||||||
|
#[case(1.0, Some(-5.0), WEEK - 10, 0.0)]
|
||||||
|
#[case(1.0, Some(-5.0), WEEK + 10, 0.0)]
|
||||||
|
fn add_increment(
|
||||||
|
#[case] first_increment: f64,
|
||||||
|
#[case] second_increment: Option<f64>,
|
||||||
|
#[case] accessed: u64,
|
||||||
|
#[case] expected: f64,
|
||||||
|
) {
|
||||||
|
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||||
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
|
let mut db = DatabaseFile::new(data_dir.path());
|
||||||
|
let mut db = db.open().unwrap();
|
||||||
|
|
||||||
|
let now = 10000000;
|
||||||
|
db.add(path, now - accessed, first_increment);
|
||||||
|
if let Some(more) = second_increment {
|
||||||
|
db.add(path, now - accessed, more);
|
||||||
|
}
|
||||||
|
let dir = &db.dirs[0];
|
||||||
|
// Float version of assert dir.score(now) == expected
|
||||||
|
assert!((dir.score(now) - expected).abs() < 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove() {
|
fn remove() {
|
||||||
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
|
||||||
|
|
@ -183,7 +235,7 @@ mod tests {
|
||||||
{
|
{
|
||||||
let mut db = DatabaseFile::new(data_dir.path());
|
let mut db = DatabaseFile::new(data_dir.path());
|
||||||
let mut db = db.open().unwrap();
|
let mut db = db.open().unwrap();
|
||||||
db.add(path, now);
|
db.add(path, now, 1.0);
|
||||||
db.save().unwrap();
|
db.save().unwrap();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue