mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
支持中文首字母搜索文件
This commit is contained in:
parent
8456d3a7dc
commit
b960aec48c
3 changed files with 109 additions and 3 deletions
|
|
@ -18,6 +18,7 @@ mod tasks;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
mod validation;
|
mod validation;
|
||||||
pub mod which;
|
pub mod which;
|
||||||
|
mod userdic;
|
||||||
|
|
||||||
pub use layout::*;
|
pub use layout::*;
|
||||||
pub(crate) use pattern::*;
|
pub(crate) use pattern::*;
|
||||||
|
|
@ -46,12 +47,14 @@ pub static THEME: RoCell<theme::Theme> = RoCell::new();
|
||||||
pub static INPUT: RoCell<popup::Input> = RoCell::new();
|
pub static INPUT: RoCell<popup::Input> = RoCell::new();
|
||||||
pub static SELECT: RoCell<popup::Select> = RoCell::new();
|
pub static SELECT: RoCell<popup::Select> = RoCell::new();
|
||||||
pub static WHICH: RoCell<which::Which> = RoCell::new();
|
pub static WHICH: RoCell<which::Which> = RoCell::new();
|
||||||
|
pub static USER_DIC: RoCell<userdic::USERDICT> = RoCell::new();
|
||||||
|
|
||||||
pub fn init() -> anyhow::Result<()> {
|
pub fn init() -> anyhow::Result<()> {
|
||||||
let config_dir = Xdg::config_dir();
|
let config_dir = Xdg::config_dir();
|
||||||
MERGED_YAZI.init(Preset::yazi(&config_dir)?);
|
MERGED_YAZI.init(Preset::yazi(&config_dir)?);
|
||||||
MERGED_KEYMAP.init(Preset::keymap(&config_dir)?);
|
MERGED_KEYMAP.init(Preset::keymap(&config_dir)?);
|
||||||
MERGED_THEME.init(Preset::theme(&config_dir)?);
|
MERGED_THEME.init(Preset::theme(&config_dir)?);
|
||||||
|
USER_DIC.init(userdic::read_user_dict(&config_dir));
|
||||||
|
|
||||||
LAYOUT.with(Default::default);
|
LAYOUT.with(Default::default);
|
||||||
|
|
||||||
|
|
|
||||||
34
yazi-config/src/userdic.rs
Normal file
34
yazi-config/src/userdic.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct USERDICT {
|
||||||
|
pub exist: bool,
|
||||||
|
pub table: HashMap<char, Vec<char>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_user_dict(p:&Path) -> USERDICT {
|
||||||
|
let mut ret = USERDICT {
|
||||||
|
exist: false,
|
||||||
|
table: HashMap::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(file) = File::open(p.join("user.dict")) {
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
for line in reader.lines() {
|
||||||
|
if let Ok(entry) = line {
|
||||||
|
let parts: Vec<char> = entry.trim().chars().collect();
|
||||||
|
if parts.len() == 3 {
|
||||||
|
ret.table.entry(parts[0])
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(parts[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.exist = true;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
@ -3,16 +3,58 @@ use std::{ffi::OsStr, ops::Range};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use regex::bytes::{Regex, RegexBuilder};
|
use regex::bytes::{Regex, RegexBuilder};
|
||||||
use yazi_shared::event::Cmd;
|
use yazi_shared::event::Cmd;
|
||||||
|
use yazi_config::USER_DIC;
|
||||||
|
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
raw: String,
|
raw: String,
|
||||||
regex: Regex,
|
regex: Regex,
|
||||||
|
chars: Vec<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MatchResult {
|
||||||
|
ismatch: bool,
|
||||||
|
range: Option<Range<usize>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn table_match(needle: &Vec<char>, haystack: Vec<char>, flag:bool) -> MatchResult {
|
||||||
|
let needle_len = needle.len();
|
||||||
|
let haystack_len = haystack.len();
|
||||||
|
if needle_len > haystack_len { return MatchResult{ismatch:false, range:None} }
|
||||||
|
|
||||||
|
for i in 0..=(haystack_len - needle_len) {
|
||||||
|
let mut found = true;
|
||||||
|
for j in 0..needle_len {
|
||||||
|
if haystack[i + j] == needle[j] { continue; }
|
||||||
|
if let Some(value) = USER_DIC.table.get(&haystack[i+j]) {
|
||||||
|
if !value.contains(&needle[j]) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
if flag {
|
||||||
|
let start:usize = haystack[0..i].into_iter().collect::<String>().len();
|
||||||
|
let end:usize = haystack[0..(i + needle.len())].into_iter().collect::<String>().len();
|
||||||
|
return MatchResult{ismatch:true, range:Some(start..end)}
|
||||||
|
} else {
|
||||||
|
return MatchResult{ismatch:true, range:None}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MatchResult{ismatch:false, range:None}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl PartialEq for Filter {
|
impl PartialEq for Filter {
|
||||||
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
|
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Filter {
|
impl Filter {
|
||||||
pub fn new(s: &str, case: FilterCase) -> Result<Self> {
|
pub fn new(s: &str, case: FilterCase) -> Result<Self> {
|
||||||
let regex = match case {
|
let regex = match case {
|
||||||
|
|
@ -23,15 +65,42 @@ impl Filter {
|
||||||
FilterCase::Sensitive => Regex::new(s)?,
|
FilterCase::Sensitive => Regex::new(s)?,
|
||||||
FilterCase::Insensitive => RegexBuilder::new(s).case_insensitive(true).build()?,
|
FilterCase::Insensitive => RegexBuilder::new(s).case_insensitive(true).build()?,
|
||||||
};
|
};
|
||||||
Ok(Self { raw: s.to_owned(), regex })
|
let chars: Vec<char> = if USER_DIC.exist { s.chars().collect()} else { Vec::new() };
|
||||||
|
|
||||||
|
Ok(Self { raw: s.to_owned(), regex, chars})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn matches(&self, name: &OsStr) -> bool { self.regex.is_match(name.as_encoded_bytes()) }
|
pub fn matches(&self, name: &OsStr) -> bool {
|
||||||
|
if self.regex.is_match(name.as_encoded_bytes()) {
|
||||||
|
return true
|
||||||
|
} else if !USER_DIC.exist {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
let name_bytes = name.as_encoded_bytes();
|
||||||
|
if let Ok(s) = std::str::from_utf8(name_bytes) {
|
||||||
|
return table_match(&self.chars, s.chars().collect(), false).ismatch
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn highlighted(&self, name: &OsStr) -> Option<Vec<Range<usize>>> {
|
pub fn highlighted(&self, name: &OsStr) -> Option<Vec<Range<usize>>> {
|
||||||
self.regex.find(name.as_encoded_bytes()).map(|m| vec![m.range()])
|
let m = self.regex.find(name.as_encoded_bytes());
|
||||||
|
return match m {
|
||||||
|
Some(r) => Some(vec![r.range()]),
|
||||||
|
None => {
|
||||||
|
let name_bytes = name.as_encoded_bytes();
|
||||||
|
return match std::str::from_utf8(name_bytes) {
|
||||||
|
Ok(s) => return match table_match(&self.chars, s.chars().collect(), true).range {
|
||||||
|
Some(r) => Some(vec![r]),
|
||||||
|
None => None
|
||||||
|
},
|
||||||
|
Err(_) => None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue