Skip to main content

vs_shell/
activate.rs

1//! Activation script rendering for supported shells.
2
3use crate::ShellError;
4
5/// Supported interactive shells.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ShellKind {
8    /// POSIX bash shell.
9    Bash,
10    /// Z shell.
11    Zsh,
12    /// Fish shell.
13    Fish,
14    /// Nushell.
15    Nushell,
16    /// PowerShell.
17    Pwsh,
18    /// Clink on Windows CMD.
19    Clink,
20}
21
22impl ShellKind {
23    /// Parses a shell kind from a CLI string.
24    pub fn parse(input: &str) -> Result<Self, ShellError> {
25        match input {
26            "bash" => Ok(Self::Bash),
27            "zsh" => Ok(Self::Zsh),
28            "fish" => Ok(Self::Fish),
29            "nushell" => Ok(Self::Nushell),
30            "pwsh" | "powershell" => Ok(Self::Pwsh),
31            "clink" => Ok(Self::Clink),
32            _ => Err(ShellError::UnknownShell(input.to_string())),
33        }
34    }
35}
36
37/// Renders the activation script for a shell.
38///
39/// The activation script only sets up the hook plumbing:
40///   1. `VS_SESSION_ID` — identifies this shell session.
41///   2. Hook function definition + registration.
42///   3. Exit trap for session cleanup.
43///   4. Initial hook call.
44///
45/// All environment variable management (`__VS_ORIG_PATH`, `PATH`,
46/// plugin vars, `__VS_VARS`, `__VS_STATE_HASH`) is handled by
47/// `vs __hook-env` so that the logic lives in one place.
48pub fn render_activation(shell: ShellKind) -> String {
49    match shell {
50        ShellKind::Bash => String::from(
51            r#"export VS_SESSION_ID="$$"
52vs_activate() {
53  local previous_exit_status=$?
54  trap -- '' SIGINT
55  eval "$(vs __hook-env bash)"
56  trap - SIGINT
57  return $previous_exit_status
58}
59if ! [[ "${PROMPT_COMMAND[*]:-}" =~ vs_activate ]]; then
60  PROMPT_COMMAND="vs_activate${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
61fi
62trap 'vs __cleanup-session 2>/dev/null' EXIT
63vs_activate
64"#,
65        ),
66        ShellKind::Zsh => String::from(
67            r#"export VS_SESSION_ID="$$"
68vs_activate() {
69  trap -- '' SIGINT
70  eval "$(vs __hook-env zsh)"
71  trap - SIGINT
72}
73typeset -ag precmd_functions
74if [[ -z "${precmd_functions[(r)vs_activate]+1}" ]]; then
75  precmd_functions=(vs_activate ${precmd_functions[@]})
76fi
77typeset -ag chpwd_functions
78if [[ -z "${chpwd_functions[(r)vs_activate]+1}" ]]; then
79  chpwd_functions=(vs_activate ${chpwd_functions[@]})
80fi
81trap 'vs __cleanup-session 2>/dev/null' EXIT
82vs_activate
83"#,
84        ),
85        ShellKind::Fish => String::from(
86            r#"set -gx VS_SESSION_ID $fish_pid
87function __vs_activate --on-event fish_prompt
88    eval (vs __hook-env fish)
89end
90function __vs_cleanup --on-event fish_exit
91    vs __cleanup-session 2>/dev/null
92end
93"#,
94        ),
95        ShellKind::Nushell => String::from(
96            r#"$env.VS_SESSION_ID = $"($nu.pid)"
97def --env __vs_activate [] {
98  vs __hook-env nushell | lines | each {|line|
99    let payload = ($line | from json)
100    if (($payload | columns | any {|name| $name == "__VS_UNSET"})) {
101      hide-env $payload.__VS_UNSET
102    } else {
103      load-env $payload
104    }
105  }
106}
107do -i { ^vs __cleanup-stale-sessions }
108__vs_activate
109"#,
110        ),
111        ShellKind::Pwsh => String::from(
112            r#"$env:VS_SESSION_ID = $PID.ToString()
113function global:Invoke-VsActivate {
114  Invoke-Expression (& vs __hook-env pwsh)
115}
116if (-not $env:__VS_INITIALIZED) {
117  $env:__VS_INITIALIZED = '1'
118  $global:__vs_original_prompt = $function:prompt
119  function global:prompt {
120    Invoke-VsActivate
121    & $global:__vs_original_prompt
122  }
123  Register-EngineEvent PowerShell.Exiting -Action {
124    & vs __cleanup-session 2>$null
125  }
126}
127Invoke-VsActivate
128"#,
129        ),
130        ShellKind::Clink => String::from(
131            r#"set VS_SESSION_ID=%VS_SESSION_ID%
132if "%VS_SESSION_ID%"=="" set VS_SESSION_ID=%RANDOM%
133vs __cleanup-stale-sessions >nul 2>nul
134for /f "delims=" %%i in ('vs __hook-env clink') do %%i
135"#,
136        ),
137    }
138}