Skip to content

Commit 37255b0

Browse files
committed
fix(autoformat): fix autoformatting of v1 -> v2
1 parent 4cbc322 commit 37255b0

File tree

3 files changed

+90
-63
lines changed

3 files changed

+90
-63
lines changed

src/entry.rs

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -161,80 +161,89 @@ impl KdlEntry {
161161
self.len() == 0
162162
}
163163

164+
/// Keeps the general entry formatting, though v1 entries will still be
165+
/// updated to v2 while preserving as much as possible.
166+
pub fn keep_format(&mut self) {
167+
if let Some(fmt) = self.format_mut() {
168+
fmt.autoformat_keep = true;
169+
}
170+
}
171+
164172
/// Auto-formats this entry.
165173
pub fn autoformat(&mut self) {
166174
// TODO once MSRV allows:
167175
//self.format.take_if(|f| !f.autoformat_keep);
168-
let value_repr = self.format.as_ref().map(|x| {
169-
match &self.value {
170-
KdlValue::String(val) => {
171-
// cleanup. I don't _think_ this should have any whitespace,
172-
// but just in case.
173-
let s = x.value_repr.trim();
174-
// convert raw strings to new format
175-
let s = s.strip_prefix('r').unwrap_or(s);
176-
let s = if crate::value::is_plain_ident(val) {
177-
val.to_string()
178-
} else if s
179-
.find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
180-
.is_some()
181-
{
182-
// Multiline string. Need triple quotes if they're not there already.
183-
if s.contains("\"\"\"") {
184-
// We're probably good. This could be more precise, but close enough.
185-
s.to_string()
186-
} else {
187-
// `"` -> `"""` but also extra newlines need to be
188-
// added because v2 strips the first and last ones.
189-
let s = s.replacen('\"', "\"\"\"\n", 1);
190-
s.chars()
191-
.rev()
192-
.collect::<String>()
193-
.replacen('\"', "\"\"\"\n", 1)
194-
.chars()
195-
.rev()
196-
.collect::<String>()
197-
}
198-
} else if !s.starts_with('#') {
199-
// `/` is no longer an escaped char in v2.
200-
s.replace("\\/", "/")
201-
} else {
202-
// We're all good! Let's move on.
203-
s.to_string()
204-
};
205-
s
206-
}
207-
// These have `#` prefixes now. The regular Display impl will
208-
// take care of that.
209-
KdlValue::Bool(_) | KdlValue::Null => format!("{}", self.value),
210-
// These should be fine as-is?
211-
KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
212-
}
213-
});
214-
176+
let old_fmt = self.format.clone();
215177
if !self
216178
.format
217179
.as_ref()
218180
.map(|f| f.autoformat_keep)
219181
.unwrap_or(false)
220182
{
221183
self.format = None;
222-
}
223-
224-
if let Some(value_repr) = value_repr.as_ref() {
225-
self.format = Some(
226-
self.format
227-
.clone()
228-
.map(|mut x| {
229-
x.value_repr = value_repr.into();
230-
x
231-
})
232-
.unwrap_or_else(|| KdlEntryFormat {
233-
value_repr: value_repr.into(),
234-
leading: " ".into(),
235-
..Default::default()
236-
}),
237-
)
184+
} else {
185+
let value_repr = old_fmt.map(|x| {
186+
match &self.value {
187+
KdlValue::String(val) => {
188+
// cleanup. I don't _think_ this should have any whitespace,
189+
// but just in case.
190+
let s = x.value_repr.trim();
191+
// convert raw strings to new format
192+
let s = s.strip_prefix('r').unwrap_or(s);
193+
let s = if crate::value::is_plain_ident(val) {
194+
val.to_string()
195+
} else if s
196+
.find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
197+
.is_some()
198+
{
199+
// Multiline string. Need triple quotes if they're not there already.
200+
if s.contains("\"\"\"") {
201+
// We're probably good. This could be more precise, but close enough.
202+
s.to_string()
203+
} else {
204+
// `"` -> `"""` but also extra newlines need to be
205+
// added because v2 strips the first and last ones.
206+
let s = s.replacen('\"', "\"\"\"\n", 1);
207+
s.chars()
208+
.rev()
209+
.collect::<String>()
210+
.replacen('\"', "\"\"\"\n", 1)
211+
.chars()
212+
.rev()
213+
.collect::<String>()
214+
}
215+
} else if !s.starts_with('#') {
216+
// `/` is no longer an escaped char in v2.
217+
s.replace("\\/", "/")
218+
} else {
219+
// We're all good! Let's move on.
220+
s.to_string()
221+
};
222+
s
223+
}
224+
// These have `#` prefixes now. The regular Display impl will
225+
// take care of that.
226+
KdlValue::Bool(_) | KdlValue::Null => format!("{}", self.value),
227+
// These should be fine as-is?
228+
KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
229+
}
230+
});
231+
232+
if let Some(value_repr) = value_repr.as_ref() {
233+
self.format = Some(
234+
self.format
235+
.clone()
236+
.map(|mut x| {
237+
x.value_repr = value_repr.into();
238+
x
239+
})
240+
.unwrap_or_else(|| KdlEntryFormat {
241+
value_repr: value_repr.into(),
242+
leading: " ".into(),
243+
..Default::default()
244+
}),
245+
)
246+
}
238247
}
239248

240249
if let Some(name) = &mut self.name {
@@ -523,42 +532,52 @@ mod test {
523532
#[test]
524533
fn v1_to_v2_format() -> miette::Result<()> {
525534
let mut entry = KdlEntry::parse_v1(r##"r#"hello, world!"#"##)?;
535+
entry.keep_format();
526536
entry.autoformat();
527537
assert_eq!(format!("{}", entry), r##" #"hello, world!"#"##);
528538

529539
let mut entry = KdlEntry::parse_v1(r#""hello, \" world!""#)?;
540+
entry.keep_format();
530541
entry.autoformat();
531542
assert_eq!(format!("{}", entry), r#" "hello, \" world!""#);
532543

533544
let mut entry = KdlEntry::parse_v1("\"foo!`~.,<>\"")?;
545+
entry.keep_format();
534546
entry.autoformat();
535547
assert_eq!(format!("{}", entry), " foo!`~.,<>");
536548

537549
let mut entry = KdlEntry::parse_v1("\"\nhello, world!\"")?;
550+
entry.keep_format();
538551
entry.autoformat();
539552
assert_eq!(format!("{}", entry), " \"\"\"\n\nhello, world!\n\"\"\"");
540553

541554
let mut entry = KdlEntry::parse_v1("r#\"\nhello, world!\"#")?;
555+
entry.keep_format();
542556
entry.autoformat();
543557
assert_eq!(format!("{}", entry), " #\"\"\"\n\nhello, world!\n\"\"\"#");
544558

545559
let mut entry = KdlEntry::parse_v1("true")?;
560+
entry.keep_format();
546561
entry.autoformat();
547562
assert_eq!(format!("{}", entry), " #true");
548563

549564
let mut entry = KdlEntry::parse_v1("false")?;
565+
entry.keep_format();
550566
entry.autoformat();
551567
assert_eq!(format!("{}", entry), " #false");
552568

553569
let mut entry = KdlEntry::parse_v1("null")?;
570+
entry.keep_format();
554571
entry.autoformat();
555572
assert_eq!(format!("{}", entry), " #null");
556573

557574
let mut entry = KdlEntry::parse_v1("1_234_567")?;
575+
entry.keep_format();
558576
entry.autoformat();
559577
assert_eq!(format!("{}", entry), " 1_234_567");
560578

561579
let mut entry = KdlEntry::parse_v1("1_234_567E-10")?;
580+
entry.keep_format();
562581
entry.autoformat();
563582
assert_eq!(format!("{}", entry), " 1_234_567E-10");
564583
Ok(())

src/fmt.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub struct FormatConfig<'a> {
1515

1616
/// Whether to remove comments. Defaults to `false`.
1717
pub no_comments: bool,
18+
19+
/// Whether to keep individual entry formatting.
20+
pub entry_autoformate_keep: bool,
1821
}
1922

2023
/// See field documentation for defaults.
@@ -44,6 +47,7 @@ impl<'a> FormatConfigBuilder<'a> {
4447
indent_level: 0,
4548
indent: " ",
4649
no_comments: false,
50+
entry_autoformate_keep: false,
4751
})
4852
}
4953

@@ -163,6 +167,7 @@ mod test {
163167
indent_level: 12,
164168
indent: " \t",
165169
no_comments: true,
170+
entry_autoformate_keep: false,
166171
}
167172
));
168173
Ok(())

src/node.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ impl KdlNode {
309309
ty.clear_format()
310310
}
311311
for entry in &mut self.entries {
312+
if config.entry_autoformate_keep {
313+
entry.keep_format();
314+
}
312315
entry.autoformat();
313316
}
314317
if let Some(children) = self.children.as_mut() {

0 commit comments

Comments
 (0)