@@ -341,15 +341,30 @@ impl KdlDocument {
341341 ///
342342 /// If the `v1-fallback` feature is enabled, this method will first try to
343343 /// parse the string as a KDL v2 document, and, if that fails, it will try
344- /// to parse again as a KDL v1 document. If both fail, only the v2 parse
345- /// errors will be returned.
344+ /// to parse again as a KDL v1 document. If both fail, a heuristic will be
345+ /// applied to try and detect the "intended" KDL version, and that version's
346+ /// error(s) will be returned.
346347 pub fn parse ( s : & str ) -> Result < Self , KdlError > {
347348 #[ cfg( not( feature = "v1-fallback" ) ) ]
348349 {
349350 KdlDocument :: parse_v2 ( s)
350351 }
351352 #[ cfg( feature = "v1-fallback" ) ]
352353 {
354+ let v2_res = KdlDocument :: parse_v2 ( s) ;
355+ if let Err ( err) = v2_res {
356+ let v1_res = KdlDocument :: parse_v2 ( s) ;
357+ if v1_res. is_err ( ) && detect_v2 ( s) {
358+ v2_res
359+ } else if detect_v1 ( s) {
360+ v1_res
361+ } else {
362+ // This does matter, because detection short-circuits.
363+ v2_res
364+ }
365+ } else {
366+ v2_res
367+ }
353368 KdlDocument :: parse_v2 ( s) . or_else ( |e| KdlDocument :: parse_v1 ( s) . map_err ( |_| e) )
354369 }
355370 }
@@ -455,6 +470,55 @@ impl From<kdlv1::KdlDocument> for KdlDocument {
455470 }
456471}
457472
473+ /// Applies heuristics to get an idea of whether the string might be intended to
474+ /// be v2.
475+ #[ allow( unused) ]
476+ pub ( crate ) fn detect_v2 ( input : & str ) -> bool {
477+ for line in input. lines ( ) {
478+ if line. contains ( "kdl-version 2" )
479+ || line. contains ( "#true" )
480+ || line. contains ( "#false" )
481+ || line. contains ( "#null" )
482+ || line. contains ( "#inf" )
483+ || line. contains ( "#-inf" )
484+ || line. contains ( "#nan" )
485+ || line. contains ( " #\" " )
486+ || line. contains ( "\" \" \" " )
487+ // Very very rough attempt at finding unquoted strings. We give up
488+ // the first time we see a quoted one on a line.
489+ || ( !line. contains ( '"' ) && line
490+ . split_whitespace ( )
491+ . skip ( 1 )
492+ . any ( |x| {
493+ x. chars ( )
494+ . next ( )
495+ . map ( |d| !d. is_ascii_digit ( ) && d != '-' && d != '+' )
496+ . unwrap_or_default ( )
497+ } ) )
498+ {
499+ return true ;
500+ }
501+ }
502+ false
503+ }
504+
505+ /// Applies heuristics to get an idea of whether the string might be intended to
506+ /// be v2.
507+ #[ allow( unused) ]
508+ pub ( crate ) fn detect_v1 ( input : & str ) -> bool {
509+ input
510+ . lines ( )
511+ . next ( )
512+ . map ( |l| l. contains ( "kdl-version 1" ) )
513+ . unwrap_or ( false )
514+ || input. contains ( " true" )
515+ || input. contains ( " false" )
516+ || input. contains ( " null" )
517+ || input. contains ( "r#\" " )
518+ || input. contains ( " \" \n " )
519+ || input. contains ( " \" \r \n " )
520+ }
521+
458522impl std:: str:: FromStr for KdlDocument {
459523 type Err = KdlError ;
460524
@@ -1109,91 +1173,8 @@ mirror_session #true
11091173"## ;
11101174 pretty_assertions:: assert_eq!( KdlDocument :: v1_to_v2( v1) ?, v2, "Converting a v1 doc to v2" ) ;
11111175 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 < ( ) > {
1118- let original = r##"
1119- // If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
1120- keybinds {
1121- normal {
1122- // uncomment this and adjust key if using copy_on_select=false
1123- // bind "Alt c" { Copy; }
1124- }
1125- locked {
1126- bind "Ctrl g" { SwitchToMode "Normal"; }
1127- }
1128- resize {
1129- bind "Ctrl n" { SwitchToMode "Normal"; }
1130- bind "h" "Left" { Resize "Increase Left"; }
1131- bind "j" "Down" { Resize "Increase Down"; }
1132- bind "k" "Up" { Resize "Increase Up"; }
1133- bind "l" "Right" { Resize "Increase Right"; }
1134- bind "H" { Resize "Decrease Left"; }
1135- bind "J" { Resize "Decrease Down"; }
1136- bind "K" { Resize "Decrease Up"; }
1137- bind "L" { Resize "Decrease Right"; }
1138- bind "=" "+" { Resize "Increase"; }
1139- bind "-" { Resize "Decrease"; }
1140- }
1141- }
1142- // Plugin aliases - can be used to change the implementation of Zellij
1143- // changing these requires a restart to take effect
1144- plugins {
1145- tab-bar location="zellij:tab-bar"
1146- status-bar location="zellij:status-bar"
1147- welcome-screen location="zellij:session-manager" {
1148- welcome_screen true
1149- }
1150- filepicker location="zellij:strider" {
1151- cwd "/"
1152- }
1153- }
1154- mouse_mode false
1155- mirror_session true
1156- "## ;
1157- let expected = r##"
1158- // If you'd like to override the default keybindings completely, be sure to change "keybinds" to "keybinds clear-defaults=true"
1159- keybinds {
1160- normal {
1161- // uncomment this and adjust key if using copy_on_select=false
1162- // bind "Alt c" { Copy; }
1163- }
1164- locked {
1165- bind "Ctrl g" { SwitchToMode Normal; }
1166- }
1167- resize {
1168- bind "Ctrl n" { SwitchToMode Normal; }
1169- bind h Left { Resize "Increase Left"; }
1170- bind j Down { Resize "Increase Down"; }
1171- bind k Up { Resize "Increase Up"; }
1172- bind l Right { Resize "Increase Right"; }
1173- bind H { Resize "Decrease Left"; }
1174- bind J { Resize "Decrease Down"; }
1175- bind K { Resize "Decrease Up"; }
1176- bind L { Resize "Decrease Right"; }
1177- bind "=" + { Resize Increase; }
1178- bind - { Resize Decrease; }
1179- }
1180- }
1181- // Plugin aliases - can be used to change the implementation of Zellij
1182- // changing these requires a restart to take effect
1183- plugins {
1184- tab-bar location=zellij:tab-bar
1185- status-bar location=zellij:status-bar
1186- welcome-screen location=zellij:session-manager {
1187- welcome_screen #true
1188- }
1189- filepicker location=zellij:strider {
1190- cwd "/"
1191- }
1192- }
1193- mouse_mode #false
1194- mirror_session #true
1195- "## ;
1196- pretty_assertions:: assert_eq!( KdlDocument :: v1_to_v2( original) ?, expected) ;
1176+ assert ! ( super :: detect_v1( v1) ) ;
1177+ assert ! ( super :: detect_v2( v2) ) ;
11971178 Ok ( ( ) )
11981179 }
11991180}
0 commit comments