Skip to content

Commit 851cf22

Browse files
zoritleandytomdavidkna
authored
feat: Add support for cygwin/msys2/git-bash evironment (starship#2020)
* feat: Add support for cygwin/msys2/git-bash evironment * Update src/init/mod.rs Co-authored-by: David Knaack <[email protected]> Co-authored-by: Thomas O'Donnell <[email protected]> Co-authored-by: David Knaack <[email protected]>
1 parent f03c3f1 commit 851cf22

File tree

1 file changed

+83
-31
lines changed

1 file changed

+83
-31
lines changed

src/init/mod.rs

+83-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::ffi::OsStr;
2-
use std::path::Path;
2+
use std::path::{Path, PathBuf};
33
use std::{env, io};
44

55
/* We use a two-phase init here: the first phase gives a simple command to the
@@ -17,12 +17,62 @@ using whatever mechanism is available in the host shell--this two-phase solution
1717
has been developed as a compatibility measure with `eval $(starship init X)`
1818
*/
1919

20-
fn path_to_starship() -> io::Result<String> {
21-
let current_exe = env::current_exe()?
22-
.to_str()
23-
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "can't convert to str"))?
24-
.to_string();
25-
Ok(current_exe)
20+
struct StarshipPath {
21+
native_path: PathBuf,
22+
}
23+
impl StarshipPath {
24+
fn init() -> io::Result<Self> {
25+
Ok(Self {
26+
native_path: env::current_exe()?,
27+
})
28+
}
29+
fn str_path(&self) -> io::Result<&str> {
30+
let current_exe = self
31+
.native_path
32+
.to_str()
33+
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "can't convert to str"))?;
34+
Ok(current_exe)
35+
}
36+
fn sprint(&self) -> io::Result<String> {
37+
self.str_path().map(|s| s.replace("\"", "\"'\"'\""))
38+
}
39+
fn sprint_posix(&self) -> io::Result<String> {
40+
// On non-Windows platform, return directly.
41+
if cfg!(not(target_os = "windows")) {
42+
return self.sprint();
43+
}
44+
let str_path = self.str_path()?;
45+
let res = std::process::Command::new("cygpath.exe")
46+
.arg(str_path)
47+
.output();
48+
let output = match res {
49+
Ok(output) => output,
50+
Err(e) => {
51+
if e.kind() != io::ErrorKind::NotFound {
52+
log::warn!("Failed to convert \"{}\" to unix path:\n{:?}", str_path, e);
53+
}
54+
// Failed to execute cygpath.exe means there're not inside cygwin evironment,return directly.
55+
return self.sprint();
56+
}
57+
};
58+
let res = String::from_utf8(output.stdout);
59+
let posix_path = match res {
60+
Ok(ref cygpath_path) if output.status.success() => cygpath_path.trim(),
61+
Ok(_) => {
62+
log::warn!(
63+
"Failed to convert \"{}\" to unix path:\n{}",
64+
str_path,
65+
String::from_utf8_lossy(&output.stderr),
66+
);
67+
str_path
68+
}
69+
Err(e) => {
70+
log::warn!("Failed to convert \"{}\" to unix path:\n{}", str_path, e);
71+
str_path
72+
}
73+
};
74+
Ok(posix_path.replace("\"", "\"'\"'\""))
75+
}
2676
}
2777

