From 5d778f3d548579d3bc69155fa194f49c24c3f8d6 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 4 Feb 2024 03:17:15 +0900 Subject: [PATCH 1/2] fix(bash/preexec): support termcap-based tput The current uses of tput specify the terminfo entry names. However, there are different implementations of the tput command. There are two ways to specify the terminal capability: terminfo and termcap names. Although recent implementations of tput (such as ncurses in Linux) accepts the terminfo name, some accept both the terminfo and termcap names, and some old implementations (such as in FreeBSD) only accept the termcap names. In this patch, we first attempt the terminfo name and then the termcap name if the terminfo name fails. Note: When both fail due to e.g. non-existent tput, we end up with outputting nothing. This does not cause a serious problem because it just does not clear the previous prompts. --- atuin/src/shell/atuin.bash | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/atuin/src/shell/atuin.bash b/atuin/src/shell/atuin.bash index c632cf9dc2b..cc44a93878a 100644 --- a/atuin/src/shell/atuin.bash +++ b/atuin/src/shell/atuin.bash @@ -107,12 +107,12 @@ __atuin_accept_line() { local __atuin_prompt __atuin_prompt_offset __atuin_evaluate_prompt local __atuin_clear_prompt - __atuin_clear_prompt=$'\r'$(tput el) + __atuin_clear_prompt=$'\r'$(tput el 2>/dev/null || tput ce 2>/dev/null) if ((__atuin_prompt_offset > 0)); then __atuin_clear_prompt+=$( - tput cuu "$__atuin_prompt_offset" - tput dl "$__atuin_prompt_offset" - tput il "$__atuin_prompt_offset" + tput cuu "$__atuin_prompt_offset" 2>/dev/null || tput UP "$__atuin_prompt_offset" 2>/dev/null + tput dl "$__atuin_prompt_offset" 2>/dev/null || tput DL "$__atuin_prompt_offset" 2>/dev/null + tput il "$__atuin_prompt_offset" 2>/dev/null || tput AL "$__atuin_prompt_offset" 2>/dev/null ) fi printf '%s\n' "$__atuin_clear_prompt$__atuin_prompt$__atuin_command" @@ -164,7 +164,7 @@ __atuin_accept_line() { # so to work for a multiline prompt we need to print it ourselves, # then go to the beginning of the last line. __atuin_evaluate_prompt - printf '%s\r%s' "$__atuin_prompt" "$(tput el)" + printf '%s\r%s' "$__atuin_prompt" "$(tput el 2>/dev/null || tput ce 2>/dev/null)" } __atuin_history() { From d003f016c5a43d637692a89ce0e0c88de8252642 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 4 Feb 2024 03:44:13 +0900 Subject: [PATCH 2/2] perf(bash/preexec): cache the results of tput With the current implementation, we spwan 10 processes of the tput command at most every time we perform `enter_accept`. In this patch, to reduce the delay, we separate the related code into a function and cache the results of the tput commands. --- atuin/src/shell/atuin.bash | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/atuin/src/shell/atuin.bash b/atuin/src/shell/atuin.bash index cc44a93878a..e40e8c562d8 100644 --- a/atuin/src/shell/atuin.bash +++ b/atuin/src/shell/atuin.bash @@ -100,22 +100,35 @@ else } fi +# The shell function `__atuin_clear_prompt N` outputs terminal control +# sequences to clear the contents of the current and N previous lines. After +# clearing, the cursor is placed at the beginning of the N-th previous line. +__atuin_clear_prompt_cache=() +__atuin_clear_prompt() { + local offset=$1 + if [[ ! ${__atuin_clear_prompt_cache[offset]+set} ]]; then + if [[ ! ${__atuin_clear_prompt_cache[0]+set} ]]; then + __atuin_clear_prompt_cache[0]=$'\r'$(tput el 2>/dev/null || tput ce 2>/dev/null) + fi + if ((offset > 0)); then + __atuin_clear_prompt_cache[offset]=${__atuin_clear_prompt_cache[0]}$( + tput cuu "$offset" 2>/dev/null || tput UP "$offset" 2>/dev/null + tput dl "$offset" 2>/dev/null || tput DL "$offset" 2>/dev/null + tput il "$offset" 2>/dev/null || tput AL "$offset" 2>/dev/null + ) + fi + fi + printf '%s' "${__atuin_clear_prompt_cache[offset]}" +} + __atuin_accept_line() { local __atuin_command=$1 # Reprint the prompt, accounting for multiple lines local __atuin_prompt __atuin_prompt_offset __atuin_evaluate_prompt - local __atuin_clear_prompt - __atuin_clear_prompt=$'\r'$(tput el 2>/dev/null || tput ce 2>/dev/null) - if ((__atuin_prompt_offset > 0)); then - __atuin_clear_prompt+=$( - tput cuu "$__atuin_prompt_offset" 2>/dev/null || tput UP "$__atuin_prompt_offset" 2>/dev/null - tput dl "$__atuin_prompt_offset" 2>/dev/null || tput DL "$__atuin_prompt_offset" 2>/dev/null - tput il "$__atuin_prompt_offset" 2>/dev/null || tput AL "$__atuin_prompt_offset" 2>/dev/null - ) - fi - printf '%s\n' "$__atuin_clear_prompt$__atuin_prompt$__atuin_command" + __atuin_clear_prompt "$__atuin_prompt_offset" + printf '%s\n' "$__atuin_prompt$__atuin_command" # Add it to the bash history history -s "$__atuin_command" @@ -164,7 +177,8 @@ __atuin_accept_line() { # so to work for a multiline prompt we need to print it ourselves, # then go to the beginning of the last line. __atuin_evaluate_prompt - printf '%s\r%s' "$__atuin_prompt" "$(tput el 2>/dev/null || tput ce 2>/dev/null)" + printf '%s' "$__atuin_prompt" + __atuin_clear_prompt 0 } __atuin_history() {