ok/jj
1
0
Fork 0
forked from mirrors/jj

Allow \0 escape for nulls

This allows safely getting e.g. multiple descriptions, and knowing where the
boundaries are
This commit is contained in:
Zachary Dremann 2023-09-13 13:50:12 -04:00
parent fedd289a13
commit 2177dc0657
6 changed files with 74 additions and 9 deletions

View file

@ -20,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj op log` now supports `--no-graph`.
* Templates now support an additional escape: `\0`. This will output a literal
null byte. This may be useful for e.g.
`jj log -T 'description ++ "\0"' --no-graph` to output descriptions only, but
be able to tell where the boundaries are
### Fixed bugs
## [0.9.0] - 2023-09-06

View file

@ -19,7 +19,7 @@
whitespace = _{ " " | "\t" | "\r" | "\n" | "\x0c" }
escape = @{ "\\" ~ ("t" | "r" | "n" | "\"" | "\\") }
escape = @{ "\\" ~ ("t" | "r" | "n" | "0" | "\"" | "\\") }
literal_char = @{ !("\"" | "\\") ~ ANY }
raw_literal = @{ literal_char+ }
literal = { "\"" ~ (raw_literal | escape)* ~ "\"" }

View file

@ -256,12 +256,13 @@ fn parse_string_literal(pair: Pair<Rule>) -> String {
Rule::raw_literal => {
result.push_str(part.as_str());
}
Rule::escape => match part.as_str().as_bytes()[1] as char {
'"' => result.push('"'),
'\\' => result.push('\\'),
't' => result.push('\t'),
'r' => result.push('\r'),
'n' => result.push('\n'),
Rule::escape => match &part.as_str()[1..] {
"\"" => result.push('"'),
"\\" => result.push('\\'),
"t" => result.push('\t'),
"r" => result.push('\r'),
"n" => result.push('\n'),
"0" => result.push('\0'),
char => panic!("invalid escape: \\{char:?}"),
},
_ => panic!("unexpected part of string: {part:?}"),
@ -963,8 +964,8 @@ mod tests {
fn test_string_literal() {
// "\<char>" escapes
assert_eq!(
parse_into_kind(r#" "\t\r\n\"\\" "#),
Ok(ExpressionKind::String("\t\r\n\"\\".to_owned())),
parse_into_kind(r#" "\t\r\n\"\\\0" "#),
Ok(ExpressionKind::String("\t\r\n\"\\\0".to_owned())),
);
// Invalid "\<char>" escape

View file

@ -314,6 +314,42 @@ fn test_log_with_or_without_diff() {
"###);
}
#[test]
fn test_log_null_terminate_multiline_descriptions() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_success(
&repo_path,
&["commit", "-m", "commit 1 line 1", "-m", "commit 1 line 2"],
);
test_env.jj_cmd_success(
&repo_path,
&["commit", "-m", "commit 2 line 1", "-m", "commit 2 line 2"],
);
test_env.jj_cmd_success(
&repo_path,
&["describe", "-m", "commit 3 line 1", "-m", "commit 3 line 2"],
);
let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"log",
"-r",
"~root()",
"-T",
r#"description ++ "\0""#,
"--no-graph",
],
);
insta::assert_debug_snapshot!(
stdout,
@r###""commit 3 line 1\n\ncommit 3 line 2\n\0commit 2 line 1\n\ncommit 2 line 2\n\0commit 1 line 1\n\ncommit 1 line 2\n\0""###
)
}
#[test]
fn test_log_shortest_accessors() {
let test_env = TestEnvironment::default();

View file

@ -146,6 +146,27 @@ fn test_op_log_no_graph() {
"###);
}
#[test]
fn test_op_log_no_graph_null_terminated() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_success(&repo_path, &["commit", "-m", "message1"]);
test_env.jj_cmd_success(&repo_path, &["commit", "-m", "message2"]);
let stdout = test_env.jj_cmd_success(
&repo_path,
&[
"op",
"log",
"--no-graph",
"--template",
r#"id.short(4) ++ "\0""#,
],
);
insta::assert_debug_snapshot!(stdout, @r###""c8b0\07277\019b8\0f1c4\0""###);
}
#[test]
fn test_op_log_template() {
let test_env = TestEnvironment::default();

View file

@ -279,6 +279,8 @@ fn test_templater_list_method() {
insta::assert_snapshot!(render(r#""".lines().join("|")"#), @"");
insta::assert_snapshot!(render(r#""a\nb\nc".lines().join("|")"#), @"a|b|c");
// Null separator
insta::assert_snapshot!(render(r#""a\nb\nc".lines().join("\0")"#), @"a\0b\0c");
// Keyword as separator
insta::assert_snapshot!(render(r#""a\nb\nc".lines().join(commit_id.short(2))"#), @"a00b00c");