mirror of https://github.com/kdl-org/kdl-rs.git
feat(v1): Add utility to auto-translate v1 to v2
This commit is contained in:
parent
3e5d7a33af
commit
c486cda7a5
|
|
@ -359,6 +359,15 @@ impl KdlDocument {
|
|||
let ret: Result<kdlv1::KdlDocument, kdlv1::KdlError> = s.parse();
|
||||
ret.map(|x| x.into()).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Takes a KDL v1 document string and returns the same document, but
|
||||
/// autoformatted into valid KDL v2 syntax.
|
||||
#[cfg(feature = "v1")]
|
||||
pub fn v1_to_v2(s: &str) -> Result<String, KdlParseFailure> {
|
||||
let mut doc = KdlDocument::parse_v1(s)?;
|
||||
doc.autoformat();
|
||||
Ok(doc.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
|
|
@ -946,4 +955,90 @@ inline { time; to; live "our" "dreams"; "y;all" }
|
|||
include_str!("../examples/zellij-unquoted-bindings.kdl").parse::<KdlDocument>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[ignore = "Formatting is still seriously broken, and this is gonna need some extra love."]
|
||||
#[cfg(feature = "v1")]
|
||||
#[test]
|
||||
fn v1_to_v2() -> miette::Result<()> {
|
||||
let original = r##"
|
||||
// If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
|
||||
keybinds {
|
||||
normal {
|
||||
// uncomment this and adjust key if using copy_on_select=false
|
||||
// bind "Alt c" { Copy; }
|
||||
}
|
||||
locked {
|
||||
bind "Ctrl g" { SwitchToMode "Normal"; }
|
||||
}
|
||||
resize {
|
||||
bind "Ctrl n" { SwitchToMode "Normal"; }
|
||||
bind "h" "Left" { Resize "Increase Left"; }
|
||||
bind "j" "Down" { Resize "Increase Down"; }
|
||||
bind "k" "Up" { Resize "Increase Up"; }
|
||||
bind "l" "Right" { Resize "Increase Right"; }
|
||||
bind "H" { Resize "Decrease Left"; }
|
||||
bind "J" { Resize "Decrease Down"; }
|
||||
bind "K" { Resize "Decrease Up"; }
|
||||
bind "L" { Resize "Decrease Right"; }
|
||||
bind "=" "+" { Resize "Increase"; }
|
||||
bind "-" { Resize "Decrease"; }
|
||||
}
|
||||
}
|
||||
// Plugin aliases - can be used to change the implementation of Zellij
|
||||
// changing these requires a restart to take effect
|
||||
plugins {
|
||||
tab-bar location="zellij:tab-bar"
|
||||
status-bar location="zellij:status-bar"
|
||||
welcome-screen location="zellij:session-manager" {
|
||||
welcome_screen true
|
||||
}
|
||||
filepicker location="zellij:strider" {
|
||||
cwd "/"
|
||||
}
|
||||
}
|
||||
mouse_mode false
|
||||
mirror_session true
|
||||
"##;
|
||||
let expected = r##"
|
||||
// If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
|
||||
keybinds {
|
||||
normal {
|
||||
// uncomment this and adjust key if using copy_on_select=false
|
||||
// bind "Alt c" { Copy; }
|
||||
}
|
||||
locked {
|
||||
bind "Ctrl g" { SwitchToMode Normal; }
|
||||
}
|
||||
resize {
|
||||
bind "Ctrl n" { SwitchToMode Normal; }
|
||||
bind h Left { Resize "Increase Left"; }
|
||||
bind j Down { Resize "Increase Down"; }
|
||||
bind k Up { Resize "Increase Up"; }
|
||||
bind l Right { Resize "Increase Right"; }
|
||||
bind H { Resize "Decrease Left"; }
|
||||
bind J { Resize "Decrease Down"; }
|
||||
bind K { Resize "Decrease Up"; }
|
||||
bind L { Resize "Decrease Right"; }
|
||||
bind "=" + { Resize Increase; }
|
||||
bind - { Resize Decrease; }
|
||||
}
|
||||
}
|
||||
// Plugin aliases - can be used to change the implementation of Zellij
|
||||
// changing these requires a restart to take effect
|
||||
plugins {
|
||||
tab-bar location=zellij:tab-bar
|
||||
status-bar location=zellij:status-bar
|
||||
welcome-screen location=zellij:session-manager {
|
||||
welcome_screen #true
|
||||
}
|
||||
filepicker location=zellij:strider {
|
||||
cwd "/"
|
||||
}
|
||||
}
|
||||
mouse_mode #false
|
||||
mirror_session #true
|
||||
"##;
|
||||
pretty_assertions::assert_eq!(KdlDocument::v1_to_v2(original)?, expected);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
110
src/entry.rs
110
src/entry.rs
|
|
@ -165,13 +165,76 @@ impl KdlEntry {
|
|||
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(),
|
||||
}
|
||||
});
|
||||
|
||||
if !self
|
||||
.format
|
||||
.as_ref()
|
||||
.map(|f| f.autoformat_keep)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.format = None
|
||||
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()
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
if let Some(name) = &mut self.name {
|
||||
|
|
@ -455,4 +518,49 @@ mod test {
|
|||
let entry = KdlEntry::new_prop("name", KdlValue::Integer(42));
|
||||
assert_eq!(format!("{}", entry), "name=42");
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
#[test]
|
||||
fn v1_to_v2_format() -> miette::Result<()> {
|
||||
let mut entry = KdlEntry::parse_v1(r##"r#"hello, world!"#"##)?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), r##" #"hello, world!"#"##);
|
||||
|
||||
let mut entry = KdlEntry::parse_v1(r#""hello, \" world!""#)?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), r#" "hello, \" world!""#);
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("\"foo!`~.,<>\"")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " foo!`~.,<>");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("\"\nhello, world!\"")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " \"\"\"\n\nhello, world!\n\"\"\"");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("r#\"\nhello, world!\"#")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " #\"\"\"\n\nhello, world!\n\"\"\"#");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("true")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " #true");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("false")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " #false");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("null")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " #null");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("1_234_567")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " 1_234_567");
|
||||
|
||||
let mut entry = KdlEntry::parse_v1("1_234_567E-10")?;
|
||||
entry.autoformat();
|
||||
assert_eq!(format!("{}", entry), " 1_234_567E-10");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/lib.rs
19
src/lib.rs
|
|
@ -10,11 +10,20 @@
|
|||
//! You can think of this crate as
|
||||
//! [`toml_edit`](https://crates.io/crates/toml_edit), but for KDL.
|
||||
//!
|
||||
//! This crate supports parsing [the final KDL 2.0.0
|
||||
//! draft](https://github.com/kdl-org/kdl/pull/434), which might get a couple
|
||||
//! more small modifications before things are truly finalized. It does not
|
||||
//! support KDL 1.0 as of this release, but versions of this crate lower than
|
||||
//! 5.0 do.
|
||||
//! This crate supports both KDL v2.0.0 and v1.0.0 (when using the non-default
|
||||
//! `v1` feature).
|
||||
//!
|
||||
//! There is also a `v1-fallback` feature that may be enabled in order to have
|
||||
//! the various `Kdl*::parse` methods try to parse their input as v2, and, if
|
||||
//! that fails, try again as v1. In either case, a dedicated `Kdl*::parse_v1`
|
||||
//! method is available for v1-exclusive parsing, as long as either `v1` or
|
||||
//! `v1-fallback` are enabled.
|
||||
//!
|
||||
//! Autoformatting a document parsed from a v1 doc will translate the document
|
||||
//! to v2 format, preserving as much of the v1 trivia as possible (comments,
|
||||
//! etc). It *should* generate a fully valid v2 document, but there may still be
|
||||
//! some corner cases that don't translate well. Please file issues as needed
|
||||
//! for those.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1578,7 +1578,7 @@ fn escline_test() {
|
|||
assert_eq!(node.entries().len(), 2);
|
||||
}
|
||||
|
||||
static NEWLINES: [&str; 7] = [
|
||||
pub(crate) static NEWLINES: [&str; 7] = [
|
||||
"\u{000D}\u{000A}",
|
||||
"\u{000D}",
|
||||
"\u{000A}",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ impl Display for KdlValue {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_plain_ident(ident: &str) -> bool {
|
||||
pub(crate) fn is_plain_ident(ident: &str) -> bool {
|
||||
let ident_bytes = ident.as_bytes();
|
||||
ident
|
||||
.find(crate::v2_parser::is_disallowed_ident_char)
|
||||
|
|
|
|||
Loading…
Reference in New Issue