Skip to content

Commit

Permalink
fix(autoformat): fix autoformatting of v1 -> v2
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Dec 20, 2024
1 parent 4cbc322 commit 37255b0
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 63 deletions.
145 changes: 82 additions & 63 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,80 +161,89 @@ impl KdlEntry {
self.len() == 0
}

/// Keeps the general entry formatting, though v1 entries will still be
/// updated to v2 while preserving as much as possible.
pub fn keep_format(&mut self) {
if let Some(fmt) = self.format_mut() {
fmt.autoformat_keep = true;
}
}

/// Auto-formats this entry.
pub fn autoformat(&mut self) {
// TODO once MSRV allows:
//self.format.take_if(|f| !f.autoformat_keep);
let value_repr = self.format.as_ref().map(|x| {
match &self.value {
KdlValue::String(val) => {
// cleanup. I don't _think_ this should have any whitespace,
// but just in case.
let s = x.value_repr.trim();
// convert raw strings to new format
let s = s.strip_prefix('r').unwrap_or(s);
let s = if crate::value::is_plain_ident(val) {
val.to_string()
} else if s
.find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
.is_some()
{
// Multiline string. Need triple quotes if they're not there already.
if s.contains("\"\"\"") {
// We're probably good. This could be more precise, but close enough.
s.to_string()
} else {
// `"` -> `"""` but also extra newlines need to be
// added because v2 strips the first and last ones.
let s = s.replacen('\"', "\"\"\"\n", 1);
s.chars()
.rev()
.collect::<String>()
.replacen('\"', "\"\"\"\n", 1)
.chars()
.rev()
.collect::<String>()
}
} else if !s.starts_with('#') {
// `/` is no longer an escaped char in v2.
s.replace("\\/", "/")
} else {
// We're all good! Let's move on.
s.to_string()
};
s
}
// These have `#` prefixes now. The regular Display impl will
// take care of that.
KdlValue::Bool(_) | KdlValue::Null => format!("{}", self.value),
// These should be fine as-is?
KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
}
});

let old_fmt = self.format.clone();
if !self
.format
.as_ref()
.map(|f| f.autoformat_keep)
.unwrap_or(false)
{
self.format = None;
}

if let Some(value_repr) = value_repr.as_ref() {
self.format = Some(
self.format
.clone()
.map(|mut x| {
x.value_repr = value_repr.into();
x
})
.unwrap_or_else(|| KdlEntryFormat {
value_repr: value_repr.into(),
leading: " ".into(),
..Default::default()
}),
)
} else {
let value_repr = old_fmt.map(|x| {
match &self.value {
KdlValue::String(val) => {
// cleanup. I don't _think_ this should have any whitespace,
// but just in case.
let s = x.value_repr.trim();
// convert raw strings to new format
let s = s.strip_prefix('r').unwrap_or(s);
let s = if crate::value::is_plain_ident(val) {
val.to_string()
} else if s
.find(|c| v2_parser::NEWLINES.iter().any(|nl| nl.contains(c)))
.is_some()
{
// Multiline string. Need triple quotes if they're not there already.
if s.contains("\"\"\"") {
// We're probably good. This could be more precise, but close enough.
s.to_string()
} else {
// `"` -> `"""` but also extra newlines need to be
// added because v2 strips the first and last ones.
let s = s.replacen('\"', "\"\"\"\n", 1);
s.chars()
.rev()
.collect::<String>()
.replacen('\"', "\"\"\"\n", 1)
.chars()
.rev()
.collect::<String>()
}
} else if !s.starts_with('#') {
// `/` is no longer an escaped char in v2.
s.replace("\\/", "/")
} else {
// We're all good! Let's move on.
s.to_string()
};
s
}
// These have `#` prefixes now. The regular Display impl will
// take care of that.
KdlValue::Bool(_) | KdlValue::Null => format!("{}", self.value),
// These should be fine as-is?
KdlValue::Integer(_) | KdlValue::Float(_) => x.value_repr.clone(),
}
});

if let Some(value_repr) = value_repr.as_ref() {
self.format = Some(
self.format
.clone()
.map(|mut x| {
x.value_repr = value_repr.into();
x
})
.unwrap_or_else(|| KdlEntryFormat {
value_repr: value_repr.into(),
leading: " ".into(),
..Default::default()
}),
)
}
}

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

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

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

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

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

let mut entry = KdlEntry::parse_v1("true")?;
entry.keep_format();
entry.autoformat();
assert_eq!(format!("{}", entry), " #true");

let mut entry = KdlEntry::parse_v1("false")?;
entry.keep_format();
entry.autoformat();
assert_eq!(format!("{}", entry), " #false");

let mut entry = KdlEntry::parse_v1("null")?;
entry.keep_format();
entry.autoformat();
assert_eq!(format!("{}", entry), " #null");

let mut entry = KdlEntry::parse_v1("1_234_567")?;
entry.keep_format();
entry.autoformat();
assert_eq!(format!("{}", entry), " 1_234_567");

let mut entry = KdlEntry::parse_v1("1_234_567E-10")?;
entry.keep_format();
entry.autoformat();
assert_eq!(format!("{}", entry), " 1_234_567E-10");
Ok(())
Expand Down
5 changes: 5 additions & 0 deletions src/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub struct FormatConfig<'a> {

/// Whether to remove comments. Defaults to `false`.
pub no_comments: bool,

/// Whether to keep individual entry formatting.
pub entry_autoformate_keep: bool,
}

/// See field documentation for defaults.
Expand Down Expand Up @@ -44,6 +47,7 @@ impl<'a> FormatConfigBuilder<'a> {
indent_level: 0,
indent: " ",
no_comments: false,
entry_autoformate_keep: false,
})
}

Expand Down Expand Up @@ -163,6 +167,7 @@ mod test {
indent_level: 12,
indent: " \t",
no_comments: true,
entry_autoformate_keep: false,
}
));
Ok(())
Expand Down
3 changes: 3 additions & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ impl KdlNode {
ty.clear_format()
}
for entry in &mut self.entries {
if config.entry_autoformate_keep {
entry.keep_format();
}
entry.autoformat();
}
if let Some(children) = self.children.as_mut() {
Expand Down

0 comments on commit 37255b0

Please sign in to comment.