# --------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # --------------------------------------------------------------------------------------------- # Prevent the script recursing when setting up if [[ -n "${VSCODE_SHELL_INTEGRATION:-}" ]]; then builtin return fi VSCODE_SHELL_INTEGRATION=1 # Run relevant rc/profile only if shell integration has been injected, not when run manually if [ "$VSCODE_INJECTION" == "1" ]; then if [ -z "$VSCODE_SHELL_LOGIN" ]; then if [ -r ~/.bashrc ]; then . ~/.bashrc fi else # Imitate -l because --init-file doesn't support it: # run the first of these files that exists if [ -r /etc/profile ]; then . /etc/profile fi # execute the first that exists if [ -r ~/.bash_profile ]; then . ~/.bash_profile elif [ -r ~/.bash_login ]; then . ~/.bash_login elif [ -r ~/.profile ]; then . ~/.profile fi builtin unset VSCODE_SHELL_LOGIN # Apply any explicit path prefix (see #99878) if [ -n "${VSCODE_PATH_PREFIX:-}" ]; then export PATH=$VSCODE_PATH_PREFIX$PATH builtin unset VSCODE_PATH_PREFIX fi fi builtin unset VSCODE_INJECTION fi if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then builtin return fi # Apply EnvironmentVariableCollections if needed if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_REPLACE" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2-)" export $VARNAME="$VALUE" done builtin unset VSCODE_ENV_REPLACE fi if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_PREPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2-)" export $VARNAME="$VALUE${!VARNAME}" done builtin unset VSCODE_ENV_PREPEND fi if [ -n "${VSCODE_ENV_APPEND:-}" ]; then IFS=':' read -ra ADDR <<< "$VSCODE_ENV_APPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo $ITEM | cut -d "=" -f 1)" VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2-)" export $VARNAME="${!VARNAME}$VALUE" done builtin unset VSCODE_ENV_APPEND fi __vsc_get_trap() { # 'trap -p DEBUG' outputs a shell command like `trap -- '…shellcode…' DEBUG`. # The terms are quoted literals, but are not guaranteed to be on a single line. # (Consider a trap like $'echo foo\necho \'bar\''). # To parse, we splice those terms into an expression capturing them into an array. # This preserves the quoting of those terms: when we `eval` that expression, they are preserved exactly. # This is different than simply exploding the string, which would split everything on IFS, oblivious to quoting. builtin local -a terms builtin eval "terms=( $(trap -p "${1:-DEBUG}") )" # |________________________| # | # \-------------------*--------------------/ # terms=( trap -- '…arbitrary shellcode…' DEBUG ) # |____||__| |_____________________| |_____| # | | | | # 0 1 2 3 # | # \--------*----/ builtin printf '%s' "${terms[2]:-}" } __vsc_escape_value_fast() { builtin local LC_ALL=C out out=${1//\\/\\\\} out=${out//;/\\x3b} builtin printf '%s\n' "${out}" } # The property (P) and command (E) codes embed values which require escaping. # Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex. __vsc_escape_value() { # If the input being too large, switch to the faster function if [ "${#1}" -ge 2000 ]; then __vsc_escape_value_fast "$1" builtin return fi # Process text byte by byte, not by codepoint. builtin local LC_ALL=C str="${1}" i byte token out='' for (( i=0; i < "${#str}"; ++i )); do byte="${str:$i:1}" # Escape backslashes, semi-colons specially, then special ASCII chars below space (0x20) if [ "$byte" = "\\" ]; then token="\\\\" elif [ "$byte" = ";" ]; then token="\\x3b" elif (( $(builtin printf '%d' "'$byte") < 31 )); then token=$(builtin printf '\\x%02x' "'$byte") else token="$byte" fi out+="$token" done builtin printf '%s\n' "${out}" } # Send the IsWindows property if the environment looks like Windows if [[ "$(uname -s)" =~ ^CYGWIN*|MINGW*|MSYS* ]]; then builtin printf '\e]633;P;IsWindows=True\a' __vsc_is_windows=1 else __vsc_is_windows=0 fi # Allow verifying $BASH_COMMAND doesn't have aliases resolved via history when the right HISTCONTROL # configuration is used if [[ "$HISTCONTROL" =~ .*(erasedups|ignoreboth|ignoredups).* ]]; then __vsc_history_verify=0 else __vsc_history_verify=1 fi __vsc_initialized=0 __vsc_original_PS1="$PS1" __vsc_original_PS2="$PS2" __vsc_custom_PS1="" __vsc_custom_PS2="" __vsc_in_command_execution="1" __vsc_current_command="" # It's fine this is in the global scope as it getting at it requires access to the shell environment __vsc_nonce="$VSCODE_NONCE" unset VSCODE_NONCE # Report continuation prompt builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a" __vsc_report_prompt() { # Expand the original PS1 similarly to how bash would normally # See https://stackoverflow.com/a/37137981 for technique if ((BASH_VERSINFO[0] >= 5 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4))); then __vsc_prompt=${__vsc_original_PS1@P} else __vsc_prompt=${__vsc_original_PS1} fi __vsc_prompt="$(builtin printf "%s" "${__vsc_prompt//[$'\001'$'\002']}")" builtin printf "\e]633;P;Prompt=%s\a" "$(__vsc_escape_value "${__vsc_prompt}")" } __vsc_prompt_start() { builtin printf '\e]633;A\a' } __vsc_prompt_end() { builtin printf '\e]633;B\a' } __vsc_update_cwd() { if [ "$__vsc_is_windows" = "1" ]; then __vsc_cwd="$(cygpath -m "$PWD")" else __vsc_cwd="$PWD" fi builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "$__vsc_cwd")" } __vsc_command_output_start() { if [[ -z "$__vsc_first_prompt" ]]; then builtin return fi builtin printf '\e]633;E;%s;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")" $__vsc_nonce builtin printf '\e]633;C\a' } __vsc_continuation_start() { builtin printf '\e]633;F\a' } __vsc_continuation_end() { builtin printf '\e]633;G\a' } __vsc_command_complete() { if [[ -z "$__vsc_first_prompt" ]]; then builtin return fi if [ "$__vsc_current_command" = "" ]; then builtin printf '\e]633;D\a' else builtin printf '\e]633;D;%s\a' "$__vsc_status" fi __vsc_update_cwd } __vsc_update_prompt() { # in command execution if [ "$__vsc_in_command_execution" = "1" ]; then # Wrap the prompt if it is not yet wrapped, if the PS1 changed this this was last set it # means the user re-exported the PS1 so we should re-wrap it if [[ "$__vsc_custom_PS1" == "" || "$__vsc_custom_PS1" != "$PS1" ]]; then __vsc_original_PS1=$PS1 __vsc_custom_PS1="\[$(__vsc_prompt_start)\]$__vsc_original_PS1\[$(__vsc_prompt_end)\]" PS1="$__vsc_custom_PS1" fi if [[ "$__vsc_custom_PS2" == "" || "$__vsc_custom_PS2" != "$PS2" ]]; then __vsc_original_PS2=$PS2 __vsc_custom_PS2="\[$(__vsc_continuation_start)\]$__vsc_original_PS2\[$(__vsc_continuation_end)\]" PS2="$__vsc_custom_PS2" fi __vsc_in_command_execution="0" fi } __vsc_precmd() { __vsc_command_complete "$__vsc_status" __vsc_current_command="" __vsc_report_prompt __vsc_first_prompt=1 __vsc_update_prompt } __vsc_preexec() { __vsc_initialized=1 if [[ ! $BASH_COMMAND == __vsc_prompt* ]]; then # Use history if it's available to verify the command as BASH_COMMAND comes in with aliases # resolved if [ "$__vsc_history_verify" = "1" ]; then __vsc_current_command="$(builtin history 1 | sed 's/ *[0-9]* *//')" else __vsc_current_command=$BASH_COMMAND fi else __vsc_current_command="" fi __vsc_command_output_start } # Debug trapping/preexec inspired by starship (ISC) if [[ -n "${bash_preexec_imported:-}" ]]; then __vsc_preexec_only() { if [ "$__vsc_in_command_execution" = "0" ]; then __vsc_in_command_execution="1" __vsc_preexec fi } precmd_functions+=(__vsc_prompt_cmd) preexec_functions+=(__vsc_preexec_only) else __vsc_dbg_trap="$(__vsc_get_trap DEBUG)" if [[ -z "$__vsc_dbg_trap" ]]; then __vsc_preexec_only() { if [ "$__vsc_in_command_execution" = "0" ]; then __vsc_in_command_execution="1" __vsc_preexec fi } trap '__vsc_preexec_only "$_"' DEBUG elif [[ "$__vsc_dbg_trap" != '__vsc_preexec "$_"' && "$__vsc_dbg_trap" != '__vsc_preexec_all "$_"' ]]; then __vsc_preexec_all() { if [ "$__vsc_in_command_execution" = "0" ]; then __vsc_in_command_execution="1" __vsc_preexec builtin eval "${__vsc_dbg_trap}" fi } trap '__vsc_preexec_all "$_"' DEBUG fi fi __vsc_update_prompt __vsc_restore_exit_code() { return "$1" } __vsc_prompt_cmd_original() { __vsc_status="$?" __vsc_restore_exit_code "${__vsc_status}" # Evaluate the original PROMPT_COMMAND similarly to how bash would normally # See https://unix.stackexchange.com/a/672843 for technique local cmd for cmd in "${__vsc_original_prompt_command[@]}"; do eval "${cmd:-}" done __vsc_precmd } __vsc_prompt_cmd() { __vsc_status="$?" __vsc_precmd } # PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of # the array?) __vsc_original_prompt_command=${PROMPT_COMMAND:-} if [[ -z "${bash_preexec_imported:-}" ]]; then if [[ -n "${__vsc_original_prompt_command:-}" && "${__vsc_original_prompt_command:-}" != "__vsc_prompt_cmd" ]]; then PROMPT_COMMAND=__vsc_prompt_cmd_original else PROMPT_COMMAND=__vsc_prompt_cmd fi fi