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
|
||||
(add)
|
||||
_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]' \
|
||||
'--help[Print help information]' \
|
||||
'-V[Print version information]' \
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ Register-ArgumentCompleter -Native -CommandName 'zoxide' -ScriptBlock {
|
|||
break
|
||||
}
|
||||
'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('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
|
||||
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
|
||||
|
|
|
|||
|
|
@ -48,12 +48,28 @@ _zoxide() {
|
|||
return 0
|
||||
;;
|
||||
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
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
fi
|
||||
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=()
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ set edit:completion:arg-completer[zoxide] = {|@words|
|
|||
cand remove 'Remove a directory from the database'
|
||||
}
|
||||
&'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 --help 'Print help 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 "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_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 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 }"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,30 @@ const completion: Fig.Spec = {
|
|||
name: "add",
|
||||
description: "Add a new directory or increment its rank",
|
||||
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"],
|
||||
description: "Print help information",
|
||||
|
|
|
|||
|
|
@ -33,6 +33,14 @@ pub enum Cmd {
|
|||
pub struct Add {
|
||||
#[clap(min_values = 1, required = true, value_hint = ValueHint::DirPath)]
|
||||
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
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ impl Run for Add {
|
|||
let exclude_dirs = config::exclude_dirs()?;
|
||||
let max_age = config::maxage()?;
|
||||
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 = db.open()?;
|
||||
|
|
@ -31,7 +38,7 @@ impl Run for Add {
|
|||
if !Path::new(path).is_dir() {
|
||||
bail!("not a directory: {}", path);
|
||||
}
|
||||
db.add(path, now);
|
||||
db.add(path, now, increment);
|
||||
}
|
||||
|
||||
if db.modified {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ use anyhow::{bail, Context, Result};
|
|||
use bincode::Options as _;
|
||||
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)]
|
||||
pub struct DirList<'a>(#[serde(borrow)] pub Vec<Dir<'a>>);
|
||||
|
||||
|
|
@ -89,10 +93,6 @@ pub struct Dir<'a> {
|
|||
|
||||
impl Dir<'_> {
|
||||
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.
|
||||
let duration = now.saturating_sub(self.last_accessed);
|
||||
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.
|
||||
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();
|
||||
|
||||
match self.dirs.iter_mut().find(|dir| dir.path == path) {
|
||||
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) => {
|
||||
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)]
|
||||
mod tests {
|
||||
use super::dir::{DAY, HOUR, WEEK};
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[test]
|
||||
fn add() {
|
||||
|
|
@ -159,8 +164,8 @@ mod tests {
|
|||
{
|
||||
let mut db = DatabaseFile::new(data_dir.path());
|
||||
let mut db = db.open().unwrap();
|
||||
db.add(path, now);
|
||||
db.add(path, now);
|
||||
db.add(path, now, 1.0);
|
||||
db.add(path, now, 1.0);
|
||||
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]
|
||||
fn remove() {
|
||||
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 = db.open().unwrap();
|
||||
db.add(path, now);
|
||||
db.add(path, now, 1.0);
|
||||
db.save().unwrap();
|
||||
}
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue