You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
12 KiB
304 lines
12 KiB
# 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 </dev/null 2>&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*"
|