Skip to content

Commit f093a89

Browse files
committed
Don't unnecessarily split argument lists with block comments.
A very subtle bug in the comment handling would cause a /* */ inline comment to force the surrounding code to full split when not needed in some cases. This fixes that issue. It also slightly tweaks the formatting of code containing inline block comments in some rare cases. In practice, the only code I see affected by that is formatter tests. In the real world, all affected code is related to the bug itself and is fixed by this change. Fix #837.
1 parent b13403c commit f093a89

File tree

13 files changed

+200
-40
lines changed

13 files changed

+200
-40
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 2.0.2-dev
2+
3+
* Don't unnecessarily split argument lists with `/* */` comments (#837).
4+
15
# 2.0.1
26

37
* Support triple-shift `>>>` and `>>>=` operators (#992).

lib/src/chunk.dart

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -384,32 +384,43 @@ class Span extends FastHash {
384384
String toString() => '$id\$$cost';
385385
}
386386

387+
enum CommentType {
388+
/// A `///` or `/**` doc comment.
389+
doc,
390+
391+
/// A non-doc line comment.
392+
line,
393+
394+
/// A `/* ... */` comment that should be on its own line.
395+
block,
396+
397+
/// A `/* ... */` comment that can share a line with other code.
398+
inlineBlock,
399+
}
400+
387401
/// A comment in the source, with a bit of information about the surrounding
388402
/// whitespace.
389403
class SourceComment extends Selection {
390404
/// The text of the comment, including `//`, `/*`, and `*/`.
391405
@override
392406
final String text;
393407

408+
/// What kind of comment this is.
409+
final CommentType type;
410+
394411
/// The number of newlines between the comment or token preceding this comment
395412
/// and the beginning of this one.
396413
///
397414
/// Will be zero if the comment is a trailing one.
398415
int linesBefore;
399416

400-
/// Whether this comment is a line comment.
401-
final bool isLineComment;
402-
403417
/// Whether this comment starts at column one in the source.
404418
///
405419
/// Comments that start at the start of the line will not be indented in the
406420
/// output. This way, commented out chunks of code do not get erroneously
407421
/// re-indented.
408422
final bool flushLeft;
409423

410-
/// Whether this comment is an inline block comment.
411-
bool get isInline => linesBefore == 0 && !isLineComment;
412-
413-
SourceComment(this.text, this.linesBefore,
414-
{required this.isLineComment, required this.flushLeft});
424+
SourceComment(this.text, this.type, this.linesBefore,
425+
{required this.flushLeft});
415426
}

lib/src/chunk_builder.dart

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -253,22 +253,19 @@ class ChunkBuilder {
253253
// Edge case: if the comments are completely inline (i.e. just a series of
254254
// block comments with no newlines before, after, or between them), then
255255
// they will eat any pending newlines. Make sure that doesn't happen by
256-
// putting the pending whitespace before the first comment and moving them
257-
// to their own line. Turns this:
256+
// putting the pending whitespace before the first comment. Turns this:
258257
//
259258
// library foo; /* a */ /* b */ import 'a.dart';
260259
//
261260
// into:
262261
//
263262
// library foo;
264263
//
265-
// /* a */ /* b */
266-
// import 'a.dart';
264+
// /* a */ /* b */ import 'a.dart';
267265
if (linesBeforeToken == 0 &&
268-
comments.every((comment) => comment.isInline) &&
269-
_pendingWhitespace.minimumLines > 0) {
266+
_pendingWhitespace.minimumLines > comments.first.linesBefore &&
267+
comments.every((comment) => comment.type == CommentType.inlineBlock)) {
270268
comments.first.linesBefore = _pendingWhitespace.minimumLines;
271-
linesBeforeToken = 1;
272269
}
273270

274271
// Write each comment and the whitespace between them.
@@ -284,18 +281,19 @@ class ChunkBuilder {
284281
}
285282
_emitPendingWhitespace();
286283

287-
if (comment.linesBefore == 0) {
284+
// See if the comment should follow text on the current line.
285+
if (comment.linesBefore == 0 || comment.type == CommentType.inlineBlock) {
288286
// If we're sitting on a split, move the comment before it to adhere it
289287
// to the preceding text.
290-
if (_shouldMoveCommentBeforeSplit(comment.text)) {
288+
if (_shouldMoveCommentBeforeSplit(comment)) {
291289
_chunks.last.allowText();
292290
}
293291

294292
// The comment follows other text, so we need to decide if it gets a
295293
// space before it or not.
296294
if (_needsSpaceBeforeComment(comment)) _writeText(' ');
297295
} else {
298-
// The comment starts a line, so make sure it stays on its own line.
296+
// The comment is not inline, so start a new line.
299297
_writeHardSplit(
300298
flushLeft: comment.flushLeft,
301299
isDouble: comment.linesBefore > 1,
@@ -743,17 +741,23 @@ class ChunkBuilder {
743741

744742
/// Returns `true` if the last chunk is a split that should be moved after the
745743
/// comment that is about to be written.
746-
bool _shouldMoveCommentBeforeSplit(String comment) {
744+
bool _shouldMoveCommentBeforeSplit(SourceComment comment) {
747745
// Not if there is nothing before it.
748746
if (_chunks.isEmpty) return false;
749747

748+
// Don't move a comment to a preceding line.
749+
if (comment.linesBefore != 0) return false;
750+
750751
// Multi-line comments are always pushed to the next line.
751-
if (comment.contains('\n')) return false;
752+
if (comment.type == CommentType.doc) return false;
753+
if (comment.type == CommentType.block) return false;
752754

753755
var text = _chunks.last.text;
754756

755757
// A block comment following a comma probably refers to the following item.
756-
if (text.endsWith(',') && comment.startsWith('/*')) return false;
758+
if (text.endsWith(',') && comment.type == CommentType.inlineBlock) {
759+
return false;
760+
}
757761

758762
// If the text before the split is an open grouping character, it looks
759763
// better to keep it with the elements than with the bracket itself.
@@ -788,13 +792,11 @@ class ChunkBuilder {
788792
// Not at the start of a line.
789793
if (!_chunks.last.canAddText) return false;
790794

791-
var text = _chunks.last.text;
792-
if (text.endsWith('\n')) return false;
793-
794795
// Always put a space before line comments.
795-
if (comment.isLineComment) return true;
796+
if (comment.type == CommentType.line) return true;
796797

797798
// Magic generic method comments like "Foo/*<T>*/" don't get spaces.
799+
var text = _chunks.last.text;
798800
if (_isGenericMethodComment(comment) &&
799801
_trailingIdentifierChar.hasMatch(text)) {
800802
return false;

lib/src/source_visitor.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3770,8 +3770,17 @@ class SourceVisitor extends ThrowingAstVisitor {
37703770
if (comment == token.precedingComments) linesBefore = 2;
37713771
}
37723772

3773-
var sourceComment = SourceComment(text, linesBefore,
3774-
isLineComment: comment.type == TokenType.SINGLE_LINE_COMMENT,
3773+
var type = CommentType.block;
3774+
if (text.startsWith('///') && !text.startsWith('////') ||
3775+
text.startsWith('/**')) {
3776+
type = CommentType.doc;
3777+
} else if (comment.type == TokenType.SINGLE_LINE_COMMENT) {
3778+
type = CommentType.line;
3779+
} else if (commentLine == previousLine || commentLine == tokenLine) {
3780+
type = CommentType.inlineBlock;
3781+
}
3782+
3783+
var sourceComment = SourceComment(text, type, linesBefore,
37753784
flushLeft: flushLeft);
37763785

37773786
// If this comment contains either of the selection endpoints, mark them

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: dart_style
22
# Note: See tool/grind.dart for how to bump the version.
3-
version: 2.0.1
3+
version: 2.0.2-dev
44
description: >-
55
Opinionated, automatic Dart source code formatter.
66
Provides an API and a CLI tool.

test/comments/classes.unit

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ class A {
3333
class A {
3434
/* comment */}
3535
<<<
36-
class A {
37-
/* comment */
38-
}
36+
class A {/* comment */}
3937
>>> inline block comment
4038
class A { /* comment */ }
4139
<<<

test/comments/extensions.unit

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ extension A on B {
3333
extension A on B {
3434
/* comment */}
3535
<<<
36-
extension A on B {
37-
/* comment */
38-
}
36+
extension A on B {/* comment */}
3937
>>> inline block comment
4038
extension A on B { /* comment */ }
4139
<<<

test/comments/functions.unit

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,36 @@ function([
143143
parameter,
144144
] /* c */) {
145145
;
146+
}
147+
>>>
148+
function(
149+
/* comment */ int a, int b, int c,
150+
[direction]) {
151+
;
152+
}
153+
<<<
154+
function(
155+
/* comment */ int a, int b, int c,
156+
[direction]) {
157+
;
158+
}
159+
>>>
160+
function(
161+
/* comment */ int a, int b, int c) {
162+
;
163+
}
164+
<<<
165+
function(
166+
/* comment */ int a, int b, int c) {
167+
;
168+
}
169+
>>>
170+
function(
171+
/* comment */ int a, int b, int c, int d) {
172+
;
173+
}
174+
<<<
175+
function(/* comment */ int a, int b,
176+
int c, int d) {
177+
;
146178
}

test/comments/statements.stmt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,33 @@ while (true) {
5454
<<<
5555
while (true) {
5656
// comment
57+
}
58+
>>>
59+
main() {
60+
/* comment */ statement;
61+
}
62+
<<<
63+
main() {
64+
/* comment */ statement;
65+
}
66+
>>>
67+
main() {
68+
code;
69+
70+
/* comment */ statement;
71+
}
72+
<<<
73+
main() {
74+
code;
75+
76+
/* comment */ statement;
77+
}
78+
>>>
79+
main() {
80+
while (b)
81+
/*unreachable*/ {}
82+
}
83+
<<<
84+
main() {
85+
while (b) /*unreachable*/ {}
5786
}

test/comments/top_level.unit

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,12 @@ library a; /* comment */ import 'b.dart';
190190
<<<
191191
library a;
192192

193-
/* comment */
194-
import 'b.dart';
193+
/* comment */ import 'b.dart';
195194
>>> inline block comment between directives
196195
import 'a.dart'; /* comment */ import 'b.dart';
197196
<<<
198197
import 'a.dart';
199-
/* comment */
200-
import 'b.dart';
198+
/* comment */ import 'b.dart';
201199
>>> block comment between directives
202200
import 'a.dart'; /* comment */
203201
import 'b.dart';

0 commit comments

Comments
 (0)