Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions contrib/completions/_zoxide

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions contrib/completions/_zoxide.ps1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion contrib/completions/zoxide.bash

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions contrib/completions/zoxide.elv

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contrib/completions/zoxide.fish

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contrib/completions/zoxide.nu

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions contrib/completions/zoxide.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion src/cmd/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ impl Run for Add {
let exclude_dirs = config::exclude_dirs()?;
let max_age = config::maxage()?;
let now = util::current_time()?;
let mut first_entry = true;

let mut db = Database::open()?;

Expand All @@ -35,7 +36,14 @@ impl Run for Add {
}

let by = self.score.unwrap_or(1.0);
db.add_update(path, by, now);

// Adds the alias only to the first entry to avoid confusion
if first_entry {
db.add_update(path, by, now, self.alias.clone());
first_entry = false;
} else {
db.add_update(path, by, now, None);
}
}

if db.dirty() {
Expand Down
5 changes: 5 additions & 0 deletions src/cmd/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ pub struct Add {
/// doesn't
#[clap(short, long)]
pub score: Option<f64>,

/// The alias for the entry to initialize without and then set afterwards or
/// initialize with it
#[clap(short, long)]
pub alias: Option<String>,
}

/// Edit the database
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ impl Run for Edit {
match &self.cmd {
Some(cmd) => {
match cmd {
EditCommand::Decrement { path } => db.add(path, -1.0, now),
EditCommand::Decrement { path } => db.add(path, -1.0, now, None),
EditCommand::Delete { path } => {
db.remove(path);
}
EditCommand::Increment { path } => db.add(path, 1.0, now),
EditCommand::Increment { path } => db.add(path, 1.0, now, None),
EditCommand::Reload => {}
}
db.save()?;
Expand Down
42 changes: 26 additions & 16 deletions src/cmd/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ fn import_autojump(db: &mut Database, buffer: &str) -> Result<()> {
// take a while to get normalized.
rank = sigmoid(rank);

db.add_unchecked(path, rank, 0);
db.add_unchecked(path, rank, 0, None);
}

if db.dirty() {
Expand All @@ -65,7 +65,7 @@ fn import_z(db: &mut Database, buffer: &str) -> Result<()> {

let path = split.next().with_context(|| format!("invalid entry: {line}"))?;

db.add_unchecked(path, rank, last_accessed);
db.add_unchecked(path, rank, last_accessed, None);
}

if db.dirty() {
Expand Down Expand Up @@ -94,7 +94,7 @@ mod tests {
("/xyzzy/thud", 8.0, 800),
("/foo/bar", 9.0, 900),
] {
db.add_unchecked(path, rank, last_accessed);
db.add_unchecked(path, rank, last_accessed, None);
}

let buffer = "\
Expand All @@ -107,12 +107,22 @@ mod tests {
println!("got: {:?}", &db.dirs());

let exp = [
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0 },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
Dir { path: "/foo/bar".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },
Dir { path: "/quux/quuz".into(), rank: 1.0 + sigmoid(5.0), last_accessed: 100 },
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0, alias: None },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600, alias: None },
Dir {
path: "/foo/bar".into(),
rank: 9.0 + sigmoid(2.0),
last_accessed: 900,
alias: None,
},
Dir {
path: "/quux/quuz".into(),
rank: 1.0 + sigmoid(5.0),
last_accessed: 100,
alias: None,
},
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300, alias: None },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800, alias: None },
];
println!("exp: {exp:?}");

Expand All @@ -134,7 +144,7 @@ mod tests {
("/xyzzy/thud", 8.0, 800),
("/foo/bar", 9.0, 900),
] {
db.add_unchecked(path, rank, last_accessed);
db.add_unchecked(path, rank, last_accessed, None);
}

let buffer = "\
Expand All @@ -148,12 +158,12 @@ mod tests {
println!("got: {:?}", &db.dirs());

let exp = [
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900 },
Dir { path: "/quux/quuz".into(), rank: 10.0, last_accessed: 500 },
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700, alias: None },
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600, alias: None },
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900, alias: None },
Dir { path: "/quux/quuz".into(), rank: 10.0, last_accessed: 500, alias: None },
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300, alias: None },
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800, alias: None },
];
println!("exp: {exp:?}");

Expand Down
1 change: 1 addition & 0 deletions src/db/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct Dir<'a> {
pub path: Cow<'a, str>,
pub rank: Rank,
pub last_accessed: Epoch,
pub alias: Option<String>,
}

impl Dir<'_> {
Expand Down
73 changes: 56 additions & 17 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,38 +64,72 @@ impl Database {
Ok(())
}

/// Increments the rank of a directory, or creates it if it does not exist.
pub fn add(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {
/// Increments the rank of a directory and updates its alias if provided, or
/// creates with an optional alias it if it does not exist.
pub fn add(
&mut self,
path: impl AsRef<str> + Into<String>,
by: Rank,
now: Epoch,
alias: Option<String>,
) {
self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {
Some(dir) => dir.rank = (dir.rank + by).max(0.0),
None => {
dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })
Some(dir) if alias.is_some() => {
dir.rank = (dir.rank + by).max(0.0);
dir.alias = alias;
}
Some(dir) => dir.rank = (dir.rank + by).max(0.0),
None => dirs.push(Dir {
path: path.into().into(),
rank: by.max(0.0),
last_accessed: now,
alias,
}),
});
self.with_dirty_mut(|dirty| *dirty = true);
}

