diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e2156d..1cb2cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ## Unreleased +### Added +- `ignore_case` option to do case-insensitie search by `/`. + +### Changed +- Symlink items linked to directory now appears in the directory section, not the file section. + +### fixed +- `z` command can now receive multiple arguments: `z dot files` works as in your terminal. + ## v2.12.1 (2024-02-04) ### Fixed diff --git a/Cargo.lock b/Cargo.lock index b08d2aa..552a402 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ dependencies = [ "lzma-rs", "natord", "nix", + "normpath", "rayon", "serde", "serde_yaml", @@ -641,6 +642,15 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "normpath" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5831952a9476f2fed74b77d74182fa5ddc4d21c72ec45a333b250e3ed0272804" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "num-conv" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3ccaf0f..87d9034 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ lzma-rs = "0.3.0" zstd = "0.12.4" unicode-width = "0.1.10" git2 = {version = "0.18.0", default-features = false } +normpath = "1.2.0" [dev-dependencies] bwrap = { version = "1.3.0", features = ["use_std"] } diff --git a/src/run.rs b/src/run.rs index 05c2ff1..67261b9 100644 --- a/src/run.rs +++ b/src/run.rs @@ -13,6 +13,7 @@ use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifier use crossterm::execute; use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen}; use log::{error, info}; +use normpath::PathExt; use std::env; use std::io::{stdout, Write}; use std::panic; @@ -118,12 +119,14 @@ pub fn run(arg: PathBuf, log: bool) -> Result<(), FxError> { let mut state = State::new(&session_path)?; state.trash_dir = trash_dir_path; state.lwd_file = lwd_file_path; - state.current_dir = if cfg!(not(windows)) { - // If executed this on windows, "//?" will be inserted at the beginning of the path. - arg.canonicalize()? - } else { - arg - }; + let normalized_arg = arg.normalize(); + if normalized_arg.is_err() { + return Err(FxError::Arg(format!( + "Invalid path: {}\n`fx -h` shows help.", + &arg.display() + ))); + } + state.current_dir = normalized_arg.unwrap().into_path_buf(); state.jumplist.add(&state.current_dir); state.is_ro = match has_write_permission(&state.current_dir) { Ok(b) => !b, @@ -2240,12 +2243,13 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { } else if commands.len() == 2 && command == "cd" { if let Ok(target) = std::path::Path::new(commands[1]) - .canonicalize() + .normalize() { if target.exists() { - if let Err(e) = - state.chdir(&target, Move::Jump) - { + if let Err(e) = state.chdir( + &target.into_path_buf(), + Move::Jump, + ) { print_warning(e, state.layout.y); } break 'command; diff --git a/src/state.rs b/src/state.rs index 23c17dc..632a818 100644 --- a/src/state.rs +++ b/src/state.rs @@ -16,6 +16,7 @@ use crossterm::event::KeyEventKind; use crossterm::event::{Event, KeyCode, KeyEvent}; use crossterm::style::Stylize; use log::info; +use normpath::PathExt; use std::collections::VecDeque; use std::collections::{BTreeMap, BTreeSet}; use std::env; @@ -1647,56 +1648,70 @@ impl State { /// Return footer string. fn make_footer(&self, item: &ItemInfo) -> String { - match &item.file_ext { - Some(ext) => { - let footer = match item.permissions { - Some(permissions) => { - format!( - " {}/{} {} {} {}", + let mut footer = String::new(); + if item.file_type == FileType::Symlink { + footer = " linked to: ".to_owned(); + match &item.symlink_dir_path { + Some(true_path) => { + footer.push_str(true_path.to_str().unwrap_or("(invalid unicode path)")) + } + None => match fs::read_link(&item.file_path) { + Ok(true_path) => match true_path.normalize() { + Ok(p) => footer + .push_str(p.as_path().to_str().unwrap_or("(invalid univode path)")), + Err(_) => footer.push_str("(invalid path)"), + }, + Err(_) => footer.push_str("(broken link)"), + }, + } + } else { + match &item.file_ext { + Some(ext) => { + footer = match item.permissions { + Some(permissions) => { + format!( + " {}/{} {} {} {}", + self.layout.nums.index + 1, + self.list.len(), + ext.clone(), + to_proper_size(item.file_size), + convert_to_permissions(permissions) + ) + } + None => format!( + " {}/{} {} {}", self.layout.nums.index + 1, self.list.len(), ext.clone(), to_proper_size(item.file_size), - convert_to_permissions(permissions) - ) - } - None => format!( - " {}/{} {} {}", - self.layout.nums.index + 1, - self.list.len(), - ext.clone(), - to_proper_size(item.file_size), - ), - }; - footer - .chars() - .take(self.layout.terminal_column.into()) - .collect() - } - None => { - let footer = match item.permissions { - Some(permissions) => { - format!( - " {}/{} {} {}", + ), + }; + } + None => { + footer = match item.permissions { + Some(permissions) => { + format!( + " {}/{} {} {}", + self.layout.nums.index + 1, + self.list.len(), + to_proper_size(item.file_size), + convert_to_permissions(permissions) + ) + } + None => format!( + " {}/{} {}", self.layout.nums.index + 1, self.list.len(), to_proper_size(item.file_size), - convert_to_permissions(permissions) - ) - } - None => format!( - " {}/{} {}", - self.layout.nums.index + 1, - self.list.len(), - to_proper_size(item.file_size), - ), - }; - footer - .chars() - .take(self.layout.terminal_column.into()) - .collect() + ), + }; + } } } + footer + .chars() + .take(self.layout.terminal_column.into()) + .collect() } /// Scroll down previewed text. @@ -1806,12 +1821,7 @@ fn read_item(entry: fs::DirEntry) -> ItemInfo { if filetype == FileType::Symlink { if let Ok(sym_meta) = fs::metadata(&path) { if sym_meta.is_dir() { - if cfg!(not(windows)) { - // Avoid error on Windows - path.canonicalize().ok() - } else { - Some(path.clone()) - } + path.normalize().map(|p| p.into_path_buf()).ok() } else { None }