Skip to content

Commit 186d00a

Browse files
committed
config: Respect shell quotations in split_name_and_args
1 parent 589ed83 commit 186d00a

File tree

6 files changed

+30
-4
lines changed

6 files changed

+30
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7979
is set in `.gitconfig` (but jj still isn't able to fetch the submodules
8080
or to operate on them).
8181

82+
* Setting the editor via `ui.editor`, `$EDITOR`, or `JJ_EDITOR` now respects shell quoting.
83+
8284
## [0.33.0] - 2025-09-03
8385

8486
### Release highlights

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ sapling-streampager = "0.11.0"
9494
scm-record = "0.8.0"
9595
serde = { version = "1.0", features = ["derive", "rc"] }
9696
serde_json = "1.0.145"
97+
shlex = "1.3.0"
9798
slab = "0.4.11"
9899
smallvec = { version = "1.15.1", features = [
99100
"const_generics",

cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ sapling-streampager = { workspace = true }
9090
scm-record = { workspace = true }
9191
serde = { workspace = true }
9292
serde_json = { workspace = true }
93+
shlex = { workspace = true }
9394
slab = { workspace = true }
9495
strsim = { workspace = true }
9596
tempfile = { workspace = true }

cli/src/config.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,17 @@ impl CommandNameAndArgs {
825825
pub fn split_name_and_args(&self) -> (Cow<'_, str>, Cow<'_, [String]>) {
826826
match self {
827827
Self::String(s) => {
828-
// Handle things like `EDITOR=emacs -nw` (TODO: parse shell escapes)
829-
let mut args = s.split(' ').map(|s| s.to_owned());
830-
(args.next().unwrap().into(), args.collect())
828+
let mut parts = shlex::Shlex::new(s);
829+
let res = (
830+
parts.next().unwrap_or_default().into(),
831+
parts.by_ref().collect(),
832+
);
833+
if parts.had_error {
834+
let mut args = s.split(' ').map(|s| s.to_owned());
835+
(args.next().unwrap_or_default().into(), args.collect())
836+
} else {
837+
res
838+
}
831839
}
832840
Self::Vec(NonEmptyCommandArgsVec(a)) => (Cow::Borrowed(&a[0]), Cow::Borrowed(&a[1..])),
833841
Self::Structured {
@@ -1055,6 +1063,7 @@ mod tests {
10551063
empty_string = ''
10561064
array = ['emacs', '-nw']
10571065
string = 'emacs -nw'
1066+
string_quoted = '\"spaced path/to/emacs\" -nw'
10581067
structured.env = { KEY1 = 'value1', KEY2 = 'value2' }
10591068
structured.command = ['emacs', '-nw']
10601069
"},
@@ -1090,6 +1099,15 @@ mod tests {
10901099
assert_eq!(name, "emacs");
10911100
assert_eq!(args, ["-nw"].as_ref());
10921101

1102+
let command_args: CommandNameAndArgs = config.get("string_quoted").unwrap();
1103+
assert_eq!(
1104+
command_args,
1105+
CommandNameAndArgs::String("\"spaced path/to/emacs\" -nw".to_owned())
1106+
);
1107+
let (name, args) = command_args.split_name_and_args();
1108+
assert_eq!(name, "spaced path/to/emacs");
1109+
assert_eq!(args, ["-nw"].as_ref());
1110+
10931111
let command_args: CommandNameAndArgs = config.get("structured").unwrap();
10941112
assert_eq!(
10951113
command_args,

cli/tests/common/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ pub fn fake_bisector_path() -> String {
3030
pub fn fake_editor_path() -> String {
3131
let path = assert_cmd::cargo::cargo_bin("fake-editor");
3232
assert!(path.is_file());
33-
path.into_os_string().into_string().unwrap()
33+
path.into_os_string()
34+
.into_string()
35+
.unwrap()
36+
.replace("\\", "\\\\")
3437
}
3538

3639
pub fn fake_diff_editor_path() -> String {

0 commit comments

Comments
 (0)