143 lines
3.5 KiB
V
143 lines
3.5 KiB
V
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license
|
|
// that can be found in the LICENSE file.
|
|
module fmt
|
|
|
|
import v.ast
|
|
|
|
pub enum CommentsLevel {
|
|
keep
|
|
indent
|
|
}
|
|
|
|
// CommentsOptions defines the way comments are going to be written
|
|
// - has_nl: adds an newline at the end of a list of comments
|
|
// - inline: line comments will be on the same line as the last statement
|
|
// - level: either .keep (don't indent), or .indent (increment indentation)
|
|
// - iembed: a /* ... */ block comment used inside expressions; // comments the whole line
|
|
// - prev_line: the line number of the previous token to save linebreaks
|
|
[params]
|
|
pub struct CommentsOptions {
|
|
has_nl bool = true
|
|
inline bool
|
|
level CommentsLevel
|
|
iembed bool
|
|
prev_line int = -1
|
|
}
|
|
|
|
pub fn (mut f Fmt) comment(node ast.Comment, options CommentsOptions) {
|
|
// Shebang in .vsh files
|
|
if node.text.starts_with('#!') {
|
|
f.writeln(node.text)
|
|
return
|
|
}
|
|
if options.level == .indent {
|
|
f.indent++
|
|
}
|
|
if options.iembed {
|
|
x := node.text.trim_left('\x01').trim_space()
|
|
if x.contains('\n') {
|
|
f.writeln('/*')
|
|
f.writeln(x)
|
|
f.write('*/')
|
|
} else {
|
|
f.write('/* $x */')
|
|
}
|
|
} else if !node.text.contains('\n') {
|
|
is_separate_line := !options.inline || node.text.starts_with('\x01')
|
|
mut s := node.text.trim_left('\x01').trim_right(' ')
|
|
mut out_s := '//'
|
|
if s != '' {
|
|
if is_char_alphanumeric(s[0]) {
|
|
out_s += ' '
|
|
}
|
|
out_s += s
|
|
}
|
|
if !is_separate_line && f.indent > 0 {
|
|
f.remove_new_line() // delete the generated \n
|
|
f.write(' ')
|
|
}
|
|
f.write(out_s)
|
|
} else {
|
|
lines := node.text.trim_space().split_into_lines()
|
|
start_break := is_char_alphanumeric(node.text[0]) || node.text[0].is_space()
|
|
end_break := is_char_alphanumeric(node.text.trim('\t').bytes().last())
|
|
|| node.text.bytes().last().is_space()
|
|
f.write('/*')
|
|
if start_break {
|
|
f.writeln('')
|
|
}
|
|
for line in lines {
|
|
f.writeln(line.trim_right(' '))
|
|
f.empty_line = false
|
|
}
|
|
if end_break {
|
|
f.empty_line = true
|
|
} else {
|
|
f.remove_new_line()
|
|
}
|
|
f.write('*/')
|
|
}
|
|
if options.level == .indent {
|
|
f.indent--
|
|
}
|
|
}
|
|
|
|
pub fn (mut f Fmt) comments(comments []ast.Comment, options CommentsOptions) {
|
|
mut prev_line := options.prev_line
|
|
for i, c in comments {
|
|
if options.prev_line > -1 && ((c.pos.line_nr > prev_line && f.out.last_n(1) != '\n')
|
|
|| (c.pos.line_nr > prev_line + 1 && f.out.last_n(2) != '\n\n')) {
|
|
f.writeln('')
|
|
}
|
|
if !f.out.last_n(1)[0].is_space() {
|
|
f.write(' ')
|
|
}
|
|
f.comment(c, options)
|
|
if !options.iembed && (i < comments.len - 1 || options.has_nl) {
|
|
f.writeln('')
|
|
}
|
|
prev_line = c.pos.last_line
|
|
}
|
|
}
|
|
|
|
pub fn (mut f Fmt) comments_before_field(comments []ast.Comment) {
|
|
// They behave the same as comments after the last field. This alias is just for clarity.
|
|
f.comments_after_last_field(comments)
|
|
}
|
|
|
|
pub fn (mut f Fmt) comments_after_last_field(comments []ast.Comment) {
|
|
for comment in comments {
|
|
f.indent++
|
|
f.empty_line = true
|
|
f.comment(comment, inline: true)
|
|
f.writeln('')
|
|
f.indent--
|
|
}
|
|
}
|
|
|
|
pub fn (mut f Fmt) import_comments(comments []ast.Comment, options CommentsOptions) {
|
|
if comments.len == 0 {
|
|
return
|
|
}
|
|
if options.inline {
|
|
f.remove_new_line(imports_buffer: true)
|
|
}
|
|
for c in comments {
|
|
ctext := c.text.trim_left('\x01')
|
|
if ctext == '' {
|
|
continue
|
|
}
|
|
mut out_s := if options.inline { ' ' } else { '' } + '//'
|
|
if is_char_alphanumeric(ctext[0]) {
|
|
out_s += ' '
|
|
}
|
|
out_s += ctext
|
|
f.out_imports.writeln(out_s)
|
|
}
|
|
}
|
|
|
|
fn is_char_alphanumeric(c u8) bool {
|
|
return c.is_letter() || c.is_digit()
|
|
}
|