2878
/* This prints the setup stub, the short piece of code which sets up the main
@@ -33,7 +83,7 @@ pub fn init_stub(shell_name: &str) -> io::Result<()> {
3383

3484
let shell_basename = Path::new(shell_name).file_stem().and_then(OsStr::to_str);
3585

36-
let starship = path_to_starship()?.replace("\"", "\"'\"'\"");
86+
let starship = StarshipPath::init()?;
3787

3888
let setup_stub = match shell_basename {
3989
Some("bash") => {
@@ -72,25 +122,28 @@ pub fn init_stub(shell_name: &str) -> io::Result<()> {
72122
format!(
73123
r#"if [ "${{BASH_VERSINFO[0]}}" -gt 4 ] || ([ "${{BASH_VERSINFO[0]}}" -eq 4 ] && [ "${{BASH_VERSINFO[1]}}" -ge 1 ])
74124
then
75-
source <("{}" init bash --print-full-init)
125+
source <("{0}" init bash --print-full-init)
76126
else
77-
source /dev/stdin <<<"$("{}" init bash --print-full-init)"
127+
source /dev/stdin <<<"$("{0}" init bash --print-full-init)"
78128
fi"#,
79-
starship, starship
129+
starship.sprint_posix()?
80130
)
81131
};
82132

83133
Some(script)
84134
}
85135
Some("zsh") => {
86-
let script = format!("source <(\"{}\" init zsh --print-full-init)", starship);
136+
let script = format!(
137+
"source <(\"{}\" init zsh --print-full-init)",
138+
starship.sprint_posix()?
139+
);
87140
Some(script)
88141
}
89142
Some("fish") => {
90143
// Fish does process substitution with pipes and psub instead of bash syntax
91144
let script = format!(
92145
"source (\"{}\" init fish --print-full-init | psub)",
93-
starship
146+
starship.sprint_posix()?
94147
);
95148
Some(script)
96149
}
@@ -105,12 +158,12 @@ fi"#,
105158
// Powershell escapes with ` instead of \ thus `n translates to a newline.
106159
let script = format!(
107160
"Invoke-Expression (@(&\"{}\" init powershell --print-full-init) -join \"`n\")",
108-
starship
161+
starship.sprint()?
109162
);
110163
Some(script)
111164
}
112165
Some("ion") => {
113-
let script = format!("eval $({} init ion --print-full-init)", starship);
166+
let script = format!("eval $({} init ion --print-full-init)", starship.sprint()?);
114167
Some(script)
115168
}
116169
None => {
@@ -143,32 +196,31 @@ fi"#,
143196
/* This function (called when `--print-full-init` is passed to `starship init`)
144197
prints out the main initialization script */
145198
pub fn init_main(shell_name: &str) -> io::Result<()> {
146-
let starship_path = path_to_starship()?.replace("\"", "\"'\"'\"");
147-
148-
let setup_script = match shell_name {
149-
"bash" => Some(BASH_INIT),
150-
"zsh" => Some(ZSH_INIT),
151-
"fish" => Some(FISH_INIT),
152-
"powershell" => Some(PWSH_INIT),
153-
"ion" => Some(ION_INIT),
199+
let starship_path = StarshipPath::init()?;
200+
201+
match shell_name {
202+
"bash" => print_script(BASH_INIT, &starship_path.sprint_posix()?),
203+
"zsh" => print_script(ZSH_INIT, &starship_path.sprint_posix()?),
204+
"fish" => print_script(FISH_INIT, &starship_path.sprint_posix()?),
205+
"powershell" => print_script(PWSH_INIT, &starship_path.sprint()?),
206+
"ion" => print_script(ION_INIT, &starship_path.sprint()?),
154207
_ => {
155208
println!(
156209
"printf \"Shell name detection failed on phase two init.\\n\
157210
This probably indicates a bug within starship: please open\\n\
158211
an issue at https://github.com/starship/starship/issues/new\\n\""
159212
);
160-
None
161213
}
162-
};
163-
if let Some(script) = setup_script {
164-
// Set up quoting for starship path in case it has spaces.
165-
let starship_path_string = format!("\"{}\"", starship_path);
166-
let script = script.replace("::STARSHIP::", &starship_path_string);
167-
print!("{}", script);
168-
};
214+
}
169215
Ok(())
170216
}
171217

218+
fn print_script(script: &str, path: &str) {
219+
let starship_path_string = format!("\"{}\"", path);
220+
let script = script.replace("::STARSHIP::", &starship_path_string);
221+
print!("{}", script);
222+
}
223+
172224
/* GENERAL INIT SCRIPT NOTES
173225
174226
Each init script will be passed as-is. Global notes for init scripts are in this

0 commit comments

Comments
 (0)