v2 / vlib / v / fmt / attrs.v
171 lines · 158 sloc · 3.7 KB · e8d07140a8d39ea7de6c8a81795534adf80a5f60
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) attrs(attrs []ast.Attr) {
9 if attrs_have_call_syntax(attrs) {
10 f.call_syntax_attrs(attrs)
11 return
12 }
13 f.legacy_attrs(attrs)
14}
15
16fn (mut f Fmt) legacy_attrs(attrs []ast.Attr) {
17 mut sorted_attrs := attrs.clone()
18 // Sort the attributes. The ones with arguments come first
19 sorted_attrs.sort_with_compare(fn (a &ast.Attr, b &ast.Attr) int {
20 d := b.arg.len - a.arg.len
21 return if d != 0 { d } else { compare_strings(b.arg, a.arg) }
22 })
23 for i, attr in sorted_attrs {
24 if attr.arg.len == 0 {
25 f.single_line_attrs(sorted_attrs[i..])
26 break
27 }
28 f.writeln('@[${attr}]')
29 }
30}
31
32fn (mut f Fmt) call_syntax_attrs(attrs []ast.Attr) {
33 mut i := 0
34 for i < attrs.len {
35 if attrs[i].call_name.len > 0 {
36 group, next_idx := attr_call_group(attrs, i)
37 f.writeln('@[${attr_call_group_str(group)}]')
38 i = next_idx
39 continue
40 }
41 mut j := i
42 for j < attrs.len && attrs[j].call_name.len == 0 {
43 j++
44 }
45 f.legacy_attrs(attrs[i..j])
46 i = j
47 }
48}
49
50@[params]
51pub struct AttrsOptions {
52pub:
53 same_line bool
54}
55
56pub fn (mut f Fmt) single_line_attrs(attrs []ast.Attr, options AttrsOptions) {
57 if attrs.len == 0 {
58 return
59 }
60 if attrs_have_call_syntax(attrs) {
61 if options.same_line {
62 f.write(' ')
63 }
64 f.write('@[')
65 f.write(single_line_attrs_text(attrs))
66 f.write(']')
67 if !options.same_line {
68 f.writeln('')
69 }
70 return
71 }
72 f.legacy_single_line_attrs(attrs, options)
73}
74
75fn (mut f Fmt) legacy_single_line_attrs(attrs []ast.Attr, options AttrsOptions) {
76 mut sorted_attrs := attrs.clone()
77 sorted_attrs.sort(a.name < b.name)
78 if options.same_line {
79 f.write(' ')
80 }
81 f.write('@[')
82 for i, attr in sorted_attrs {
83 if i > 0 {
84 f.write('; ')
85 }
86 f.write('${attr}')
87 }
88 f.write(']')
89 if !options.same_line {
90 f.writeln('')
91 }
92}
93
94fn inline_attrs_len(attrs []ast.Attr) int {
95 if attrs.len == 0 {
96 return 0
97 }
98 return 3 + single_line_attrs_text(attrs).len // ' [' + ']'.len
99}
100
101fn attrs_have_call_syntax(attrs []ast.Attr) bool {
102 return attrs.any(it.call_name.len > 0)
103}
104
105fn single_line_attrs_text(attrs []ast.Attr) string {
106 if !attrs_have_call_syntax(attrs) {
107 mut sorted_attrs := attrs.clone()
108 sorted_attrs.sort(a.name < b.name)
109 mut parts := []string{cap: sorted_attrs.len}
110 for attr in sorted_attrs {
111 parts << '${attr}'
112 }
113 return parts.join('; ')
114 }
115 mut parts := []string{}
116 mut i := 0
117 for i < attrs.len {
118 if attrs[i].call_name.len > 0 {
119 group, next_idx := attr_call_group(attrs, i)
120 parts << attr_call_group_str(group)
121 i = next_idx
122 continue
123 }
124 parts << '${attrs[i]}'
125 i++
126 }
127 return parts.join('; ')
128}
129
130fn attr_call_group(attrs []ast.Attr, start int) ([]ast.Attr, int) {
131 first := attrs[start]
132 mut end := start + 1
133 for end < attrs.len && attrs[end].call_name == first.call_name
134 && attrs[end].pos.pos == first.pos.pos {
135 end++
136 }
137 return attrs[start..end], end
138}
139
140fn attr_call_group_str(attrs []ast.Attr) string {
141 if attrs.len == 0 {
142 return ''
143 }
144 mut ordered_attrs := attrs.clone()
145 ordered_attrs.sort(a.call_arg_idx < b.call_arg_idx)
146 mut args := []string{}
147 for attr in ordered_attrs {
148 if !attr.has_arg {
149 continue
150 }
151 mut arg := ''
152 if attr.call_arg_name.len > 0 {
153 arg += '${attr.call_arg_name}: '
154 }
155 arg += attr_value_str(attr)
156 args << arg
157 }
158 if args.len == 0 {
159 return '${attrs[0].call_name}()'
160 }
161 return '${attrs[0].call_name}(${args.join(', ')})'
162}
163
164fn attr_value_str(attr ast.Attr) string {
165 quote := if attr.quote == `"` { '"' } else { "'" }
166 return match attr.kind {
167 .plain, .number, .bool { attr.arg }
168 .string { '${quote}${attr.arg}${quote}' }
169 .comptime_define { 'if ${attr.arg}' }
170 }
171}
172