Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dark-light = { git = "https://github.com/rust-dark-light/dark-light.git", rev =
nucleo-matcher = "0.3.1"
tokio = {version="1.49.0", features=["full"]}
tokio-stream = {version="0.1.18", features = ["sync"]}
log4rs_test_utils = "0.2.3"

# The profile that 'dist' will build with
[profile.dist]
Expand Down
6 changes: 6 additions & 0 deletions dbschema/3.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS paths_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL,
date INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS paths_history_path_date_id ON paths_history (path, date DESC, id DESC);
8 changes: 8 additions & 0 deletions dbschema/current.sql
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ CREATE TABLE IF NOT EXISTS shortcuts (
description TEXT
);
CREATE INDEX IF NOT EXISTS shortcuts_name ON shortcuts (name);

-- Path table
CREATE TABLE IF NOT EXISTS paths_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL,
date INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS paths_history_path_date_id ON paths_history (path, date DESC, id DESC);
17 changes: 17 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ $ cdir config-file

Edit this file to adjust your settings as described below:

## Smart suggestions (experimental)

By default, the smart suggestions feature is *disabled*.
You can enable it by setting the following option to `true` in the configuration file:

```yaml
smart_suggestions_active: true
```

The following parameter controls the maximum number of suggestions shown when opening the UI:

```yaml
smart_suggestions_count: 5
```

The default value is `3`.

## Themes and colors

### Selecting a theme
Expand Down
7 changes: 7 additions & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Return to them quickly from the UI or from the command line.
In the UI, enter text to filter and find any previously visited directory or shortcut.
If the default search is not enough, you can use fuzzy search.

## :material-check-bold: Smart suggestions (experimental)

When opening the UI, `cdir` can suggsts directories based on your current directory, recent activity, and frequency of visits.

This feature is experimental.
It is disabled by default but can be activated in the configuration file.

## :material-check-bold: Multi-shell support

Works with both `zsh` and `bash`. More shells may be supported in the future.
Expand Down
14 changes: 14 additions & 0 deletions docs/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,17 @@ You can also specify that you want to search for a word at the beginning e.g. `^
You can also reverse the search with ! e.g. `!src$`.

In both the *Directory history* view and in the *Shortcuts view*, the directories are ordered by best match.

## Smart suggestions (experimental)

When activated, the smart suggestions feature suggsts directories based on your current directory, recent activity, and frequency of visits.

They appear at the top of the *Directory history view* when you open the GUI.
They are recognizable by the `@` character on the left side in place of the date.

You can enable or disable this feature in the configuration file.

!!! info
This feature is based on your history of visited directories.
If you have just started using `cdir` or just installed the release implementing it, or calling `cdir` from a
directory you have never visited before, no recommendation will be shown.
19 changes: 19 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const DEFAULT_LOG_CONFIG_PATH: fn() -> Option<PathBuf> = || {
Some(path)
};

const DEFAULT_SMART_SUGGESTIONS_DEPTH: fn() -> usize = || 5;

const DEFAULT_SMART_SUGGESTIONS_COUNT: fn() -> usize = || 3;

const DEFAULT_THEMES_DIRECTORY_PATH: fn() -> Option<PathBuf> = || {
let mut path = dirs::home_dir().unwrap();
path.push(".config");
Expand Down Expand Up @@ -57,6 +61,15 @@ pub struct Config {
#[serde(default = "DEFAULT_TRUE")]
pub path_search_include_shortcuts: bool,

#[serde(default = "DEFAULT_TRUE")]
pub smart_suggestions_active: bool,

#[serde(default = "DEFAULT_SMART_SUGGESTIONS_DEPTH")]
pub smart_suggestions_depth: usize,

#[serde(default = "DEFAULT_SMART_SUGGESTIONS_COUNT")]
pub smart_suggestions_count: usize,

#[serde(default = "DEFAULT_THEMES_DIRECTORY_PATH")]
pub themes_directory_path: Option<PathBuf>,

Expand Down Expand Up @@ -393,6 +406,9 @@ impl Default for Config {
inline_theme_dark: Default::default(),
inline_theme_light: Default::default(),
styles: Default::default(),
smart_suggestions_active: true,
smart_suggestions_depth: DEFAULT_SMART_SUGGESTIONS_DEPTH(),
smart_suggestions_count: DEFAULT_SMART_SUGGESTIONS_COUNT(),
themes_directory_path: Default::default(),
date_formater: Box::new(|date| date.to_string()),
db_path: Default::default(),
Expand All @@ -414,6 +430,9 @@ impl Clone for Config {
inline_theme_dark: self.inline_theme_dark.clone(),
inline_theme_light: self.inline_theme_light.clone(),
styles: self.styles.clone(),
smart_suggestions_active: self.smart_suggestions_active,
smart_suggestions_depth: self.smart_suggestions_depth,
smart_suggestions_count: self.smart_suggestions_count,
themes_directory_path: self.themes_directory_path.clone(),
db_path: self.db_path.clone(),
log_config_path: self.log_config_path.clone(),
Expand Down
67 changes: 58 additions & 9 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
use log::debug;
use ratatui::{
layout::Constraint,
style::Style,
style::{Modifier, Style},
text::{Line, Span},
widgets::Row,
};
Expand All @@ -34,6 +34,28 @@ pub(crate) struct Gui {
shortcut_view_container: Option<ViewBuilder>,
}

// Blends two colors with the given weight (0.0 to 1.0)
// weight of 0.8 means 80% of color1 and 20% of color2
// fn blend_colors(color1: Color, color2: Color, weight: f32) -> Color {
// let weight = weight.clamp(0.0, 1.0);
// let weight2 = 1.0 - weight;

// match (color1, color2) {
// (Color::Rgb(r1, g1, b1), Color::Rgb(r2, g2, b2)) => {
// let r = (r1 as f32 * weight + r2 as f32 * weight2) as u8;
// let g = (g1 as f32 * weight + g2 as f32 * weight2) as u8;
// let b = (b1 as f32 * weight + b2 as f32 * weight2) as u8;
// Color::Rgb(r, g, b)
// }
// // For other color types, try to convert to RGB first
// _ => {
// // If we can't blend properly, just return color1
// // In a more complete implementation, we'd convert all color types to RGB
// color1
// }
// }
// }

impl Gui {
/// Return a Line with where HOME is replaced by '~'
pub(crate) fn reduce_path(path: String, size: u16, home_tild_style: Style) -> Line<'static> {
Expand Down Expand Up @@ -213,25 +235,42 @@ impl Gui {
paths
.iter()
.map(move |path| {
let path = path.clone();
let path_init = path.clone();
// format the date
let date: Line = Line::from(
Span::from((config.date_formater)(path.date))
.style(config.styles.date_style),
);
let date: Line = if !path_init.smart_path {
Line::from(
Span::from((config.date_formater)(path_init.date))
.style(config.styles.date_style),
)
} else {
Line::from(
Span::from(" @ ")
.style(config.styles.date_style /*.bg(bgc)*/),
)
};

// format the path using the embedded shortcut
let shortened_line =
match table_view_state.lock().unwrap().display_with_shortcuts {
true => Self::shorten_path_for_path(config.as_ref(), &path, size[1]),
true => {
Self::shorten_path_for_path(config.as_ref(), &path_init, size[1])
}
false => None,
};
let path = shortened_line
.unwrap_or_else(|| {
Self::reduce_path(path.path, size[1], config.styles.home_tilde_style)
Self::reduce_path(
path_init.path,
size[1],
config.styles.home_tilde_style,
)
})
.style(config.styles.path_style);

let path = if path_init.smart_path {
path.style(Style::default().add_modifier(Modifier::ITALIC)) //.bg(bgc))
} else {
path
};
vec![date, path]
})
.map(Row::new)
Expand Down Expand Up @@ -466,6 +505,7 @@ mod tests {
path: "/home/user/docs/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let result = Gui::shorten_path_for_shortcut(&config, &shortcuts, &path.path, 80);
assert!(result.is_some());
Expand All @@ -488,6 +528,7 @@ mod tests {
path: "/home/user/other/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let result = Gui::shorten_path_for_shortcut(&config, &shortcuts, &path.path, 80);
assert!(result.is_none());
Expand Down Expand Up @@ -540,6 +581,7 @@ mod tests {
path: "/home/user/docs/work/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let result = Gui::shorten_path_for_shortcut(&config, &shortcuts, &path.path, 80);
assert!(result.is_some());
Expand All @@ -562,6 +604,7 @@ mod tests {
path: "/home/user/docs/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let result = Gui::shorten_path_for_shortcut(&config, &shortcuts, &path.path, 14);
assert!(result.is_some());
Expand Down Expand Up @@ -612,6 +655,7 @@ mod tests {
path: format!("{}/project", home),
date: 0,
shortcut: None,
smart_path: false,
};
let line = Gui::reduce_path(path.path, 80, Style::new());
let line_str = line.to_string();
Expand All @@ -629,6 +673,7 @@ mod tests {
path: home.to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let line = Gui::reduce_path(path.path, 80, Style::new());
let line_str = line.to_string();
Expand All @@ -645,6 +690,7 @@ mod tests {
path: "/other/path/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};
let line = Gui::reduce_path(path.path, 80, Style::new());
let line_str = line.to_string();
Expand All @@ -662,6 +708,7 @@ mod tests {
path: format!("{}/project", home),
date: 0,
shortcut: None,
smart_path: false,
};

let line = Gui::reduce_path(path.path.clone(), 9, Style::new());
Expand Down Expand Up @@ -700,6 +747,7 @@ mod tests {
path: home.to_string(),
date: 0,
shortcut: None,
smart_path: false,
};

let line = Gui::reduce_path(path.path.clone(), 2, Style::new());
Expand All @@ -722,6 +770,7 @@ mod tests {
path: "/other/path/project".to_string(),
date: 0,
shortcut: None,
smart_path: false,
};

let line = Gui::reduce_path(path.path.clone(), 19, Style::new());
Expand Down
1 change: 1 addition & 0 deletions src/history_view_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl HistoryViewContainer {
view_state,
delete_fn,
editor_modal_view_builder,
Box::new(|paths| paths.iter().position(|p| !p.smart_path).unwrap_or(0)),
)
.with_publish_events(true),
)
Expand Down
1 change: 1 addition & 0 deletions src/shortcut_view_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl ShortcutViewContainer {
view_state,
delete_fn,
editor_modal_view_builder,
Box::new(|_| 0),
)
.with_publish_events(true),
)
Expand Down
Loading