summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqkzk <qkzk@users.noreply.github.com>2024-09-24 21:18:46 +0200
committerGitHub <noreply@github.com>2024-09-24 21:18:46 +0200
commit272e8d862650a30c76699fec02b1e88ec2df38cd (patch)
tree06966e1375120d2f9225a2b82f8fe193f5ede75b
parentde9173906fbbfc595bfd4fb6ef1866e8841bb292 (diff)
parentc7e26b15c6ef1a4e3f492c83e8ae96a2973ea347 (diff)
Merge pull request #97 from qkzk/v0.1.29-devv0.1.29
V0.1.29 dev
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--build.rs19
-rw-r--r--config_files/fm/config.yaml78
-rw-r--r--development.md63
-rw-r--r--src/app/application.rs56
-rw-r--r--src/app/header_footer.rs143
-rw-r--r--src/app/internal_settings.rs12
-rw-r--r--src/app/session.rs3
-rw-r--r--src/app/status.rs194
-rw-r--r--src/app/tab.rs283
-rw-r--r--src/common/constant_strings_paths.rs6
-rw-r--r--src/common/format.rs54
-rw-r--r--src/common/mod.rs2
-rw-r--r--src/common/utils.rs37
-rw-r--r--src/config/colors.rs3
-rw-r--r--src/config/configuration.rs30
-rw-r--r--src/config/gradient.rs3
-rw-r--r--src/config/oncelock_static.rs8
-rw-r--r--src/event/event_dispatch.rs6
-rw-r--r--src/event/event_exec.rs183
-rw-r--r--src/io/commands.rs8
-rw-r--r--src/io/display.rs176
-rw-r--r--src/io/git.rs5
-rw-r--r--src/io/input_history.rs9
-rw-r--r--src/io/log.rs3
-rw-r--r--src/io/opendal.rs24
-rw-r--r--src/io/opener.rs11
-rw-r--r--src/main.rs6
-rw-r--r--src/modes/display/content_window.rs17
-rw-r--r--src/modes/display/directory.rs120
-rw-r--r--src/modes/display/fileinfo.rs155
-rw-r--r--src/modes/display/mod.rs7
-rw-r--r--src/modes/display/preview.rs1266
-rw-r--r--src/modes/display/skim.rs5
-rw-r--r--src/modes/display/tree.rs11
-rw-r--r--src/modes/display/uber.rs362
-rw-r--r--src/modes/edit/bulkrename.rs6
-rw-r--r--src/modes/edit/cli_menu.rs10
-rw-r--r--src/modes/edit/completion.rs4
-rw-r--r--src/modes/edit/compress.rs4
-rw-r--r--src/modes/edit/context.rs92
-rw-r--r--src/modes/edit/copy_move.rs5
-rw-r--r--src/modes/edit/cryptsetup.rs17
-rw-r--r--src/modes/edit/decompress.rs4
-rw-r--r--src/modes/edit/flagged.rs26
-rw-r--r--src/modes/edit/help.rs3
-rw-r--r--src/modes/edit/history.rs19
-rw-r--r--src/modes/edit/leave_mode.rs66
-rw-r--r--src/modes/edit/marks.rs8
-rw-r--r--src/modes/edit/menu.rs59
-rw-r--r--src/modes/edit/mod.rs2
-rw-r--r--src/modes/edit/mount_help.rs3
-rw-r--r--src/modes/edit/node_creation.rs3
-rw-r--r--src/modes/edit/regex.rs3
-rw-r--r--src/modes/edit/removable_devices.rs11
-rw-r--r--src/modes/edit/search.rs44
-rw-r--r--src/modes/edit/second_line.rs1
-rw-r--r--src/modes/edit/shell_parser.rs3
-rw-r--r--src/modes/edit/shortcut.rs8
-rw-r--r--src/modes/edit/sort.rs14
-rw-r--r--src/modes/edit/trash.rs11
-rw-r--r--src/modes/edit/tui_menu.rs12
-rw-r--r--src/modes/mode.rs6
64 files changed, 1802 insertions, 2014 deletions
diff --git a/Cargo.lock b/Cargo.lock
index acee30b9..3ab8d77a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -932,7 +932,7 @@ dependencies = [
[[package]]
name = "fm-tui"
-version = "0.1.28"
+version = "0.1.29"
dependencies = [
"anyhow",
"chrono",
diff --git a/Cargo.toml b/Cargo.toml
index b1589a6c..bbecca0f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "fm-tui"
-version = "0.1.28"
+version = "0.1.29"
authors = ["Quentin Konieczko <qu3nt1n@gmail.com>"]
edition = "2021"
license-file = "LICENSE.txt"
diff --git a/build.rs b/build.rs
index 0decca27..37e3e13d 100644
--- a/build.rs
+++ b/build.rs
@@ -20,25 +20,6 @@ fn main() {
Ok(_) => (),
Err(e) => eprintln!("{e:?}"),
}
-
- update_breaking_config()
-}
-
-/// Remove old binds from user config file.
-///
-/// Remove all binds to `Jump` and `Mocp...` variants since they were removed from fm.
-fn update_breaking_config() {
- let config = tilde("~/.config/fm/config.yaml");
- let config: &str = config.borrow();
- let content = std::fs::read_to_string(config)
- .expect("config file should be readable")
- .lines()
- .map(String::from)
- .filter(|line| !line.contains("Jump"))
- .filter(|line| !line.contains("Mocp"))
- .collect::<Vec<String>>()
- .join("\n");
- std::fs::write(config, content).expect("config should be writabe");
}
fn home_dir() -> Option<std::path::PathBuf> {
diff --git a/config_files/fm/config.yaml b/config_files/fm/config.yaml
index ca0157f1..31d2b33f 100644
--- a/config_files/fm/config.yaml
+++ b/config_files/fm/config.yaml
@@ -7,53 +7,54 @@ terminal: st
# configurable colors
-# Define a palette for the "normal" files.
-# File will be colored randomly from their extensions.
-# Accepted values are "red", "green", "blue" or a rgb color
+# Colors for "non normal" files. The list is below.
+# you can define an ANSI color or an rgb color for any kind of file except "normal" files.
+# colors for normal files are calculated on the fly from the "palette" values provided above.
+# List of allowed ANSI colors:
+# white, black, red, green, blue, yellow, cyan, magenta
+# light_white, light_black, light_red, light_green, light_blue, light_yellow, light_cyan, light_magenta
# Allowed Format for rgb color :
# - `rgb(r, g, b)` where r, g and b are integers between 0 and 255 included.
# - `#rrggbb` aka hex colors, where rr, gg and bb are hexadecimals
-# Unfortunatelly, not all ansi color names are accepted.
-# palette:
-# start: "rgb(255, 110, 128)"
-# stop: "rgb(123, 100, 255)"
-palette:
- start: green
- stop: blue
-
-# Colors for "non normal" files. The list is below.
colors:
- # you can define an ANSI color or an rgb color for any kind of file except "normal" files.
- # colors for normal files are calculated on the fly.
- # List of allowed colors:
- # white, black, red, green, blue, yellow, cyan, magenta
- # light_white, light_black, light_red, light_green, light_blue, light_yellow, light_cyan, light_magenta
- # Allowed Format for rgb color :
- # - `rgb(r, g, b)` where r, g and b are integers between 0 and 255 included.
- # - `#rrggbb` aka hex colors, where rr, gg and bb are hexadecimals
- directory: red
- block: yellow
- char: green
- fifo: blue
- socket: cyan
- symlink: magenta
- broken: light_magenta
+ # Gradient from start to stop for normal files.
+ # the files are colored randomly according to their extension
+ # ANSI colors won't be linked to their configured values but to the default ANSI values.
+ normal_start: rgb(187, 102, 255)
+ normal_stop: rgb(255, 102, 187)
+
+ # color for different filekinds
+ # here you can use ANSI values
+ # colors for folders / directory
+ directory: rgb(45, 250, 209)
+ # block device (hard drives...)
+ block: rgb(231, 100, 100)
+ # char devices (ttys, /dev/urandom etc.)
+ char: rgb(230, 189, 87)
+ # fifos
+ fifo: rgb(180, 255, 255)
+ # opened sockets
+ socket: rgb(231, 100, 100)
+ # valid symlinks
+ symlink: rgb(59, 204, 255)
+ # broken symlinks
+ broken: rgb(140, 140, 140)
-# Colors for menus, headers, footers etc.
-menu_colors:
+ # Colors for menus, headers, footers etc.
+ # The same values are accepted.
# first color
- first: rgb(45, 250, 209)
+ header_first: rgb(45, 250, 209)
# second color
- second: rgb(230, 189, 87)
+ header_second: rgb(230, 189, 87)
# selected tab border
- selected_border: rgb(45, 250, 209)
+ selected_border: rgb(45, 250, 209)
# non selected tab border
- inert_border: rgb(120, 120, 120)
- # palette of 4 elements, used in menus (second window) and header
- palette_1: rgb(45, 250, 209)
- palette_2: rgb(230, 189, 87)
- palette_3: rgb(230, 167, 255)
- palette_4: rgb(59, 204, 255)
+ inert_border: rgb(120, 120, 120)
+ # palette of 4 elements, used in menus (second window)
+ palette_1: rgb(45, 250, 209)
+ palette_2: rgb(230, 189, 87)
+ palette_3: rgb(230, 167, 255)
+ palette_4: rgb(59, 204, 255)
# keybindings
#
@@ -148,6 +149,7 @@ keys:
custom:
# open the selected file with chrome
'alt-u': "/usr/bin/google-chrome-stable %s"
+ # drag & drop the selected file to a GUI application
'shift-d': "/usr/bin/dragon-drop %s"
# DO NOT EDIT UNLESS YOU WANT TO ADD AN UNKNOWN TERMINAL EMULATOR
diff --git a/development.md b/development.md
index 867c1667..4c73a137 100644
--- a/development.md
+++ b/development.md
@@ -1052,8 +1052,6 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally.
- [ ] FIX: alt + g, type, complete, back crash. Can't reproduce
- [x] FIX: too much thing on menu and last line
-## Current dev
-
### Version 0.1.28
#### Summary
@@ -1102,10 +1100,71 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally.
- [x] Fix: opening tree mode with selection on "." doesn't display "." as selected
- [x] refactor draw tree line
- [x] Fix: Crash when quiting. "sending on a closed channel". From quit -> refresher::quit
+- [ ]
- [ ] Badges to latest version
+## Current dev
+
+### Version 0.1.29
+
+#### Summary
+
+- Fixed the documentation and updated the badges
+- Flagged files are now displayed in a menu instead of the main window. You can still jump to them, removed them individually
+- Fix jump mode (Alt+g) to allow paths to file. It will jump there and select the file.
+- Preview valid symlink as their target. Broken symlink aren't previewed at all
+- Fixed a few bugs. See details below.
+- **Breaking changes**: color configuration.
+
+ Only one mapping is used in config.yml. All colors are now regrouped there.
+ Some names have changed :
+
+ - "start" -> "normal_start"
+ - "stop" -> "normal_stop"
+
+ Your old config will still be loaded, but their colors won't be recognized unless you update the config.
+ See the [source](config_file/fm/config.yml) for more infos.
+
+#### Changelog
+
+- [x] refactor path reducer
+- [x] Fix: when ".." is selected, header path is wrong. This is a big one...
+- [x] Fix: directory mode when path is root, header is wrong, should just be "/""run" not "/""/run"
+- [x] Fix attempt of docs. Don't panic in build if no config file is found. That was dumb
+- [x] Fix: Tree mode, when path is root, header is wrong
+- [x] Alt+g should accept pathes to file from input and go there
+- [x] Preview refactor
+ - [x] mod for ueberzug
+ - [x] builder for ueberzug
+ - [x] creator for thumbnail
+ - [x] video slideshow
+ - [x] Fix: ueberzug creation may crash the app (poison lock etc.) `Error: Error locking status: poisoned lock: another task failed inside`
+ - [x] preview builder
+ - [x] preview symlink to folder as the target
+ - [x] preview symlink to files as the target. Must change PreviewBuilder completely
+ - [x] merge all previews which hold text into a single variant
+ - [x] simplify the mess as much as possible
+- [x] flagged should be a menu
+ - [x] use a menu to display flagged files
+ - [x] display binds
+- [x] refactor context menu "more info" into a struct with explicit methods
+- [x] simplify history a little
+- [x] simplify color configuration
+ - [x] only one "colors" map in yaml file
+ - [x] migration of config file : detailed in release
+
## TODO
+- [ ] google drive should be a display ?
+- [ ] status, tab, event exec refactor. What should go where ? status is too big and should be splitted
+- [ ] menus refactor
+ - [ ] open 2 menus at once
+ - [ ] maybe use the same system as preview, create them on the fly
+ - [ ] attach menus to tab, where they belong
+ - [ ] leaving left menu shouldn't reset right menu
+- [ ] replace tuikit by ratatui + crossterm
+- [ ] Focus & mouseover. Mousemove require raw terminal mode.. Requires to rewrite every event (Mousepress, mouse release etc.)
+ Another motivation to switch to ratatui + crossterm.
- [ ] ideas from broot : https://dystroy.org/broot/#apply-commands-on-several-files
- [ ] floating windows ?
- [ ] rclone
diff --git a/src/app/application.rs b/src/app/application.rs
index c67e7db1..fbfe7f19 100644
--- a/src/app/application.rs
+++ b/src/app/application.rs
@@ -2,31 +2,18 @@ use std::process::exit;
use std::sync::Arc;
use std::sync::Mutex;
-use anyhow::anyhow;
-use anyhow::Context;
-use anyhow::Result;
+use anyhow::{anyhow, Context, Result};
use clap::Parser;
-use crate::app::Displayer;
-use crate::app::Refresher;
-use crate::app::Status;
-use crate::common::CONFIG_PATH;
-use crate::common::{clear_tmp_file, init_term};
-use crate::config::cloud_config;
-use crate::config::load_config;
-use crate::config::set_configurable_static;
-use crate::config::Config;
-use crate::config::START_FOLDER;
-use crate::event::EventDispatcher;
-use crate::event::EventReader;
-use crate::event::FmEvents;
-use crate::io::set_loggers;
-use crate::io::Args;
-use crate::io::Opener;
+use crate::app::{Displayer, Refresher, Status};
+use crate::common::{clear_tmp_files, init_term, print_on_quit, CONFIG_PATH};
+use crate::config::{cloud_config, load_config, set_configurable_static, Config, START_FOLDER};
+use crate::event::{EventDispatcher, EventReader, FmEvents};
+use crate::io::{set_loggers, Args, Opener};
use crate::log_info;
/// Holds everything about the application itself.
-/// Most attributes holds an `Arc<tuikit::Term::term>`.
+/// Most attributes holds an [`std::sync::Arc`] with [`tuikit::term::Term`].
/// Dropping the instance of FM allows to write again to stdout.
pub struct FM {
/// Poll the event sent to the terminal by the user or the OS
@@ -127,7 +114,7 @@ impl FM {
fn exit_wrong_config() -> ! {
eprintln!("Couldn't load the config file at {CONFIG_PATH}. See https://raw.githubusercontent.com/qkzk/fm/master/config_files/fm/config.yaml for an example.");
log_info!("Couldn't read the config file {CONFIG_PATH}");
- std::process::exit(1)
+ exit(1)
}
/// Return the last event received by the terminal
@@ -160,15 +147,15 @@ impl FM {
}
}
- /// Run the status loop.
- pub fn run(&mut self) -> Result<()> {
+ /// Run the status loop and returns itself after completion.
+ pub fn run(mut self) -> Result<Self> {
while let Ok(event) = self.poll_event() {
self.update(event)?;
if self.must_quit()? {
break;
}
}
- Ok(())
+ Ok(self)
}
/// Display the cursor,
@@ -179,19 +166,22 @@ impl FM {
///
/// May fail if the terminal crashes
/// May also fail if the thread running in [`crate::app::Refresher`] crashed
- pub fn quit(self) -> Result<String> {
- clear_tmp_file();
+ pub fn quit(self) -> Result<()> {
+ let final_path = self
+ .status
+ .lock()
+ .map_err(|error| anyhow!("Error locking status: {error}"))?
+ .current_tab_path_str()
+ .to_owned();
+
+ clear_tmp_files();
drop(self.event_reader);
drop(self.event_dispatcher);
self.displayer.quit();
self.refresher.quit();
+ drop(self.status);
- let status = self
- .status
- .lock()
- .map_err(|error| anyhow!("Error locking status: {error}"))?;
- let final_path = status.current_tab_path_str().to_owned();
- drop(status);
- Ok(final_path)
+ print_on_quit(final_path);
+ Ok(())
}
}
diff --git a/src/app/header_footer.rs b/src/app/header_footer.rs
index 39697129..7af21ab3 100644
--- a/src/app/header_footer.rs
+++ b/src/app/header_footer.rs
@@ -3,17 +3,16 @@ mod inner {
use crate::app::{Status, Tab};
use crate::common::{
- UtfWidth, HELP_FIRST_SENTENCE, HELP_SECOND_SENTENCE, LOG_FIRST_SENTENCE,
+ PathShortener, UtfWidth, HELP_FIRST_SENTENCE, HELP_SECOND_SENTENCE, LOG_FIRST_SENTENCE,
LOG_SECOND_SENTENCE,
};
use crate::event::ActionMap;
use crate::modes::{
- shorten_path, ColoredText, Content, Display, FileInfo, FilterKind, Preview, Search,
- Selectable, TextKind,
+ Content, Display, FileInfo, FilterKind, Preview, Search, Selectable, Text, TextKind,
};
#[derive(Clone, Copy)]
- pub enum HorizontalAlign {
+ pub enum Align {
Left,
Right,
}
@@ -36,11 +35,11 @@ mod inner {
/// It calculates its position with `col` and `align`.
/// If left aligned, the text size will be added to `col` and the text will span from col to col + width.
/// otherwise, the text will spawn from col - width to col.
- fn new(text: String, align: HorizontalAlign, action: ActionMap, col: usize) -> Self {
+ fn new(text: String, align: Align, action: ActionMap, col: usize) -> Self {
let width = text.utf_width();
let (left, right) = match align {
- HorizontalAlign::Left => (col, col + width),
- HorizontalAlign::Right => (col - width - 3, col - 3),
+ Align::Left => (col, col + width),
+ Align::Right => (col - width - 3, col - 3),
};
Self {
text,
@@ -146,8 +145,13 @@ mod inner {
fn elem_shorten_path(tab: &Tab, left: usize) -> Result<ClickableString> {
Ok(ClickableString::new(
- format!(" {}", shorten_path(&tab.directory.path, None)?),
- HorizontalAlign::Left,
+ format!(
+ " {}",
+ PathShortener::path(&tab.directory.path)
+ .context("Couldn't parse path")?
+ .shorten()
+ ),
+ Align::Left,
ActionMap::Cd,
left,
))
@@ -155,43 +159,48 @@ mod inner {
fn elem_filename(tab: &Tab, width: usize, left: usize) -> Result<ClickableString> {
let text = match tab.display_mode {
- Display::Tree => format!(
- "/{rel}",
- rel =
- shorten_path(tab.tree.selected_path_relative_to_root()?, Some(width / 2))?
- ),
- _ => {
- if let Some(fileinfo) = tab.directory.selected() {
- fileinfo.filename_without_dot_dotdot()
- } else {
- "".to_owned()
- }
- }
+ Display::Tree => Self::elem_tree_filename(tab, width)?,
+ _ => Self::elem_directory_filename(tab),
};
Ok(ClickableString::new(
text,
- HorizontalAlign::Left,
+ Align::Left,
ActionMap::Rename,
left,
))
}
+ fn elem_tree_filename(tab: &Tab, width: usize) -> Result<String> {
+ Ok(format!(
+ "{sep}{rel}",
+ rel = PathShortener::path(tab.tree.selected_path_relative_to_root()?)
+ .context