Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: .file path args do not need double backslash on Windows #1053

Merged
merged 1 commit into from
Dec 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: .file path args do not need double backslash on Windows
  • Loading branch information
sigoden committed Dec 12, 2024
commit 1adbeb07e813bbb7a802eb16786c97b850504961
132 changes: 105 additions & 27 deletions src/repl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ use reedline::{
use reedline::{MenuBuilder, Signal};
use std::{env, process};

lazy_static::lazy_static! {
static ref SPLIT_FILES_TEXT_ARGS_RE: Regex =
Regex::new(r"(?m) (-- |--\n|--\r\n|--\r|--$)").unwrap();
}

const MENU_NAME: &str = "completion_menu";

lazy_static::lazy_static! {
Expand Down Expand Up @@ -406,8 +401,7 @@ impl Repl {
},
".file" => match args {
Some(args) => {
let (files, text) = split_files_text(args);
let files = shell_words::split(files).with_context(|| "Invalid args")?;
let (files, text) = split_files_text(args, cfg!(windows));
let input = Input::from_files_with_spinner(
&self.config,
text,
Expand Down Expand Up @@ -708,19 +702,72 @@ fn split_args(args: Option<&str>) -> Option<(&str, Option<&str>)> {
})
}

fn split_files_text(args: &str) -> (&str, &str) {
match SPLIT_FILES_TEXT_ARGS_RE.find(args).ok().flatten() {
Some(mat) => {
let files = &args[0..mat.start()];
let text = if mat.end() < args.len() {
&args[mat.end()..]
} else {
""
};
(files, text)
fn split_files_text(line: &str, is_win: bool) -> (Vec<String>, &str) {
let mut words = Vec::new();
let mut word = String::new();
let mut unbalance: Option<char> = None;
let mut prev_char: Option<char> = None;
let mut text_starts_at = None;
let unquote_word = |word: &str| {
if ((word.starts_with('"') && word.ends_with('"'))
|| (word.starts_with('\'') && word.ends_with('\'')))
&& word.len() >= 2
{
word[1..word.len() - 1].to_string()
} else {
word.to_string()
}
};

for (i, char) in line.chars().enumerate() {
match unbalance {
Some(ub_char) if ub_char == char => {
word.push(char);
unbalance = None;
}
Some(_) => {
word.push(char);
}
None => match char {
' ' | '\t' | '\r' | '\n' => {
if let Some('\\') = prev_char.filter(|_| !is_win) {
word.push(char);
} else if !word.is_empty() {
if word == "--" {
word.clear();
text_starts_at = Some(i);
break;
}
words.push(unquote_word(&word));
word.clear();
}
}
'\'' | '"' => {
word.push(char);
unbalance = Some(char);
}
'\\' => {
if is_win || prev_char.map(|c| c == '\\').unwrap_or_default() {
word.push(char);
}
}
_ => {
word.push(char);
}
},
}
None => (args, ""),
prev_char = Some(char);
}

if !word.is_empty() && word != "--" {
words.push(unquote_word(&word));
}
let text = match text_starts_at {
Some(start) => line[start..].trim(),
None => "",
};

(words, text)
}

#[cfg(test)]
Expand Down Expand Up @@ -748,20 +795,51 @@ mod tests {

#[test]
fn test_split_files_text() {
assert_eq!(split_files_text("file.txt"), ("file.txt", ""));
assert_eq!(split_files_text("file.txt --"), ("file.txt", ""));
assert_eq!(split_files_text("file.txt -- hello"), ("file.txt", "hello"));
assert_eq!(
split_files_text("file.txt --\nhello"),
("file.txt", "hello")
split_files_text("file.txt", false),
(vec!["file.txt".into()], "")
);
assert_eq!(
split_files_text("file.txt --", false),
(vec!["file.txt".into()], "")
);
assert_eq!(
split_files_text("file.txt -- hello", false),
(vec!["file.txt".into()], "hello")
);
assert_eq!(
split_files_text("file.txt --\nhello", false),
(vec!["file.txt".into()], "hello")
);
assert_eq!(
split_files_text("file.txt --\r\nhello", false),
(vec!["file.txt".into()], "hello")
);
assert_eq!(
split_files_text("file.txt --\rhello", false),
(vec!["file.txt".into()], "hello")
);
assert_eq!(
split_files_text("file.txt --\r\nhello"),
("file.txt", "hello")
split_files_text(r#"file1.txt 'file2.txt' "file3.txt""#, false),
(
vec!["file1.txt".into(), "file2.txt".into(), "file3.txt".into()],
""
)
);
assert_eq!(
split_files_text(r#"./file1.txt 'file1 - Copy.txt' file\ 2.txt"#, false),
(
vec![
"./file1.txt".into(),
"file1 - Copy.txt".into(),
"file 2.txt".into()
],
""
)
);
assert_eq!(
split_files_text("file.txt --\rhello"),
("file.txt", "hello")
split_files_text(r#".\file.txt C:\dir\file.txt"#, true),
(vec![".\\file.txt".into(), "C:\\dir\\file.txt".into()], "")
);
}
}
Loading