From c56f8ff736b8369b9b5cb3bdb42718396247f212 Mon Sep 17 00:00:00 2001 From: Matthieu LAURENT Date: Mon, 29 Jan 2024 13:17:36 +0100 Subject: Add xonsh support (#1375) * Add basic xonsh support * Add init xonsh command * Add Xonsh install instructions in docs * Add xonsh ctrl-R search * update xonsh script and instructions Summary of changes: * Added duration to postcommand hook * Switched main search operation to use `subproccess.run()` rather than running as an xonsh shell command - this a) allows us to capture stderr without needing a temporary file and b) avoids a weird broken-buffer state that results from running a fullscreen TUI and then programmatically editing the buffer * Added support for immediately executing chosen command via `__atuin_accept__:` (like bash/zsh/fish) * strip newline from command before sending to atuin * Add basic xonsh support * Add init xonsh command * Add xonsh ctrl-R search * Remove advanced-install guide (was accidentally re-added during rebase) * Clean up Xonsh doesn't import private functions into the local namespace when sourcing a file * Add xonsh ro readme * Respect ATUIN_NOBIND * Format with black, and improve PEP8 compliance * Add up search * Format rust code --------- Co-authored-by: Joseph Montanaro --- README.md | 9 ++++ atuin-common/src/utils.rs | 5 ++ atuin/src/command/client/search/interactive.rs | 4 +- atuin/src/command/init.rs | 21 ++++++++ atuin/src/shell/atuin.xsh | 67 ++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 atuin/src/shell/atuin.xsh diff --git a/README.md b/README.md index 1b893f49..dff561e2 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ I wanted to. And I **really** don't want to. - bash - fish - nushell +- xonsh ## Community @@ -327,6 +328,14 @@ Add to `config.nu`: source ~/.local/share/atuin/init.nu ``` +### Xonsh + +Add +``` +execx($(atuin init xonsh)) +``` +to the end of your `~/.xonshrc` + # Contributors diff --git a/atuin-common/src/utils.rs b/atuin-common/src/utils.rs index 87c4b14c..889c7811 100644 --- a/atuin-common/src/utils.rs +++ b/atuin-common/src/utils.rs @@ -101,6 +101,11 @@ pub fn is_bash() -> bool { env::var("ATUIN_SHELL_BASH").is_ok() } +pub fn is_xonsh() -> bool { + // only set on xonsh + env::var("ATUIN_SHELL_XONSH").is_ok() +} + /// Extension trait for anything that can behave like a string to make it easy to escape control /// characters. /// diff --git a/atuin/src/command/client/search/interactive.rs b/atuin/src/command/client/search/interactive.rs index 1fc37b57..27c82f2c 100644 --- a/atuin/src/command/client/search/interactive.rs +++ b/atuin/src/command/client/search/interactive.rs @@ -967,7 +967,9 @@ pub async fn history( match result { InputAction::Accept(index) if index < results.len() => { let mut command = results.swap_remove(index).command; - if accept && (utils::is_zsh() || utils::is_fish() || utils::is_bash()) { + if accept + && (utils::is_zsh() || utils::is_fish() || utils::is_bash() || utils::is_xonsh()) + { command = String::from("__atuin_accept__:") + &command; } diff --git a/atuin/src/command/init.rs b/atuin/src/command/init.rs index eb841c9f..3eb50d68 100644 --- a/atuin/src/command/init.rs +++ b/atuin/src/command/init.rs @@ -23,6 +23,8 @@ pub enum Shell { Fish, /// Nu setup Nu, + /// Xonsh setup + Xonsh, } impl Cmd { @@ -140,12 +142,31 @@ bind -M insert \e\[A _atuin_bind_up"; } } + fn init_xonsh(&self) { + let base = include_str!("../shell/atuin.xsh"); + let (bind_ctrl_r, bind_up_arrow) = if std::env::var("ATUIN_NOBIND").is_ok() { + (false, false) + } else { + (!self.disable_ctrl_r, !self.disable_up_arrow) + }; + println!( + "_ATUIN_BIND_CTRL_R={}", + if bind_ctrl_r { "True" } else { "False" } + ); + println!( + "_ATUIN_BIND_UP_ARROW={}", + if bind_up_arrow { "True" } else { "False" } + ); + println!("{base}"); + } + pub fn run(self) { match self.shell { Shell::Zsh => self.init_zsh(), Shell::Bash => self.init_bash(), Shell::Fish => self.init_fish(), Shell::Nu => self.init_nu(), + Shell::Xonsh => self.init_xonsh(), } } } diff --git a/atuin/src/shell/atuin.xsh b/atuin/src/shell/atuin.xsh new file mode 100644 index 00000000..5df26b26 --- /dev/null +++ b/atuin/src/shell/atuin.xsh @@ -0,0 +1,67 @@ +import subprocess +from prompt_toolkit.keys import Keys + +$ATUIN_SESSION=$(atuin uuid).rstrip('\n') + + +@events.on_precommand +def _atuin_precommand(cmd: str): + cmd = cmd.rstrip("\n") + $ATUIN_HISTORY_ID = $(atuin history start -- @(cmd)).rstrip("\n") + + +@events.on_postcommand +def _atuin_postcommand(cmd: str, rtn: int, out, ts): + if "ATUIN_HISTORY_ID" not in ${...}: + return + + duration = ts[1] - ts[0] + # Duration is float representing seconds, but atuin expects integer of nanoseconds + nanos = round(duration * 10 ** 9) + with ${...}.swap(ATUIN_LOG="error"): + # This causes the entire .xonshrc to be re-executed, which is incredibly slow + # This happens when using a subshell and using output redirection at the same time + # For more details, see https://github.com/xonsh/xonsh/issues/5224 + # (atuin history end --exit @(rtn) -- $ATUIN_HISTORY_ID &) > /dev/null 2>&1 + atuin history end --exit @(rtn) --duration @(nanos) -- $ATUIN_HISTORY_ID > /dev/null 2>&1 + del $ATUIN_HISTORY_ID + + +def _search(event, extra_args: list[str]): + buffer = event.current_buffer + cmd = ["atuin", "search", "--interactive", *extra_args, "--", buffer.text] + # We need to explicitly pass in xonsh env, in case user has set XDG_HOME or something else that matters + env = ${...}.detype() + env["ATUIN_SHELL_XONSH"] = "t" + + p = subprocess.run(cmd, stderr=subprocess.PIPE, encoding="utf-8", env=env) + result = p.stderr.rstrip("\n") + # redraw prompt - necessary if atuin is configured to run inline, rather than fullscreen + event.cli.renderer.erase() + + if not result: + return + + buffer.reset() + if result.startswith("__atuin_accept__:"): + buffer.insert_text(result[17:]) + buffer.validate_and_handle() + else: + buffer.insert_text(result) + + +@events.on_ptk_create +def _custom_keybindings(bindings, **kw): + @bindings.add(Keys.ControlR, filter=_ATUIN_BIND_CTRL_R) + def r_search(event): + _search(event, extra_args=[]) + + @bindings.add(Keys.Up, filter=_ATUIN_BIND_UP_ARROW) + def up_search(event): + # Only trigger if the buffer is a single line + if not '\n' in buffer.text: + _search(event, extra_args=["--shell-up-key-binding"]) + return + + # Run the default behavior for up arrow + event.current_buffer.auto_up(count=event.arg) -- cgit v1.2.3