config: Respect shell quotations in split_name_and_args

This commit is contained in:
Lukas Wirth
2025-09-29 11:56:42 +02:00
committed by _WD_
parent 4449a3feaa
commit 1414af41a8
6 changed files with 30 additions and 5 deletions

View File

@@ -33,6 +33,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
configuration options which are set.
[#7774](https://github.com/jj-vcs/jj/issues/7774)
* Setting the editor via `ui.editor`, `$EDITOR`, or `JJ_EDITOR` now respects shell quoting.
>>>>>>> Conflict 1 of 1 ends
## [0.37.0] - 2026-01-07
### Release highlights

1
Cargo.lock generated
View File

@@ -2391,6 +2391,7 @@ dependencies = [
"scm-record",
"serde",
"serde_json",
"shlex",
"slab",
"strsim",
"tempfile",

View File

@@ -94,6 +94,7 @@ sapling-streampager = "0.11.2"
scm-record = "0.9.0"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0.148"
shlex = "1.3.0"
slab = "0.4.11"
smallvec = { version = "1.15.1", features = [
"const_generics",

View File

@@ -95,6 +95,7 @@ sapling-streampager = { workspace = true }
scm-record = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
shlex = { workspace = true }
slab = { workspace = true }
strsim = { workspace = true }
tempfile = { workspace = true }

View File

@@ -790,9 +790,18 @@ impl CommandNameAndArgs {
pub fn split_name_and_args(&self) -> (Cow<'_, str>, Cow<'_, [String]>) {
match self {
Self::String(s) => {
// Handle things like `EDITOR=emacs -nw` (TODO: parse shell escapes)
if s.contains('"') || s.contains('\'') {
let mut parts = shlex::Shlex::new(s);
let res = (
parts.next().unwrap_or_default().into(),
parts.by_ref().collect(),
);
if !parts.had_error {
return res;
}
}
let mut args = s.split(' ').map(|s| s.to_owned());
(args.next().unwrap().into(), args.collect())
(args.next().unwrap_or_default().into(), args.collect())
}
Self::Vec(NonEmptyCommandArgsVec(a)) => (Cow::Borrowed(&a[0]), Cow::Borrowed(&a[1..])),
Self::Structured {
@@ -1020,6 +1029,7 @@ mod tests {
empty_string = ''
array = ['emacs', '-nw']
string = 'emacs -nw'
string_quoted = '\"spaced path/to/emacs\" -nw'
structured.env = { KEY1 = 'value1', KEY2 = 'value2' }
structured.command = ['emacs', '-nw']
"},
@@ -1055,6 +1065,15 @@ mod tests {
assert_eq!(name, "emacs");
assert_eq!(args, ["-nw"].as_ref());
let command_args: CommandNameAndArgs = config.get("string_quoted").unwrap();
assert_eq!(
command_args,
CommandNameAndArgs::String("\"spaced path/to/emacs\" -nw".to_owned())
);
let (name, args) = command_args.split_name_and_args();
assert_eq!(name, "spaced path/to/emacs");
assert_eq!(args, ["-nw"].as_ref());
let command_args: CommandNameAndArgs = config.get("structured").unwrap();
assert_eq!(
command_args,

View File

@@ -24,19 +24,19 @@ pub use self::test_environment::TestWorkDir;
pub fn fake_bisector_path() -> String {
let path = assert_cmd::cargo::cargo_bin!("fake-bisector");
assert!(path.is_file());
path.as_os_str().to_str().unwrap().to_owned()
path.as_os_str().to_str().unwrap().replace("\\", "\\\\")
}
pub fn fake_editor_path() -> String {
let path = assert_cmd::cargo::cargo_bin!("fake-editor");
assert!(path.is_file());
path.as_os_str().to_str().unwrap().to_owned()
path.as_os_str().to_str().unwrap().replace("\\", "\\\\")
}
pub fn fake_diff_editor_path() -> String {
let path = assert_cmd::cargo::cargo_bin!("fake-diff-editor");
assert!(path.is_file());
path.as_os_str().to_str().unwrap().to_owned()
path.as_os_str().to_str().unwrap().replace("\\", "\\\\")
}
/// Forcibly enable interactive prompt.