Merge 99d682717c
into 30129f4344
This commit is contained in:
commit
ca6332b3b0
|
@ -7,6 +7,13 @@ 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/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `$_ZO_CASE_SENSITIVITY` to support case-sensitive querying in addition to the
|
||||||
|
default case-insensitive querying.
|
||||||
|
|
||||||
## [0.9.8] - 2025-05-27
|
## [0.9.8] - 2025-05-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -426,6 +426,9 @@ When calling `zoxide init`, the following flags are available:
|
||||||
Environment variables[^2] can be used for configuration. They must be set before
|
Environment variables[^2] can be used for configuration. They must be set before
|
||||||
`zoxide init` is called.
|
`zoxide init` is called.
|
||||||
|
|
||||||
|
- `_ZO_CASE_SENSITIVITY`
|
||||||
|
- Defaults to case-insensitive searches. Set to `case-sensitive` for
|
||||||
|
case-sensitive searching.
|
||||||
- `_ZO_DATA_DIR`
|
- `_ZO_DATA_DIR`
|
||||||
- Specifies the directory in which the database is stored.
|
- Specifies the directory in which the database is stored.
|
||||||
- The default value varies across OSes:
|
- The default value varies across OSes:
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/ajeetdsouza/zoxide
|
||||||
{all-args}{after-help}
|
{all-args}{after-help}
|
||||||
|
|
||||||
<bold><underline>Environment variables:</underline></bold>
|
<bold><underline>Environment variables:</underline></bold>
|
||||||
|
{tab}<bold>_ZO_CASE_SENSITIVITY</bold>{tab}Set case-sensitivity: case-sensitive or case-insensitive (default)
|
||||||
{tab}<bold>_ZO_DATA_DIR</bold> {tab}Path for zoxide data files
|
{tab}<bold>_ZO_DATA_DIR</bold> {tab}Path for zoxide data files
|
||||||
{tab}<bold>_ZO_ECHO</bold> {tab}Print the matched directory before navigating to it when set to 1
|
{tab}<bold>_ZO_ECHO</bold> {tab}Print the matched directory before navigating to it when set to 1
|
||||||
{tab}<bold>_ZO_EXCLUDE_DIRS</bold> {tab}List of directory globs to be excluded
|
{tab}<bold>_ZO_EXCLUDE_DIRS</bold> {tab}List of directory globs to be excluded
|
||||||
|
|
|
@ -79,6 +79,7 @@ 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_case_sensitivity(config::case_sensitivity())
|
||||||
.with_exclude(config::exclude_dirs()?);
|
.with_exclude(config::exclude_dirs()?);
|
||||||
if !self.all {
|
if !self.all {
|
||||||
let resolve_symlinks = config::resolve_symlinks();
|
let resolve_symlinks = config::resolve_symlinks();
|
||||||
|
|
|
@ -6,6 +6,33 @@ use anyhow::{Context, Result, ensure};
|
||||||
use glob::Pattern;
|
use glob::Pattern;
|
||||||
|
|
||||||
use crate::db::Rank;
|
use crate::db::Rank;
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
pub enum CaseSensitivity {
|
||||||
|
CaseInsensitive,
|
||||||
|
CaseSensitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CaseSensitivity {
|
||||||
|
pub fn convert_case(&self, s: &str) -> String {
|
||||||
|
match self {
|
||||||
|
CaseSensitivity::CaseInsensitive => util::to_lowercase(s),
|
||||||
|
CaseSensitivity::CaseSensitive => s.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn case_sensitivity() -> CaseSensitivity {
|
||||||
|
env::var_os("_ZO_CASE_SENSITIVITY")
|
||||||
|
.map_or(CaseSensitivity::CaseInsensitive, map_case_sensitivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_case_sensitivity(s: OsString) -> CaseSensitivity {
|
||||||
|
match s.to_str() {
|
||||||
|
Some("case-sensitive") => CaseSensitivity::CaseSensitive,
|
||||||
|
_ => CaseSensitivity::CaseInsensitive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn data_dir() -> Result<PathBuf> {
|
pub fn data_dir() -> Result<PathBuf> {
|
||||||
let dir = match env::var_os("_ZO_DATA_DIR") {
|
let dir = match env::var_os("_ZO_DATA_DIR") {
|
||||||
|
|
|
@ -4,8 +4,9 @@ use std::{fs, path};
|
||||||
|
|
||||||
use glob::Pattern;
|
use glob::Pattern;
|
||||||
|
|
||||||
|
use crate::config::CaseSensitivity;
|
||||||
use crate::db::{Database, Dir, Epoch};
|
use crate::db::{Database, Dir, Epoch};
|
||||||
use crate::util::{self, MONTH};
|
use crate::util::MONTH;
|
||||||
|
|
||||||
pub struct Stream<'a> {
|
pub struct Stream<'a> {
|
||||||
db: &'a mut Database,
|
db: &'a mut Database,
|
||||||
|
@ -48,13 +49,18 @@ impl<'a> Stream<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_by_keywords(&self, path: &str) -> bool {
|
fn filter_by_keywords(&self, path: &str) -> bool {
|
||||||
let (keywords_last, keywords) = match self.options.keywords.split_last() {
|
let keywords: Vec<String> = self
|
||||||
|
.options
|
||||||
|
.keywords
|
||||||
|
.iter()
|
||||||
|
.map(|s| self.options.case_sensitivity.convert_case(s))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (keywords_last, keywords) = match keywords.split_last() {
|
||||||
Some(split) => split,
|
Some(split) => split,
|
||||||
None => return true,
|
None => return true,
|
||||||
};
|
};
|
||||||
|
let mut path = &self.options.case_sensitivity.convert_case(path)[..];
|
||||||
let path = util::to_lowercase(path);
|
|
||||||
let mut path = path.as_str();
|
|
||||||
match path.rfind(keywords_last) {
|
match path.rfind(keywords_last) {
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
if path[idx + keywords_last.len()..].contains(path::is_separator) {
|
if path[idx + keywords_last.len()..].contains(path::is_separator) {
|
||||||
|
@ -112,6 +118,9 @@ pub struct StreamOptions {
|
||||||
/// 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,
|
||||||
|
|
||||||
|
/// Whether searching should be perform case sensitively.
|
||||||
|
case_sensitivity: CaseSensitivity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamOptions {
|
impl StreamOptions {
|
||||||
|
@ -123,6 +132,7 @@ impl StreamOptions {
|
||||||
exists: false,
|
exists: false,
|
||||||
resolve_symlinks: false,
|
resolve_symlinks: false,
|
||||||
ttl: now.saturating_sub(3 * MONTH),
|
ttl: now.saturating_sub(3 * MONTH),
|
||||||
|
case_sensitivity: CaseSensitivity::CaseInsensitive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +141,12 @@ impl StreamOptions {
|
||||||
I: IntoIterator,
|
I: IntoIterator,
|
||||||
I::Item: AsRef<str>,
|
I::Item: AsRef<str>,
|
||||||
{
|
{
|
||||||
self.keywords = keywords.into_iter().map(util::to_lowercase).collect();
|
self.keywords = keywords.into_iter().map(|s| s.as_ref().into()).collect();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_case_sensitivity(mut self, case_sensitivity: CaseSensitivity) -> Self {
|
||||||
|
self.case_sensitivity = case_sensitivity;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,4 +200,16 @@ mod tests {
|
||||||
let stream = Stream::new(db, options);
|
let stream = Stream::new(db, options);
|
||||||
assert_eq!(is_match, stream.filter_by_keywords(path));
|
assert_eq!(is_match, stream.filter_by_keywords(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
// Case normalization
|
||||||
|
#[case(&["fOo", "bAr"], "/foo/bar", false)]
|
||||||
|
fn query_case_sensitive(#[case] keywords: &[&str], #[case] path: &str, #[case] is_match: bool) {
|
||||||
|
let db = &mut Database::new(PathBuf::new(), Vec::new(), |_| Vec::new(), false);
|
||||||
|
let options = StreamOptions::new(0)
|
||||||
|
.with_keywords(keywords.iter())
|
||||||
|
.with_case_sensitivity(CaseSensitivity::CaseSensitive);
|
||||||
|
let stream = Stream::new(db, options);
|
||||||
|
assert_eq!(is_match, stream.filter_by_keywords(path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue