summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChristoph Jabs <98587286+chrjabs@users.noreply.github.com>2024-04-03 21:28:18 +0300
committerGitHub <noreply@github.com>2024-04-03 14:28:18 -0400
commitd9496689e29d9aaa1d55ec9ca822d36ef895381d (patch)
tree1b91ab964a2c171176c1d4aacedaf4ec7a05638a /src
parent2392951815447d2f1252fbd6ea7df23542c419cc (diff)
Zoxide related features (#506)HEADmain
* make `:z` act more like zoxide before checking the zoxide database, check if the query is a directory that can be navigated to as by `:cd` also interpret `~` and `-` correctly * optionally track all movement in zoxide Add new config option `zoxide_update` with default false. When manually enabled, all directory navigation (via manually navigating or `:cd` and `:z`) will update the `zoxide` database. Since navigation via `:z` always updates the database, ensure that it doesn't update it twice. * support arguments for zoxide interactive support for `:zi <args>` where arguments are used to prefilter the matches provided in the interactive zoxide prompt * allow command aliases with arguments note: only aliases that do not contain spaces can be used with arguments since either the entire command or only the first word are checked against the aliases
Diffstat (limited to 'src')
-rw-r--r--src/commands/change_directory.rs6
-rw-r--r--src/commands/command_line.rs10
-rw-r--r--src/commands/zoxide.rs33
-rw-r--r--src/config/clean/app/config.rs2
-rw-r--r--src/config/raw/app/config.rs2
-rw-r--r--src/key_command/command.rs2
-rw-r--r--src/key_command/impl_appcommand.rs2
-rw-r--r--src/key_command/impl_appexecute.rs4
-rw-r--r--src/key_command/impl_comment.rs2
-rw-r--r--src/key_command/impl_from_str.rs29
-rw-r--r--src/util/unix.rs10
11 files changed, 89 insertions, 13 deletions
diff --git a/src/commands/change_directory.rs b/src/commands/change_directory.rs
index ebf471f..84d8fdb 100644
--- a/src/commands/change_directory.rs
+++ b/src/commands/change_directory.rs
@@ -1,6 +1,6 @@
use std::path;
-use crate::commands::reload;
+use crate::commands::{reload, zoxide};
use crate::context::AppContext;
use crate::error::AppResult;
use crate::history::{generate_entries_to_root, DirectoryHistory};
@@ -10,6 +10,10 @@ use crate::util::cwd;
pub fn cd(path: &path::Path, context: &mut AppContext) -> std::io::Result<()> {
cwd::set_current_dir(path)?;
context.tab_context_mut().curr_tab_mut().set_cwd(path);
+ if context.config_ref().zoxide_update {
+ debug_assert!(path.is_absolute());
+ zoxide::zoxide_add(path.to_str().expect("cannot convert path to string"))?;
+ }
Ok(())
}
diff --git a/src/commands/command_line.rs b/src/commands/command_line.rs
index d423835..5f9d0de 100644
--- a/src/commands/command_line.rs
+++ b/src/commands/command_line.rs
@@ -22,12 +22,20 @@ pub fn read_and_execute(
.suffix(suffix)
.get_input(backend, context, &mut listener);
- if let Some(s) = user_input {
+ if let Some(mut s) = user_input {
let mut trimmed = s.trim_start();
let _ = context.commandline_context_mut().history_mut().add(trimmed);
+ let (command, arg) = match trimmed.find(' ') {
+ Some(i) => (&trimmed[..i], &trimmed[i..]),
+ None => (trimmed, ""),
+ };
+
if let Some(alias) = context.config_ref().cmd_aliases.get(trimmed) {
trimmed = alias;
+ } else if let Some(alias) = context.config_ref().cmd_aliases.get(command) {
+ s.replace_range(..s.len() - arg.len(), alias);
+ trimmed = &s;
}
let command = Command::from_str(trimmed)?;
diff --git a/src/commands/zoxide.rs b/src/commands/zoxide.rs
index 222bb9d..4ebddff 100644
--- a/src/commands/zoxide.rs
+++ b/src/commands/zoxide.rs
@@ -10,18 +10,34 @@ use crate::ui::AppBackend;
pub fn zoxide_query(context: &mut AppContext, args: &str) -> AppResult {
let cwd = std::env::current_dir()?;
+ let path = Path::new(args);
+ if change_directory::change_directory(context, path).is_ok() {
+ if !context.config_ref().zoxide_update {
+ let cwd = context
+ .tab_context_ref()
+ .curr_tab_ref()
+ .cwd()
+ .to_str()
+ .expect("path cannot be converted to string");
+ zoxide_add(cwd)?;
+ }
+ return Ok(());
+ }
+
let zoxide_output = Command::new("zoxide")
.arg("query")
.arg("--exclude")
.arg(&cwd)
.arg("--")
- .args(args.split(' ').collect::<Vec<&str>>())
+ .args(args.split(' '))
.output()?;
if zoxide_output.status.success() {
if let Ok(zoxide_str) = std::str::from_utf8(&zoxide_output.stdout) {
let zoxide_path = &zoxide_str[..zoxide_str.len() - 1];
- zoxide_add(zoxide_path)?;
+ if !context.config_ref().zoxide_update {
+ zoxide_add(zoxide_path)?;
+ }
let path = Path::new(zoxide_path);
context
@@ -37,13 +53,18 @@ pub fn zoxide_query(context: &mut AppContext, args: &str) -> AppResult {
Ok(())
}
-pub fn zoxide_query_interactive(context: &mut AppContext, backend: &mut AppBackend) -> AppResult {
+pub fn zoxide_query_interactive(
+ context: &mut AppContext,
+ backend: &mut AppBackend,
+ args: &str,
+) -> AppResult {
backend.terminal_drop();
let zoxide_process = Command::new("zoxide")
.arg("query")
.arg("-i")
.arg("--")
+ .args(args.split(' '))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
@@ -54,7 +75,9 @@ pub fn zoxide_query_interactive(context: &mut AppContext, backend: &mut AppBacke
if zoxide_output.status.success() {
if let Ok(zoxide_str) = std::str::from_utf8(&zoxide_output.stdout) {
let zoxide_path = &zoxide_str[..zoxide_str.len() - 1];
- zoxide_add(zoxide_path)?;
+ if !context.config_ref().zoxide_update {
+ zoxide_add(zoxide_path)?;
+ }
let path = Path::new(zoxide_path);
context
@@ -70,7 +93,7 @@ pub fn zoxide_query_interactive(context: &mut AppContext, backend: &mut AppBacke
Ok(())
}
-fn zoxide_add(s: &str) -> io::Result<()> {
+pub fn zoxide_add(s: &str) -> io::Result<()> {
Command::new("zoxide").arg("add").arg(s).output()?;
Ok(())
}
diff --git a/src/config/clean/app/config.rs b/src/config/clean/app/config.rs
index 3d0be5a..740430a 100644
--- a/src/config/clean/app/config.rs
+++ b/src/config/clean/app/config.rs
@@ -24,6 +24,7 @@ pub struct AppConfig {
pub focus_on_create: bool,
pub mouse_support: bool,
pub cmd_aliases: HashMap<String, String>,
+ pub zoxide_update: bool,
pub _display_options: DisplayOption,
pub _preview_options: PreviewOption,
pub _search_options: SearchOption,
@@ -90,6 +91,7 @@ impl From<AppConfigRaw> for AppConfig {
cmd_aliases: raw.cmd_aliases,
focus_on_create: raw.focus_on_create,
mouse_support: raw.mouse_support,
+ zoxide_update: raw.zoxide_update,
_display_options: DisplayOption::from(raw.display_options),
_preview_options: PreviewOption::from(raw.preview_options),
_search_options: SearchOption::from(raw.search_options),
diff --git a/src/config/raw/app/config.rs b/src/config/raw/app/config.rs
index 2980e4c..9c18ff8 100644
--- a/src/config/raw/app/config.rs
+++ b/src/config/raw/app/config.rs
@@ -39,6 +39,8 @@ pub struct AppConfigRaw {
#[serde(default = "default_true")]
pub mouse_support: bool,
#[serde(default)]
+ pub zoxide_update: bool,
+ #[serde(default)]
pub cmd_aliases: HashMap<String, String>,
#[serde(default, rename = "display")]
pub display_options: DisplayOptionRaw,
diff --git a/src/key_command/command.rs b/src/key_command/command.rs
index 87ab74b..84e407e 100644
--- a/src/key_command/command.rs
+++ b/src/key_command/command.rs
@@ -191,7 +191,7 @@ pub enum Command {
processor: PostProcessor,
},
Zoxide(String),
- ZoxideInteractive,
+ ZoxideInteractive(String),
CustomSearch(Vec<String>),
CustomSearchInteractive(Vec<String>),
diff --git a/src/key_command/impl_appcommand.rs b/src/key_command/impl_appcommand.rs
index df9ffcf..fe75dc5 100644
--- a/src/key_command/impl_appcommand.rs
+++ b/src/key_command/impl_appcommand.rs
@@ -111,7 +111,7 @@ impl AppCommand for Command {
Self::SubdirFzf => CMD_SUBDIR_FZF,
Self::SelectFzf { .. } => CMD_SELECT_FZF,
Self::Zoxide(_) => CMD_ZOXIDE,
- Self::ZoxideInteractive => CMD_ZOXIDE_INTERACTIVE,
+ Self::ZoxideInteractive(_) => CMD_ZOXIDE_INTERACTIVE,
Self::CustomSearch(_) => CMD_CUSTOM_SEARCH,
Self::CustomSearchInteractive(_) => CMD_CUSTOM_SEARCH_INTERACTIVE,
diff --git a/src/key_command/impl_appexecute.rs b/src/key_command/impl_appexecute.rs
index 5ab59b7..25d7041 100644
--- a/src/key_command/impl_appexecute.rs
+++ b/src/key_command/impl_appexecute.rs
@@ -167,7 +167,9 @@ impl AppExecute for Command {
Self::SubdirFzf => subdir_fzf::subdir_fzf(context, backend),
Self::SelectFzf { options } => select_fzf::select_fzf(context, backend, options),
Self::Zoxide(arg) => zoxide::zoxide_query(context, arg),
- Self::ZoxideInteractive => zoxide::zoxide_query_interactive(context, backend),
+ Self::ZoxideInteractive(args) => {
+ zoxide::zoxide_query_interactive(context, backend, args)
+ }
Self::BookmarkAdd => bookmark::add_bookmark(context, backend),
Self::BookmarkChangeDirectory => bookmark::change_directory_bookmark(context, backend),
diff --git a/src/key_command/impl_comment.rs b/src/key_command/impl_comment.rs
index 6fa744d..e22618c 100644
--- a/src/key_command/impl_comment.rs
+++ b/src/key_command/impl_comment.rs
@@ -140,7 +140,7 @@ impl CommandComment for Command {
Self::SubdirFzf => "Switch to a child directory via fzf",
Self::SelectFzf { .. } => "Select via fzf",
Self::Zoxide(_) => "Zoxide",
- Self::ZoxideInteractive => "Zoxide interactive",
+ Self::ZoxideInteractive(_) => "Zoxide interactive",
Self::BookmarkAdd => "Add a bookmark",
Self::BookmarkChangeDirectory => "Navigate to a bookmark",
diff --git a/src/key_command/impl_from_str.rs b/src/key_command/impl_from_str.rs
index 55a7fd7..74bac5d 100644
--- a/src/key_command/impl_from_str.rs
+++ b/src/key_command/impl_from_str.rs
@@ -113,8 +113,11 @@ impl std::str::FromStr for Command {
Self::CustomSearchInteractive(arg.split(' ').map(|x| x.to_string()).collect())
);
simple_command_conversion_case!(command, CMD_SUBDIR_FZF, Self::SubdirFzf);
- simple_command_conversion_case!(command, CMD_ZOXIDE, Self::Zoxide(arg.to_string()));
- simple_command_conversion_case!(command, CMD_ZOXIDE_INTERACTIVE, Self::ZoxideInteractive);
+ simple_command_conversion_case!(
+ command,
+ CMD_ZOXIDE_INTERACTIVE,
+ Self::ZoxideInteractive(arg.to_string())
+ );
if command == CMD_QUIT {
match arg {
@@ -602,6 +605,28 @@ impl std::str::FromStr for Command {
Ok(Self::FilterString {
pattern: arg.to_string(),
})
+ } else if command == CMD_ZOXIDE {
+ match arg {
+ "" => match HOME_DIR.as_ref() {
+ Some(s) => Ok(Self::ChangeDirectory { path: s.clone() }),
+ None => Err(AppError::new(
+ AppErrorKind::EnvVarNotPresent,
+ format!("{}: Cannot find home directory", command),
+ )),
+ },
+ ".." => Ok(Self::ParentDirectory),
+ "-" => Ok(Self::PreviousDirectory),
+ arg => {
+ let (head, tail) = match arg.find(' ') {
+ Some(i) => (&arg[..i], &arg[i..]),
+ None => (arg, ""),
+ };
+ let head = unix::expand_shell_string_cow(head);
+ let mut args = String::from(head);
+ args.push_str(tail);
+ Ok(Self::Zoxide(args))
+ }
+ }
} else {
Err(AppError::new(
AppErrorKind::UnrecognizedCommand,
diff --git a/src/util/unix.rs b/src/util/unix.rs
index 61d41fb..2d71359 100644
--- a/src/util/unix.rs
+++ b/src/util/unix.rs
@@ -56,6 +56,16 @@ pub fn mode_to_string(mode: u32) -> String {
mode_str
}
+pub fn expand_shell_string_cow(s: &str) -> std::borrow::Cow<'_, str> {
+ let dir = dirs_next::home_dir();
+ let os_str = dir.map(|s| s.as_os_str().to_owned());
+ let context_func = || {
+ let cow_str = os_str.as_ref().map(|s| s.to_string_lossy());
+ cow_str
+ };
+ shellexpand::tilde_with_context(s, context_func)
+}
+
pub fn expand_shell_string(s: &str) -> path::PathBuf {
let dir = dirs_next::home_dir();
let os_str = dir.map(|s| s.as_os_str().to_owned());