diff options
author | Kyohei Uto <im@kyoheiu.dev> | 2023-02-12 07:15:27 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-12 07:15:27 +0900 |
commit | 053c4dfcfb99f6cd723e9934d5a191ec47aef0ff (patch) | |
tree | 30725af83c770ca14672f92e1aacc58b0823e8a2 | |
parent | 43923f20527e24533a07a3f86f5014943d2e8556 (diff) | |
parent | bf06a8a12fa739c5a6b60595ba978b14d65cfda8 (diff) |
Merge pull request #182 from kyoheiu/developv2.2.5
v2.2.5
-rw-r--r-- | CHANGELOG.md | 16 | ||||
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | README.md | 25 | ||||
-rw-r--r-- | config.yaml | 5 | ||||
-rw-r--r-- | src/config.rs | 7 | ||||
-rw-r--r-- | src/errors.rs | 4 | ||||
-rw-r--r-- | src/functions.rs | 17 | ||||
-rw-r--r-- | src/help.rs | 2 | ||||
-rw-r--r-- | src/run.rs | 99 | ||||
-rw-r--r-- | src/state.rs | 69 | ||||
-rw-r--r-- | src/term.rs | 4 |
12 files changed, 155 insertions, 97 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9edad50..176b87e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ ## Unreleased +## v2.2.5 (2023-02-12) + +### Added +- Allow renaming even when item name contains non-ascii chars (i.e. wide chars). +- Key command with arguments is now supported: For example, + ``` + exec: + 'feh -.': + [jpg, jpeg, png, gif, svg, hdr] + ``` + this configuration enables you to execute `feh -. <item path>` by `Enter | l | Right`, or `o`. +- Check for out-of-boundary of the cursor at the top of loop. + +### Fixed +- Display when using in kitty: Correctly show the cursor and preview. + ## v2.2.4 (2023-02-01) ### Fixed @@ -371,7 +371,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "felix" -version = "2.2.4" +version = "2.2.5" dependencies = [ "chrono", "content_inspector", @@ -1,6 +1,6 @@ [package] name = "felix" -version = "2.2.4" +version = "2.2.5" authors = ["Kyohei Uto <im@kyoheiu.dev>"] edition = "2021" description = "tui file manager with vim-like key mapping" @@ -23,22 +23,21 @@ For more detailed document, visit https://kyoheiu.dev/felix. ## New release -## v2.2.4 (2023-02-01) +## v2.2.5 (2023-02-12) -### Fixed -- Disable remove_and_yank in the trash dir. -- Clear selection in the select mode if something fails. -- Cursor move after deleting multiple items in select mode. - -## v2.2.3 (2023-01-20) +### Added +- Allow renaming even when item name contains non-ascii chars (i.e. wide chars). +- Key command with arguments is now supported: For example, + ``` + exec: + 'feh -.': + [jpg, jpeg, png, gif, svg, hdr] + ``` + this configuration enables you to execute `feh -. <item path>` by `Enter | l | Right`, or `o`. +- Check for out-of-boundary of the cursor at the top of loop. ### Fixed -- Wide chars handling: Using unicode_width, now felix can properly split file name or previewed texts. -- Preview space height: When horizontally split, image preview could break the layout. Fixed this by adjusting the height. - -### Added -- `chafa`'s minimal supported version: >= v1.10.0 -- Add pacman installation. +- Display when using in kitty: Correctly show the cursor and preview. For more details, see `CHANGELOG.md`. diff --git a/config.yaml b/config.yaml index f55710c..36420fb 100644 --- a/config.yaml +++ b/config.yaml @@ -5,11 +5,12 @@ # (Optional) # key (the command you want to use when opening file): [values] (extensions) +# In the key, You can use arguments. # exec: -# feh: -# [jpg, jpeg, png, gif, svg] # zathura: # [pdf] +# 'feh -.': +# [jpg, jpeg, png, gif, svg, hdr] # (Optional) # Whether to use syntax highlighting in the preview mode. diff --git a/src/config.rs b/src/config.rs index 25cf4b7..5e216c5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,12 +14,13 @@ pub const CONFIG_EXAMPLE: &str = "# (Optional) # default: nvim # (Optional) -# key (the command you want to use): [values] (extensions) +# key (the command you want to use when opening files): [values] (extensions) +# In the key, You can use arguments. # exec: -# feh: -# [jpg, jpeg, png, gif, svg] # zathura: # [pdf] +# 'feh -.': +# [jpg, jpeg, png, gif, svg, hdr] # (Optional) # Whether to use syntax highlighting in the preview mode. diff --git a/src/errors.rs b/src/errors.rs index 54e6efd..bb49aa1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -7,7 +7,7 @@ pub enum FxError { Io(String), Dirs(String), GetItem, - OpenItem, + OpenItem(String), OpenNewWindow(String), Yaml(String), WalkDir(String), @@ -33,7 +33,7 @@ impl std::fmt::Display for FxError { FxError::Io(s) => s.to_owned(), FxError::Dirs(s) => s.to_owned(), FxError::GetItem => "Error: Cannot get item info".to_owned(), - FxError::OpenItem => "Error: Cannot open item".to_owned(), + FxError::OpenItem(s) => s.to_owned(), FxError::OpenNewWindow(s) => s.to_owned(), FxError::Yaml(s) => s.to_owned(), FxError::WalkDir(s) => s.to_owned(), diff --git a/src/functions.rs b/src/functions.rs index 42dc9bb..fb09451 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -229,11 +229,6 @@ pub fn print_help(v: &[String], skip_number: usize, row: u16) { } } -/// Check if we can edit the file name safely. -pub fn is_editable(s: &str) -> bool { - s.is_ascii() -} - /// Initialize the log if `-l` option is added. pub fn init_log(data_local_path: &Path) -> Result<(), FxError> { let mut log_name = chrono::Local::now().format("%F-%H-%M-%S").to_string(); @@ -368,18 +363,6 @@ mod tests { } #[test] - fn test_is_editable() { - let s1 = "Hello, world!"; - let s2 = "image.jpg"; - let s3 = "a̐éö̲\r\n"; - let s4 = "日本の首都は東京です"; - assert!(is_editable(s1)); - assert!(is_editable(s2)); - assert!(!is_editable(s3)); - assert!(!is_editable(s4)); - } - - #[test] fn test_convert_to_permissions() { let file = 33188; let dir = 16877; diff --git a/src/help.rs b/src/help.rs index 4cb9c1e..5b0ce9a 100644 --- a/src/help.rs +++ b/src/help.rs @@ -1,5 +1,5 @@ /// Help text. -pub const HELP: &str = "# felix v2.2.4 +pub const HELP: &str = "# felix v2.2.5 A simple TUI file manager with vim-like keymapping. ## Usage @@ -24,7 +24,6 @@ pub const TRASH: &str = "Trash"; /// Where the item list starts to scroll. const SCROLL_POINT: u16 = 3; const CLRSCR: &str = "\x1B[2J"; -const INITIAL_POS_RENAME: u16 = 12; const INITIAL_POS_SEARCH: usize = 3; const INITIAL_POS_SHELL: u16 = 3; @@ -149,6 +148,10 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { screen.flush()?; 'main: loop { + if state.is_out_of_bounds() { + state.layout.nums.reset(); + state.redraw(BEGINNING_ROW); + } screen.flush()?; let len = state.list.len(); @@ -987,14 +990,6 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { continue; } let item = state.get_item()?.clone(); - if !is_editable(&item.file_name) { - print_warning( - "Item name cannot be renamed due to the character type.", - state.layout.y, - ); - continue; - } - show_cursor(); let mut rename = item.file_name.chars().collect::<Vec<char>>(); to_info_line(); @@ -1002,7 +997,8 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { print!("New name: {}", &rename.iter().collect::<String>(),); screen.flush()?; - let mut current_pos: u16 = 12 + item.file_name.len() as u16; + let (mut current_pos, _) = cursor_pos()?; + let mut current_char_pos = rename.len(); loop { if let Event::Key(KeyEvent { code, .. }) = event::read()? { match code { @@ -1040,55 +1036,73 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { } KeyCode::Left => { - if current_pos == INITIAL_POS_RENAME { + if current_char_pos == 0 { continue; }; - current_pos -= 1; - move_left(1); + if let Some(to_be_skipped) = + unicode_width::UnicodeWidthChar::width( + rename[current_char_pos - 1], + ) + { + current_char_pos -= 1; + current_pos -= to_be_skipped as u16; + move_left(to_be_skipped as u16); + } } KeyCode::Right => { - if current_pos as usize - == rename.len() + INITIAL_POS_RENAME as usize - { + if current_char_pos as usize == rename.len() { continue; }; - current_pos += 1; - move_right(1); + if let Some(to_be_skipped) = + unicode_width::UnicodeWidthChar::width( + rename[current_char_pos], + ) + { + current_char_pos += 1; + current_pos += to_be_skipped as u16; + move_right(to_be_skipped as u16); + } } KeyCode::Char(c) => { - rename.insert( - (current_pos - INITIAL_POS_RENAME).into(), - c, - ); - current_pos += 1; + if let Some(to_be_added) = + unicode_width::UnicodeWidthChar::width(c) + { + rename.insert((current_char_pos).into(), c); + current_char_pos += 1; + current_pos += to_be_added as u16; - to_info_line(); - clear_current_line(); - print!( - "New name: {}", - &rename.iter().collect::<String>(), - ); - move_to(current_pos, 2); + to_info_line(); + clear_current_line(); + print!( + "New name: {}", + &rename.iter().collect::<String>(), + ); + move_to(current_pos + 1, 2); + } } KeyCode::Backspace => { - if current_pos == INITIAL_POS_RENAME { + if current_char_pos == 0 { continue; }; - rename.remove( - (current_pos - INITIAL_POS_RENAME - 1).into(), - ); - current_pos -= 1; + let removed = + rename.remove((current_char_pos - 1).into()); + if let Some(to_be_removed) = + unicode_width::UnicodeWidthChar::width(removed) + { + current_char_pos -= 1; + current_pos -= to_be_removed as u16; - to_info_line(); - clear_current_line(); - print!( - "New name: {}", - &rename.iter().collect::<String>(), - ); - move_to(current_pos, 2); + to_info_line(); + clear_current_line(); + print!( + "New name: {}", + &rename.iter().collect::<String>(), + ); + move_to(current_pos + 1, 2); + } } _ => continue, @@ -1618,6 +1632,7 @@ fn _run(mut state: State, session_path: PathBuf) -> Result<(), FxError> { print!("{}", CLRSCR); state.clear_and_show_headline(); state.list_up(); + state.move_cursor(state.layout.y); screen.flush()?; } } diff --git a/src/state.rs b/src/state.rs index d3f29d1..a1c61e7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -200,15 +200,38 @@ impl State { info!("OPEN: {:?}", path); match map { - None => default.arg(path).status().or(Err(FxError::OpenItem)), + None => default + .arg(path) + .status() + .map_err(|e| FxError::OpenItem(e.to_string())), Some(map) => match extension { - None => default.arg(path).status().or(Err(FxError::OpenItem)), + None => default + .arg(path) + .status() + .map_err(|e| FxError::OpenItem(e.to_string())), Some(extension) => match map.get(extension) { Some(command) => { - let mut ex = Command::new(command); - ex.arg(path).status().or(Err(FxError::OpenItem)) + let command: Vec<&str> = command.split_ascii_whitespace().collect(); + //If the key has no arguments + if command.len() == 1 { + let mut ex = Command::new(command[0]); + ex.arg(path) + .status() + .map_err(|e| FxError::OpenItem(e.to_string())) + } else { + let mut args: Vec<&OsStr> = + command[1..].iter().map(|x| x.as_ref()).collect(); + args.push(path.as_ref()); + let mut ex = Command::new(command[0]); + ex.args(args) + .status() + .map_err(|e| FxError::OpenItem(e.to_string())) + } } - None => default.arg(path).status().or(Err(FxError::OpenItem)), + None => default + .arg(path) + .status() + .map_err(|e| FxError::OpenItem(e.to_string())), }, }, } @@ -236,15 +259,31 @@ impl State { } nix::unistd::ForkResult::Child => { nix::unistd::setsid()?; - let mut ex = Command::new(command); - ex.arg(path) - .stdout(Stdio::null()) - .stdin(Stdio::null()) - .spawn() - .and(Ok(())) - .map_err(|_| FxError::OpenItem)?; - drop(ex); - std::process::exit(0); + let command: Vec<&str> = command.split_ascii_whitespace().collect(); + if command.len() == 1 { + let mut ex = Command::new(command[0]); + ex.arg(path) + .stdout(Stdio::null()) + .stdin(Stdio::null()) + .spawn() + .and(Ok(())) + .map_err(|e| FxError::OpenItem(e.to_string()))?; + drop(ex); + std::process::exit(0); + } else { + let mut args: Vec<&OsStr> = + command[1..].iter().map(|x| x.as_ref()).collect(); + args.push(path.as_ref()); + let mut ex = Command::new(command[0]); + ex.args(args) + .stdout(Stdio::null()) + .stdin(Stdio::null()) + .spawn() + .and(Ok(())) + .map_err(|e| FxError::OpenItem(e.to_string()))?; + drop(ex); + std::process::exit(0); + } } }, Err(e) => Err(FxError::Nix(e.to_string())), @@ -282,7 +321,7 @@ impl State { .stdin(Stdio::null()) .spawn() .and(Ok(())) - .or(Err(FxError::OpenItem)) + .map_err(|e| (FxError::OpenItem(e.to_string()))) } None => Err(FxError::OpenNewWindow( "Cannot open this type of item in new window".to_owned(), diff --git a/src/term.rs b/src/term.rs index 0c25c4a..812e780 100644 --- a/src/term.rs +++ b/src/term.rs @@ -32,6 +32,10 @@ pub fn terminal_size() -> Result<(u16, u16), FxError> { crossterm::terminal::size().map_err(|_| FxError::TerminalSizeDetection) } +pub fn cursor_pos() -> Result<(u16, u16), FxError> { + Ok(crossterm::cursor::position()?) +} + pub fn move_to(x: u16, y: u16) { print!("{}", MoveTo(x - 1, y - 1)); } |