DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/Environment/Terminal/
ShellIntegration.rs1use std::path::{Path, PathBuf};
28
29use tauri::{AppHandle, Manager};
30
31use crate::dev_log;
32
33pub struct Injection {
35 pub EnvVars:Vec<(String, String)>,
37 pub PrependArgs:Vec<String>,
39 pub AppendArgs:Vec<String>,
41}
42
43fn ScriptPath(AppHandle:&AppHandle, Name:&str) -> Option<PathBuf> {
45 let Base = AppHandle.path().resource_dir().ok()?;
46
47 let Candidate = Base.join("scripts/shell-integration").join(Name);
48
49 if Candidate.exists() {
50 Some(Candidate)
51 } else {
52 dev_log!(
53 "terminal",
54 "[ShellIntegration] script not found at {} (bundled .app only)",
55 Candidate.display()
56 );
57 None
58 }
59}
60
61fn ShellName(ShellPath:&str) -> &str { Path::new(ShellPath).file_name().and_then(|N| N.to_str()).unwrap_or("") }
63
64pub fn Compute(AppHandle:&AppHandle, ShellPath:&str) -> Option<Injection> {
68 if std::env::var("LAND_SHELL_INTEGRATION").as_deref() == Ok("0") {
69 dev_log!("terminal", "[ShellIntegration] disabled via LAND_SHELL_INTEGRATION=0");
70 return None;
71 }
72
73 let Shell = ShellName(ShellPath);
74 dev_log!("terminal", "[ShellIntegration] shell={} path={}", Shell, ShellPath);
75
76 match Shell {
77 "bash" => {
78 let Script = ScriptPath(AppHandle, "bash.sh")?;
79 dev_log!("terminal", "[ShellIntegration] bash: --init-file {}", Script.display());
80 Some(Injection {
81 EnvVars:vec![("VSCODE_SHELL_INTEGRATION".into(), "1".into())],
82 PrependArgs:Vec::new(),
83 AppendArgs:vec!["--init-file".into(), Script.to_string_lossy().into_owned()],
84 })
85 },
86
87 "zsh" => {
88 let Script = ScriptPath(AppHandle, "zsh.zsh")?;
89 dev_log!(
90 "terminal",
91 "[ShellIntegration] zsh: ZDOTDIR injection script={}",
92 Script.display()
93 );
94
95 let TmpDir = std::env::temp_dir().join(format!("land-zsh-integration-{}", std::process::id()));
99
100 if std::fs::create_dir_all(&TmpDir).is_err() {
101 return None;
102 }
103
104 let OrigZdotDir = std::env::var("ZDOTDIR").unwrap_or_else(|_| std::env::var("HOME").unwrap_or_default());
105
106 let ZshRcContent = format!(
108 "export LAND_ORIG_ZDOTDIR=\"{}\"\nexport LAND_SHELL_INTEGRATION_ACTIVE=1\nsource \"{}\"\n",
109 OrigZdotDir.replace('"', "\\\""),
110 Script.to_string_lossy().replace('"', "\\\""),
111 );
112
113 let ZshRcPath = TmpDir.join(".zshrc");
114
115 if std::fs::write(&ZshRcPath, ZshRcContent).is_err() {
116 return None;
117 }
118
119 Some(Injection {
120 EnvVars:vec![
121 ("ZDOTDIR".into(), TmpDir.to_string_lossy().into_owned()),
122 ("VSCODE_SHELL_INTEGRATION".into(), "1".into()),
123 ],
124 PrependArgs:Vec::new(),
125 AppendArgs:Vec::new(),
126 })
127 },
128
129 "fish" => {
130 let Script = ScriptPath(AppHandle, "fish.fish")?;
131 dev_log!(
132 "terminal",
133 "[ShellIntegration] fish: --init-command source {}",
134 Script.display()
135 );
136 Some(Injection {
137 EnvVars:vec![("VSCODE_SHELL_INTEGRATION".into(), "1".into())],
138 PrependArgs:Vec::new(),
139 AppendArgs:vec![
140 "--init-command".into(),
141 format!("source \"{}\"", Script.to_string_lossy().replace('"', "\\\"")),
142 ],
143 })
144 },
145
146 Other => {
147 dev_log!("terminal", "[ShellIntegration] unsupported shell '{}' - no injection", Other);
148 None
149 },
150 }
151}