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