2
2
use miette:: SourceSpan ;
3
3
use std:: fmt:: Display ;
4
4
5
- use crate :: { FormatConfig , KdlError , KdlNode , KdlValue } ;
5
+ use crate :: { FormatConfig , KdlError , KdlNode , KdlNodeFormat , KdlValue } ;
6
6
7
7
/// Represents a KDL
8
8
/// [`Document`](https://github.com/kdl-org/kdl/blob/main/SPEC.md#document).
@@ -344,15 +344,20 @@ impl KdlDocument {
344
344
pub fn parse ( s : & str ) -> Result < Self , KdlError > {
345
345
#[ cfg( not( feature = "v1-fallback" ) ) ]
346
346
{
347
- crate :: v2_parser :: try_parse ( crate :: v2_parser :: document , s)
347
+ KdlDocument :: parse_v2 ( s)
348
348
}
349
349
#[ cfg( feature = "v1-fallback" ) ]
350
350
{
351
- crate :: v2_parser:: try_parse ( crate :: v2_parser:: document, s)
352
- . or_else ( |e| KdlDocument :: parse_v1 ( s) . map_err ( |_| e) )
351
+ KdlDocument :: parse_v2 ( s) . or_else ( |e| KdlDocument :: parse_v1 ( s) . map_err ( |_| e) )
353
352
}
354
353
}
355
354
355
+ /// Parses a KDL v2 string into a document.
356
+ #[ cfg( feature = "v1" ) ]
357
+ pub fn parse_v2 ( s : & str ) -> Result < Self , KdlError > {
358
+ crate :: v2_parser:: try_parse ( crate :: v2_parser:: document, s)
359
+ }
360
+
356
361
/// Parses a KDL v1 string into a document.
357
362
#[ cfg( feature = "v1" ) ]
358
363
pub fn parse_v1 ( s : & str ) -> Result < Self , KdlError > {
@@ -365,9 +370,74 @@ impl KdlDocument {
365
370
#[ cfg( feature = "v1" ) ]
366
371
pub fn v1_to_v2 ( s : & str ) -> Result < String , KdlError > {
367
372
let mut doc = KdlDocument :: parse_v1 ( s) ?;
368
- doc. autoformat ( ) ;
373
+ doc. ensure_v2 ( ) ;
369
374
Ok ( doc. to_string ( ) )
370
375
}
376
+
377
+ /// Takes a KDL v2 document string and returns the same document, but
378
+ /// autoformatted into valid KDL v2 syntax.
379
+ #[ cfg( feature = "v1" ) ]
380
+ pub fn v2_to_v1 ( s : & str ) -> Result < String , KdlError > {
381
+ let mut doc = KdlDocument :: parse_v2 ( s) ?;
382
+ doc. ensure_v1 ( ) ;
383
+ Ok ( doc. to_string ( ) )
384
+ }
385
+
386
+ /// Makes sure this document is in v2 format.
387
+ #[ cfg( feature = "v1" ) ]
388
+ pub fn ensure_v2 ( & mut self ) {
389
+ // No need to touch KdlDocumentFormat, probably. In the longer term,
390
+ // we'll want to make sure to parse out whitespace and comments and make
391
+ // sure they're actually compliant, but this is good enough for now.
392
+ for node in self . nodes_mut ( ) . iter_mut ( ) {
393
+ node. ensure_v2 ( ) ;
394
+ }
395
+ }
396
+
397
+ /// Makes sure this document is in v1 format.
398
+ #[ cfg( feature = "v1" ) ]
399
+ pub fn ensure_v1 ( & mut self ) {
400
+ // No need to touch KdlDocumentFormat, probably. In the longer term,
401
+ // we'll want to make sure to parse out whitespace and comments and make
402
+ // sure they're actually compliant, but this is good enough for now.
403
+
404
+ // the last node in v1 docs/children has to have a semicolon.
405
+ let mut iter = self . nodes_mut ( ) . iter_mut ( ) . rev ( ) ;
406
+ let last = iter. next ( ) ;
407
+ let penult = iter. next ( ) ;
408
+ if let Some ( last) = last {
409
+ if let Some ( fmt) = last. format_mut ( ) {
410
+ if !fmt. trailing . contains ( ";" )
411
+ && fmt
412
+ . trailing
413
+ . chars ( )
414
+ . any ( |c| crate :: v2_parser:: NEWLINES . iter ( ) . any ( |nl| nl. contains ( c) ) )
415
+ {
416
+ fmt. terminator = ";" . into ( ) ;
417
+ }
418
+ } else {
419
+ let maybe_indent = {
420
+ if let Some ( penult) = penult {
421
+ if let Some ( fmt) = penult. format ( ) {
422
+ fmt. leading . clone ( )
423
+ } else {
424
+ "" . into ( )
425
+ }
426
+ } else {
427
+ "" . into ( )
428
+ }
429
+ } ;
430
+ last. format = Some ( KdlNodeFormat {
431
+ leading : maybe_indent,
432
+ terminator : "\n " . into ( ) ,
433
+ ..Default :: default ( )
434
+ } )
435
+ }
436
+ }
437
+ for node in self . nodes_mut ( ) . iter_mut ( ) {
438
+ node. ensure_v1 ( ) ;
439
+ }
440
+ }
371
441
}
372
442
373
443
#[ cfg( feature = "v1" ) ]
@@ -956,10 +1026,95 @@ inline { time; to; live "our" "dreams"; "y;all" }
956
1026
Ok ( ( ) )
957
1027
}
958
1028
959
- #[ ignore = "Formatting is still seriously broken, and this is gonna need some extra love." ]
960
1029
#[ cfg( feature = "v1" ) ]
961
1030
#[ test]
962
- fn v1_to_v2 ( ) -> miette:: Result < ( ) > {
1031
+ fn v1_v2_conversions ( ) -> miette:: Result < ( ) > {
1032
+ let v1 = r##"
1033
+ // If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
1034
+ keybinds {
1035
+ normal {
1036
+ // uncomment this and adjust key if using copy_on_select=false
1037
+ // bind "Alt c" { Copy; }
1038
+ }
1039
+ locked {
1040
+ bind "Ctrl g" { SwitchToMode "Normal"; }
1041
+ }
1042
+ resize {
1043
+ bind "Ctrl n" { SwitchToMode "Normal"; }
1044
+ bind "h" "Left" { Resize "Increase Left"; }
1045
+ bind "j" "Down" { Resize "Increase Down"; }
1046
+ bind "k" "Up" { Resize "Increase Up"; }
1047
+ bind "l" "Right" { Resize "Increase Right"; }
1048
+ bind "H" { Resize "Decrease Left"; }
1049
+ bind "J" { Resize "Decrease Down"; }
1050
+ bind "K" { Resize "Decrease Up"; }
1051
+ bind "L" { Resize "Decrease Right"; }
1052
+ bind "=" "+" { Resize "Increase"; }
1053
+ bind "-" { Resize "Decrease"; }
1054
+ }
1055
+ }
1056
+ // Plugin aliases - can be used to change the implementation of Zellij
1057
+ // changing these requires a restart to take effect
1058
+ plugins {
1059
+ tab-bar location="zellij:tab-bar"
1060
+ status-bar location="zellij:status-bar"
1061
+ welcome-screen location="zellij:session-manager" {
1062
+ welcome_screen true
1063
+ }
1064
+ filepicker location="zellij:strider" {
1065
+ cwd "\/"
1066
+ }
1067
+ }
1068
+ mouse_mode false
1069
+ mirror_session true
1070
+ "## ;
1071
+ let v2 = r##"
1072
+ // If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
1073
+ keybinds {
1074
+ normal {
1075
+ // uncomment this and adjust key if using copy_on_select=false
1076
+ // bind "Alt c" { Copy; }
1077
+ }
1078
+ locked {
1079
+ bind "Ctrl g" { SwitchToMode Normal; }
1080
+ }
1081
+ resize {
1082
+ bind "Ctrl n" { SwitchToMode Normal; }
1083
+ bind h Left { Resize "Increase Left"; }
1084
+ bind j Down { Resize "Increase Down"; }
1085
+ bind k Up { Resize "Increase Up"; }
1086
+ bind l Right { Resize "Increase Right"; }
1087
+ bind H { Resize "Decrease Left"; }
1088
+ bind J { Resize "Decrease Down"; }
1089
+ bind K { Resize "Decrease Up"; }
1090
+ bind L { Resize "Decrease Right"; }
1091
+ bind "=" + { Resize Increase; }
1092
+ bind - { Resize Decrease; }
1093
+ }
1094
+ }
1095
+ // Plugin aliases - can be used to change the implementation of Zellij
1096
+ // changing these requires a restart to take effect
1097
+ plugins {
1098
+ tab-bar location=zellij:tab-bar
1099
+ status-bar location=zellij:status-bar
1100
+ welcome-screen location=zellij:session-manager {
1101
+ welcome_screen #true
1102
+ }
1103
+ filepicker location=zellij:strider {
1104
+ cwd "/"
1105
+ }
1106
+ }
1107
+ mouse_mode #false
1108
+ mirror_session #true
1109
+ "## ;
1110
+ pretty_assertions:: assert_eq!( KdlDocument :: v1_to_v2( v1) ?, v2, "Converting a v1 doc to v2" ) ;
1111
+ pretty_assertions:: assert_eq!( KdlDocument :: v2_to_v1( v2) ?, v1, "Converting a v2 doc to v1" ) ;
1112
+ Ok ( ( ) )
1113
+ }
1114
+
1115
+ #[ cfg( feature = "v1" ) ]
1116
+ #[ test]
1117
+ fn v2_to_v1 ( ) -> miette:: Result < ( ) > {
963
1118
let original = r##"
964
1119
// If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
965
1120
keybinds {
0 commit comments