diff --git a/completions/pengwin-setup b/completions/pengwin-setup index 3cb17a33..8011fcf8 100644 --- a/completions/pengwin-setup +++ b/completions/pengwin-setup @@ -42,7 +42,7 @@ function _pengwin_setup() { # By convention, the function name mapfile -t COMPREPLY < <(compgen -W 'EXPLORER COLORTOOL LANGUAGE SHELLS' -- "${cur}") ;; SHELLS) - mapfile -t COMPREPLY < <(compgen -W 'BASH-RL CSH FISH ZSH' -- "${cur}") + mapfile -t COMPREPLY < <(compgen -W 'BASH-RL CSH FISH SHELLINT ZSH' -- "${cur}") ;; TOOLS) mapfile -t COMPREPLY < <(compgen -W 'ANSIBLE CLOUDCLI DOCKER FZF HOMEBREW POWERSHELL' -- "${cur}") @@ -51,7 +51,7 @@ function _pengwin_setup() { # By convention, the function name mapfile -t COMPREPLY < <(compgen -W 'AWS AZURE DO IBM KUBERNETES OPENSTACK TERRAFORM' -- "${cur}") ;; UNINSTALL) - mapfile -t COMPREPLY < <(compgen -W 'ANSIBLE AWS AZURE BASH-RL C++ CASSANDRA COLORTOOL COPILOT-CLI COPILOT-VIM DIGITALOCEAN DOCKER DOTNET FCITX FISH GO GUILIB HIDPI HOMEBREW IBM IBUS JAVA JETBRAINS KEYCHAIN KUBERNETES LAMP MSEDIT NIM NODEJS OPENSTACK POETRY POWERSHELL PYENV RCLOCAL RUBY RUST STARTMENU SSH SYNAPTIC SYSTEMD TERRAFORM VCXSRV VSCODE WINTHEME WSLTTY X410 XFCE' -- "${cur}") + mapfile -t COMPREPLY < <(compgen -W 'ANSIBLE AWS AZURE BASH-RL C++ CASSANDRA COLORTOOL COPILOT-CLI COPILOT-VIM DIGITALOCEAN DOCKER DOTNET FCITX FISH GO GUILIB HIDPI HOMEBREW IBM IBUS JAVA JETBRAINS KEYCHAIN KUBERNETES LAMP MSEDIT NIM NODEJS OPENSTACK POETRY POWERSHELL PYENV RCLOCAL RUBY RUST SHELLINT STARTMENU SSH SYNAPTIC SYSTEMD TERRAFORM VCXSRV VSCODE WINTHEME WSLTTY X410 XFCE' -- "${cur}") ;; *) mapfile -t COMPREPLY < <(compgen -W "--debug -d --verbose -v -y --yes --assume-yes --noupdate --norebuildicons -q \ diff --git a/pengwin-setup.d/shell-integration.sh b/pengwin-setup.d/shell-integration.sh new file mode 100755 index 00000000..2a9a7fd2 --- /dev/null +++ b/pengwin-setup.d/shell-integration.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# shellcheck source=common.sh +source "$(dirname "$0")/common.sh" "$@" + +#Imported from common.sh +declare HOME + +readonly PENGWIN_SHELL_INTEGRATION_MARKER='### PENGWIN WINDOWS TERMINAL SHELL INTEGRATION' +readonly SHELL_INTEGRATION_DIR='/usr/local/share/pengwin' +readonly SHELL_INTEGRATION_SCRIPT="${SHELL_INTEGRATION_DIR}/wt-shell-integration.sh" + +####################################### +# Install Windows Terminal shell integration +# Installs script to /usr/local/share/pengwin and adds source line to ~/.bashrc +# Globals: +# HOME +# PENGWIN_SHELL_INTEGRATION_MARKER +# SHELL_INTEGRATION_DIR +# SHELL_INTEGRATION_SCRIPT +# Arguments: +# None +####################################### +function install_shell_integration() { + echo "Installing Windows Terminal shell integration" + + local bashrc="${HOME}/.bashrc" + + if [[ ! -f "${bashrc}" ]]; then + touch "${bashrc}" + fi + + # Check if already installed + if grep -q "${PENGWIN_SHELL_INTEGRATION_MARKER}" "${bashrc}" 2>/dev/null; then + echo "Previous Pengwin Windows Terminal shell integration detected. Cancelling install..." + message --title "Warning!" --msgbox "Previous install of Windows Terminal shell integration detected. To reinstall, please run the uninstaller first or manually edit \"${bashrc}\" and remove the lines between:\n${PENGWIN_SHELL_INTEGRATION_MARKER}" 10 95 + return 1 + fi + + # Create the directory if it doesn't exist + sudo mkdir -p "${SHELL_INTEGRATION_DIR}" + + # Install shell integration script + # Based on: https://learn.microsoft.com/en-us/windows/terminal/tutorials/shell-integration + sudo tee "${SHELL_INTEGRATION_SCRIPT}" >/dev/null <<'SCRIPT_EOF' +#!/bin/bash +# Windows Terminal shell integration +# See: https://learn.microsoft.com/en-us/windows/terminal/tutorials/shell-integration +# Also supports WezTerm which implements the same OSC sequences + +# Only run for bash in Windows Terminal or WezTerm +if [[ -z "${BASH_VERSION}" ]]; then + return 0 +fi + +if [[ "${TERM_PROGRAM}" == "WezTerm" || -n "${WT_SESSION}" ]]; then + __wt_osc() { printf '\e]%s\e\\' "$1"; } + + __wt_mark_prompt_start() { __wt_osc '133;A'; } + __wt_mark_command_start() { __wt_osc '133;B'; } + __wt_mark_command_executed() { __wt_osc '133;C'; } + __wt_mark_command_finished() { __wt_osc "133;D;$1"; } + __wt_set_cwd() { __wt_osc "9;9;${PWD}"; } + + __wt_update_prompt() { + local last_exit_code="$?" + __wt_mark_command_finished "${last_exit_code}" + __wt_set_cwd + __wt_mark_prompt_start + PS1="\[\$(__wt_mark_command_start)\]${__wt_original_ps1}" + return "${last_exit_code}" + } + + if [[ -z "${__wt_original_ps1}" ]]; then + __wt_original_ps1="${PS1}" + PROMPT_COMMAND="__wt_update_prompt${PROMPT_COMMAND:+;}${PROMPT_COMMAND}" + PS1="\[\$(__wt_mark_command_start)\]${PS1}" + # PS0 is executed after reading a command but before executing it + PS0='\[$(__wt_mark_command_executed)\]' + fi +fi +SCRIPT_EOF + + # Add minimal source line to .bashrc (since PS1 modifications need to happen after .bashrc sets PS1) + cat >>"${bashrc}" </dev/null; then + inclusive_file_clean "${bashrc}" "${PENGWIN_SHELL_INTEGRATION_MARKER}" + echo "Source line removed from ${bashrc}" + else + echo "... shell integration not found in ${bashrc}!" + fi + else + echo "... ${bashrc} not found!" + fi + + # Remove the shell integration script + echo "Removing ${SHELL_INTEGRATION_SCRIPT}" + sudo_rem_file "${SHELL_INTEGRATION_SCRIPT}" + + echo "Windows Terminal shell integration uninstalled successfully." +} + +if show_warning "Windows Terminal shell integration" "$@"; then + main "$@" +fi diff --git a/tests/run_tests.sh b/tests/run_tests.sh index a45dbbb6..a45336e0 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -28,9 +28,11 @@ if [ -z "${CIRCLE_NODE_TOTAL}" ]; then run_test ./motd_settings.sh run_test ./kubernetes.sh run_test ./latex.sh + run_test ./shell-integration.sh elif [[ ${CIRCLE_NODE_INDEX} == $((i++)) ]]; then #0 run_test ./nodejs_nvm.sh run_test ./motd_settings.sh + run_test ./shell-integration.sh elif [[ ${CIRCLE_NODE_INDEX} == $((i++)) ]]; then #1 run_test ./desktop.sh run_test ./lamp.sh "BUILTIN" diff --git a/tests/shell-integration.sh b/tests/shell-integration.sh new file mode 100755 index 00000000..47c82d4b --- /dev/null +++ b/tests/shell-integration.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +source commons.sh + +declare TEST_USER + +readonly PENGWIN_SHELL_INTEGRATION_MARKER='### PENGWIN WINDOWS TERMINAL SHELL INTEGRATION' +readonly SHELL_INTEGRATION_SCRIPT='/usr/local/share/pengwin/wt-shell-integration.sh' + +function test_main() { + run_pengwinsetup autoinstall SETTINGS SHELLS SHELLINT + + # Check that the source line was added to .bashrc + local bashrc="/home/${TEST_USER}/.bashrc" + + test -f "${bashrc}" + assertEquals ".bashrc file should exist" "0" "$?" + + grep -q "${PENGWIN_SHELL_INTEGRATION_MARKER}" "${bashrc}" + assertEquals "Shell integration marker should be present in .bashrc" "0" "$?" + + grep -q "wt-shell-integration.sh" "${bashrc}" + assertEquals "Source line for shell integration script should be present in .bashrc" "0" "$?" + + # Check that the script was installed to /usr/local/share/pengwin + test -f "${SHELL_INTEGRATION_SCRIPT}" + assertEquals "Shell integration script should exist" "0" "$?" + + grep -q "__wt_update_prompt" "${SHELL_INTEGRATION_SCRIPT}" + assertEquals "Shell integration function should be present in script" "0" "$?" + + grep -q "PS0=" "${SHELL_INTEGRATION_SCRIPT}" + assertEquals "PS0 should be set for command executed mark" "0" "$?" + + grep -q "WT_SESSION" "${SHELL_INTEGRATION_SCRIPT}" + assertEquals "WT_SESSION check should be present in script" "0" "$?" +} + +function test_uninstall() { + # First ensure it's installed + run_pengwinsetup autoinstall SETTINGS SHELLS SHELLINT + + # Then uninstall + run_pengwinsetup autoinstall UNINSTALL SHELLINT + + local bashrc="/home/${TEST_USER}/.bashrc" + + # Verify the source line was removed from .bashrc + if [[ -f "${bashrc}" ]]; then + grep -q "${PENGWIN_SHELL_INTEGRATION_MARKER}" "${bashrc}" + assertNotEquals "Shell integration marker should NOT be present after uninstall" "0" "$?" + fi + + # Verify the script was removed + test -f "${SHELL_INTEGRATION_SCRIPT}" + assertNotEquals "Shell integration script should NOT exist after uninstall" "0" "$?" +} + +# shellcheck disable=SC1091 +source shunit2