v2 / vlib / v / fmt / comments.v
138 lines · 129 sloc · 3.39 KB · 2ac347829605c88232f7c7a61449731a5137ab1d
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 enum CommentsLevel {
9 keep
10 indent
11}
12
13// CommentsOptions defines the way comments are going to be written
14// - has_nl: adds an newline at the end of a list of comments
15// - same_line: line comments will be on the same line as the last statement
16// - level: either .keep (don't indent), or .indent (increment indentation)
17// - prev_line: the line number of the previous token to save linebreaks
18@[minify; params]
19pub struct CommentsOptions {
20 has_nl bool = true
21 same_line bool
22 level CommentsLevel
23 prev_line int = -1
24}
25
26pub fn (mut f Fmt) comment(node ast.Comment, options CommentsOptions) {
27 if node.text.starts_with('\x01 vfmt on') {
28 f.vfmt_on(node.pos.line_nr)
29 }
30 defer {
31 // ensure that the `vfmt off` comment itself was sent to the output,
32 // by deferring the check for that state transition:
33 if node.text.starts_with('\x01 vfmt off') {
34 f.vfmt_off(node.pos.line_nr)
35 }
36 }
37 // Shebang in .vsh files
38 if node.text.starts_with('#!') {
39 f.writeln(node.text)
40 return
41 }
42 if options.level == .indent {
43 f.indent++
44 }
45 if !node.text.contains('\n') {
46 is_separate_line := !options.same_line || node.text.starts_with('\x01')
47 mut s := node.text.trim_left('\x01').trim_right(' ')
48 mut out_s := '//'
49 if s != '' {
50 if is_char_alphanumeric(s[0]) {
51 out_s += ' '
52 }
53 out_s += s
54 }
55 if !is_separate_line && f.indent > 0 {
56 f.remove_new_line() // delete the generated \n
57 f.write(' ')
58 }
59 f.write(out_s)
60 } else {
61 lines := node.text.split_into_lines()
62 f.write('/*')
63 for i, line in lines {
64 f.empty_line = false
65 if i == lines.len - 1 {
66 f.write(line)
67 if node.text[node.text.len - 1] == `\n` {
68 f.writeln('')
69 }
70 f.write('*/')
71 } else {
72 f.writeln(line.trim_right(' '))
73 }
74 }
75 }
76 if options.level == .indent {
77 f.indent--
78 }
79}
80
81pub fn (mut f Fmt) comments(comments []ast.Comment, options CommentsOptions) {
82 mut prev_line := options.prev_line
83 for i, c in comments {
84 if options.prev_line > -1
85 && ((c.pos.line_nr > prev_line && f.out.len > 1 && f.out.last_n(1) != '\n')
86 || (c.pos.line_nr > prev_line + 1 && f.out.len > 2 && f.out.last_n(2) != '\n\n')) {
87 f.writeln('')
88 }
89 if i == 0 && f.out.len > 1 && !f.out.last_n(1)[0].is_space() {
90 f.write(' ')
91 }
92 f.comment(c, options)
93 if i < comments.len - 1 || options.has_nl {
94 f.writeln('')
95 }
96 prev_line = c.pos.last_line
97 }
98}
99
100pub fn (mut f Fmt) comments_before_field(comments []ast.Comment) {
101 // They behave the same as comments after the last field. This alias is just for clarity.
102 f.comments_after_last_field(comments)
103}
104
105pub fn (mut f Fmt) comments_after_last_field(comments []ast.Comment) {
106 for comment in comments {
107 f.indent++
108 f.empty_line = true
109 f.comment(comment, same_line: true)
110 f.writeln('')
111 f.indent--
112 }
113}
114
115pub fn (mut f Fmt) import_comments(comments []ast.Comment, options CommentsOptions) {
116 if comments.len == 0 {
117 return
118 }
119 if options.same_line {
120 f.remove_new_line()
121 }
122 for c in comments {
123 ctext := c.text.trim_left('\x01')
124 if ctext == '' {
125 continue
126 }
127 mut out_s := if options.same_line { ' ' } else { '' } + '//'
128 if is_char_alphanumeric(ctext[0]) {
129 out_s += ' '
130 }
131 out_s += ctext
132 f.writeln(out_s)
133 }
134}
135
136fn is_char_alphanumeric(c u8) bool {
137 return c.is_letter() || c.is_digit()
138}
139