Add enter_accept to immediately execute an accepted command (#1311)
* make enter execute the command, tab copy it * Add config for enter_accept enter_accept will make Atuin immediately accept an execute a command when selected. It defaults to false in our binary, but the default config enables it. This means that users who already use atuin will not default to the new behaviour unless they opt in, but new users will have it by default. Thanks to @davidhewitt for the patch and bulk of this implementation! Currently we have it just for zsh, but I'll follow up with other shells (unless anyone beats me to it :D) * Add docs * we need to tidy up the ui code anyway * Check if using zsh * Update docs/docs/config/config.md Co-authored-by: Conrad Ludgate <conradludgate@gmail.com> --------- Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Co-authored-by: Conrad Ludgate <conradludgate@gmail.com>
This commit is contained in:
parent
ad4868567e
commit
88f3e2a04f
6 changed files with 55 additions and 3 deletions
|
@ -123,3 +123,7 @@
|
||||||
## 4. Slack webhooks
|
## 4. Slack webhooks
|
||||||
## 5. Stripe live/test keys
|
## 5. Stripe live/test keys
|
||||||
# secrets_filter = true
|
# secrets_filter = true
|
||||||
|
|
||||||
|
## Defaults to true. If enabled, upon hitting enter Atuin will immediately execute the command. Press tab to return to the shell and edit.
|
||||||
|
# This applies for new installs. Old installs will keep the old behaviour unless configured otherwise.
|
||||||
|
enter_accept = true
|
||||||
|
|
|
@ -179,6 +179,7 @@ pub struct Settings {
|
||||||
|
|
||||||
pub network_connect_timeout: u64,
|
pub network_connect_timeout: u64,
|
||||||
pub network_timeout: u64,
|
pub network_timeout: u64,
|
||||||
|
pub enter_accept: bool,
|
||||||
|
|
||||||
// This is automatically loaded when settings is created. Do not set in
|
// This is automatically loaded when settings is created. Do not set in
|
||||||
// config! Keep secrets and settings apart.
|
// config! Keep secrets and settings apart.
|
||||||
|
@ -378,6 +379,12 @@ impl Settings {
|
||||||
.set_default("secrets_filter", true)?
|
.set_default("secrets_filter", true)?
|
||||||
.set_default("network_connect_timeout", 5)?
|
.set_default("network_connect_timeout", 5)?
|
||||||
.set_default("network_timeout", 30)?
|
.set_default("network_timeout", 30)?
|
||||||
|
// enter_accept defaults to false here, but true in the default config file. The dissonance is
|
||||||
|
// intentional!
|
||||||
|
// Existing users will get the default "False", so we don't mess with any potential
|
||||||
|
// muscle memory.
|
||||||
|
// New users will get the new default, that is more similar to what they are used to.
|
||||||
|
.set_default("enter_accept", false)?
|
||||||
.add_source(
|
.add_source(
|
||||||
Environment::with_prefix("atuin")
|
Environment::with_prefix("atuin")
|
||||||
.prefix_separator("_")
|
.prefix_separator("_")
|
||||||
|
@ -391,6 +398,7 @@ impl Settings {
|
||||||
|
|
||||||
create_dir_all(&config_dir)
|
create_dir_all(&config_dir)
|
||||||
.wrap_err_with(|| format!("could not create dir {config_dir:?}"))?;
|
.wrap_err_with(|| format!("could not create dir {config_dir:?}"))?;
|
||||||
|
|
||||||
create_dir_all(&data_dir).wrap_err_with(|| format!("could not create dir {data_dir:?}"))?;
|
create_dir_all(&data_dir).wrap_err_with(|| format!("could not create dir {data_dir:?}"))?;
|
||||||
|
|
||||||
let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
|
let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
|
||||||
|
|
|
@ -113,6 +113,11 @@ pub fn get_current_dir() -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_zsh() -> bool {
|
||||||
|
// only set on zsh
|
||||||
|
env::var("ZSH_VERSION").is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use time::Month;
|
use time::Month;
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use atuin_common::utils;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{
|
event::{
|
||||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
|
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers,
|
||||||
|
@ -47,6 +48,7 @@ struct State {
|
||||||
switched_search_mode: bool,
|
switched_search_mode: bool,
|
||||||
search_mode: SearchMode,
|
search_mode: SearchMode,
|
||||||
results_len: usize,
|
results_len: usize,
|
||||||
|
accept: bool,
|
||||||
|
|
||||||
search: SearchState,
|
search: SearchState,
|
||||||
engine: Box<dyn SearchEngine>,
|
engine: Box<dyn SearchEngine>,
|
||||||
|
@ -130,7 +132,14 @@ impl State {
|
||||||
ExitMode::ReturnQuery => RETURN_QUERY,
|
ExitMode::ReturnQuery => RETURN_QUERY,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
KeyCode::Tab => {
|
||||||
|
return Some(self.results_state.selected());
|
||||||
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
|
if settings.enter_accept {
|
||||||
|
self.accept = true;
|
||||||
|
}
|
||||||
|
|
||||||
return Some(self.results_state.selected());
|
return Some(self.results_state.selected());
|
||||||
}
|
}
|
||||||
KeyCode::Char('y') if ctrl => {
|
KeyCode::Char('y') if ctrl => {
|
||||||
|
@ -588,7 +597,7 @@ impl Write for Stdout {
|
||||||
// this is a big blob of horrible! clean it up!
|
// this is a big blob of horrible! clean it up!
|
||||||
// for now, it works. But it'd be great if it were more easily readable, and
|
// for now, it works. But it'd be great if it were more easily readable, and
|
||||||
// modular. I'd like to add some more stats and stuff at some point
|
// modular. I'd like to add some more stats and stuff at some point
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
|
||||||
pub async fn history(
|
pub async fn history(
|
||||||
query: &[String],
|
query: &[String],
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
|
@ -646,10 +655,12 @@ pub async fn history(
|
||||||
},
|
},
|
||||||
engine: engines::engine(search_mode),
|
engine: engines::engine(search_mode),
|
||||||
results_len: 0,
|
results_len: 0,
|
||||||
|
accept: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut results = app.query_results(&mut db).await?;
|
let mut results = app.query_results(&mut db).await?;
|
||||||
|
|
||||||
|
let accept;
|
||||||
let index = 'render: loop {
|
let index = 'render: loop {
|
||||||
terminal.draw(|f| app.draw(f, &results, settings))?;
|
terminal.draw(|f| app.draw(f, &results, settings))?;
|
||||||
|
|
||||||
|
@ -664,6 +675,7 @@ pub async fn history(
|
||||||
if event_ready?? {
|
if event_ready?? {
|
||||||
loop {
|
loop {
|
||||||
if let Some(i) = app.handle_input(settings, &event::read()?, &mut std::io::stdout())? {
|
if let Some(i) = app.handle_input(settings, &event::read()?, &mut std::io::stdout())? {
|
||||||
|
accept = app.accept;
|
||||||
break 'render i;
|
break 'render i;
|
||||||
}
|
}
|
||||||
if !event::poll(Duration::ZERO)? {
|
if !event::poll(Duration::ZERO)? {
|
||||||
|
@ -690,8 +702,12 @@ pub async fn history(
|
||||||
}
|
}
|
||||||
|
|
||||||
if index < results.len() {
|
if index < results.len() {
|
||||||
|
let mut command = results.swap_remove(index).command;
|
||||||
|
if accept && utils::is_zsh() {
|
||||||
|
command = String::from("__atuin_accept__:") + &command;
|
||||||
|
}
|
||||||
// index is in bounds so we return that entry
|
// index is in bounds so we return that entry
|
||||||
Ok(results.swap_remove(index).command)
|
Ok(command)
|
||||||
} else if index == RETURN_ORIGINAL {
|
} else if index == RETURN_ORIGINAL {
|
||||||
Ok(String::new())
|
Ok(String::new())
|
||||||
} else if index == COPY_QUERY {
|
} else if index == COPY_QUERY {
|
||||||
|
|
|
@ -36,12 +36,18 @@ _atuin_search() {
|
||||||
# shellcheck disable=SC2048
|
# shellcheck disable=SC2048
|
||||||
output=$(ATUIN_LOG=error atuin search $* -i -- $BUFFER 3>&1 1>&2 2>&3)
|
output=$(ATUIN_LOG=error atuin search $* -i -- $BUFFER 3>&1 1>&2 2>&3)
|
||||||
|
|
||||||
|
zle reset-prompt
|
||||||
|
|
||||||
if [[ -n $output ]]; then
|
if [[ -n $output ]]; then
|
||||||
RBUFFER=""
|
RBUFFER=""
|
||||||
LBUFFER=$output
|
LBUFFER=$output
|
||||||
fi
|
fi
|
||||||
|
|
||||||
zle reset-prompt
|
if [[ $LBUFFER == __atuin_accept__:* ]]
|
||||||
|
then
|
||||||
|
LBUFFER=${LBUFFER#__atuin_accept__:}
|
||||||
|
zle accept-line
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_atuin_up_search() {
|
_atuin_up_search() {
|
||||||
|
|
|
@ -305,3 +305,16 @@ Default: 5
|
||||||
|
|
||||||
The max time (in seconds) we wait for a connection to become established with a
|
The max time (in seconds) we wait for a connection to become established with a
|
||||||
remote sync server. Any longer than this and the request will fail.
|
remote sync server. Any longer than this and the request will fail.
|
||||||
|
|
||||||
|
## enter_accept
|
||||||
|
Default: false
|
||||||
|
|
||||||
|
Only supported on Zsh.
|
||||||
|
|
||||||
|
When set to true, Atuin will default to immediately executing a command rather
|
||||||
|
than the user having to press enter twice. Pressing tab will return to the
|
||||||
|
shell and give the user a chance to edit.
|
||||||
|
|
||||||
|
This technically defaults to true for new users, but false for existing. We
|
||||||
|
have set `enter_accept = true` in the default config file. This is likely to
|
||||||
|
change to be the default for everyone in a later release.
|
||||||
|
|
Loading…
Reference in a new issue