@@ -22,111 +22,180 @@ use std::path::Path;
22
22
23
23
use super :: { fmt, writer} ;
24
24
25
- pub fn run ( files : Vec < std:: path:: PathBuf > , inplace : bool ) -> std:: io:: Result < ( ) > {
25
+ pub fn run ( files : Vec < std:: path:: PathBuf > , inplace : bool ) -> std:: io:: Result < usize > {
26
+ let mut changed_or_error_files = 0 ;
27
+
26
28
for path in files {
27
- let source = std:: fs:: read_to_string ( & path) ?;
29
+ let source = std:: fs:: read ( & path) ?;
30
+
31
+ match process_file ( source. clone ( ) , & path) {
32
+ Ok ( result) if result == source => { }
33
+ Ok ( result) => {
34
+ changed_or_error_files += 1 ;
28
35
29
- if inplace {
30
- let file = BufWriter :: new ( std:: fs:: File :: create ( & path) ?) ;
31
- process_file ( source, path, file) ?
32
- } else {
33
- process_file ( source, path, std:: io:: stdout ( ) ) ?
36
+ if inplace {
37
+ let mut file = BufWriter :: new ( std:: fs:: File :: create ( & path) ?) ;
38
+ file. write_all ( & result) ?;
39
+ } else {
40
+ std:: io:: stdout ( ) . write_all ( & result) ?;
41
+ }
42
+ }
43
+ Err ( e) => {
44
+ println ! ( "Error in {path:?}: {e}" ) ;
45
+ changed_or_error_files += 1 ;
46
+ }
34
47
}
35
48
}
36
- Ok ( ( ) )
49
+ Ok ( changed_or_error_files )
37
50
}
38
51
39
52
/// FIXME! this is duplicated with the updater
40
- fn process_rust_file ( source : String , mut file : impl Write ) -> std:: io:: Result < ( ) > {
53
+ fn process_rust_file ( source : Vec < u8 > ) -> std:: io:: Result < Vec < u8 > > {
54
+ let mut result = String :: new ( ) ;
55
+ let source =
56
+ String :: from_utf8 ( source) . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
41
57
let mut last = 0 ;
42
58
for range in i_slint_compiler:: lexer:: locate_slint_macro ( & source) {
43
- file . write_all ( source[ last..=range. start ] . as_bytes ( ) ) ? ;
59
+ result . push_str ( & source[ last..=range. start ] ) ;
44
60
last = range. end ;
45
61
let code = & source[ range] ;
46
62
47
63
let mut diag = BuildDiagnostics :: default ( ) ;
48
64
let syntax_node = i_slint_compiler:: parser:: parse ( code. to_owned ( ) , None , & mut diag) ;
49
65
let len = syntax_node. text_range ( ) . end ( ) . into ( ) ;
50
- visit_node ( syntax_node, & mut file) ?;
66
+ result. push_str ( & visit_node ( syntax_node) ?) ;
67
+
51
68
if diag. has_errors ( ) {
52
- file . write_all ( & code. as_bytes ( ) [ len..] ) ? ;
69
+ result . push_str ( & code[ len..] ) ;
53
70
diag. print ( ) ;
71
+ return Err ( std:: io:: Error :: new (
72
+ std:: io:: ErrorKind :: Other ,
73
+ String :: from ( "Failed to parse file" ) ,
74
+ ) ) ;
75
+ }
76
+ }
77
+ result. push_str ( & source[ last..] ) ;
78
+ Ok ( result. as_bytes ( ) . to_vec ( ) )
79
+ }
80
+
81
+ fn find_slint_code_in_markdown ( content : & [ u8 ] ) -> Option < ( usize , usize ) > {
82
+ const CODE_FENCE_START : & [ u8 ] = b"```slint\n " ;
83
+ const CODE_FENCE_END : & [ u8 ] = b"```\n " ;
84
+
85
+ let mut it = content. iter ( ) . enumerate ( ) ;
86
+
87
+ let mut fence_offset = 0 ;
88
+
89
+ let mut code_start = usize:: MAX ;
90
+ let mut code_end = usize:: MAX ;
91
+
92
+ #[ allow( clippy:: while_let_on_iterator) ]
93
+ while let Some ( ( idx, b) ) = it. next ( ) {
94
+ if * b == CODE_FENCE_START [ fence_offset] {
95
+ fence_offset += 1 ;
96
+ }
97
+
98
+ if fence_offset == CODE_FENCE_START . len ( ) {
99
+ code_start = idx + 1 ;
100
+ break ;
101
+ }
102
+ }
103
+
104
+ fence_offset = 0 ;
105
+ let mut possible_end_offset = 0 ;
106
+
107
+ #[ allow( clippy:: while_let_on_iterator) ]
108
+ while let Some ( ( idx, b) ) = it. next ( ) {
109
+ if * b == CODE_FENCE_END [ fence_offset] {
110
+ if fence_offset == 0 {
111
+ possible_end_offset = idx - 1 ;
112
+ }
113
+ fence_offset += 1 ;
114
+ }
115
+
116
+ if fence_offset == CODE_FENCE_END . len ( ) {
117
+ code_end = possible_end_offset;
118
+ break ;
54
119
}
55
120
}
56
- file. write_all ( source[ last..] . as_bytes ( ) )
121
+
122
+ if code_end != usize:: MAX {
123
+ Some ( ( code_start, code_end) )
124
+ } else {
125
+ None
126
+ }
57
127
}
58
128
59
129
/// FIXME! this is duplicated with the updater
60
- fn process_markdown_file ( source : String , mut file : impl Write ) -> std:: io:: Result < ( ) > {
130
+ fn process_markdown_file ( source : Vec < u8 > ) -> std:: io:: Result < Vec < u8 > > {
131
+ let mut result = Vec :: new ( ) ;
132
+
61
133
let mut source_slice = & source[ ..] ;
62
- const CODE_FENCE_START : & str = "```slint\n " ;
63
- const CODE_FENCE_END : & str = "```\n " ;
64
- ' l: while let Some ( code_start) =
65
- source_slice. find ( CODE_FENCE_START ) . map ( |idx| idx + CODE_FENCE_START . len ( ) )
66
- {
67
- let code_end = if let Some ( code_end) = source_slice[ code_start..] . find ( CODE_FENCE_END ) {
68
- code_end
69
- } else {
70
- break ' l;
71
- } ;
72
- file. write_all ( source_slice[ ..=code_start - 1 ] . as_bytes ( ) ) ?;
73
- source_slice = & source_slice[ code_start..] ;
74
- let code = & source_slice[ ..code_end] ;
134
+ while let Some ( ( code_start, code_end) ) = find_slint_code_in_markdown ( source_slice) {
135
+ result. extend ( & source_slice[ ..code_start] ) ;
136
+ let code = Vec :: from ( & source_slice[ code_start..code_end] ) ;
137
+ let code = String :: from_utf8 ( code)
138
+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
75
139
source_slice = & source_slice[ code_end..] ;
76
140
77
141
let mut diag = BuildDiagnostics :: default ( ) ;
78
- let syntax_node = i_slint_compiler:: parser:: parse ( code. to_owned ( ) , None , & mut diag) ;
79
- let len = syntax_node. text_range ( ) . end ( ) . into ( ) ;
80
- visit_node ( syntax_node, & mut file) ?;
142
+ let syntax_node = i_slint_compiler:: parser:: parse ( code, None , & mut diag) ;
143
+
144
+ result. extend ( visit_node ( syntax_node) ?. as_bytes ( ) ) ;
145
+
81
146
if diag. has_errors ( ) {
82
- file. write_all ( & code. as_bytes ( ) [ len..] ) ?;
83
147
diag. print ( ) ;
148
+ return Err ( std:: io:: Error :: new (
149
+ std:: io:: ErrorKind :: Other ,
150
+ String :: from ( "Failed to parse file" ) ,
151
+ ) ) ;
84
152
}
85
153
}
86
- file. write_all ( source_slice. as_bytes ( ) )
154
+ result. extend ( source_slice) ;
155
+ Ok ( result)
87
156
}
88
157
89
- fn process_slint_file (
90
- source : String ,
91
- path : std:: path:: PathBuf ,
92
- mut file : impl Write ,
93
- ) -> std:: io:: Result < ( ) > {
158
+ fn process_slint_file ( source : Vec < u8 > , path : & std:: path:: Path ) -> std:: io:: Result < Vec < u8 > > {
94
159
let mut diag = BuildDiagnostics :: default ( ) ;
95
- let syntax_node = i_slint_compiler:: parser:: parse ( source. clone ( ) , Some ( & path) , & mut diag) ;
96
- let len = syntax_node. node . text_range ( ) . end ( ) . into ( ) ;
97
- visit_node ( syntax_node, & mut file) ?;
160
+ let source =
161
+ String :: from_utf8 ( source) . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
162
+
163
+ let syntax_node = i_slint_compiler:: parser:: parse ( source, Some ( path) , & mut diag) ;
164
+
165
+ let result = visit_node ( syntax_node) ?. as_bytes ( ) . to_vec ( ) ;
166
+
98
167
if diag. has_errors ( ) {
99
- file. write_all ( & source. as_bytes ( ) [ len..] ) ?;
100
168
diag. print ( ) ;
169
+ return Err ( std:: io:: Error :: new (
170
+ std:: io:: ErrorKind :: Other ,
171
+ String :: from ( "Failed to parse file" ) ,
172
+ ) ) ;
101
173
}
102
- Ok ( ( ) )
174
+ Ok ( result )
103
175
}
104
176
105
- fn process_file (
106
- source : String ,
107
- path : std:: path:: PathBuf ,
108
- mut file : impl Write ,
109
- ) -> std:: io:: Result < ( ) > {
177
+ fn process_file ( source : Vec < u8 > , path : & std:: path:: Path ) -> std:: io:: Result < Vec < u8 > > {
110
178
match path. extension ( ) {
111
- Some ( ext) if ext == "rs" => process_rust_file ( source, file ) ,
112
- Some ( ext) if ext == "md" => process_markdown_file ( source, file ) ,
179
+ Some ( ext) if ext == "rs" => process_rust_file ( source) ,
180
+ Some ( ext) if ext == "md" => process_markdown_file ( source) ,
113
181
// Formatting .60 files because of backwards compatibility (project was recently renamed)
114
- Some ( ext) if ext == "slint" || ext == ".60" => process_slint_file ( source, path, file ) ,
182
+ Some ( ext) if ext == "slint" || ext == ".60" => process_slint_file ( source, path) ,
115
183
_ => {
116
- // This allows usage like `cat x.slint | slint-lsp format /dev/stdin `
117
- if path. as_path ( ) == Path :: new ( "/dev/stdin" ) {
118
- return process_slint_file ( source, path, file ) ;
184
+ // This allows usage like `cat x.slint | slint-lsp format - `
185
+ if path == Path :: new ( "/dev/stdin" ) || path == Path :: new ( "- ") {
186
+ return process_slint_file ( source, path) ;
119
187
}
120
- // With other file types, we just output them in their original form.
121
- file . write_all ( source. as_bytes ( ) )
188
+
189
+ Ok ( source. to_vec ( ) )
122
190
}
123
191
}
124
192
}
125
193
126
- fn visit_node ( node : SyntaxNode , file : & mut impl Write ) -> std:: io:: Result < ( ) > {
194
+ fn visit_node ( node : SyntaxNode ) -> std:: io:: Result < String > {
127
195
if let Some ( doc) = syntax_nodes:: Document :: new ( node) {
128
- let mut writer = writer:: FileWriter { file } ;
129
- fmt:: format_document ( doc, & mut writer)
196
+ let mut writer = writer:: StringWriter :: default ( ) ;
197
+ fmt:: format_document ( doc, & mut writer) ?;
198
+ Ok ( writer. finalize ( ) )
130
199
} else {
131
200
Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , "Not a Document" ) )
132
201
}
0 commit comments