v2 / vlib / v / fmt / struct.v
406 lines · 397 sloc · 10.69 KB · 9184983d06c103c470ace35390066fb017c66b11
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module fmt
5
6import v.ast
7
8pub fn (mut f Fmt) struct_decl(node ast.StructDecl, is_anon bool) {
9 f.attrs(node.attrs)
10 if node.is_pub && !is_anon {
11 f.write('pub ')
12 }
13 if node.is_option {
14 f.write('?')
15 }
16 if node.is_union {
17 f.write('union')
18 } else {
19 f.write('struct')
20 }
21 name := node.name.after('.') // strip prepended module
22 if !is_anon {
23 f.write(' ')
24 f.write_language_prefix(node.language)
25 f.write(name)
26 }
27 f.write_generic_types(node.generic_types)
28 if node.is_implements {
29 f.write(' implements ')
30 for i, t in node.implements_types {
31 f.write(f.table.type_to_str_using_aliases(t.typ, f.mod2alias))
32 if i < node.implements_types.len - 1 {
33 f.write(', ')
34 }
35 }
36 }
37 if node.fields.len == 0 && node.embeds.len == 0 && node.pos.line_nr == node.pos.last_line {
38 f.writeln(' {}')
39 return
40 }
41 mut type_align := new_field_align(use_break_line: true)
42 mut default_expr_align := new_field_align(use_threshold: true)
43 mut attr_align := new_field_align(use_threshold: true)
44 mut comment_align := new_field_align(use_threshold: true)
45 mut field_types := []string{cap: node.fields.len}
46 // Calculate the alignments first
47 f.calculate_alignment(node.fields, mut type_align, mut comment_align, mut default_expr_align, mut
48 attr_align, mut field_types)
49 f.writeln(' {')
50 if node.pre_comments.len > 0 {
51 f.comments_before_field(node.pre_comments)
52 }
53 for embed in node.embeds {
54 styp := f.table.type_to_str_using_aliases(embed.typ, f.mod2alias)
55
56 pre_comments := embed.comments.filter(it.pos.pos < embed.pos.pos)
57 comments := embed.comments[pre_comments.len..]
58
59 f.comments_before_field(pre_comments)
60 if comments.len == 0 {
61 f.writeln('\t${styp}')
62 } else {
63 f.write('\t${styp}')
64 f.comments(comments, level: .indent)
65 }
66 }
67 // Now handle each field
68 mut inc_indent := false // for correct indents with multi line default exprs
69 for i, field in node.fields {
70 match true {
71 i == node.mut_pos {
72 f.writeln('mut:')
73 }
74 i == node.pub_pos {
75 f.writeln('pub:')
76 }
77 i == node.pub_mut_pos {
78 f.writeln('pub mut:')
79 }
80 i == node.global_pos {
81 f.writeln('__global:')
82 }
83 i == node.module_pos {
84 f.writeln('module:')
85 }
86 i > 0 && field.has_prev_newline {
87 f.writeln('')
88 }
89 else {}
90 }
91
92 // Handle comments before the field
93 if field.pre_comments.len > 0 {
94 f.comments(field.pre_comments, level: .indent)
95 }
96 volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
97 f.write('\t${volatile_prefix}${field.name} ')
98 f.write(' '.repeat(type_align.max_len(field.pos.line_nr) - field.name.len))
99 // Handle anon structs recursively
100 if !f.write_anon_struct_field_decl(field.typ, field.anon_struct_decl) {
101 f.write(field_types[i])
102 }
103 attrs_len := inline_attrs_len(field.attrs)
104 if field.has_default_expr {
105 f.write(' '.repeat(default_expr_align.max_len(field.pos.line_nr) - field_types[i].len))
106 f.write(' = ')
107 default_expr_pos := field.default_expr.pos()
108 if default_expr_pos.line_nr < default_expr_pos.last_line
109 || !expr_is_single_line(field.default_expr) {
110 f.indent++
111 inc_indent = true
112 }
113 f.expr(field.default_expr)
114 if inc_indent {
115 f.indent--
116 inc_indent = false
117 }
118 }
119 if field.attrs.len > 0 {
120 f.write(' '.repeat(attr_align.max_len(field.pos.line_nr) - field_types[i].len))
121 f.single_line_attrs(field.attrs, same_line: true)
122 }
123 // Handle comments at the end of the line
124 if field.comments.len > 0 {
125 if field.has_default_expr {
126 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) -
127 field.default_expr.str().len - 2))
128 } else if field.attrs.len > 0 {
129 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - attrs_len))
130 } else {
131 f.write(' '.repeat(comment_align.max_len(field.pos.line_nr) - field_types[i].len))
132 }
133 f.write(' ')
134 f.comments(field.comments, level: .indent)
135 } else {
136 f.writeln('')
137 }
138 // Handle comments on the next lines
139 if field.next_comments.len > 0 {
140 f.comments(field.next_comments, level: .indent)
141 }
142 }
143 if is_anon || node.end_comments.len > 0 {
144 f.write('}')
145 } else {
146 f.writeln('}')
147 }
148 if node.end_comments.len > 0 {
149 f.comments(node.end_comments, same_line: true)
150 }
151}
152
153fn (mut f Fmt) write_anon_struct_field_decl(field_typ ast.Type, field_anon_decl ast.StructDecl) bool {
154 sym := f.table.sym(field_typ)
155 match sym.kind {
156 .struct {
157 info := sym.info as ast.Struct
158 if info.is_anon {
159 f.indent++
160 if info.is_shared {
161 f.write('shared ')
162 }
163 f.struct_decl(field_anon_decl, true)
164 f.indent--
165 return true
166 }
167 }
168 .array {
169 if sym.info is ast.Array {
170 elem_sym := f.table.sym(sym.info.elem_type)
171 if elem_sym.info is ast.Struct {
172 if elem_sym.info.is_anon {
173 if field_typ.has_flag(.option) {
174 f.write('?')
175 }
176 f.write('[]'.repeat(sym.info.nr_dims))
177 f.write_anon_struct_field_decl(sym.info.elem_type, field_anon_decl)
178 return true
179 }
180 }
181 }
182 }
183 .array_fixed {
184 if sym.info is ast.ArrayFixed {
185 elem_sym := f.table.sym(sym.info.elem_type)
186 if elem_sym.info is ast.Struct {
187 if elem_sym.info.is_anon {
188 f.write('[${sym.info.size}]')
189 f.write_anon_struct_field_decl(sym.info.elem_type, field_anon_decl)
190 return true
191 }
192 }
193 }
194 }
195 else {}
196 }
197
198 return false
199}
200
201fn (mut f Fmt) write_anon_struct_type(typ ast.Type) bool {
202 sym := f.table.sym(typ)
203 if sym.info is ast.Struct && sym.info.is_anon {
204 f.struct_decl(ast.StructDecl{
205 fields: sym.info.fields
206 }, true)
207 return true
208 }
209 return false
210}
211
212pub fn (mut f Fmt) struct_init(node ast.StructInit) {
213 struct_init_save := f.is_struct_init
214 f.is_struct_init = true
215 defer {
216 f.is_struct_init = struct_init_save
217 }
218 use_type_expr := node.typ_expr !is ast.EmptyExpr && node.typ == ast.void_type
219 sym_name := if use_type_expr { '' } else { f.table.sym(node.typ).name }
220 // f.write('<old name: ${type_sym.name}>')
221 mut name := if !sym_name.starts_with('C.') && !sym_name.starts_with('JS.') {
222 f.no_cur_mod(f.short_module(sym_name)) // TODO: f.type_to_str?
223 } else {
224 sym_name
225 }
226 if name == 'void' {
227 name = ''
228 }
229 if node.typ.has_flag(.option) {
230 f.write('?')
231 }
232 if node.is_anon {
233 // Write the full anonymous struct definition inline, e.g.:
234 // `struct { foo string; bar int }{}`
235 sym := f.table.sym(node.typ)
236 if sym.info is ast.Struct && sym.info.is_anon {
237 f.writeln('struct {')
238 f.indent++
239 for field in sym.info.fields {
240 f.write(field.name)
241 f.write(' ')
242 f.write(f.table.type_to_str_using_aliases(field.typ, f.mod2alias))
243 f.writeln('')
244 }
245 f.indent--
246 f.write('}')
247 if node.init_fields.len == 0 && !node.has_update_expr {
248 f.write('{}')
249 } else {
250 f.writeln('{')
251 f.indent++
252 for init_field in node.init_fields {
253 f.write('${init_field.name}: ')
254 f.expr(init_field.expr)
255 f.writeln('')
256 }
257 f.indent--
258 f.write('}')
259 }
260 return
261 }
262 f.write('struct ')
263 }
264 if node.init_fields.len == 0 && !node.has_update_expr {
265 // `Foo{}` on one line if there are no fields or comments
266 if node.pre_comments.len == 0 {
267 if use_type_expr {
268 f.expr(node.typ_expr)
269 f.write('{}')
270 } else {
271 f.write('${name}{}')
272 }
273 } else {
274 if use_type_expr {
275 f.expr(node.typ_expr)
276 f.writeln('{')
277 } else {
278 f.writeln('${name}{')
279 }
280 f.comments(node.pre_comments, same_line: true, has_nl: true, level: .indent)
281 f.write('}')
282 }
283 } else if node.no_keys {
284 // `Foo{1,2,3}` (short syntax, no keys)
285 if use_type_expr {
286 f.expr(node.typ_expr)
287 f.write('{')
288 } else {
289 f.write('${name}{')
290 }
291 if node.has_update_expr {
292 f.write('...')
293 f.expr(node.update_expr)
294 f.write(', ')
295 }
296 for i, init_field in node.init_fields {
297 f.expr(init_field.expr)
298 if i < node.init_fields.len - 1 {
299 f.write(', ')
300 }
301 }
302 f.write('}')
303 } else {
304 use_short_args := f.use_short_fn_args && !node.has_update_expr
305 f.use_short_fn_args = false
306 mut single_line_fields := f.single_line_fields
307 f.single_line_fields = false
308 if node.pos.line_nr < node.pos.last_line || node.pre_comments.len > 0 {
309 single_line_fields = false
310 }
311 if !use_short_args || node.is_anon {
312 if use_type_expr {
313 f.expr(node.typ_expr)
314 f.write('{')
315 } else {
316 f.write('${name}{')
317 }
318 if single_line_fields {
319 f.write(' ')
320 }
321 }
322 fields_start := f.out.len
323 fields_loop: for {
324 if !single_line_fields {
325 if use_short_args && f.out.last() == ` ` {
326 // v Remove space at tail of line
327 // f(a, b, c, \n
328 // f1: 0\n
329 // f2: 1\n
330 // )
331 f.out.go_back(1)
332 }
333 f.writeln('')
334 f.indent++
335 }
336 f.comments(node.pre_comments, same_line: true, has_nl: true, level: .keep)
337 if node.has_update_expr {
338 f.write('...')
339 f.expr(node.update_expr)
340 if single_line_fields {
341 if node.init_fields.len > 0 {
342 f.write(', ')
343 }
344 } else {
345 f.writeln('')
346 }
347 f.comments(node.update_expr_comments, same_line: true, has_nl: true, level: .keep)
348 }
349 mut value_align := new_field_align(use_break_line: true)
350 mut comment_align := new_field_align(use_threshold: true)
351 for init_field in node.init_fields {
352 value_align.add_info(init_field.name.len, init_field.pos.line_nr,
353 init_field.has_break_line)
354 if init_field.end_comments.len > 0 {
355 comment_align.add_info(init_field.expr.str().len, init_field.pos.line_nr,
356 init_field.has_break_line)
357 }
358 }
359 for i, init_field in node.init_fields {
360 if i > 0 && init_field.has_prev_newline {
361 f.writeln('')
362 }
363 if init_field.pre_comments.len > 0 {
364 f.comments(init_field.pre_comments, has_nl: true, level: .keep)
365 }
366 f.write('${init_field.name}: ')
367 if !single_line_fields {
368 f.write(' '.repeat(value_align.max_len(init_field.pos.line_nr) - init_field.name.len))
369 }
370 f.expr(init_field.expr)
371 if init_field.end_comments.len > 0 {
372 f.write(' '.repeat(comment_align.max_len(init_field.pos.line_nr) -
373 init_field.expr.str().len + 1))
374 f.comments(init_field.end_comments, has_nl: false, level: .indent)
375 }
376 if single_line_fields {
377 if i < node.init_fields.len - 1 {
378 f.write(', ')
379 }
380 } else {
381 f.writeln('')
382 }
383 f.comments(init_field.next_comments, has_nl: true, level: .keep)
384 if single_line_fields && (init_field.end_comments.len > 0
385 || init_field.next_comments.len > 0
386 || !expr_is_single_line(init_field.expr) || f.line_len > max_len) {
387 single_line_fields = false
388 f.out.go_back_to(fields_start)
389 f.line_len = fields_start
390 f.remove_new_line()
391 continue fields_loop
392 }
393 }
394 break
395 }
396 if !single_line_fields {
397 f.indent--
398 }
399 if !use_short_args || node.is_anon {
400 if single_line_fields {
401 f.write(' ')
402 }
403 f.write('}')
404 }
405 }
406}
407