@@ -28,7 +28,14 @@ pub fn format_text(
28
28
return Ok ( None ) ;
29
29
} ;
30
30
31
- Ok ( format_root ( input_text, & root_value, format_with_host) )
31
+ Ok ( match format_root ( input_text, & root_value, format_with_host) {
32
+ Some ( text) => {
33
+ #[ cfg( debug_assertions) ]
34
+ validate_output_json ( & text) ?;
35
+ Some ( text)
36
+ }
37
+ None => None ,
38
+ } )
32
39
}
33
40
34
41
fn format_root (
@@ -53,6 +60,29 @@ fn format_root(
53
60
}
54
61
}
55
62
63
+ #[ cfg( debug_assertions) ]
64
+ fn validate_output_json ( text : & str ) -> Result < ( ) > {
65
+ // ensures the output is correct in debug mode
66
+ let result = jsonc_parser:: parse_to_ast (
67
+ text,
68
+ & CollectOptions {
69
+ comments : false ,
70
+ tokens : false ,
71
+ } ,
72
+ & ParseOptions {
73
+ allow_comments : true ,
74
+ allow_loose_object_property_names : false ,
75
+ allow_trailing_commas : true ,
76
+ } ,
77
+ ) ;
78
+ match result {
79
+ Ok ( _) => Ok ( ( ) ) ,
80
+ Err ( err) => {
81
+ anyhow:: bail!( "dprint-plugin-jupyter produced invalid json. Please open an issue with reproduction steps at https://github.com/dprint/dprint-plugin-jupyter/issues\n {:#}\n \n == TEXT ==\n {}" , err, text) ;
82
+ }
83
+ }
84
+ }
85
+
56
86
fn get_cell_text_change (
57
87
file_text : & str ,
58
88
cell : & jsonc_parser:: ast:: Value ,
@@ -74,7 +104,11 @@ fn get_cell_text_change(
74
104
// many plugins will add a final newline, but that doesn't look nice in notebooks, so trim it off
75
105
let formatted_text = formatted_text. trim_end ( ) ;
76
106
77
- let new_text = build_json_text ( formatted_text, code_block. indent_text ) ;
107
+ let new_text = if code_block. is_array {
108
+ build_array_json_text ( formatted_text, code_block. indent_text )
109
+ } else {
110
+ serde_json:: to_string ( & formatted_text) . unwrap ( )
111
+ } ;
78
112
79
113
Some ( TextChange {
80
114
range : code_block. replace_range ,
@@ -83,6 +117,9 @@ fn get_cell_text_change(
83
117
}
84
118
85
119
struct CodeBlockText < ' a > {
120
+ // Can be either a string or an array of strings.
121
+ // (https://github.com/jupyter/nbformat/blob/0708dd627d9ef81b12f231defb0d94dd7e80e3f4/nbformat/v4/nbformat.v4.5.schema.json#L460C7-L468C8)
122
+ is_array : bool ,
86
123
indent_text : & ' a str ,
87
124
replace_range : std:: ops:: Range < usize > ,
88
125
source : String ,
@@ -91,8 +128,10 @@ struct CodeBlockText<'a> {
91
128
fn analyze_code_block < ' a > ( cell : & jsonc_parser:: ast:: Object < ' a > , file_text : & ' a str ) -> Option < CodeBlockText < ' a > > {
92
129
let mut indent_text = "" ;
93
130
let mut replace_range = std:: ops:: Range :: default ( ) ;
131
+ let mut is_array = false ;
94
132
let cell_source = match & cell. get ( "source" ) ?. value {
95
133
jsonc_parser:: ast:: Value :: Array ( items) => {
134
+ is_array = true ;
96
135
let mut strings = Vec :: with_capacity ( items. elements . len ( ) ) ;
97
136
for ( i, element) in items. elements . iter ( ) . enumerate ( ) {
98
137
let string_lit = element. as_string_lit ( ) ?;
@@ -112,18 +151,22 @@ fn analyze_code_block<'a>(cell: &jsonc_parser::ast::Object<'a>, file_text: &'a s
112
151
}
113
152
text
114
153
}
115
- jsonc_parser:: ast:: Value :: StringLit ( string) => string. value . to_string ( ) ,
154
+ jsonc_parser:: ast:: Value :: StringLit ( string) => {
155
+ replace_range = string. range . start ..string. range . end ;
156
+ string. value . to_string ( )
157
+ }
116
158
_ => return None ,
117
159
} ;
118
160
Some ( CodeBlockText {
161
+ is_array,
119
162
indent_text,
120
163
replace_range,
121
164
source : cell_source,
122
165
} )
123
166
}
124
167
125
168
/// Turn the formatted text into a json array, split up by line breaks.
126
- fn build_json_text ( formatted_text : & str , indent_text : & str ) -> String {
169
+ fn build_array_json_text ( formatted_text : & str , indent_text : & str ) -> String {
127
170
let mut new_text = String :: new ( ) ;
128
171
let mut current_end_index = 0 ;
129
172
for ( i, line) in formatted_text. split ( '\n' ) . enumerate ( ) {
0 commit comments