Skip to content

Commit acbc771

Browse files
committed
print: centralize string literal printing, and improve escaping/multi-line support.
1 parent 7d6bae2 commit acbc771

File tree

1 file changed

+72
-33
lines changed

1 file changed

+72
-33
lines changed

src/print/mod.rs

+72-33
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,9 @@ impl Printer<'_> {
11451145
fn string_literal_style(&self) -> pretty::Styles {
11461146
pretty::Styles::color(pretty::palettes::simple::RED)
11471147
}
1148+
fn string_literal_escape_style(&self) -> pretty::Styles {
1149+
pretty::Styles::color(pretty::palettes::simple::ORANGE)
1150+
}
11481151
fn declarative_keyword_style(&self) -> pretty::Styles {
11491152
pretty::Styles::color(pretty::palettes::simple::BLUE)
11501153
}
@@ -1188,9 +1191,54 @@ impl Printer<'_> {
11881191
}
11891192

11901193
impl<'a> Printer<'a> {
1191-
/// Pretty-print a `name: ` style "named argument" prefix.
1194+
/// Pretty-print a string literal with escaping and styling.
11921195
//
1193-
// FIXME(eddyb) add methods like this for all styled text (e.g. literals).
1196+
// FIXME(eddyb) add methods like this for all styled text (e.g. numeric literals).
1197+
fn pretty_string_literal(&self, s: &str) -> pretty::Fragment {
1198+
// HACK(eddyb) this is somewhat inefficient, but we need to allocate a
1199+
// `String` for every piece anyway, so might as well make it convenient.
1200+
pretty::Fragment::new(
1201+
// HACK(eddyb) this allows aligning the actual string contents,
1202+
// (see `c == '\n'` special-casing below for when this applies).
1203+
(s.contains('\n').then_some(Either::Left(' ')).into_iter())
1204+
.chain([Either::Left('"')])
1205+
.chain(s.chars().flat_map(|c| {
1206+
let escaped = c.escape_debug();
1207+
let maybe_escaped = if c == '\'' {
1208+
// Unescape single quotes, we're in a double-quoted string.
1209+
assert_eq!(escaped.collect_tuple(), Some(('\\', c)));
1210+
Either::Left(c)
1211+
} else if let Some((single,)) = escaped.clone().collect_tuple() {
1212+
assert_eq!(single, c);
1213+
Either::Left(c)
1214+
} else {
1215+
assert_eq!(escaped.clone().next(), Some('\\'));
1216+
Either::Right(escaped)
1217+
};
1218+
1219+
// HACK(eddyb) move escaped `\n` to the start of a new line,
1220+
// using Rust's trailing `\` on the previous line, which eats
1221+
// all following whitespace (and only stops at the escape).
1222+
let extra_prefix_unescaped = if c == '\n' { "\\\n" } else { "" };
1223+
1224+
(extra_prefix_unescaped.chars().map(Either::Left)).chain([maybe_escaped])
1225+
}))
1226+
.chain([Either::Left('"')])
1227+
.group_by(|maybe_escaped| maybe_escaped.is_right())
1228+
.into_iter()
1229+
.map(|(escaped, group)| {
1230+
if escaped {
1231+
self.string_literal_escape_style()
1232+
.apply(group.flat_map(Either::unwrap_right).collect::<String>())
1233+
} else {
1234+
self.string_literal_style()
1235+
.apply(group.map(Either::unwrap_left).collect::<String>())
1236+
}
1237+
}),
1238+
)
1239+
}
1240+
1241+
/// Pretty-print a `name: ` style "named argument" prefix.
11941242
fn pretty_named_argument_prefix(&self, name: impl Into<Cow<'static, str>>) -> pretty::Fragment {
11951243
// FIXME(eddyb) avoid the cost of allocating here.
11961244
self.named_argument_label_style()
@@ -1835,9 +1883,9 @@ impl Print for spv::Dialect {
18351883
printer.pretty_named_argument_prefix("extensions"),
18361884
pretty::join_comma_sep(
18371885
"{",
1838-
extensions.iter().map(|ext| {
1839-
printer.string_literal_style().apply(format!("{ext:?}"))
1840-
}),
1886+
extensions
1887+
.iter()
1888+
.map(|ext| printer.pretty_string_literal(ext)),
18411889
"}",
18421890
),
18431891
])
@@ -1991,13 +2039,15 @@ impl Print for spv::ModuleDebugInfo {
19912039
.iter()
19922040
.map(|(&file, contents)| {
19932041
pretty::Fragment::new([
1994-
printer.string_literal_style().apply(
1995-
format!("{:?}", &printer.cx[file]),
2042+
printer.pretty_string_literal(
2043+
&printer.cx[file],
2044+
),
2045+
pretty::join_space(
2046+
":",
2047+
[printer.pretty_string_literal(
2048+
contents,
2049+
)],
19962050
),
1997-
": ".into(),
1998-
printer
1999-
.string_literal_style()
2000-
.apply(format!("{contents:?}")),
20012051
])
20022052
})
20032053
.map(|entry| {
@@ -2026,9 +2076,9 @@ impl Print for spv::ModuleDebugInfo {
20262076
printer.pretty_named_argument_prefix("source_extensions"),
20272077
pretty::join_comma_sep(
20282078
"[",
2029-
source_extensions.iter().map(|ext| {
2030-
printer.string_literal_style().apply(format!("{ext:?}"))
2031-
}),
2079+
source_extensions
2080+
.iter()
2081+
.map(|ext| printer.pretty_string_literal(ext)),
20322082
"]",
20332083
),
20342084
])
@@ -2038,9 +2088,9 @@ impl Print for spv::ModuleDebugInfo {
20382088
printer.pretty_named_argument_prefix("module_processes"),
20392089
pretty::join_comma_sep(
20402090
"[",
2041-
module_processes.iter().map(|proc| {
2042-
printer.string_literal_style().apply(format!("{proc:?}"))
2043-
}),
2091+
module_processes
2092+
.iter()
2093+
.map(|proc| printer.pretty_string_literal(proc)),
20442094
"]",
20452095
),
20462096
])
@@ -2058,10 +2108,7 @@ impl Print for ExportKey {
20582108
type Output = pretty::Fragment;
20592109
fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
20602110
match self {
2061-
&Self::LinkName(name) => printer
2062-
.string_literal_style()
2063-
.apply(format!("{:?}", &printer.cx[name]))
2064-
.into(),
2111+
&Self::LinkName(name) => printer.pretty_string_literal(&printer.cx[name]),
20652112

20662113
// HACK(eddyb) `interface_global_vars` should be recomputed by
20672114
// `spv::lift` anyway, so hiding them here mimics that.
@@ -2645,10 +2692,7 @@ impl Print for ConstDef {
26452692
ConstCtor::SpvStringLiteralForExtInst(s) => pretty::Fragment::new([
26462693
printer.pretty_spv_opcode(printer.spv_op_style(), wk.OpString),
26472694
"(".into(),
2648-
printer
2649-
.string_literal_style()
2650-
.apply(format!("{:?}", &printer.cx[s]))
2651-
.into(),
2695+
printer.pretty_string_literal(&printer.cx[s]),
26522696
")".into(),
26532697
]),
26542698
}),
@@ -2661,11 +2705,9 @@ impl Print for Import {
26612705
fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
26622706
match self {
26632707
&Self::LinkName(name) => pretty::Fragment::new([
2664-
printer.declarative_keyword_style().apply("import"),
2708+
printer.declarative_keyword_style().apply("import").into(),
26652709
" ".into(),
2666-
printer
2667-
.string_literal_style()
2668-
.apply(format!("{:?}", &printer.cx[name])),
2710+
printer.pretty_string_literal(&printer.cx[name]),
26692711
]),
26702712
}
26712713
}
@@ -3379,10 +3421,7 @@ impl Print for DataInstDef {
33793421
printer
33803422
.pretty_spv_opcode(printer.spv_op_style(), wk.OpExtInstImport),
33813423
"(".into(),
3382-
printer
3383-
.string_literal_style()
3384-
.apply(format!("{:?}", &printer.cx[ext_set]))
3385-
.into(),
3424+
printer.pretty_string_literal(&printer.cx[ext_set]),
33863425
")".into(),
33873426
]),
33883427
printer

0 commit comments

Comments
 (0)