Skip to content

Commit 4fede65

Browse files
Merge pull request #1007 from Wind010/BetterWindowsSupport
Better Windows support
2 parents ae0c475 + a14d512 commit 4fede65

7 files changed

Lines changed: 84 additions & 53 deletions

File tree

docs/examples/configuration/config-example.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ finder:
3232

3333
shell:
3434
# Shell used for shell out. Possible values: bash, zsh, dash, ...
35-
# For Windows, use `cmd.exe` instead.
35+
# For Windows, use `pwsh.exe`, `powershell.exe` or `cmd.exe` instead.
36+
# Default behavior is to check if pwsh.exe exists and use it, otherwise fallback to powershell.exe and then cmd.exe.
3637
command: bash
37-
38+
show_command: true # whether to print the command before executing it
39+
command_print_color: green # color for the printed command. possible values: https://bit.ly/3gloNNI
40+
3841
# finder_command: bash # similar, but for fzf's internals

src/commands/core/actor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ pub fn act(
253253
cmd.spawn()
254254
.map_err(|e| ShellSpawnError::new(&interpolated_snippet[..], e))?
255255
.wait()
256-
.context("bash was not running")?;
256+
.context("Shell was not running")?;
257257
}
258258
},
259259
};

src/common/clipboard.rs

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,42 @@ use crate::common::shell::{self, ShellSpawnError, EOF};
22
use crate::prelude::*;
33

44
pub fn copy(text: String) -> Result<()> {
5-
let cmd = r#"
6-
exst() {
7-
type "$1" &>/dev/null
8-
}
9-
10-
_copy() {
11-
if exst pbcopy; then
12-
pbcopy
13-
elif exst xclip; then
14-
xclip -selection clipboard
15-
elif exst clip.exe; then
16-
clip.exe
17-
else
18-
exit 55
19-
fi
20-
}"#;
21-
22-
shell::out()
23-
.arg(
24-
format!(
25-
r#"{cmd}
26-
read -r -d '' x <<'{EOF}'
5+
let shell_cmd = CONFIG.shell().to_lowercase();
6+
if shell_cmd.contains("powershell") || shell_cmd.contains("cmd.exe") {
7+
// Use Windows native clipboard
8+
let mut cmd = std::process::Command::new("cmd.exe");
9+
cmd.arg("/C").arg("clip.exe");
10+
let mut child = cmd.stdin(std::process::Stdio::piped()).spawn()
11+
.map_err(|e| ShellSpawnError::new("clip.exe", e))?;
12+
if let Some(stdin) = child.stdin.as_mut() {
13+
use std::io::Write;
14+
stdin.write_all(text.as_bytes())?;
15+
}
16+
child.wait()?;
17+
Ok(())
18+
} else {
19+
// Use bash/zsh/fish/etc logic
20+
let script = format!(
21+
r#"exst() {{ type "$1" &>/dev/null; }}
22+
_copy() {{
23+
if exst pbcopy; then pbcopy
24+
elif exst xclip; then xclip -selection clipboard
25+
elif exst clip.exe; then clip.exe
26+
else exit 55; fi
27+
}}
28+
read -r -d '' x <<"{EOF}"
2729
{text}
2830
{EOF}
29-
30-
echo -n "$x" | _copy"#,
31-
)
32-
.as_str(),
33-
)
34-
.spawn()
35-
.map_err(|e| ShellSpawnError::new(cmd, e))?
36-
.wait()?;
37-
38-
Ok(())
31+
echo -n "$x" | _copy
32+
"#,
33+
EOF = EOF,
34+
text = text
35+
);
36+
shell::out()
37+
.arg(script.as_str())
38+
.spawn()
39+
.map_err(|e| ShellSpawnError::new(script.as_str(), e))?
40+
.wait()?;
41+
Ok(())
42+
}
3943
}

src/config/yaml.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub struct Search {
7676
#[serde(default)]
7777
pub struct Shell {
7878
pub command: String,
79-
pub finder_command: Option<String>,
79+
pub finder_command: Option<String>
8080
}
8181

8282
#[derive(Deserialize, Debug)]
@@ -182,11 +182,39 @@ impl Default for Finder {
182182
}
183183
}
184184

185+
#[cfg(target_family = "windows")]
186+
fn command_exists(cmd: &str) -> bool {
187+
std::process::Command::new("where")
188+
.arg(cmd)
189+
.output()
190+
.map(|output| output.status.success())
191+
.unwrap_or(false)
192+
}
193+
185194
impl Default for Shell {
186195
fn default() -> Self {
187-
Self {
188-
command: "bash".to_string(),
189-
finder_command: None,
196+
#[cfg(target_family = "windows")]
197+
{
198+
// Check for pwsh.exe first, then powershell.exe, then fallback to cmd.exe
199+
let command = if command_exists("pwsh.exe") {
200+
"pwsh.exe"
201+
} else if command_exists("powershell.exe") {
202+
"powershell.exe"
203+
} else {
204+
"cmd.exe"
205+
};
206+
207+
Self {
208+
command: command.to_string(),
209+
finder_command: None,
210+
}
211+
}
212+
#[cfg(not(target_family = "windows"))]
213+
{
214+
Self {
215+
command: "bash".to_string(),
216+
finder_command: None,
217+
}
190218
}
191219
}
192220
}

src/filesystem.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,13 @@ mod tests {
261261

262262
#[test]
263263
fn splitting_of_dirs_param_may_not_contain_empty_items() {
264-
// Trailing colon indicates potential extra path. Split returns an empty item for it. This empty item should be filtered away, which is what this test checks.
265-
let given_path_config = "SOME_PATH:ANOTHER_PATH:";
264+
// Trailing separator indicates potential extra path. Split returns an empty item for it. This empty item should be filtered away, which is what this test checks.
265+
let given_path_config = format!("SOME_PATH{sep}ANOTHER_PATH{sep}", sep = JOIN_SEPARATOR);
266266

267-
let found_paths = paths_from_path_param(given_path_config);
267+
let found_paths: Vec<&str> = paths_from_path_param(&given_path_config).collect();
268+
let expected_paths = vec!["SOME_PATH", "ANOTHER_PATH"];
268269

269-
let mut expected_paths = vec!["SOME_PATH", "ANOTHER_PATH"].into_iter();
270-
271-
for found in found_paths {
272-
let expected = expected_paths.next().unwrap();
273-
assert_eq!(found, expected)
274-
}
270+
assert_eq!(found_paths, expected_paths);
275271
}
276272

277273
#[test]

src/parser.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,9 @@ fn parse_variable_line(line: &str) -> Result<(&str, &str, Option<FinderOpts>)> {
106106
}
107107

108108
fn without_prefix(line: &str) -> String {
109-
if line.len() > 2 {
110-
String::from(line[2..].trim())
111-
} else {
112-
String::from("")
113-
}
109+
let trimmed = line.trim_start();
110+
let without_prefix: String = trimmed.chars().skip(2).collect();
111+
without_prefix.trim().to_string()
114112
}
115113

116114
#[derive(Clone, Default)]

tests/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ finder:
1616
shell:
1717
finder_command: bash
1818
command: env BASH_ENV="${NAVI_HOME}/tests/helpers.sh" bash --norc --noprofile
19+
show_command: true
20+
command_print_color: green

0 commit comments

Comments
 (0)