aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@atuin.sh>2025-07-22 19:32:10 +0200
committerGitHub <noreply@github.com>2025-07-22 19:32:10 +0200
commit84c267eadacaf89101c32fd6aec37a114e863834 (patch)
treed1142d80178fc77de8061905e5289779d24dbb49 /crates
parentchore: update to rust 1.88 (#2815) (diff)
downloadatuin-84c267eadacaf89101c32fd6aec37a114e863834.zip
feat: command chaining (#2834)
* feat: command chaining Allow for smart completion of commands ending in && or || * fmt
Diffstat (limited to 'crates')
-rw-r--r--crates/atuin-client/config.toml3
-rw-r--r--crates/atuin-client/src/settings.rs2
-rw-r--r--crates/atuin/src/command/client/search/interactive.rs25
-rw-r--r--crates/atuin/src/shell/atuin.bash4
-rw-r--r--crates/atuin/src/shell/atuin.fish4
-rw-r--r--crates/atuin/src/shell/atuin.zsh5
6 files changed, 41 insertions, 2 deletions
diff --git a/crates/atuin-client/config.toml b/crates/atuin-client/config.toml
index ac9f8a1a..a154a65b 100644
--- a/crates/atuin-client/config.toml
+++ b/crates/atuin-client/config.toml
@@ -149,6 +149,9 @@
# This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
enter_accept = true
+## Defaults to false. If enabled, when triggered after && or ||, Atuin will complete commands to chain rather than replace the current line.
+# command_chaining = false
+
## Defaults to "emacs". This specifies the keymap on the startup of `atuin
## search`. If this is set to "auto", the startup keymap mode in the Atuin
## search is automatically selected based on the shell's keymap where the
diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs
index d1f37e39..88bf27d5 100644
--- a/crates/atuin-client/src/settings.rs
+++ b/crates/atuin-client/src/settings.rs
@@ -494,6 +494,7 @@ pub struct Settings {
pub local_timeout: f64,
pub enter_accept: bool,
pub smart_sort: bool,
+ pub command_chaining: bool,
#[serde(default)]
pub stats: Stats,
@@ -799,6 +800,7 @@ impl Settings {
.set_default("keymap_mode_shell", "auto")?
.set_default("keymap_cursor", HashMap::<String, String>::new())?
.set_default("smart_sort", false)?
+ .set_default("command_chaining", false)?
.set_default("store_failed", true)?
.set_default("daemon.sync_frequency", 300)?
.set_default("daemon.enabled", false)?
diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs
index ec40cc8d..708c36e4 100644
--- a/crates/atuin/src/command/client/search/interactive.rs
+++ b/crates/atuin/src/command/client/search/interactive.rs
@@ -1069,7 +1069,24 @@ pub async fn history(
},
)?;
- let mut input = Cursor::from(query.join(" "));
+ let original_query = query.join(" ");
+
+ // Check if this is a command chaining scenario
+ let is_command_chaining = if settings.command_chaining {
+ let trimmed = original_query.trim_end();
+ trimmed.ends_with("&&") || trimmed.ends_with("||")
+ } else {
+ false
+ };
+
+ // For command chaining, start with empty input to allow searching for new commands
+ let search_input = if is_command_chaining {
+ String::new()
+ } else {
+ original_query.clone()
+ };
+
+ let mut input = Cursor::from(search_input);
// Put the cursor at the end of the query by default
input.end();
@@ -1216,7 +1233,11 @@ pub async fn history(
if accept
&& (utils::is_zsh() || utils::is_fish() || utils::is_bash() || utils::is_xonsh())
{
- command = String::from("__atuin_accept__:") + &command;
+ if is_command_chaining {
+ command = String::from("__atuin_chain_command__:") + &command;
+ } else {
+ command = String::from("__atuin_accept__:") + &command;
+ }
}
// index is in bounds so we return that entry
diff --git a/crates/atuin/src/shell/atuin.bash b/crates/atuin/src/shell/atuin.bash
index cd5a8eff..eeb24e39 100644
--- a/crates/atuin/src/shell/atuin.bash
+++ b/crates/atuin/src/shell/atuin.bash
@@ -271,6 +271,10 @@ __atuin_history() {
READLINE_LINE=""
READLINE_POINT=${#READLINE_LINE}
+ elif [[ $__atuin_output == __atuin_chain_command__:* ]]; then
+ local new_command=${__atuin_output#__atuin_chain_command__:}
+ READLINE_LINE="$READLINE_LINE $new_command"
+ READLINE_POINT=${#READLINE_LINE}
else
READLINE_LINE=$__atuin_output
READLINE_POINT=${#READLINE_LINE}
diff --git a/crates/atuin/src/shell/atuin.fish b/crates/atuin/src/shell/atuin.fish
index 6ef1e2d2..7e73ad0c 100644
--- a/crates/atuin/src/shell/atuin.fish
+++ b/crates/atuin/src/shell/atuin.fish
@@ -44,6 +44,10 @@ function _atuin_search
commandline -f repaint
commandline -f execute
return
+ else if string match --quiet '__atuin_chain_command__:*' "$ATUIN_H"
+ set -l new_command (string replace "__atuin_chain_command__:" "" -- "$ATUIN_H" | string collect)
+ set -l current_command (commandline -b)
+ commandline -r "$current_command $new_command"
else
commandline -r "$ATUIN_H"
end
diff --git a/crates/atuin/src/shell/atuin.zsh b/crates/atuin/src/shell/atuin.zsh
index f26c3fab..9475b339 100644
--- a/crates/atuin/src/shell/atuin.zsh
+++ b/crates/atuin/src/shell/atuin.zsh
@@ -65,6 +65,7 @@ _atuin_search() {
echo -n ${zle_bracketed_paste[1]} >/dev/tty
if [[ -n $output ]]; then
+ local original_buffer=$BUFFER
RBUFFER=""
LBUFFER=$output
@@ -72,6 +73,10 @@ _atuin_search() {
then
LBUFFER=${LBUFFER#__atuin_accept__:}
zle accept-line
+ elif [[ $LBUFFER == __atuin_chain_command__:* ]]
+ then
+ local new_command=${LBUFFER#__atuin_chain_command__:}
+ LBUFFER="$original_buffer $new_command"
fi
fi
}