diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 0a4e5be2c1..0273a25b96 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -61,14 +61,10 @@ pub fn fmt(file ast.File, table &table.Table, is_debug bool) string { } f.process_file_imports(file) f.set_current_module_name('main') - for stmt in file.stmts { - if stmt is ast.Import { - // Just remember the position of the imports for now - f.import_pos = f.out.len - // f.imports(f.file.imports) - } - f.stmt(stmt) - } + // As these are toplevel stmts, the indent increase done in f.stmts() has to be compensated + f.indent-- + f.stmts(file.stmts) + f.indent++ // for comment in file.comments { println('$comment.line_nr $comment.text') } f.imports(f.file.imports) // now that we have all autoimports, handle them res := f.out.str().trim_space() + '\n' @@ -271,20 +267,39 @@ pub fn (f Fmt) imp_stmt_str(imp ast.Import) string { return '$imp.mod$imp_alias_suffix' } -pub fn (mut f Fmt) stmts(stmts []ast.Stmt) { - f.indent++ - mut prev_line_nr := 0 - if stmts.len >= 1 { - prev_pos := stmts[0].position() - prev_line_nr = util.imax(prev_pos.line_nr, prev_pos.last_line) +fn (mut f Fmt) should_insert_newline_before_stmt(stmt ast.Stmt, prev_stmt ast.Stmt) bool { + prev_line_nr := prev_stmt.position().last_line + // The stmt either has or shouldn't have a newline before + if stmt.position().line_nr - prev_line_nr <= 1 || f.out.last_n(2) == '\n\n' { + return false } + // Imports are handled special hence they are ignored here + if stmt is ast.Import || prev_stmt is ast.Import { + return false + } + // Attributes are not respected in the stmts position, so we have to check it manually + if stmt is ast.StructDecl { + if stmt.attrs.len > 0 && stmt.attrs[0].pos.line_nr - prev_line_nr <= 1 { + return false + } + } + if stmt is ast.FnDecl { + if stmt.attrs.len > 0 && stmt.attrs[0].pos.line_nr - prev_line_nr <= 1 { + return false + } + } + return true +} + +pub fn (mut f Fmt) stmts(stmts []ast.Stmt) { + mut prev_stmt := if stmts.len > 0 { stmts[0] } else { ast.Stmt{} } + f.indent++ for stmt in stmts { - if stmt.position().line_nr - prev_line_nr > 1 { + if f.should_insert_newline_before_stmt(stmt, prev_stmt) { f.out.writeln('') } f.stmt(stmt) - prev_pos := stmt.position() - prev_line_nr = util.imax(prev_pos.line_nr, prev_pos.last_line) + prev_stmt = stmt } f.indent-- } @@ -362,6 +377,8 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { } ast.Import { // Imports are handled after the file is formatted, to automatically add necessary modules + // Just remember the position of the imports for now + f.import_pos = f.out.len // f.imports(f.file.imports) } ast.InterfaceDecl { diff --git a/vlib/v/fmt/tests/comment_after_import_expected.vv b/vlib/v/fmt/tests/comment_after_import_expected.vv index 722b07b144..ca9039b6ce 100644 --- a/vlib/v/fmt/tests/comment_after_import_expected.vv +++ b/vlib/v/fmt/tests/comment_after_import_expected.vv @@ -1,5 +1,6 @@ import semver // as semver + fn main() { } diff --git a/vlib/v/fmt/tests/hashstmts_keep.vv b/vlib/v/fmt/tests/hashstmts_keep.vv new file mode 100644 index 0000000000..4c36edb03e --- /dev/null +++ b/vlib/v/fmt/tests/hashstmts_keep.vv @@ -0,0 +1,8 @@ +// comment above HashStmts +#flag linux -lsdl2 +#include "stdio.h" + +// comment between with newlines around + +#include "header.h" +#include "sqlite3.h" diff --git a/vlib/v/fmt/tests/import_keep.vv b/vlib/v/fmt/tests/import_keep.vv new file mode 100644 index 0000000000..568d3f75d1 --- /dev/null +++ b/vlib/v/fmt/tests/import_keep.vv @@ -0,0 +1,5 @@ +// import time This should be commented out +import os +import rand + +// another comment after imports diff --git a/vlib/v/fmt/tests/newlines_keep.vv b/vlib/v/fmt/tests/newlines_keep.vv new file mode 100644 index 0000000000..74e5389866 --- /dev/null +++ b/vlib/v/fmt/tests/newlines_keep.vv @@ -0,0 +1,19 @@ +// Module with attribute +[manualfree] +module websocket + +fn main() {} + +// This should stay between both functions + +fn x() {} + +// doc comment above an attributed function +[inline] +fn y_with_attr() { +} + +// doc comment above an attributed struct +[typedef] +struct Foo { +} diff --git a/vlib/v/fmt/tests/types_expected.vv b/vlib/v/fmt/tests/types_expected.vv index 454fa522ca..31daa95d22 100644 --- a/vlib/v/fmt/tests/types_expected.vv +++ b/vlib/v/fmt/tests/types_expected.vv @@ -13,6 +13,7 @@ type MyInt = int pub type Abc = f32 // Fn type decl + type EmptyFn = fn () type OneArgFn = fn (i int) diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 98908e7c74..5c31de9b73 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -16,7 +16,7 @@ const ( // // #include, #flag, #v fn (mut p Parser) hash() ast.HashStmt { - mut pos := p.prev_tok.position() + pos := p.tok.position() val := p.tok.lit kind := val.all_before(' ') p.next() diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index 546474dd17..04518fe131 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -15,12 +15,11 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { p.inside_ct_if_expr = was_inside_ct_if_expr } p.inside_if_expr = true - mut pos := if is_comptime { + mut pos := p.tok.position() + if is_comptime { p.inside_ct_if_expr = true p.next() // `$` - p.prev_tok.position().extend(p.tok.position()) - } else { - p.tok.position() + pos = p.prev_tok.position().extend(p.tok.position()) } mut branches := []ast.IfBranch{} mut has_else := false diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 39f3dede9e..9e39157b24 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -204,7 +204,11 @@ pub fn (mut p Parser) parse() ast.File { } // module module_decl := p.module_decl() - stmts << module_decl + if module_decl.is_skipped { + stmts.insert(0, ast.Stmt(module_decl)) + } else { + stmts << module_decl + } // imports for { if p.tok.kind == .key_import { @@ -513,8 +517,10 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { return p.struct_decl() } .dollar { + if_expr := p.if_expr(true) return ast.ExprStmt{ - expr: p.if_expr(true) + expr: if_expr + pos: if_expr.pos } } .hash { @@ -1753,6 +1759,7 @@ fn (mut p Parser) parse_number_literal() ast.Expr { fn (mut p Parser) module_decl() ast.Module { mut module_attrs := []table.Attr{} + mut attrs_pos := p.tok.position() if p.tok.kind == .lsbr { p.attributes() module_attrs = p.attrs @@ -1788,7 +1795,7 @@ fn (mut p Parser) module_decl() ast.Module { return mod_node } } - module_pos = module_pos.extend(name_pos) + module_pos = attrs_pos.extend(name_pos) } full_name := util.qualify_module(name, p.file_name) p.mod = full_name diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 6664feb18e..9f7f1c57c7 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -154,10 +154,11 @@ pub fn (mut s Scanner) set_current_tidx(cidx int) { fn (mut s Scanner) new_token(tok_kind token.Kind, lit string, len int) token.Token { cidx := s.tidx s.tidx++ + line_offset := if tok_kind == .hash { 0 } else { 1 } return token.Token{ kind: tok_kind lit: lit - line_nr: s.line_nr + 1 + line_nr: s.line_nr + line_offset pos: s.pos - len + 1 len: len tidx: cidx