/// Creates a new directory. This will create a duplicate entry if this
/// directory is always in the database, it is expected that the user either
/// does a check before calling this, or calls `dedup()` afterward.
pub fn add_unchecked(&mut self, path: impl AsRef<str> + Into<String>, rank: Rank, now: Epoch) {
pub fn add_unchecked(
&mut self,
path: impl AsRef<str> + Into<String>,
rank: Rank,
now: Epoch,
alias: Option<String>,
) {
self.with_dirs_mut(|dirs| {
dirs.push(Dir { path: path.into().into(), rank, last_accessed: now })
dirs.push(Dir { path: path.into().into(), rank, last_accessed: now, alias })
});
self.with_dirty_mut(|dirty| *dirty = true);
}

/// Increments the rank and updates the last_accessed of a directory, or
/// creates it if it does not exist.
pub fn add_update(&mut self, path: impl AsRef<str> + Into<String>, by: Rank, now: Epoch) {
/// Increments the rank and updates the last_accessed and alias of a
/// directory or creates it if it does not exist.
pub fn add_update(
&mut self,
path: impl AsRef<str> + Into<String>,
by: Rank,
now: Epoch,
alias: Option<String>,
) {
self.with_dirs_mut(|dirs| match dirs.iter_mut().find(|dir| dir.path == path.as_ref()) {
Some(dir) => {
Some(dir) if alias.is_some() => {
dir.rank = (dir.rank + by).max(0.0);
dir.last_accessed = now;
dir.alias = alias;
}
None => {
dirs.push(Dir { path: path.into().into(), rank: by.max(0.0), last_accessed: now })
Some(dir) => {
dir.rank = (dir.rank + by).max(0.0);
dir.last_accessed = now;
}
None => dirs.push(Dir {
path: path.into().into(),
rank: by.max(0.0),
last_accessed: now,
alias,
}),
});
self.with_dirty_mut(|dirty| *dirty = true);
}
Expand Down Expand Up @@ -240,11 +274,14 @@ mod tests {
let data_dir = tempfile::tempdir().unwrap();
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
let now = 946684800;
let empty_alias: Option<String> = None;
let alias: Option<String> = Some(String::from("alias"));

{
let mut db = Database::open_dir(data_dir.path()).unwrap();
db.add(path, 1.0, now);
db.add(path, 1.0, now);
db.add(path, 1.0, now, empty_alias.clone());
db.add(path, 1.0, now, alias.clone());
db.add(path, 1.0, now, empty_alias.clone());
db.save().unwrap();
}

Expand All @@ -254,8 +291,10 @@ mod tests {

let dir = &db.dirs()[0];
assert_eq!(dir.path, path);
assert!((dir.rank - 2.0).abs() < 0.01);
assert!((dir.rank - 3.0).abs() < 0.01);
assert_eq!(dir.last_accessed, now);
assert_eq!(dir.alias, alias);
assert_ne!(dir.alias, empty_alias);
}
}

Expand All @@ -267,7 +306,7 @@ mod tests {

{
let mut db = Database::open_dir(data_dir.path()).unwrap();
db.add(path, 1.0, now);
db.add(path, 1.0, now, None);
db.save().unwrap();
}

Expand Down
18 changes: 15 additions & 3 deletions src/db/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ impl<'a> Stream<'a> {
while let Some(idx) = self.idxs.next() {
let dir = &self.db.dirs()[idx];

if !self.filter_by_keywords(&dir.path) {
if !self.filter_path_by_keywords(&dir.path)
&& !self.filter_alias_by_keywords(&dir.alias)
{
continue;
}

Expand Down Expand Up @@ -77,7 +79,7 @@ impl<'a> Stream<'a> {
resolver(path).map(|metadata| metadata.is_dir()).unwrap_or_default()
}

fn filter_by_keywords(&self, path: &str) -> bool {
fn filter_path_by_keywords(&self, path: &str) -> bool {
let (keywords_last, keywords) = match self.options.keywords.split_last() {
Some(split) => split,
None => return true,
Expand All @@ -104,6 +106,16 @@ impl<'a> Stream<'a> {

true
}

fn filter_alias_by_keywords(&self, alias: &Option<String>) -> bool {
match alias {
Some(alias) => {
let alias = alias.to_lowercase();
self.options.keywords.iter().all(|kw| alias.contains(kw))
}
None => false,
}
}
}

pub struct StreamOptions {
Expand Down Expand Up @@ -206,6 +218,6 @@ mod tests {
let db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);
let options = StreamOptions::new(0).with_keywords(keywords.iter());
let stream = Stream::new(db, options);
assert_eq!(is_match, stream.filter_by_keywords(path));
assert_eq!(is_match, stream.filter_path_by_keywords(path));
}
}
Loading