Initial commit, 90% there
This commit is contained in:
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,478 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
vsc_env_keys=()
|
||||
vsc_env_values=()
|
||||
use_associative_array=0
|
||||
bash_major_version=${BASH_VERSINFO[0]}
|
||||
|
||||
__vscode_shell_env_reporting="${VSCODE_SHELL_ENV_REPORTING:-}"
|
||||
unset VSCODE_SHELL_ENV_REPORTING
|
||||
|
||||
envVarsToReport=()
|
||||
IFS=',' read -ra envVarsToReport <<< "$__vscode_shell_env_reporting"
|
||||
|
||||
if (( BASH_VERSINFO[0] >= 4 )); then
|
||||
use_associative_array=1
|
||||
# Associative arrays are only available in bash 4.0+
|
||||
declare -A vsc_aa_env
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Register Python shell activate hooks
|
||||
# Prevent multiple activation with guard
|
||||
if [ -z "${VSCODE_PYTHON_AUTOACTIVATE_GUARD:-}" ]; then
|
||||
export VSCODE_PYTHON_AUTOACTIVATE_GUARD=1
|
||||
if [ -n "${VSCODE_PYTHON_BASH_ACTIVATE:-}" ] && [ "$TERM_PROGRAM" = "vscode" ]; then
|
||||
# Prevent crashing by negating exit code
|
||||
if ! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"; then
|
||||
__vsc_activation_status=$?
|
||||
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
|
||||
fi
|
||||
fi
|
||||
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 -r LC_ALL=C
|
||||
builtin local -r str="${1}"
|
||||
builtin local -ir len="${#str}"
|
||||
|
||||
builtin local -i i
|
||||
builtin local -i val
|
||||
builtin local byte
|
||||
builtin local token
|
||||
builtin local out=''
|
||||
|
||||
for (( i=0; i < "${#str}"; ++i )); do
|
||||
# Escape backslashes, semi-colons specially, then special ASCII chars below space (0x20).
|
||||
byte="${str:$i:1}"
|
||||
builtin printf -v val '%d' "'$byte"
|
||||
if (( val < 31 )); then
|
||||
builtin printf -v token '\\x%02x' "'$byte"
|
||||
elif (( val == 92 )); then # \
|
||||
token="\\\\"
|
||||
elif (( val == 59 )); then # ;
|
||||
token="\\x3b"
|
||||
else
|
||||
token="$byte"
|
||||
fi
|
||||
|
||||
out+="$token"
|
||||
done
|
||||
|
||||
builtin printf '%s\n' "$out"
|
||||
}
|
||||
|
||||
# Send the IsWindows property if the environment looks like Windows
|
||||
__vsc_regex_environment="^CYGWIN*|MINGW*|MSYS*"
|
||||
if [[ "$(uname -s)" =~ $__vsc_regex_environment ]]; 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
|
||||
__vsc_regex_histcontrol=".*(erasedups|ignoreboth|ignoredups).*"
|
||||
if [[ "${HISTCONTROL:-}" =~ $__vsc_regex_histcontrol ]]; then
|
||||
__vsc_history_verify=0
|
||||
else
|
||||
__vsc_history_verify=1
|
||||
fi
|
||||
|
||||
builtin unset __vsc_regex_environment
|
||||
builtin unset __vsc_regex_histcontrol
|
||||
|
||||
__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
|
||||
|
||||
# Some features should only work in Insiders
|
||||
__vsc_stable="$VSCODE_STABLE"
|
||||
unset VSCODE_STABLE
|
||||
|
||||
# Report continuation prompt
|
||||
if [ "$__vsc_stable" = "0" ]; then
|
||||
builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a"
|
||||
fi
|
||||
|
||||
if [ -n "$STARSHIP_SESSION_KEY" ]; then
|
||||
builtin printf '\e]633;P;PromptType=starship\a'
|
||||
elif [ -n "$POSH_SESSION_ID" ]; then
|
||||
builtin printf '\e]633;P;PromptType=oh-my-posh\a'
|
||||
fi
|
||||
|
||||
# Report this shell supports rich command detection
|
||||
builtin printf '\e]633;P;HasRichCommandDetection=True\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")"
|
||||
}
|
||||
|
||||
__updateEnvCacheAA() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if [ "$use_associative_array" = 1 ]; then
|
||||
if [[ "${vsc_aa_env[$key]}" != "$value" ]]; then
|
||||
vsc_aa_env["$key"]="$value"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__updateEnvCache() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
for i in "${!vsc_env_keys[@]}"; do
|
||||
if [[ "${vsc_env_keys[$i]}" == "$key" ]]; then
|
||||
if [[ "${vsc_env_values[$i]}" != "$value" ]]; then
|
||||
vsc_env_values[$i]="$value"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
vsc_env_keys+=("$key")
|
||||
vsc_env_values+=("$value")
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
}
|
||||
|
||||
__vsc_update_env() {
|
||||
if [[ ${#envVarsToReport[@]} -gt 0 ]]; then
|
||||
builtin printf '\e]633;EnvSingleStart;%s;%s\a' 0 $__vsc_nonce
|
||||
|
||||
if [ "$use_associative_array" = 1 ]; then
|
||||
if [ ${#vsc_aa_env[@]} -eq 0 ]; then
|
||||
# Associative array is empty, do not diff, just add
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [ -n "${!key+x}" ]; then
|
||||
local value="${!key}"
|
||||
vsc_aa_env["$key"]="$value"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Diff approach for associative array
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [ -n "${!key+x}" ]; then
|
||||
local value="${!key}"
|
||||
__updateEnvCacheAA "$key" "$value"
|
||||
fi
|
||||
done
|
||||
# Track missing env vars not needed for now, as we are only tracking pre-defined env var from terminalEnvironment.
|
||||
fi
|
||||
|
||||
else
|
||||
if [[ -z ${vsc_env_keys[@]} ]] && [[ -z ${vsc_env_values[@]} ]]; then
|
||||
# Non associative arrays are both empty, do not diff, just add
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [ -n "${!key+x}" ]; then
|
||||
local value="${!key}"
|
||||
vsc_env_keys+=("$key")
|
||||
vsc_env_values+=("$value")
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Diff approach for non-associative arrays
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [ -n "${!key+x}" ]; then
|
||||
local value="${!key}"
|
||||
__updateEnvCache "$key" "$value"
|
||||
fi
|
||||
done
|
||||
# Track missing env vars not needed for now, as we are only tracking pre-defined env var from terminalEnvironment.
|
||||
fi
|
||||
fi
|
||||
builtin printf '\e]633;EnvSingleEnd;%s;\a' $__vsc_nonce
|
||||
fi
|
||||
}
|
||||
|
||||
__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
|
||||
__vsc_update_cwd
|
||||
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=""
|
||||
# Report prompt is a work in progress, currently encoding is too slow
|
||||
if [ "$__vsc_stable" = "0" ]; then
|
||||
__vsc_report_prompt
|
||||
fi
|
||||
__vsc_first_prompt=1
|
||||
__vsc_update_prompt
|
||||
__vsc_update_env
|
||||
}
|
||||
|
||||
__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="$?"
|
||||
builtin local cmd
|
||||
__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
|
||||
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
|
||||
@@ -0,0 +1,16 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
if [[ -f $USER_ZDOTDIR/.zshenv ]]; then
|
||||
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
|
||||
# prevent recursion
|
||||
if [[ $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
|
||||
. $USER_ZDOTDIR/.zshenv
|
||||
fi
|
||||
|
||||
USER_ZDOTDIR=$ZDOTDIR
|
||||
ZDOTDIR=$VSCODE_ZDOTDIR
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
# Prevent recursive sourcing
|
||||
if [[ -n "$VSCODE_LOGIN_INITIALIZED" ]]; then
|
||||
return
|
||||
fi
|
||||
export VSCODE_LOGIN_INITIALIZED=1
|
||||
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
if [[ $options[norcs] = off && -o "login" && -f $ZDOTDIR/.zlogin ]]; then
|
||||
. $ZDOTDIR/.zlogin
|
||||
fi
|
||||
@@ -0,0 +1,25 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
# Prevent recursive sourcing
|
||||
if [[ -n "$VSCODE_PROFILE_INITIALIZED" ]]; then
|
||||
return
|
||||
fi
|
||||
export VSCODE_PROFILE_INITIALIZED=1
|
||||
|
||||
if [[ $options[norcs] = off && -o "login" ]]; then
|
||||
if [[ -f $USER_ZDOTDIR/.zprofile ]]; then
|
||||
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
. $USER_ZDOTDIR/.zprofile
|
||||
ZDOTDIR=$VSCODE_ZDOTDIR
|
||||
fi
|
||||
|
||||
# Apply any explicit path prefix (see #99878)
|
||||
if (( ${+VSCODE_PATH_PREFIX} )); then
|
||||
export PATH="$VSCODE_PATH_PREFIX$PATH"
|
||||
fi
|
||||
builtin unset VSCODE_PATH_PREFIX
|
||||
fi
|
||||
@@ -0,0 +1,321 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
builtin autoload -Uz add-zsh-hook is-at-least
|
||||
|
||||
# Prevent the script recursing when setting up
|
||||
if [ -n "$VSCODE_SHELL_INTEGRATION" ]; then
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
builtin return
|
||||
fi
|
||||
|
||||
# This variable allows the shell to both detect that VS Code's shell integration is enabled as well
|
||||
# as disable it by unsetting the variable.
|
||||
VSCODE_SHELL_INTEGRATION=1
|
||||
|
||||
# By default, zsh will set the $HISTFILE to the $ZDOTDIR location automatically. In the case of the
|
||||
# shell integration being injected, this means that the terminal will use a different history file
|
||||
# to other terminals. To fix this issue, set $HISTFILE back to the default location before ~/.zshrc
|
||||
# is called as that may depend upon the value.
|
||||
if [[ "$VSCODE_INJECTION" == "1" ]]; then
|
||||
HISTFILE=$USER_ZDOTDIR/.zsh_history
|
||||
fi
|
||||
|
||||
# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
|
||||
if [[ "$VSCODE_INJECTION" == "1" ]]; then
|
||||
if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
|
||||
VSCODE_ZDOTDIR=$ZDOTDIR
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
|
||||
. $USER_ZDOTDIR/.zshrc
|
||||
fi
|
||||
fi
|
||||
|
||||
__vsc_use_aa=0
|
||||
__vsc_env_keys=()
|
||||
__vsc_env_values=()
|
||||
|
||||
# Associative array are only available in zsh 4.3 or later
|
||||
if is-at-least 4.3; then
|
||||
__vsc_use_aa=1
|
||||
typeset -A vsc_aa_env
|
||||
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%%=*})"
|
||||
export $VARNAME="$(echo -e ${ITEM#*=})"
|
||||
done
|
||||
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%%=*})"
|
||||
export $VARNAME="$(echo -e ${ITEM#*=})${(P)VARNAME}"
|
||||
done
|
||||
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%%=*})"
|
||||
export $VARNAME="${(P)VARNAME}$(echo -e ${ITEM#*=})"
|
||||
done
|
||||
unset VSCODE_ENV_APPEND
|
||||
fi
|
||||
|
||||
# Register Python shell activate hooks
|
||||
# Prevent multiple activation with guard
|
||||
if [ -z "${VSCODE_PYTHON_AUTOACTIVATE_GUARD:-}" ]; then
|
||||
export VSCODE_PYTHON_AUTOACTIVATE_GUARD=1
|
||||
if [ -n "${VSCODE_PYTHON_ZSH_ACTIVATE:-}" ] && [ "$TERM_PROGRAM" = "vscode" ]; then
|
||||
# Prevent crashing by negating exit code
|
||||
if ! builtin eval "$VSCODE_PYTHON_ZSH_ACTIVATE"; then
|
||||
__vsc_activation_status=$?
|
||||
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python zsh activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Report prompt type
|
||||
if [ -n "${P9K_SSH:-}" ] || [ -n "${P9K_TTY:-}" ]; then
|
||||
builtin printf '\e]633;P;PromptType=p10k\a'
|
||||
# Force shell integration on for p10k
|
||||
# typeset -g POWERLEVEL9K_TERM_SHELL_INTEGRATION=true
|
||||
elif [ -n "${ZSH:-}" ] && [ -n "$ZSH_VERSION" ] && (( ${+functions[omz]} )); then
|
||||
builtin printf '\e]633;P;PromptType=oh-my-zsh\a'
|
||||
elif [ -n "${STARSHIP_SESSION_KEY:-}" ]; then
|
||||
builtin printf '\e]633;P;PromptType=starship\a'
|
||||
fi
|
||||
|
||||
# Shell integration was disabled by the shell, exit without warning assuming either the shell has
|
||||
# explicitly disabled shell integration as it's incompatible or it implements the protocol.
|
||||
if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then
|
||||
builtin return
|
||||
fi
|
||||
|
||||
# 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() {
|
||||
builtin emulate -L zsh
|
||||
|
||||
# Process text byte by byte, not by codepoint.
|
||||
builtin local LC_ALL=C str="$1" i byte token out='' val
|
||||
|
||||
for (( i = 0; i < ${#str}; ++i )); do
|
||||
# Escape backslashes, semi-colons specially, then special ASCII chars below space (0x20).
|
||||
byte="${str:$i:1}"
|
||||
val=$(printf "%d" "'$byte")
|
||||
if (( val < 31 )); then
|
||||
# For control characters, use hex encoding
|
||||
token=$(printf "\\\\x%02x" "'$byte")
|
||||
elif [ "$byte" = "\\" ]; then
|
||||
token="\\\\"
|
||||
elif [ "$byte" = ";" ]; then
|
||||
token="\\x3b"
|
||||
else
|
||||
token="$byte"
|
||||
fi
|
||||
|
||||
out+="$token"
|
||||
done
|
||||
|
||||
builtin print -r -- "$out"
|
||||
}
|
||||
|
||||
__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
|
||||
|
||||
__vscode_shell_env_reporting="${VSCODE_SHELL_ENV_REPORTING:-}"
|
||||
unset VSCODE_SHELL_ENV_REPORTING
|
||||
|
||||
envVarsToReport=()
|
||||
IFS=',' read -rA envVarsToReport <<< "$__vscode_shell_env_reporting"
|
||||
|
||||
builtin printf "\e]633;P;ContinuationPrompt=%s\a" "$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')"
|
||||
|
||||
# Report this shell supports rich command detection
|
||||
builtin printf '\e]633;P;HasRichCommandDetection=True\a'
|
||||
|
||||
__vsc_prompt_start() {
|
||||
builtin printf '\e]633;A\a'
|
||||
}
|
||||
|
||||
__vsc_prompt_end() {
|
||||
builtin printf '\e]633;B\a'
|
||||
}
|
||||
|
||||
__vsc_update_cwd() {
|
||||
builtin printf '\e]633;P;Cwd=%s\a' "$(__vsc_escape_value "${PWD}")"
|
||||
}
|
||||
|
||||
__update_env_cache_aa() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
if [ $__vsc_use_aa -eq 1 ]; then
|
||||
if [[ "${vsc_aa_env["$key"]}" != "$value" ]]; then
|
||||
vsc_aa_env["$key"]="$value"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
__update_env_cache() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
|
||||
for (( i=1; i <= $#__vsc_env_keys; i++ )); do
|
||||
if [[ "${__vsc_env_keys[$i]}" == "$key" ]]; then
|
||||
if [[ "${__vsc_env_values[$i]}" != "$value" ]]; then
|
||||
__vsc_env_values[$i]="$value"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# Key does not exist so add key, value pair
|
||||
__vsc_env_keys+=("$key")
|
||||
__vsc_env_values+=("$value")
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
}
|
||||
|
||||
__vsc_update_env() {
|
||||
if [[ ${#envVarsToReport[@]} -gt 0 ]]; then
|
||||
builtin printf '\e]633;EnvSingleStart;%s;%s;\a' 0 $__vsc_nonce
|
||||
if [ $__vsc_use_aa -eq 1 ]; then
|
||||
if [[ ${#vsc_aa_env[@]} -eq 0 ]]; then
|
||||
# Associative array is empty, do not diff, just add
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [[ -n "$key" && -n "${(P)key+_}" ]]; then
|
||||
vsc_aa_env["$key"]="${(P)key}"
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "${(P)key}")" "$__vsc_nonce"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Diff approach for associative array
|
||||
for var in "${envVarsToReport[@]}"; do
|
||||
if [[ -n "$var" && -n "${(P)var+_}" ]]; then
|
||||
value="${(P)var}"
|
||||
__update_env_cache_aa "$var" "$value"
|
||||
fi
|
||||
done
|
||||
# Track missing env vars not needed for now, as we are only tracking pre-defined env var from terminalEnvironment.
|
||||
fi
|
||||
else
|
||||
# Two arrays approach
|
||||
if [[ ${#__vsc_env_keys[@]} -eq 0 ]] && [[ ${#__vsc_env_values[@]} -eq 0 ]]; then
|
||||
# Non-associative arrays are both empty, do not diff, just add
|
||||
for key in "${envVarsToReport[@]}"; do
|
||||
if [[ -n "$key" && -n "${(P)key+_}" ]]; then
|
||||
value="${(P)key}"
|
||||
__vsc_env_keys+=("$key")
|
||||
__vsc_env_values+=("$value")
|
||||
builtin printf '\e]633;EnvSingleEntry;%s;%s;%s\a' "$key" "$(__vsc_escape_value "$value")" "$__vsc_nonce"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Diff approach for non-associative arrays
|
||||
for var in "${envVarsToReport[@]}"; do
|
||||
if [[ -n "$var" && -n "${(P)var+_}" ]]; then
|
||||
value="${(P)var}"
|
||||
__update_env_cache "$var" "$value"
|
||||
fi
|
||||
done
|
||||
# Track missing env vars not needed for now, as we are only tracking pre-defined env var from terminalEnvironment.
|
||||
fi
|
||||
fi
|
||||
|
||||
builtin printf '\e]633;EnvSingleEnd;%s;\a' $__vsc_nonce
|
||||
fi
|
||||
}
|
||||
|
||||
__vsc_command_output_start() {
|
||||
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_right_prompt_start() {
|
||||
builtin printf '\e]633;H\a'
|
||||
}
|
||||
|
||||
__vsc_right_prompt_end() {
|
||||
builtin printf '\e]633;I\a'
|
||||
}
|
||||
|
||||
__vsc_command_complete() {
|
||||
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
|
||||
}
|
||||
|
||||
if [[ -o NOUNSET ]]; then
|
||||
if [ -z "${RPROMPT-}" ]; then
|
||||
RPROMPT=""
|
||||
fi
|
||||
fi
|
||||
__vsc_update_prompt() {
|
||||
__vsc_prior_prompt="$PS1"
|
||||
__vsc_prior_prompt2="$PS2"
|
||||
__vsc_in_command_execution=""
|
||||
PS1="%{$(__vsc_prompt_start)%}$PS1%{$(__vsc_prompt_end)%}"
|
||||
PS2="%{$(__vsc_continuation_start)%}$PS2%{$(__vsc_continuation_end)%}"
|
||||
if [ -n "$RPROMPT" ]; then
|
||||
__vsc_prior_rprompt="$RPROMPT"
|
||||
RPROMPT="%{$(__vsc_right_prompt_start)%}$RPROMPT%{$(__vsc_right_prompt_end)%}"
|
||||
fi
|
||||
}
|
||||
|
||||
__vsc_precmd() {
|
||||
builtin local __vsc_status="$?"
|
||||
if [ -z "${__vsc_in_command_execution-}" ]; then
|
||||
# not in command execution
|
||||
__vsc_command_output_start
|
||||
fi
|
||||
|
||||
__vsc_command_complete "$__vsc_status"
|
||||
__vsc_current_command=""
|
||||
|
||||
# in command execution
|
||||
if [ -n "$__vsc_in_command_execution" ]; then
|
||||
# non null
|
||||
__vsc_update_prompt
|
||||
fi
|
||||
__vsc_update_env
|
||||
}
|
||||
|
||||
__vsc_preexec() {
|
||||
PS1="$__vsc_prior_prompt"
|
||||
PS2="$__vsc_prior_prompt2"
|
||||
if [ -n "$RPROMPT" ]; then
|
||||
RPROMPT="$__vsc_prior_rprompt"
|
||||
fi
|
||||
__vsc_in_command_execution="1"
|
||||
__vsc_current_command=$1
|
||||
__vsc_command_output_start
|
||||
}
|
||||
add-zsh-hook precmd __vsc_precmd
|
||||
add-zsh-hook preexec __vsc_preexec
|
||||
|
||||
if [[ $options[login] = off && $USER_ZDOTDIR != $VSCODE_ZDOTDIR ]]; then
|
||||
ZDOTDIR=$USER_ZDOTDIR
|
||||
fi
|
||||
@@ -0,0 +1,252 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Visual Studio Code terminal integration for fish
|
||||
#
|
||||
# Manual installation:
|
||||
#
|
||||
# (1) Add the following to the end of `$__fish_config_dir/config.fish`:
|
||||
#
|
||||
# string match -q "$TERM_PROGRAM" "vscode"
|
||||
# and . (code --locate-shell-integration-path fish)
|
||||
#
|
||||
# (2) Restart fish.
|
||||
|
||||
# Don't run in scripts, other terminals, or more than once per session.
|
||||
status is-interactive
|
||||
and string match --quiet "$TERM_PROGRAM" "vscode"
|
||||
and ! set --query VSCODE_SHELL_INTEGRATION
|
||||
or exit
|
||||
|
||||
set --global VSCODE_SHELL_INTEGRATION 1
|
||||
set --global __vscode_shell_env_reporting $VSCODE_SHELL_ENV_REPORTING
|
||||
set -e VSCODE_SHELL_ENV_REPORTING
|
||||
set -g envVarsToReport
|
||||
if test -n "$__vscode_shell_env_reporting"
|
||||
set envVarsToReport (string split "," "$__vscode_shell_env_reporting")
|
||||
end
|
||||
|
||||
# Apply any explicit path prefix (see #99878)
|
||||
# On fish, '$fish_user_paths' is always prepended to the PATH, for both login and non-login shells, so we need
|
||||
# to apply the path prefix fix always, not only for login shells (see #232291)
|
||||
if set -q VSCODE_PATH_PREFIX
|
||||
set -gx PATH "$VSCODE_PATH_PREFIX$PATH"
|
||||
end
|
||||
set -e VSCODE_PATH_PREFIX
|
||||
|
||||
set -g vsc_env_keys
|
||||
set -g vsc_env_values
|
||||
|
||||
# Tracks if the shell has been initialized, this prevents
|
||||
set -g vsc_initialized 0
|
||||
|
||||
set -g __vsc_applied_env_vars 0
|
||||
function __vsc_apply_env_vars
|
||||
if test $__vsc_applied_env_vars -eq 1;
|
||||
return
|
||||
end
|
||||
set -l __vsc_applied_env_vars 1
|
||||
# Apply EnvironmentVariableCollections if needed
|
||||
if test -n "$VSCODE_ENV_REPLACE"
|
||||
set ITEMS (string split : $VSCODE_ENV_REPLACE)
|
||||
for B in $ITEMS
|
||||
set split (string split -m1 = $B)
|
||||
set -gx "$split[1]" (echo -e "$split[2]")
|
||||
end
|
||||
set -e VSCODE_ENV_REPLACE
|
||||
end
|
||||
if test -n "$VSCODE_ENV_PREPEND"
|
||||
set ITEMS (string split : $VSCODE_ENV_PREPEND)
|
||||
for B in $ITEMS
|
||||
set split (string split -m1 = $B)
|
||||
set -gx "$split[1]" (echo -e "$split[2]")"$$split[1]" # avoid -p as it adds a space
|
||||
end
|
||||
set -e VSCODE_ENV_PREPEND
|
||||
end
|
||||
if test -n "$VSCODE_ENV_APPEND"
|
||||
set ITEMS (string split : $VSCODE_ENV_APPEND)
|
||||
for B in $ITEMS
|
||||
set split (string split -m1 = $B)
|
||||
set -gx "$split[1]" "$$split[1]"(echo -e "$split[2]") # avoid -a as it adds a space
|
||||
end
|
||||
set -e VSCODE_ENV_APPEND
|
||||
end
|
||||
end
|
||||
|
||||
# Register Python shell activate hooks
|
||||
# Prevent multiple activation with guard
|
||||
if not set -q VSCODE_PYTHON_AUTOACTIVATE_GUARD
|
||||
set -gx VSCODE_PYTHON_AUTOACTIVATE_GUARD 1
|
||||
if test -n "$VSCODE_PYTHON_FISH_ACTIVATE"; and test "$TERM_PROGRAM" = "vscode"
|
||||
# Fish does not crash on eval failure, so don't need negation.
|
||||
eval $VSCODE_PYTHON_FISH_ACTIVATE
|
||||
set __vsc_activation_status $status
|
||||
|
||||
if test $__vsc_activation_status -ne 0
|
||||
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python fish activation failed with exit code %d \x1b[0m \n' "$__vsc_activation_status"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Handle the shell integration nonce
|
||||
if set -q VSCODE_NONCE
|
||||
set -l __vsc_nonce $VSCODE_NONCE
|
||||
set -e VSCODE_NONCE
|
||||
end
|
||||
|
||||
# Helper function
|
||||
function __vsc_esc -d "Emit escape sequences for VS Code shell integration"
|
||||
builtin printf "\e]633;%s\a" (string join ";" -- $argv)
|
||||
end
|
||||
|
||||
# Sent right before executing an interactive command.
|
||||
# Marks the beginning of command output.
|
||||
function __vsc_cmd_executed --on-event fish_preexec
|
||||
__vsc_esc E (__vsc_escape_value "$argv") $__vsc_nonce
|
||||
__vsc_esc C
|
||||
|
||||
# Creates a marker to indicate a command was run.
|
||||
set --global _vsc_has_cmd
|
||||
end
|
||||
|
||||
|
||||
# Escape a value for use in the 'P' ("Property") or 'E' ("Command Line") sequences.
|
||||
# Backslashes are doubled and non-alphanumeric characters are hex encoded.
|
||||
function __vsc_escape_value
|
||||
# Escape backslashes and semi-colons
|
||||
echo $argv \
|
||||
| string replace --all '\\' '\\\\' \
|
||||
| string replace --all ';' '\\x3b' \
|
||||
;
|
||||
end
|
||||
|
||||
# Sent right after an interactive command has finished executing.
|
||||
# Marks the end of command output.
|
||||
function __vsc_cmd_finished --on-event fish_postexec
|
||||
__vsc_esc D $status
|
||||
end
|
||||
|
||||
# Sent when a command line is cleared or reset, but no command was run.
|
||||
# Marks the cleared line with neither success nor failure.
|
||||
function __vsc_cmd_clear --on-event fish_cancel
|
||||
if test $vsc_initialized -eq 0;
|
||||
return
|
||||
end
|
||||
__vsc_esc E "" $__vsc_nonce
|
||||
__vsc_esc C
|
||||
__vsc_esc D
|
||||
end
|
||||
|
||||
# Preserve the user's existing prompt, to wrap in our escape sequences.
|
||||
function __preserve_fish_prompt --on-event fish_prompt
|
||||
if functions --query fish_prompt
|
||||
if functions --query __vsc_fish_prompt
|
||||
# Erase the fallback so it can be set to the user's prompt
|
||||
functions --erase __vsc_fish_prompt
|
||||
end
|
||||
functions --copy fish_prompt __vsc_fish_prompt
|
||||
functions --erase __preserve_fish_prompt
|
||||
# Now __vsc_fish_prompt is guaranteed to be defined
|
||||
__init_vscode_shell_integration
|
||||
else
|
||||
if functions --query __vsc_fish_prompt
|
||||
functions --erase __preserve_fish_prompt
|
||||
__init_vscode_shell_integration
|
||||
else
|
||||
# There is no fish_prompt set, so stick with the default
|
||||
# Now __vsc_fish_prompt is guaranteed to be defined
|
||||
function __vsc_fish_prompt
|
||||
echo -n (whoami)@(prompt_hostname) (prompt_pwd) '~> '
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sent whenever a new fish prompt is about to be displayed.
|
||||
# Updates the current working directory.
|
||||
function __vsc_update_cwd --on-event fish_prompt
|
||||
__vsc_esc P Cwd=(__vsc_escape_value "$PWD")
|
||||
|
||||
# If a command marker exists, remove it.
|
||||
# Otherwise, the commandline is empty and no command was run.
|
||||
if set --query _vsc_has_cmd
|
||||
set --erase _vsc_has_cmd
|
||||
else
|
||||
__vsc_cmd_clear
|
||||
end
|
||||
end
|
||||
|
||||
if test -n "$__vscode_shell_env_reporting"
|
||||
function __vsc_update_env --on-event fish_prompt
|
||||
if test (count $envVarsToReport) -gt 0
|
||||
__vsc_esc EnvSingleStart 1
|
||||
|
||||
for key in $envVarsToReport
|
||||
if set -q $key
|
||||
set -l value $$key
|
||||
__vsc_esc EnvSingleEntry $key (__vsc_escape_value "$value")
|
||||
end
|
||||
end
|
||||
|
||||
__vsc_esc EnvSingleEnd
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sent at the start of the prompt.
|
||||
# Marks the beginning of the prompt (and, implicitly, a new line).
|
||||
function __vsc_fish_prompt_start
|
||||
# Applying environment variables is deferred to after config.fish has been
|
||||
# evaluated
|
||||
__vsc_apply_env_vars
|
||||
__vsc_esc A
|
||||
set -g vsc_initialized 1
|
||||
end
|
||||
|
||||
# Sent at the end of the prompt.
|
||||
# Marks the beginning of the user's command input.
|
||||
function __vsc_fish_cmd_start
|
||||
__vsc_esc B
|
||||
end
|
||||
|
||||
function __vsc_fish_has_mode_prompt -d "Returns true if fish_mode_prompt is defined and not empty"
|
||||
functions fish_mode_prompt | string match -rvq '^ *(#|function |end$|$)'
|
||||
end
|
||||
|
||||
# Preserve and wrap fish_mode_prompt (which appears to the left of the regular
|
||||
# prompt), but only if it's not defined as an empty function (which is the
|
||||
# officially documented way to disable that feature).
|
||||
function __init_vscode_shell_integration
|
||||
if __vsc_fish_has_mode_prompt
|
||||
functions --copy fish_mode_prompt __vsc_fish_mode_prompt
|
||||
|
||||
function fish_mode_prompt
|
||||
__vsc_fish_prompt_start
|
||||
__vsc_fish_mode_prompt
|
||||
end
|
||||
|
||||
function fish_prompt
|
||||
__vsc_fish_prompt
|
||||
__vsc_fish_cmd_start
|
||||
end
|
||||
else
|
||||
# No fish_mode_prompt, so put everything in fish_prompt.
|
||||
function fish_prompt
|
||||
__vsc_fish_prompt_start
|
||||
__vsc_fish_prompt
|
||||
__vsc_fish_cmd_start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Report prompt type
|
||||
if set -q POSH_SESSION_ID
|
||||
__vsc_esc P PromptType=oh-my-posh
|
||||
end
|
||||
|
||||
# Report this shell supports rich command detection
|
||||
__vsc_esc P HasRichCommandDetection=True
|
||||
|
||||
__preserve_fish_prompt
|
||||
@@ -0,0 +1,262 @@
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
# Prevent installing more than once per session
|
||||
if ((Test-Path variable:global:__VSCodeState) -and $null -ne $Global:__VSCodeState.OriginalPrompt) {
|
||||
return;
|
||||
}
|
||||
|
||||
# Disable shell integration when the language mode is restricted
|
||||
if ($ExecutionContext.SessionState.LanguageMode -ne "FullLanguage") {
|
||||
return;
|
||||
}
|
||||
|
||||
$Global:__VSCodeState = @{
|
||||
OriginalPrompt = $function:Prompt
|
||||
LastHistoryId = -1
|
||||
IsInExecution = $false
|
||||
EnvVarsToReport = @()
|
||||
Nonce = $null
|
||||
IsStable = $null
|
||||
IsA11yMode = $null
|
||||
IsWindows10 = $false
|
||||
}
|
||||
|
||||
# Store the nonce in a regular variable and unset the environment variable. It's by design that
|
||||
# anything that can execute PowerShell code can read the nonce, as it's basically impossible to hide
|
||||
# in PowerShell. The most important thing is getting it out of the environment.
|
||||
$Global:__VSCodeState.Nonce = $env:VSCODE_NONCE
|
||||
$env:VSCODE_NONCE = $null
|
||||
|
||||
$Global:__VSCodeState.IsStable = $env:VSCODE_STABLE
|
||||
$env:VSCODE_STABLE = $null
|
||||
|
||||
$Global:__VSCodeState.IsA11yMode = $env:VSCODE_A11Y_MODE
|
||||
$env:VSCODE_A11Y_MODE = $null
|
||||
|
||||
$__vscode_shell_env_reporting = $env:VSCODE_SHELL_ENV_REPORTING
|
||||
$env:VSCODE_SHELL_ENV_REPORTING = $null
|
||||
if ($__vscode_shell_env_reporting) {
|
||||
$Global:__VSCodeState.EnvVarsToReport = $__vscode_shell_env_reporting.Split(',')
|
||||
}
|
||||
Remove-Variable -Name __vscode_shell_env_reporting -ErrorAction SilentlyContinue
|
||||
|
||||
$osVersion = [System.Environment]::OSVersion.Version
|
||||
$Global:__VSCodeState.IsWindows10 = $IsWindows -and $osVersion.Major -eq 10 -and $osVersion.Minor -eq 0 -and $osVersion.Build -lt 22000
|
||||
Remove-Variable -Name osVersion -ErrorAction SilentlyContinue
|
||||
|
||||
if ($env:VSCODE_ENV_REPLACE) {
|
||||
$Split = $env:VSCODE_ENV_REPLACE.Split(":")
|
||||
foreach ($Item in $Split) {
|
||||
$Inner = $Item.Split('=', 2)
|
||||
[Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':'))
|
||||
}
|
||||
$env:VSCODE_ENV_REPLACE = $null
|
||||
}
|
||||
if ($env:VSCODE_ENV_PREPEND) {
|
||||
$Split = $env:VSCODE_ENV_PREPEND.Split(":")
|
||||
foreach ($Item in $Split) {
|
||||
$Inner = $Item.Split('=', 2)
|
||||
[Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':') + [Environment]::GetEnvironmentVariable($Inner[0]))
|
||||
}
|
||||
$env:VSCODE_ENV_PREPEND = $null
|
||||
}
|
||||
if ($env:VSCODE_ENV_APPEND) {
|
||||
$Split = $env:VSCODE_ENV_APPEND.Split(":")
|
||||
foreach ($Item in $Split) {
|
||||
$Inner = $Item.Split('=', 2)
|
||||
[Environment]::SetEnvironmentVariable($Inner[0], [Environment]::GetEnvironmentVariable($Inner[0]) + $Inner[1].Replace('\x3a', ':'))
|
||||
}
|
||||
$env:VSCODE_ENV_APPEND = $null
|
||||
}
|
||||
|
||||
# Register Python shell activate hooks
|
||||
# Prevent multiple activation with guard
|
||||
if (-not $env:VSCODE_PYTHON_AUTOACTIVATE_GUARD) {
|
||||
$env:VSCODE_PYTHON_AUTOACTIVATE_GUARD = '1'
|
||||
if ($env:VSCODE_PYTHON_PWSH_ACTIVATE -and $env:TERM_PROGRAM -eq 'vscode') {
|
||||
$activateScript = $env:VSCODE_PYTHON_PWSH_ACTIVATE
|
||||
Remove-Item Env:VSCODE_PYTHON_PWSH_ACTIVATE
|
||||
|
||||
try {
|
||||
Invoke-Expression $activateScript
|
||||
$Global:__VSCodeState.OriginalPrompt = $function:Prompt
|
||||
}
|
||||
catch {
|
||||
$activationError = $_
|
||||
Write-Host "`e[0m`e[7m * `e[0;103m VS Code Python powershell activation failed with exit code $($activationError.Exception.Message) `e[0m"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Global:__VSCode-Escape-Value([string]$value) {
|
||||
# NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
|
||||
# Replace any non-alphanumeric characters.
|
||||
[regex]::Replace($value, "[$([char]0x00)-$([char]0x1f)\\\n;]", { param($match)
|
||||
# Encode the (ascii) matches as `\x<hex>`
|
||||
-Join (
|
||||
[System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function Global:Prompt() {
|
||||
$FakeCode = [int]!$global:?
|
||||
# NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
|
||||
# error when $LastHistoryEntry is null, and is not otherwise useful.
|
||||
Set-StrictMode -Off
|
||||
$LastHistoryEntry = Get-History -Count 1
|
||||
$Result = ""
|
||||
# Skip finishing the command if the first command has not yet started or an execution has not
|
||||
# yet begun
|
||||
if ($Global:__VSCodeState.LastHistoryId -ne -1 -and ($Global:__VSCodeState.HasPSReadLine -eq $false -or $Global:__VSCodeState.IsInExecution -eq $true)) {
|
||||
$Global:__VSCodeState.IsInExecution = $false
|
||||
if ($LastHistoryEntry.Id -eq $Global:__VSCodeState.LastHistoryId) {
|
||||
# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
|
||||
$Result += "$([char]0x1b)]633;D`a"
|
||||
}
|
||||
else {
|
||||
# Command finished exit code
|
||||
# OSC 633 ; D [; <ExitCode>] ST
|
||||
$Result += "$([char]0x1b)]633;D;$FakeCode`a"
|
||||
}
|
||||
}
|
||||
# Prompt started
|
||||
# OSC 633 ; A ST
|
||||
$Result += "$([char]0x1b)]633;A`a"
|
||||
# Current working directory
|
||||
# OSC 633 ; <Property>=<Value> ST
|
||||
$Result += if ($pwd.Provider.Name -eq 'FileSystem') { "$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a" }
|
||||
|
||||
# Send current environment variables as JSON
|
||||
# OSC 633 ; EnvJson ; <Environment> ; <Nonce>
|
||||
if ($Global:__VSCodeState.EnvVarsToReport.Count -gt 0) {
|
||||
$envMap = @{}
|
||||
foreach ($varName in $Global:__VSCodeState.EnvVarsToReport) {
|
||||
if (Test-Path "env:$varName") {
|
||||
$envMap[$varName] = (Get-Item "env:$varName").Value
|
||||
}
|
||||
}
|
||||
$envJson = $envMap | ConvertTo-Json -Compress
|
||||
$Result += "$([char]0x1b)]633;EnvJson;$(__VSCode-Escape-Value $envJson);$($Global:__VSCodeState.Nonce)`a"
|
||||
}
|
||||
|
||||
# Before running the original prompt, put $? back to what it was:
|
||||
if ($FakeCode -ne 0) {
|
||||
Write-Error "failure" -ea ignore
|
||||
}
|
||||
# Run the original prompt
|
||||
$OriginalPrompt += $Global:__VSCodeState.OriginalPrompt.Invoke()
|
||||
$Result += $OriginalPrompt
|
||||
|
||||
# Prompt
|
||||
# OSC 633 ; <Property>=<Value> ST
|
||||
if ($Global:__VSCodeState.IsStable -eq "0") {
|
||||
$Result += "$([char]0x1b)]633;P;Prompt=$(__VSCode-Escape-Value $OriginalPrompt)`a"
|
||||
}
|
||||
|
||||
# Write command started
|
||||
$Result += "$([char]0x1b)]633;B`a"
|
||||
$Global:__VSCodeState.LastHistoryId = $LastHistoryEntry.Id
|
||||
return $Result
|
||||
}
|
||||
|
||||
# Report prompt type
|
||||
if ($env:STARSHIP_SESSION_KEY) {
|
||||
[Console]::Write("$([char]0x1b)]633;P;PromptType=starship`a")
|
||||
}
|
||||
elseif ($env:POSH_SESSION_ID) {
|
||||
[Console]::Write("$([char]0x1b)]633;P;PromptType=oh-my-posh`a")
|
||||
}
|
||||
elseif ((Test-Path variable:global:GitPromptSettings) -and $Global:GitPromptSettings) {
|
||||
[Console]::Write("$([char]0x1b)]633;P;PromptType=posh-git`a")
|
||||
}
|
||||
|
||||
if ($Global:__VSCodeState.IsA11yMode -eq "1") {
|
||||
if (-not (Get-Module -Name PSReadLine)) {
|
||||
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$specialPsrlPath = Join-Path $scriptRoot 'psreadline'
|
||||
Import-Module $specialPsrlPath
|
||||
if (Get-Module -Name PSReadLine) {
|
||||
Set-PSReadLineOption -EnableScreenReaderMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Only send the command executed sequence when PSReadLine is loaded, if not shell integration should
|
||||
# still work thanks to the command line sequence
|
||||
$Global:__VSCodeState.HasPSReadLine = $false
|
||||
if (Get-Module -Name PSReadLine) {
|
||||
$Global:__VSCodeState.HasPSReadLine = $true
|
||||
[Console]::Write("$([char]0x1b)]633;P;HasRichCommandDetection=True`a")
|
||||
|
||||
$Global:__VSCodeState.OriginalPSConsoleHostReadLine = $function:PSConsoleHostReadLine
|
||||
function Global:PSConsoleHostReadLine {
|
||||
$CommandLine = $Global:__VSCodeState.OriginalPSConsoleHostReadLine.Invoke()
|
||||
$Global:__VSCodeState.IsInExecution = $true
|
||||
|
||||
# Command line
|
||||
# OSC 633 ; E [; <CommandLine> [; <Nonce>]] ST
|
||||
$Result = "$([char]0x1b)]633;E;"
|
||||
$Result += $(__VSCode-Escape-Value $CommandLine)
|
||||
# Only send the nonce if the OS is not Windows 10 as it seems to echo to the terminal
|
||||
# sometimes
|
||||
if ($Global:__VSCodeState.IsWindows10 -eq $false) {
|
||||
$Result += ";$($Global:__VSCodeState.Nonce)"
|
||||
}
|
||||
$Result += "`a"
|
||||
|
||||
# Command executed
|
||||
# OSC 633 ; C ST
|
||||
$Result += "$([char]0x1b)]633;C`a"
|
||||
|
||||
# Write command executed sequence directly to Console to avoid the new line from Write-Host
|
||||
[Console]::Write($Result)
|
||||
|
||||
$CommandLine
|
||||
}
|
||||
|
||||
# Set ContinuationPrompt property
|
||||
$Global:__VSCodeState.ContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt
|
||||
if ($Global:__VSCodeState.ContinuationPrompt) {
|
||||
[Console]::Write("$([char]0x1b)]633;P;ContinuationPrompt=$(__VSCode-Escape-Value $Global:__VSCodeState.ContinuationPrompt)`a")
|
||||
}
|
||||
}
|
||||
|
||||
# Set IsWindows property
|
||||
if ($PSVersionTable.PSVersion -lt "6.0") {
|
||||
# Windows PowerShell is only available on Windows
|
||||
[Console]::Write("$([char]0x1b)]633;P;IsWindows=$true`a")
|
||||
}
|
||||
else {
|
||||
[Console]::Write("$([char]0x1b)]633;P;IsWindows=$IsWindows`a")
|
||||
}
|
||||
|
||||
# Set always on key handlers which map to default VS Code keybindings
|
||||
function Set-MappedKeyHandler {
|
||||
param ([string[]] $Chord, [string[]]$Sequence)
|
||||
try {
|
||||
$Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
|
||||
}
|
||||
catch [System.Management.Automation.ParameterBindingException] {
|
||||
# PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
|
||||
# so we check what's bound and filter it.
|
||||
$Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
|
||||
}
|
||||
if ($Handler) {
|
||||
Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
|
||||
}
|
||||
}
|
||||
|
||||
function Set-MappedKeyHandlers {
|
||||
Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
|
||||
Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
|
||||
Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
|
||||
Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
|
||||
}
|
||||
|
||||
if ($Global:__VSCodeState.HasPSReadLine) {
|
||||
Set-MappedKeyHandlers
|
||||
}
|
||||
Reference in New Issue
Block a user