diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart index 9c5a6818..82bce0d1 100644 --- a/lib/src/dart_formatter.dart +++ b/lib/src/dart_formatter.dart @@ -21,6 +21,11 @@ import 'short/source_visitor.dart'; import 'source_code.dart'; import 'string_compare.dart' as string_compare; +/// Regular expression that matches a format width comment like: +/// +/// // dart format width=123 +final RegExp _widthCommentPattern = RegExp(r'^// dart format width=(\d+)$'); + /// Dart source code formatter. final class DartFormatter { /// The latest Dart language version that can be parsed and formatted by this @@ -178,8 +183,21 @@ final class DartFormatter { SourceCode output; if (experimentFlags.contains(tallStyleExperimentFlag)) { + // Look for a page width comment before the code. + int? pageWidthFromComment; + for (Token? comment = node.beginToken.precedingComments; + comment != null; + comment = comment.next) { + if (_widthCommentPattern.firstMatch(comment.lexeme) case var match?) { + // If integer parsing fails for some reason, the returned `null` + // means we correctly ignore the comment. + pageWidthFromComment = int.tryParse(match[1]!); + break; + } + } + var visitor = AstNodeVisitor(this, lineInfo, unitSourceCode); - output = visitor.run(unitSourceCode, node); + output = visitor.run(unitSourceCode, node, pageWidthFromComment); } else { var visitor = SourceVisitor(this, lineInfo, unitSourceCode); output = visitor.run(node); diff --git a/lib/src/front_end/ast_node_visitor.dart b/lib/src/front_end/ast_node_visitor.dart index 87b4d169..05587a04 100644 --- a/lib/src/front_end/ast_node_visitor.dart +++ b/lib/src/front_end/ast_node_visitor.dart @@ -66,7 +66,10 @@ final class AstNodeVisitor extends ThrowingAstVisitor with PieceFactory { /// /// This is the only method that should be called externally. Everything else /// is effectively private. - SourceCode run(SourceCode source, AstNode node) { + /// + /// If there is a `// dart format width=123` comment before the formatted + /// code, then [pageWidthFromComment] is that width. + SourceCode run(SourceCode source, AstNode node, [int? pageWidthFromComment]) { Profile.begin('AstNodeVisitor.run()'); Profile.begin('AstNodeVisitor build Piece tree'); @@ -123,7 +126,7 @@ final class AstNodeVisitor extends ThrowingAstVisitor with PieceFactory { Profile.end('AstNodeVisitor build Piece tree'); // Finish writing and return the complete result. - var result = pieces.finish(source, unitPiece); + var result = pieces.finish(source, unitPiece, pageWidthFromComment); Profile.end('AstNodeVisitor.run()'); diff --git a/lib/src/front_end/piece_writer.dart b/lib/src/front_end/piece_writer.dart index 624c6f72..c1746085 100644 --- a/lib/src/front_end/piece_writer.dart +++ b/lib/src/front_end/piece_writer.dart @@ -390,7 +390,11 @@ final class PieceWriter { /// Finishes writing and returns a [SourceCode] containing the final output /// and updated selection, if any. - SourceCode finish(SourceCode source, Piece rootPiece) { + /// + /// If there is a `// dart format width=123` comment before the formatted + /// code, then [pageWidthFromComment] is that width. + SourceCode finish( + SourceCode source, Piece rootPiece, int? pageWidthFromComment) { if (debug.tracePieceBuilder) { debug.log(debug.pieceTree(rootPiece)); } @@ -399,7 +403,8 @@ final class PieceWriter { var cache = SolutionCache(); var solver = Solver(cache, - pageWidth: _formatter.pageWidth, leadingIndent: _formatter.indent); + pageWidth: pageWidthFromComment ?? _formatter.pageWidth, + leadingIndent: _formatter.indent); var solution = solver.format(rootPiece); var output = solution.code.build(source, _formatter.lineEnding); diff --git a/test/tall/other/format_off.unit b/test/tall/other/format_off.unit index fc96f283..4f281dff 100644 --- a/test/tall/other/format_off.unit +++ b/test/tall/other/format_off.unit @@ -193,4 +193,134 @@ main() { unformatted + code + // dart format on after + region; +} +>>> "dart format off" whitespace must match exactly. +main() { + //dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on +} +<<< +main() { + //dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on + + // dart format off + unformatted + code; + // dart format on +} +>>> "dart format on" whitespace must match exactly. +main() { + // dart format off + // Doesn't actually turn back on: + //dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; +} +<<< +main() { + // dart format off + // Doesn't actually turn back on: + //dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; + + // dart format off + // Doesn't actually turn back on: + // dart format on + unformatted + code; + + // Does now: + // dart format on + unformatted + code; +} +>>> Can't be doc comment. +main() { + here + gets + formatted ; + /// dart format off + here + does + too ; + /// dart format on + and + here + does ; +} +<<< +main() { + here + gets + formatted; + + /// dart format off + here + does + too; + + /// dart format on + and + here + does; } \ No newline at end of file diff --git a/test/tall/other/format_width.unit b/test/tall/other/format_width.unit new file mode 100644 index 00000000..a8396d9e --- /dev/null +++ b/test/tall/other/format_width.unit @@ -0,0 +1,116 @@ +40 columns | +### Tests for the comment to set formatting width. +>>> Comment sets page width. +// dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width=30 +main() { + fitsUnsplitAt40 + + butNotAt30; +} +>>> Comment only takes effect if it appears before code. +main() { + // dart format width=30 + fitsUnsplitAt40 + butNotAt30; +} +<<< +main() { + // dart format width=30 + fitsUnsplitAt40 + butNotAt30; +} +>>> If there are multiple comments, first one wins. +// dart format width=30 +// dart format width=60 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width=30 +// dart format width=60 +main() { + fitsUnsplitAt40 + + butNotAt30; +} +>>> Does nothing if width is not an integer. +// dart format width=wat +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width=wat +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Can't have trailing text. +// dart format width=30 some more text +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width=30 some more text +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Whitespace must match exactly. +//dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +//dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Whitespace must match exactly. +// dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Whitespace must match exactly. +// dart format width = 30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +// dart format width = 30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Can't be a doc comment. +/// dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +/// dart format width=30 +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Can't be nested inside another comment. +/* // dart format width=30 */ +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +/* // dart format width=30 */ +main() { + fitsUnsplitAt40 + butNotAt30; +} +>>> Can't be inside a string literal. +var c = '// dart format width=30'; +main() { + fitsUnsplitAt40 + butNotAt30; +} +<<< +var c = '// dart format width=30'; +main() { + fitsUnsplitAt40 + butNotAt30; +} \ No newline at end of file