diff --git a/.iterm2/shell_integration.bash b/.iterm2/shell_integration.bash old mode 100755 new mode 100644 index 172587b..c24ff13 --- a/.iterm2/shell_integration.bash +++ b/.iterm2/shell_integration.bash @@ -1,15 +1,39 @@ #!/bin/bash +# The iTerm2 customizations fall under the following license: +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + # -- BEGIN ITERM2 CUSTOMIZATIONS -- -if [[ "$TERM" != screen && "$ITERM_SHELL_INTEGRATION_INSTALLED" = "" && "$-" == *i* ]]; then +if [[ "$ITERM_ENABLE_SHELL_INTEGRATION_WITH_TMUX""$TERM" != screen && "$ITERM_SHELL_INTEGRATION_INSTALLED" = "" && "$-" == *i* ]]; then + +if shopt extdebug | grep on > /dev/null; then + echo "iTerm2 Shell Integration not installed." + echo "" + echo "Your shell has 'extdebug' turned on." + echo "This is incompatible with shell integration." + echo "Find 'shopt -s extdebug' in bash's rc scripts and remove it." + return 0 +fi + ITERM_SHELL_INTEGRATION_INSTALLED=Yes # Saved copy of your PS1. This is used to detect if the user changes PS1 # directly. ITERM_PREV_PS1 will hold the last value that this script set PS1 to # (including various custom escape sequences). ITERM_PREV_PS1="$PS1" -# -- END ITERM2 CUSTOMIZATIONS -- - # The following chunk of code, bash-preexec.sh, is licensed like this: # The MIT License # @@ -33,6 +57,9 @@ ITERM_PREV_PS1="$PS1" # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +# Wrap bash-preexec.sh in a function so that, if it exits early due to having +# been sourced elsewhere, it doesn't exit our entire script. +_install_bash_preexec () { # -- BEGIN BASH-PREEXEC.SH -- #!/bin/bash # @@ -47,7 +74,22 @@ ITERM_PREV_PS1="$PS1" # Author: Ryan Caloras (ryan@bashhub.com) # Forked from Original Author: Glyph Lefkowitz # -# V0.3.3 +# V0.3.7 +# +# -- END ITERM2 CUSTOMIZATIONS -- + +# bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions. +# https://github.com/rcaloras/bash-preexec +# +# +# 'preexec' functions are executed before each interactive command is +# executed, with the interactive command as its argument. The 'precmd' +# function is executed before each prompt is displayed. +# +# Author: Ryan Caloras (ryan@bashhub.com) +# Forked from Original Author: Glyph Lefkowitz +# +# V0.3.7 # # General Usage: @@ -62,13 +104,13 @@ ITERM_PREV_PS1="$PS1" # # preexec_functions+=(my_preexec_function) # -# 3. If you have anything that's using the Debug Trap, change it to use -# preexec. (Optional) change anything using PROMPT_COMMAND to now use -# precmd instead. +# 3. Consider changing anything using the DEBUG trap or PROMPT_COMMAND +# to use preexec and precmd instead. Preexisting usages will be +# preserved, but doing so manually may be less surprising. # -# Note: This module requires two bash features which you must not otherwise be -# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. prexec_and_precmd_install -# will override these and if you override one or the other this will most likely break. +# Note: This module requires two Bash features which you must not otherwise be +# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override +# either of these after bash-preexec has been installed it will most likely break. # Avoid duplicate inclusion if [[ "$__bp_imported" == "defined" ]]; then @@ -81,9 +123,19 @@ __bp_imported="defined" __bp_last_ret_value="$?" __bp_last_argument_prev_command="$_" -# Command to set our preexec trap. It's invoked once via -# PROMPT_COMMAND and then removed. -__bp_trap_install_string="trap '__bp_preexec_invoke_exec \"\$_\"' DEBUG;" +__bp_inside_precmd=0 +__bp_inside_preexec=0 + +# Fails if any of the given variables are readonly +# Reference https://stackoverflow.com/a/4441178 +__bp_require_not_readonly() { + for var; do + if ! ( unset "$var" 2> /dev/null ); then + echo "iTerm2 Shell Integration: bash-preexec requires write access to ${var}" >&2 + return 1 + fi + done +} # Remove ignorespace and or replace ignoreboth from HISTCONTROL # so we can accurately invoke preexec with a command from our @@ -123,11 +175,19 @@ __bp_interactive_mode() { # This function is installed as part of the PROMPT_COMMAND. # It will invoke any functions defined in the precmd_functions array. __bp_precmd_invoke_cmd() { - - # Save the returned value from our last command + # Save the returned value from our last command. Note: this MUST be the + # first thing done in this function. __bp_last_ret_value="$?" - # For every function defined in our function array. Invoke it. + # Don't invoke precmds if we are inside an execution of an "original + # prompt command" by another precmd execution loop. This avoids infinite + # recursion. + if (( __bp_inside_precmd > 0 )); then + return + fi + local __bp_inside_precmd=1 + + # Invoke every function defined in our function array. local precmd_function for precmd_function in "${precmd_functions[@]}"; do @@ -135,7 +195,8 @@ __bp_precmd_invoke_cmd() { # Test existence of functions with: declare -[Ff] if type -t "$precmd_function" 1>/dev/null; then __bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command" - $precmd_function + # Quote our function invocation to prevent issues with IFS + "$precmd_function" fi done } @@ -173,12 +234,16 @@ __bp_in_prompt_command() { # environment to attempt to detect if the current command is being invoked # interactively, and invoke 'preexec' if so. __bp_preexec_invoke_exec() { - - # Save the contents of $_ so that it can be restored later on. # https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702 __bp_last_argument_prev_command="$1" + # Don't invoke preexecs if we are inside of another preexec. + if (( __bp_inside_preexec > 0 )); then + return + fi + local __bp_inside_preexec=1 + # Checks if the file descriptor is not standard out (i.e. '1') # __bp_delay_install checks if we're in test. Needed for bats to run. # Prevents preexec from being invoked for functions in PS1 @@ -214,7 +279,7 @@ __bp_preexec_invoke_exec() { fi local this_command - this_command=$(HISTTIMEFORMAT= history 1 | { read -r _ this_command; echo "$this_command"; }) + this_command=$(HISTTIMEFORMAT= builtin history 1 | { IFS=" " read -r _ this_command; echo "$this_command"; }) # Sanity check to make sure we have something to invoke our function with. if [[ -z "$this_command" ]]; then @@ -225,8 +290,9 @@ __bp_preexec_invoke_exec() { # the command is in fact interactive and we should invoke the user's # preexec functions. - # For every function defined in our function array. Invoke it. + # Invoke every function defined in our function array. local preexec_function + local preexec_function_ret_value local preexec_ret_value=0 for preexec_function in "${preexec_functions[@]}"; do @@ -234,54 +300,42 @@ __bp_preexec_invoke_exec() { # Test existence of function with: declare -[fF] if type -t "$preexec_function" 1>/dev/null; then __bp_set_ret_value $__bp_last_ret_value - $preexec_function "$this_command" - preexec_ret_value="$?" + # Quote our function invocation to prevent issues with IFS + "$preexec_function" "$this_command" + preexec_function_ret_value="$?" + if [[ "$preexec_function_ret_value" != 0 ]]; then + preexec_ret_value="$preexec_function_ret_value" + fi fi done - # Restore the last argument of the last executed command - # Also preserves the return value of the last function executed in preexec - # If `extdebug` is enabled a non-zero return value from the last function - # in prexec causes the command not to execute + # Restore the last argument of the last executed command, and set the return + # value of the DEBUG trap to be the return code of the last preexec function + # to return an error. + # If `extdebug` is enabled a non-zero return value from any preexec function + # will cause the user's command not to execute. # Run `shopt -s extdebug` to enable __bp_set_ret_value "$preexec_ret_value" "$__bp_last_argument_prev_command" } -# Returns PROMPT_COMMAND with a semicolon appended -# if it doesn't already have one. -__bp_prompt_command_with_semi_colon() { - - # Trim our existing PROMPT_COMMAND - local trimmed - trimmed=$(__bp_trim_whitespace "$PROMPT_COMMAND") - - # Take our existing prompt command and append a semicolon to it - # if it doesn't already have one. - local existing_prompt_command - if [[ -n "$trimmed" ]]; then - existing_prompt_command=${trimmed%${trimmed##*[![:space:]]}} - existing_prompt_command=${existing_prompt_command%;} - existing_prompt_command=${existing_prompt_command/%/;} - else - existing_prompt_command="" - fi - - echo -n "$existing_prompt_command" -} - __bp_install() { - - # Remove setting our trap from PROMPT_COMMAND - PROMPT_COMMAND="${PROMPT_COMMAND//$__bp_trap_install_string}" - - # Remove this function from our PROMPT_COMMAND - PROMPT_COMMAND="${PROMPT_COMMAND//__bp_install;}" - # Exit if we already have this installed. if [[ "$PROMPT_COMMAND" == *"__bp_precmd_invoke_cmd"* ]]; then return 1; fi + trap '__bp_preexec_invoke_exec "$_"' DEBUG + + # Preserve any prior DEBUG trap as a preexec function + local prior_trap=$(sed "s/[^']*'\(.*\)'[^']*/\1/" <<<"$__bp_trap_string") + unset __bp_trap_string + if [[ -n "$prior_trap" ]]; then + eval '__bp_original_debug_trap() { + '"$prior_trap"' + }' + preexec_functions+=(__bp_original_debug_trap) + fi + # Adjust our HISTCONTROL Variable if needed. __bp_adjust_histcontrol @@ -297,28 +351,21 @@ __bp_install() { shopt -s extdebug > /dev/null 2>&1 fi; - - local existing_prompt_command - existing_prompt_command=$(__bp_prompt_command_with_semi_colon) - # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've # actually entered something. - PROMPT_COMMAND="__bp_precmd_invoke_cmd; ${existing_prompt_command} __bp_interactive_mode" - eval "$__bp_trap_install_string" + PROMPT_COMMAND="__bp_precmd_invoke_cmd; __bp_interactive_mode" # Add two functions to our arrays for convenience # of definition. precmd_functions+=(precmd) preexec_functions+=(preexec) - # Since this is in PROMPT_COMMAND, invoke any precmd functions we have defined. - __bp_precmd_invoke_cmd - # Put us in interactive mode for our first command. - __bp_interactive_mode + # Since this function is invoked via PROMPT_COMMAND, re-execute PC now that it's properly set + eval "$PROMPT_COMMAND" } # Sets our trap and __bp_install as part of our PROMPT_COMMAND to install -# after our session has started. This allows bash-preexec to be inlucded +# after our session has started. This allows bash-preexec to be included # at any point in our bash profile. Ideally we could set our trap inside # __bp_install, but if a trap already exists it'll only set locally to # the function. @@ -329,20 +376,35 @@ __bp_install_after_session_init() { return 1; fi - local existing_prompt_command - existing_prompt_command=$(__bp_prompt_command_with_semi_colon) + # bash-preexec needs to modify these variables in order to work correctly + # if it can't, just stop the installation + __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return - # Add our installation to be done last via our PROMPT_COMMAND. These are - # removed by __bp_install when it's invoked so it only runs once. - PROMPT_COMMAND="${existing_prompt_command} $__bp_trap_install_string __bp_install;" + # If there's an existing PROMPT_COMMAND capture it and convert it into a function + # So it is preserved and invoked during precmd. + if [[ -n "$PROMPT_COMMAND" ]]; then + eval '__bp_original_prompt_command() { + '"$PROMPT_COMMAND"' + }' + precmd_functions+=(__bp_original_prompt_command) + fi + + # Installation is finalized in PROMPT_COMMAND, which allows us to override the DEBUG + # trap. __bp_install sets PROMPT_COMMAND to its final value, so these are only + # invoked once. + # It's necessary to clear any existing DEBUG trap in order to set it from the install function. + # Using \n as it's the most universal delimiter of bash commands + PROMPT_COMMAND=$'\n__bp_trap_string="$(trap -p DEBUG)"\ntrap DEBUG\n__bp_install\n' } # Run our install so long as we're not delaying it. if [[ -z "$__bp_delay_install" ]]; then __bp_install_after_session_init fi; - -# -- BEGIN BASH-PREEXEC.SH -- +# -- END BASH-PREEXEC.SH -- +} +_install_bash_preexec +unset -f _install_bash_preexec # -- BEGIN ITERM2 CUSTOMIZATIONS -- @@ -410,7 +472,7 @@ function iterm2_prompt_suffix() { function iterm2_print_version_number() { iterm2_begin_osc - printf "1337;ShellIntegrationVersion=8;shell=bash" + printf "1337;ShellIntegrationVersion=12;shell=bash" iterm2_end_osc } @@ -490,6 +552,15 @@ function __iterm2_precmd () { export ITERM_ORIG_PS1="$PS1" fi + # If you want to generate PS1 dynamically from PROMPT_COMMAND, the best way + # to do it is to define a function named iterm2_generate_ps1 that sets PS1. + # Issue 5964. Other shells don't have this issue because they don't need + # such extremes to get precmd and preexec. + if [ -n "$(type -t iterm2_generate_ps1)" ] && [ "$(type -t iterm2_generate_ps1)" = function ]; then + iterm2_generate_ps1 + fi + + if [[ "$PS1" != "$ITERM_PREV_PS1" ]] then export ITERM_ORIG_PS1="$PS1" @@ -522,8 +593,9 @@ function __iterm2_precmd () { preexec_functions+=(__iterm2_preexec) precmd_functions+=(__iterm2_precmd) +iterm2_print_state_data +iterm2_print_version_number fi # -- END ITERM2 CUSTOMIZATIONS -- -alias imgcat=~/.iterm2/imgcat;alias imgls=~/.iterm2/imgls;alias it2attention=~/.iterm2/it2attention;alias it2check=~/.iterm2/it2check;alias it2copy=~/.iterm2/it2copy;alias it2dl=~/.iterm2/it2dl;alias it2getvar=~/.iterm2/it2getvar;alias it2setcolor=~/.iterm2/it2setcolor;alias it2setkeylabel=~/.iterm2/it2setkeylabel;alias it2ul=~/.iterm2/it2ul;alias it2universion=~/.iterm2/it2universion