From 4c98a351c9584421ce54d01799ca04910dc9a6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Wed, 13 Sep 2023 23:57:58 +0800 Subject: [PATCH 1/4] feat: show keywords when in search mode (#152) --- app/src/header/layout.rs | 8 ++++---- core/src/manager/tab.rs | 3 ++- shared/src/url.rs | 10 ++++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/src/header/layout.rs b/app/src/header/layout.rs index 15695166..4371b95f 100644 --- a/app/src/header/layout.rs +++ b/app/src/header/layout.rs @@ -19,11 +19,11 @@ impl<'a> Widget for Layout<'a> { .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(area); - let current = &self.cx.manager.current(); - let location = if current.cwd.is_search() { - format!("{} (search)", readable_path(¤t.cwd)) + let cwd = &self.cx.manager.current().cwd; + let location = if cwd.is_search() { + format!("{} (search: {})", readable_path(cwd), cwd.frag().unwrap()) } else { - readable_path(¤t.cwd) + readable_path(cwd) }; Paragraph::new(location).style(Style::new().fg(Color::Cyan)).render(chunks[0], buf); diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index 6e8b656a..b24fb4b2 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -289,7 +289,7 @@ impl Tab { handle.abort(); } - let cwd = self.current.cwd.to_search(); + let mut cwd = self.current.cwd.clone(); let hidden = self.show_hidden; self.search = Some(tokio::spawn(async move { @@ -297,6 +297,7 @@ impl Tab { bail!("canceled") }; + cwd = cwd.into_search(subject.clone()); let rx = if grep { external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject }) } else { diff --git a/shared/src/url.rs b/shared/src/url.rs index 9b5b340c..10e45db3 100644 --- a/shared/src/url.rs +++ b/shared/src/url.rs @@ -4,6 +4,7 @@ use std::{ffi::{OsStr, OsString}, fmt::{Debug, Formatter}, ops::{Deref, DerefMut pub struct Url { scheme: UrlScheme, path: PathBuf, + frag: Option, } #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] @@ -114,11 +115,12 @@ impl Url { pub fn is_search(&self) -> bool { self.scheme == UrlScheme::Search } #[inline] - pub fn to_search(&self) -> Self { self.clone().into_search() } + pub fn to_search(&self, frag: String) -> Self { self.clone().into_search(frag) } #[inline] - pub fn into_search(mut self) -> Self { + pub fn into_search(mut self, frag: String) -> Self { self.scheme = UrlScheme::Search; + self.frag = Some(frag); self } @@ -137,4 +139,8 @@ impl Url { // --- Path #[inline] pub fn set_path(&mut self, path: PathBuf) { self.path = path; } + + // --- Frag + #[inline] + pub fn frag(&self) -> Option<&str> { self.frag.as_deref() } } From 9c6b3c9d1d1763074341042ef0f74ab45bb56da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20=C4=90=E1=BB=A9c=20To=C3=A0n?= <33489972+ndtoan96@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:01:16 +0700 Subject: [PATCH 2/4] feat: fallback to built-in highlighting if `jq` is not installed (#151) --- core/src/manager/preview/provider.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/manager/preview/provider.rs b/core/src/manager/preview/provider.rs index 4ac499db..3271b0ae 100644 --- a/core/src/manager/preview/provider.rs +++ b/core/src/manager/preview/provider.rs @@ -3,6 +3,7 @@ use std::{io::BufRead, path::Path, sync::atomic::{AtomicUsize, Ordering}}; use adaptor::ADAPTOR; use anyhow::anyhow; use config::{MANAGER, PREVIEW}; +use futures::TryFutureExt; use shared::{MimeKind, PeekError}; use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped}; use tokio::fs; @@ -69,7 +70,9 @@ impl Provider { } pub(super) async fn json(path: &Path, skip: usize) -> Result { - external::jq(path, skip, MANAGER.layout.preview_height()).await + external::jq(path, skip, MANAGER.layout.preview_height()) + .or_else(|_| Provider::highlight(path, skip)) + .await } pub(super) async fn archive(path: &Path, skip: usize) -> Result { From b62307dc4a7635eb1f31d3222f65856e5553c36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Thu, 14 Sep 2023 09:34:07 +0800 Subject: [PATCH 3/4] feat: make the glob expr case insensitive by default, and prepend `\s` to make it sensitive (#156) --- config/docs/yazi.md | 4 ++-- config/src/pattern.rs | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/config/docs/yazi.md b/config/docs/yazi.md index 7e6f4255..0500fde2 100644 --- a/config/docs/yazi.md +++ b/config/docs/yazi.md @@ -81,8 +81,8 @@ rules = [ Available rule parameters are as follows: -- name: Glob expression for matching the file name -- mime: Glob expression for matching the MIME type +- name: Glob expression for matching the file name. Case insensitive by default, add `\s` to the beginning to make it sensitive. +- mime: Glob expression for matching the MIME type. Case insensitive by default, add `\s` to the beginning to make it sensitive. - use: Opener name corresponding to the names in the opener section. ## tasks diff --git a/config/src/pattern.rs b/config/src/pattern.rs index 1b5bdc60..7a9d7c30 100644 --- a/config/src/pattern.rs +++ b/config/src/pattern.rs @@ -1,18 +1,26 @@ use std::path::Path; +use glob::MatchOptions; use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(try_from = "String")] pub struct Pattern { inner: glob::Pattern, + sensitive: bool, is_folder: bool, full_path: bool, } impl Pattern { #[inline] - pub fn matches(&self, str: impl AsRef) -> bool { self.inner.matches(str.as_ref()) } + pub fn matches(&self, str: impl AsRef) -> bool { + self.inner.matches_with(str.as_ref(), MatchOptions { + case_sensitive: self.sensitive, + require_literal_separator: false, + require_literal_leading_dot: false, + }) + } #[inline] pub fn match_path(&self, path: impl AsRef, is_folder: Option) -> bool { @@ -20,7 +28,7 @@ impl Pattern { let s = if self.full_path { path.to_str() } else { - path.file_name().and_then(|n| n.to_str()).or(path.to_str()) + path.file_name().and_then(|n| n.to_str()).or_else(|| path.to_str()) }; is_folder.map_or(true, |f| f == self.is_folder) && s.map_or(false, |s| self.matches(s)) } @@ -30,11 +38,13 @@ impl TryFrom<&str> for Pattern { type Error = anyhow::Error; fn try_from(s: &str) -> Result { - let new = s.trim_end_matches('/'); + let a = s.trim_start_matches("\\s"); + let b = a.trim_end_matches('/'); Ok(Self { - inner: glob::Pattern::new(new)?, - is_folder: new.len() < s.len(), - full_path: new.contains('/'), + inner: glob::Pattern::new(b)?, + sensitive: a.len() < s.len(), + is_folder: b.len() < a.len(), + full_path: b.contains('/'), }) } } From 902c0942244f2e1753899ed0aa1d4faf5b01c7eb Mon Sep 17 00:00:00 2001 From: Linus <23507341+Linus789@users.noreply.github.com> Date: Thu, 14 Sep 2023 01:56:50 +0000 Subject: [PATCH 4/4] feat: new `sort_sensitive` option, and sort case-insensitively by default (#155) --- app/src/executor.rs | 1 + config/docs/yazi.md | 5 +++++ config/preset/yazi.toml | 1 + config/src/manager/manager.rs | 1 + core/src/files/sorter.rs | 32 +++++++++++++++++++++++--------- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/src/executor.rs b/app/src/executor.rs index 6c7e41ae..bbc1ffec 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -151,6 +151,7 @@ impl Executor { let b = cx.manager.active_mut().set_sorter(FilesSorter { by: SortBy::try_from(exec.args.get(0).cloned().unwrap_or_default()) .unwrap_or_default(), + sensitive: exec.named.contains_key("sensitive"), reverse: exec.named.contains_key("reverse"), dir_first: exec.named.contains_key("dir_first"), }); diff --git a/config/docs/yazi.md b/config/docs/yazi.md index 0500fde2..0c609692 100644 --- a/config/docs/yazi.md +++ b/config/docs/yazi.md @@ -14,6 +14,11 @@ - `"natural"`: Sort naturally, e.g. `1.md` < `2.md` < `10.md` - `"size"`: Sort by file size +- sort_sensitive: Sort case-sensitively + + - `true`: Case-sensitive + - `false`: Case-insensitive + - sort_reverse: Display files in reverse order - `true`: Reverse order diff --git a/config/preset/yazi.toml b/config/preset/yazi.toml index ed101007..0e23f707 100644 --- a/config/preset/yazi.toml +++ b/config/preset/yazi.toml @@ -1,6 +1,7 @@ [manager] layout = [ 1, 4, 3 ] sort_by = "modified" +sort_sensitive = false sort_reverse = true sort_dir_first = true show_hidden = false diff --git a/config/src/manager/manager.rs b/config/src/manager/manager.rs index ddc1627a..f1acf380 100644 --- a/config/src/manager/manager.rs +++ b/config/src/manager/manager.rs @@ -9,6 +9,7 @@ pub struct Manager { // Sorting pub sort_by: SortBy, + pub sort_sensitive: bool, pub sort_reverse: bool, pub sort_dir_first: bool, diff --git a/core/src/files/sorter.rs b/core/src/files/sorter.rs index 36ce4794..5149a9ac 100644 --- a/core/src/files/sorter.rs +++ b/core/src/files/sorter.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, collections::BTreeMap, mem}; +use std::{cmp::Ordering, collections::BTreeMap, mem, ops::Deref}; use config::{manager::SortBy, MANAGER}; use shared::Url; @@ -8,6 +8,7 @@ use super::File; #[derive(Clone, Copy, PartialEq)] pub struct FilesSorter { pub by: SortBy, + pub sensitive: bool, pub reverse: bool, pub dir_first: bool, } @@ -16,6 +17,7 @@ impl Default for FilesSorter { fn default() -> Self { Self { by: MANAGER.sort_by, + sensitive: MANAGER.sort_sensitive, reverse: MANAGER.sort_reverse, dir_first: MANAGER.sort_dir_first, } @@ -29,9 +31,17 @@ impl FilesSorter { } match self.by { - SortBy::Alphabetical => { - items.sort_unstable_by(|a, b| self.cmp(&*a.url, &*b.url, self.promote(a, b))) - } + SortBy::Alphabetical => items.sort_unstable_by(|a, b| { + if self.sensitive { + return self.cmp(&*a.url, &*b.url, self.promote(a, b)); + } + + self.cmp( + a.url.as_os_str().to_ascii_lowercase(), + b.url.as_os_str().to_ascii_lowercase(), + self.promote(a, b), + ) + }), SortBy::Created => items.sort_unstable_by(|a, b| { if let (Ok(aa), Ok(bb)) = (a.meta.created(), b.meta.created()) { return self.cmp(aa, bb, self.promote(a, b)); @@ -65,12 +75,16 @@ impl FilesSorter { indices.sort_unstable_by(|&a, &b| { let promote = self.promote(entities[a].1, entities[b].1); if promote != Ordering::Equal { - promote - } else if self.reverse { - natord::compare(&entities[b].0, &entities[a].0) - } else { - natord::compare(&entities[a].0, &entities[b].0) + return promote; } + + let ordering = if self.sensitive { + natord::compare(&entities[a].0, &entities[b].0) + } else { + natord::compare_ignore_case(&entities[a].0, &entities[b].0) + }; + + if self.reverse { ordering.reverse() } else { ordering } }); let dummy = File {