mirror of https://github.com/kdl-org/kdl-rs.git
feat: control format of multi-line strings
This commit is contained in:
parent
268f3a2d00
commit
1aaa178d00
53
src/entry.rs
53
src/entry.rs
|
|
@ -169,6 +169,59 @@ impl KdlEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Auto-formats this entry with multiline string handling.
|
||||||
|
pub(crate) fn autoformat_with_multiline(
|
||||||
|
&mut self,
|
||||||
|
expand: crate::fmt::MultilineStringExpansion,
|
||||||
|
) {
|
||||||
|
use crate::fmt::MultilineStringExpansion::*;
|
||||||
|
|
||||||
|
let should_preserve_repr = match expand {
|
||||||
|
Never => false,
|
||||||
|
Always => {
|
||||||
|
if let Some(s) = self.value.as_string() {
|
||||||
|
if s.contains('\n') {
|
||||||
|
// Convert to multiline format
|
||||||
|
let multiline = format!("\"\"\"\n{}\n\"\"\"", s);
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
self.ensure_v2();
|
||||||
|
self.format = Some(KdlEntryFormat {
|
||||||
|
value_repr: multiline,
|
||||||
|
leading: " ".into(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
if let Some(name) = &mut self.name {
|
||||||
|
name.autoformat();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
TripleQuotes => self
|
||||||
|
.format
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|f| f.value_repr.starts_with("\"\"\"")),
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_preserve_repr {
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
|
self.ensure_v2();
|
||||||
|
self.format = self.format.take().map(|f| KdlEntryFormat {
|
||||||
|
value_repr: f.value_repr,
|
||||||
|
leading: " ".into(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.autoformat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = &mut self.name {
|
||||||
|
name.autoformat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Auto-formats this entry.
|
/// Auto-formats this entry.
|
||||||
pub fn autoformat(&mut self) {
|
pub fn autoformat(&mut self) {
|
||||||
// TODO once MSRV allows (1.80.0):
|
// TODO once MSRV allows (1.80.0):
|
||||||
|
|
|
||||||
24
src/fmt.rs
24
src/fmt.rs
|
|
@ -1,5 +1,17 @@
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
|
||||||
|
/// Controls how multiline strings are handled during formatting.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub enum MultilineStringExpansion {
|
||||||
|
/// Keep strings as-is (current behavior).
|
||||||
|
#[default]
|
||||||
|
Never,
|
||||||
|
/// Convert any string with `\n` to `"""..."""` syntax.
|
||||||
|
Always,
|
||||||
|
/// Only preserve existing `"""..."""` syntax.
|
||||||
|
TripleQuotes,
|
||||||
|
}
|
||||||
|
|
||||||
/// Formatting configuration for use with [`KdlDocument::autoformat_config`](`crate::KdlDocument::autoformat_config`)
|
/// Formatting configuration for use with [`KdlDocument::autoformat_config`](`crate::KdlDocument::autoformat_config`)
|
||||||
/// and [`KdlNode::autoformat_config`](`crate::KdlNode::autoformat_config`).
|
/// and [`KdlNode::autoformat_config`](`crate::KdlNode::autoformat_config`).
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
@ -18,6 +30,9 @@ pub struct FormatConfig<'a> {
|
||||||
|
|
||||||
/// Whether to keep individual entry formatting.
|
/// Whether to keep individual entry formatting.
|
||||||
pub entry_autoformate_keep: bool,
|
pub entry_autoformate_keep: bool,
|
||||||
|
|
||||||
|
/// How to handle multiline strings during formatting.
|
||||||
|
pub expand_multiline: MultilineStringExpansion,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See field documentation for defaults.
|
/// See field documentation for defaults.
|
||||||
|
|
@ -48,6 +63,7 @@ impl<'a> FormatConfigBuilder<'a> {
|
||||||
indent: " ",
|
indent: " ",
|
||||||
no_comments: false,
|
no_comments: false,
|
||||||
entry_autoformate_keep: false,
|
entry_autoformate_keep: false,
|
||||||
|
expand_multiline: MultilineStringExpansion::Never,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +121,13 @@ impl<'a> FormatConfigBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How to handle multiline strings during formatting.
|
||||||
|
/// Defaults to `Never` iff not specified.
|
||||||
|
pub const fn expand_multiline(mut self, expand_multiline: MultilineStringExpansion) -> Self {
|
||||||
|
self.0.expand_multiline = expand_multiline;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the [`FormatConfig`].
|
/// Builds the [`FormatConfig`].
|
||||||
pub const fn build(self) -> FormatConfig<'a> {
|
pub const fn build(self) -> FormatConfig<'a> {
|
||||||
self.0
|
self.0
|
||||||
|
|
@ -168,6 +191,7 @@ mod test {
|
||||||
indent: " \t",
|
indent: " \t",
|
||||||
no_comments: true,
|
no_comments: true,
|
||||||
entry_autoformate_keep: false,
|
entry_autoformate_keep: false,
|
||||||
|
expand_multiline: MultilineStringExpansion::Never,
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -311,8 +311,10 @@ impl KdlNode {
|
||||||
for entry in &mut self.entries {
|
for entry in &mut self.entries {
|
||||||
if config.entry_autoformate_keep {
|
if config.entry_autoformate_keep {
|
||||||
entry.keep_format();
|
entry.keep_format();
|
||||||
|
entry.autoformat();
|
||||||
|
} else {
|
||||||
|
entry.autoformat_with_multiline(config.expand_multiline);
|
||||||
}
|
}
|
||||||
entry.autoformat();
|
|
||||||
}
|
}
|
||||||
if let Some(children) = self.children.as_mut() {
|
if let Some(children) = self.children.as_mut() {
|
||||||
children.autoformat_config(&FormatConfig {
|
children.autoformat_config(&FormatConfig {
|
||||||
|
|
|
||||||
|
|
@ -24,3 +24,96 @@ fn build_and_format() {
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_string_never() -> miette::Result<()> {
|
||||||
|
let input = r#"multi """
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
"""
|
||||||
|
multi-explicit "a\nb\nc""#;
|
||||||
|
|
||||||
|
let mut doc: KdlDocument = input.parse()?;
|
||||||
|
doc.autoformat();
|
||||||
|
|
||||||
|
let output = doc.to_string();
|
||||||
|
assert_eq!(
|
||||||
|
output,
|
||||||
|
"multi \"a\\nb\\nc\"\nmulti-explicit \"a\\nb\\nc\"\n"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_string_triple_quotes() -> miette::Result<()> {
|
||||||
|
let input = r#"multi """
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
"""
|
||||||
|
multi-explicit "a\nb\nc""#;
|
||||||
|
|
||||||
|
let mut doc: KdlDocument = input.parse()?;
|
||||||
|
doc.autoformat_config(
|
||||||
|
&kdl::FormatConfig::builder()
|
||||||
|
.expand_multiline(kdl::MultilineStringExpansion::TripleQuotes)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = doc.to_string();
|
||||||
|
assert!(output.contains("multi \"\"\""));
|
||||||
|
assert!(output.contains("multi-explicit \"a\\nb\\nc\""));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_string_always() -> miette::Result<()> {
|
||||||
|
let input = r#"multi """
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
"""
|
||||||
|
multi-explicit "a\nb\nc""#;
|
||||||
|
|
||||||
|
let mut doc: KdlDocument = input.parse()?;
|
||||||
|
doc.autoformat_config(
|
||||||
|
&kdl::FormatConfig::builder()
|
||||||
|
.expand_multiline(kdl::MultilineStringExpansion::Always)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = doc.to_string();
|
||||||
|
assert!(output.contains("multi \"\"\""));
|
||||||
|
assert!(output.contains("multi-explicit \"\"\""));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_string_nested_triple_quotes() -> miette::Result<()> {
|
||||||
|
let input = r#"some-node {
|
||||||
|
multi """
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
"""
|
||||||
|
explicit "a\nb\nc"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let mut doc: KdlDocument = input.parse()?;
|
||||||
|
doc.autoformat_config(
|
||||||
|
&kdl::FormatConfig::builder()
|
||||||
|
.expand_multiline(kdl::MultilineStringExpansion::TripleQuotes)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = doc.to_string();
|
||||||
|
// Multiline string should be preserved
|
||||||
|
assert!(output.contains("multi \"\"\""));
|
||||||
|
// Regular string should be normalized
|
||||||
|
assert!(output.contains("explicit \"a\\nb\\nc\""));
|
||||||
|
// Indentation should be normalized to 4 spaces
|
||||||
|
assert!(output.contains(" multi"));
|
||||||
|
assert!(output.contains(" explicit"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue