# Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. [ -z "${INTELLIJ_TERMINAL_COMMAND_BLOCKS-}" ] && return JETBRAINS_INTELLIJ_BASH_DIR="$(dirname "${BASH_SOURCE[0]}")" if [[ ! -n "${bash_preexec_imported:-}" ]]; then # Load bash-preexec if it still not # https://github.com/rcaloras/bash-preexec/tree/master#library-authors if [[ -r "${JETBRAINS_INTELLIJ_BASH_DIR}/bash-preexec.bash" ]]; then source "${JETBRAINS_INTELLIJ_BASH_DIR}/bash-preexec.bash" else unset JETBRAINS_INTELLIJ_BASH_DIR return fi fi if [ -r "${JETBRAINS_INTELLIJ_BASH_DIR}/bash-fig.bash" ]; then source "${JETBRAINS_INTELLIJ_BASH_DIR}/bash-fig.bash" fi unset JETBRAINS_INTELLIJ_BASH_DIR __jetbrains_intellij_encode_slow() { local out='' # Use LC_CTYPE=C to process text byte-by-byte and # LC_COLLATE=C to compare byte-for-byte. Ensure that # LC_ALL and LANG are not set so they don't interfere. builtin local i hexch LC_CTYPE=C LC_COLLATE=C LC_ALL= LANG= builtin local value="$1" for ((i = 1; i <= ${#value}; ++i)); do builtin printf -v hexch "%02X" "'$value[i]" out+="$hexch" done builtin printf "%s" "$out" } # Util method. Serializes string so that it could be safely passed to the escape sequence payload. __jetbrains_intellij_encode() { builtin local value="$1" if builtin command -v od > /dev/null && builtin command -v tr > /dev/null; then builtin printf "%s" "$value" | builtin command od -An -tx1 -v | builtin command tr -d "[:space:]" else __jetbrains_intellij_encode_slow "$value" fi } __jetbrains_intellij_is_generator_command() { [[ "$1" == *"__jetbrains_intellij_run_generator"* || "$1" == *"__jetbrains_intellij_report_shell_editor_buffer"* ]] } __jetbrains_intellij_run_generator() { __JETBRAINS_INTELLIJ_GENERATOR_COMMAND=1 builtin local request_id="$1" builtin local command="$2" # Can't be joined with an assignment, otherwise we will fail to capture the exit code of eval. builtin local result result="$(eval "$command" 2>&1)" builtin local exit_code=$? builtin printf '\e]1341;generator_finished;request_id=%s;result=%s;exit_code=%s\a' "$request_id" \ "$(__jetbrains_intellij_encode "$result")" \ "$exit_code" } __jetbrains_intellij_get_directory_files() { command ls -1ap "$1" } __jetbrains_intellij_get_aliases() { __jetbrains_intellij_escape_json "$(alias)" } __jetbrains_intellij_get_environment() { builtin local env_vars="$(__jetbrains_intellij_escape_json "$(builtin compgen -A export)")" builtin local keyword_names="$(__jetbrains_intellij_escape_json "$(builtin compgen -A keyword)")" builtin local builtin_names="$(__jetbrains_intellij_escape_json "$(builtin compgen -A builtin)")" builtin local function_names="$(__jetbrains_intellij_escape_json "$(builtin compgen -A function)")" builtin local command_names="$(__jetbrains_intellij_escape_json "$(builtin compgen -A command)")" builtin local aliases_mapping="$(__jetbrains_intellij_get_aliases)" builtin local result="{\"envs\": \"$env_vars\", \"keywords\": \"$keyword_names\", \"builtins\": \"$builtin_names\", \"functions\": \"$function_names\", \"commands\": \"$command_names\", \"aliases\": \"$aliases_mapping\"}" builtin printf '%s' "$result" } __jetbrains_intellij_escape_json() { builtin command sed -e 's/\\/\\\\/g'\ -e 's/"/\\"/g'\ <<< "$1" } # Store our PS1 value in a variable to reference it in a convenient way # Surround 'prompt shown' esc sequence with \[ \] to not count characters inside as part of prompt width __JETBRAINS_INTELLIJ_PS1='\[\e]1341;prompt_shown\a\]' __jetbrains_intellij_configure_prompt() { if [ "$PS1" != $__JETBRAINS_INTELLIJ_PS1 ]; then # Remember the original prompt to use it in '__jetbrains_intellij_report_prompt_state' __JETBRAINS_INTELLIJ_ORIGINAL_PS1=$PS1 fi # Trick: We put escape sequence to the PS1 so that every time prompt is shown, the event is triggered for IJ, but it stays invisible for end-user. PS1=$__JETBRAINS_INTELLIJ_PS1 } __jetbrains_intellij_debug_log() { if [ -n "${JETBRAINS_INTELLIJ_TERMINAL_DEBUG_LOG_LEVEL-}" ]; then builtin printf "%s\n" "$1" fi } __jetbrains_intellij_command_started() { # The real command, typed by user. builtin local typed_command="$1" # Resolved command to be really executed by Bash. (i.e. alias value) builtin local bash_command="$BASH_COMMAND" if __jetbrains_intellij_is_generator_command "$bash_command" then return 0 fi __jetbrains_intellij_clear_all_and_move_cursor_to_top_left __jetbrains_intellij_debug_log "command_started '$bash_command'" builtin local current_directory="$PWD" builtin printf '\e]1341;command_started;command=%s;current_directory=%s\a' \ "$(__jetbrains_intellij_encode "$typed_command")" \ "$(__jetbrains_intellij_encode "$current_directory")" } __jetbrains_intellij_clear_all_and_move_cursor_to_top_left() { builtin printf '\e[3J\e[1;1H' } __jetbrains_intellij_initialized="" __jetbrains_intellij_command_terminated() { builtin local last_exit_code="$?" __jetbrains_intellij_configure_prompt if [ -n "${__JETBRAINS_INTELLIJ_GENERATOR_COMMAND-}" ] then unset __JETBRAINS_INTELLIJ_GENERATOR_COMMAND return 0 fi if [ -z "$__jetbrains_intellij_initialized" ]; then __jetbrains_intellij_initialized='1' __jetbrains_intellij_fix_prompt_command_order builtin local hist="$(HISTTIMEFORMAT="" builtin history)" builtin printf '\e]1341;command_history;history_string=%s\a' "$(__jetbrains_intellij_encode "$hist")" builtin local shell_info="$(__jetbrains_intellij_collect_shell_info)" __jetbrains_intellij_debug_log 'initialized' builtin printf '\e]1341;initialized;shell_info=%s\a' "$(__jetbrains_intellij_encode $shell_info)" else __jetbrains_intellij_debug_log "command_finished exit_code=$last_exit_code" builtin printf '\e]1341;command_finished;exit_code=%s\a' "$last_exit_code" fi __jetbrains_intellij_report_prompt_state } __jetbrains_intellij_report_prompt_state() { builtin local current_directory="$PWD" builtin local user_name="${USER:-}" builtin local user_home="${HOME:-}" builtin local git_branch="" builtin local virtual_env="" builtin local conda_env="" if builtin command -v git > /dev/null then git_branch="$(builtin command git symbolic-ref --short HEAD 2> /dev/null || builtin command git rev-parse --short HEAD 2> /dev/null)" fi if [[ -n $VIRTUAL_ENV ]] then virtual_env="$VIRTUAL_ENV" fi if [[ -n $CONDA_DEFAULT_ENV ]] then conda_env="$CONDA_DEFAULT_ENV" fi builtin local prompt="$__JETBRAINS_INTELLIJ_ORIGINAL_PS1" builtin local expanded_prompt="" # Prompt expansion was introduced in 4.4 version of Bash if [[ -n "${BASH_VERSINFO-}" ]] && (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )) then expanded_prompt=${prompt@P} else # Launch a subshell with a desired prompt, then parse the output expanded_prompt=$(PS1="$prompt" "$BASH" --norc -i &1 | sed -n '${s/^\(.*\)exit$/\1/p;}') fi builtin printf '\e]1341;prompt_state_updated;current_directory=%s;user_name=%s;user_home=%s;git_branch=%s;virtual_env=%s;conda_env=%s;original_prompt=%s;original_right_prompt=%s\a' \ "$(__jetbrains_intellij_encode "${current_directory}")" \ "$(__jetbrains_intellij_encode "${user_name}")" \ "$(__jetbrains_intellij_encode "${user_home}")" \ "$(__jetbrains_intellij_encode "${git_branch}")" \ "$(__jetbrains_intellij_encode "${virtual_env}")" \ "$(__jetbrains_intellij_encode "${conda_env}")" \ "$(__jetbrains_intellij_encode "${expanded_prompt}")" \ "" # there is no dedicated variable for right prompt in Bash, so send an empty string } __jetbrains_intellij_collect_shell_info() { builtin local is_oh_my_bash='false' if [ -n "${OSH_THEME:-}" ] || [ -n "${OSH:-}" ] || [ -n "${OSH_CACHE_DIR:-}" ]; then is_oh_my_bash='true' fi builtin local is_starship='false' if [ -n "${STARSHIP_START_TIME:-}" ] || [ -n "${STARSHIP_SHELL:-}" ] || [ -n "${STARSHIP_SESSION_KEY:-}" ]; then is_starship='true' fi builtin local is_bash_it='false' if [ -n "${BASH_IT_THEME:-}" ] || [ -n "${BASH_IT:-}" ] || [ -n "${BASH_IT_BASHRC:-}" ]; then is_bash_it='true' fi builtin local oh_my_bash_theme="${OSH_THEME:-}" builtin local bash_it_theme="${BASH_IT_THEME:-}" builtin local oh_my_posh_theme='' if [ -n "${POSH_THEME:-}" ] || [ -n "${POSH_PID:-}" ] || [ -n "${POSH_SHELL_VERSION:-}" ]; then oh_my_posh_theme="${POSH_THEME:-default}" fi builtin local content_json="{"\ "\"shellVersion\": \"$(__jetbrains_intellij_escape_json "${BASH_VERSION:-}")\", "\ "\"isOhMyBash\": \"$is_oh_my_bash\", "\ "\"isStarship\": \"$is_starship\", "\ "\"isBashIt\": \"$is_bash_it\", "\ "\"ohMyBashTheme\": \"$(__jetbrains_intellij_escape_json $oh_my_bash_theme)\", "\ "\"ohMyPoshTheme\": \"$(__jetbrains_intellij_escape_json $oh_my_posh_theme)\", "\ "\"bashItTheme\": \"$(__jetbrains_intellij_escape_json $bash_it_theme)\""\ "}" builtin printf '%s' $content_json } # Bash-preexec lib is modifying the PROMPT_COMMAND variable in order to call precmd_functions. # But it is placing '__bp_precmd_invoke_cmd' function to the start of the PROMPT_COMMAND, # so all other hooks from the plugins (like PS1 updating) are invoked after our precmd_functions. # And at the moment of our '__jetbrains_intellij_command_terminated' call, we see an outdated PS1. # This function is reordering the functions in the PROMPT_COMMAND placing the Bash-preexec hooks to the end. function __jetbrains_intellij_fix_prompt_command_order() { function cleanup_command() { builtin local command="$1" command="${command//__bp_precmd_invoke_cmd/}" command="${command//__bp_interactive_mode/}" command="${command//$'\n':$'\n'/$'\n'}" # it is the function from the Bash-preexec __bp_sanitize_string command "$command" if [[ "${command:-:}" == ":" ]]; then command= fi printf '%s' "$command" } __jetbrains_intellij_debug_log "Before PROMPT_COMMAND modification: $(declare -p PROMPT_COMMAND)" # PROMPT_COMMAND is an array in Bash >= 5.1, so we need two implementations if [[ -n "${BASH_VERSINFO-}" ]] && (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) )); then # Remove the bash-preexec functions from the PROMPT_COMMAND array for index in "${!PROMPT_COMMAND[@]}"; do builtin local cur_command="${PROMPT_COMMAND[$index]}" cur_command="$(cleanup_command "$cur_command")" if [[ -n $cur_command ]]; then PROMPT_COMMAND[$index]=$cur_command else unset 'PROMPT_COMMAND[$index]' fi done # Add removed functions to the end of the array PROMPT_COMMAND+=('__bp_precmd_invoke_cmd') PROMPT_COMMAND+=('__bp_interactive_mode') # Fix the gaps in the array because of removed items builtin local new_array for i in "${!PROMPT_COMMAND[@]}"; do new_array+=( "${PROMPT_COMMAND[i]}" ) done PROMPT_COMMAND=("${new_array[@]}") else PROMPT_COMMAND="$(cleanup_command "$PROMPT_COMMAND")" PROMPT_COMMAND+=$'\n__bp_precmd_invoke_cmd\n__bp_interactive_mode' fi unset -f cleanup_command __jetbrains_intellij_debug_log "After PROMPT_COMMAND modification: $(declare -p PROMPT_COMMAND)" } # Avoid conflict with user defined alias unalias clear 2>/dev/null # Override clear behaviour to handle it on IDE side and remove the blocks function clear() { builtin printf '\e]1341;clear_invoked\a' } # This function will be triggered by a key bindings. function __jetbrains_intellij_report_shell_editor_buffer () { # The commands executed by `bind -x` also trigger `PREEXEC` and `PRECMD` (unlike ZSH' `bindkey`). # Mark as generator to avoid triggering `command_started` and `command_finished` events. __JETBRAINS_INTELLIJ_GENERATOR_COMMAND=1 builtin printf '\e]1341;shell_editor_buffer_reported;shell_editor_buffer=%s\a' "$(__jetbrains_intellij_encode "${READLINE_LINE:-}")" } # Remove binding if exists. builtin bind -r '"\eo"' # Bind [Esc, o] key sequence to report prompt buffer. builtin bind -x '"\eo":"__jetbrains_intellij_report_shell_editor_buffer"' preexec_functions+=(__jetbrains_intellij_command_started) precmd_functions+=(__jetbrains_intellij_command_terminated) HISTIGNORE="${HISTIGNORE-}:__jetbrains_intellij_run_generator*" HISTIGNORE="${HISTIGNORE-}:__jetbrains_intellij_report_shell_editor_buffer*"