diff --git a/CHANGELOG.md b/CHANGELOG.md index 12998dc1..211bba48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ * Allow passing a language version to `DartFomatter()`. Formatted code will be parsed at that version. If omitted, defaults to the latest version. In a future release, this parameter will become required. +* Allow opting out of formatting for a region of code using `// dart format off` + and `// dart format on` comments. Note: This only works using the new tall + style and requires passing the `--enable-experiment=tall-style` experiment + flag (#361). * Preserve type parameters on old-style function-typed formals that also use `this.` or `super.` (#1321). * Remove temporary work around for analyzer 6.2.0 from dart_style 2.3.6. diff --git a/benchmark/run.dart b/benchmark/run.dart index 8d8f6467..bf8da687 100644 --- a/benchmark/run.dart +++ b/benchmark/run.dart @@ -160,7 +160,7 @@ double _runTrial(DartFormatter formatter, ParseStringResult parseResult, result = visitor.run(parseResult.unit).text; } else { var visitor = AstNodeVisitor(formatter, parseResult.lineInfo, source); - result = visitor.run(parseResult.unit).text; + result = visitor.run(source, parseResult.unit).text; } } diff --git a/example/format.dart b/example/format.dart index ba66c087..d6d06035 100644 --- a/example/format.dart +++ b/example/format.dart @@ -25,7 +25,7 @@ void main(List args) { class C {} '''); - _runTest('selection/selection.stmt', 2); + _runTest('other/selection.stmt', 2); } void _formatStmt(String source, {bool tall = true, int pageWidth = 40}) { @@ -68,7 +68,7 @@ void _drawRuler(String label, int width) { /// directory. Future _runTest(String path, int line, {int pageWidth = 40, bool tall = true}) async { - var testFile = await TestFile.read(path); + var testFile = await TestFile.read('${tall ? 'tall' : 'short'}/$path'); var formatTest = testFile.tests.firstWhere((test) => test.line == line); var formatter = DartFormatter( diff --git a/lib/src/back_end/code.dart b/lib/src/back_end/code.dart index 47c06eb1..52416a1d 100644 --- a/lib/src/back_end/code.dart +++ b/lib/src/back_end/code.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import '../source_code.dart'; + /// Base class for an object that represents fully formatted code. /// /// We use this instead of immediately generating a string for the resulting @@ -13,14 +15,59 @@ /// to be fast. Appending strings to a [StringBuffer] is fairly fast, but not /// as fast simply appending a single [GroupCode] to the parent solution's /// [GroupCode]. -sealed class Code {} +sealed class Code { + /// Traverse the [Code] tree and generate a string for debugging purposes. + String toDebugString() { + var buffer = StringBuffer(); + var prefix = ''; + + void write(String text) { + if (buffer.isNotEmpty) buffer.write(prefix); + buffer.writeln(text); + } + + void trace(Code code) { + switch (code) { + case _NewlineCode(): + write('Newline(blank: ${code._blank}, indent: ${code._indent})'); + + case _TextCode(): + write('`${code._text}`'); + + case GroupCode(): + write('Group(indent: ${code._indent}):'); + prefix += '| '; + for (var child in code._children) { + trace(child); + } + prefix = prefix.substring(2); + + case _MarkerCode(): + write('Marker(${code._marker}, offset: ${code._offset})'); + + case _EnableFormattingCode(): + write('EnableFormattingCode(enabled: ${code._enabled}, ' + 'offset: ${code._sourceOffset})'); + } + } + + trace(this); + + return buffer.toString(); + } +} /// A [Code] object which can be written to and contain other child [Code] /// objects. final class GroupCode extends Code { + /// How many spaces the first text inside this group should be indented. + final int _indent; + /// The child [Code] objects contained in this group. final List _children = []; + GroupCode(this._indent); + /// Appends [text] to this code. void write(String text) { _children.add(_TextCode(text)); @@ -32,7 +79,10 @@ final class GroupCode extends Code { /// single newline is written. The [indent] parameter is the number of spaces /// of leading indentation on the next line after the newline. void newline({required bool blank, required int indent}) { - _children.add(_NewlineCode(blank: blank, indent: indent)); + // Don't insert a redundant newline at the top of a group. + if (_children.isNotEmpty) { + _children.add(_NewlineCode(blank: blank, indent: indent)); + } } /// Adds an entire existing code [group] as a child of this one. @@ -52,65 +102,32 @@ final class GroupCode extends Code { _children.add(_MarkerCode(_Marker.end, offset)); } + /// Disables or re-enables formatting in a region of code. + void setFormattingEnabled(bool enabled, int sourceOffset) { + _children.add(_EnableFormattingCode(enabled, sourceOffset)); + } + /// Traverse the [Code] tree and build the final formatted string. /// /// Whenever a newline is written, writes [lineEnding]. If omitted, defaults /// to '\n'. /// /// Returns the formatted string and the selection markers if there are any. - ({String code, int? selectionStart, int? selectionEnd}) build( - [String? lineEnding]) { + SourceCode build(SourceCode source, [String? lineEnding]) { lineEnding ??= '\n'; - var buffer = StringBuffer(); - int? selectionStart; - int? selectionEnd; - - _build(buffer, lineEnding, (marker, offset) { - if (marker == _Marker.start) { - selectionStart = offset; - } else { - selectionEnd = offset; - } - }); - - return ( - code: buffer.toString(), - selectionStart: selectionStart, - selectionEnd: selectionEnd - ); - } - - void _build(StringBuffer buffer, String lineEnding, - void Function(_Marker marker, int offset) markSelection) { - for (var i = 0; i < _children.length; i++) { - var child = _children[i]; - switch (child) { - case _NewlineCode(): - // Don't write any leading newlines at the top of the buffer. - if (i > 0) { - buffer.write(lineEnding); - if (child._blank) buffer.write(lineEnding); - } - - buffer.write(_indents[child._indent] ?? (' ' * child._indent)); - - case _TextCode(): - buffer.write(child._text); - - case GroupCode(): - child._build(buffer, lineEnding, markSelection); - - case _MarkerCode(): - markSelection(child._marker, buffer.length + child._offset); - } - } + var builder = _StringBuilder(source, lineEnding); + builder.traverse(this); + return builder.finish(); } } /// A [Code] object for a newline followed by any leading indentation. final class _NewlineCode extends Code { + /// True if a blank line (two newlines) should be written. final bool _blank; + + /// The number of spaces of indentation after this newline. final int _indent; _NewlineCode({required bool blank, required int indent}) @@ -132,49 +149,230 @@ final class _MarkerCode extends Code { /// What kind of selection endpoint is being marked. final _Marker _marker; - /// The number of characters past this object where the marker should appear - /// in the resulting code. + /// The number of characters into the next [Code] object where the marker + /// should appear in the resulting output. final int _offset; _MarkerCode(this._marker, this._offset); } +final class _EnableFormattingCode extends Code { + /// Whether this comment disables formatting (`format off`) or re-enables it + /// (`format on`). + final bool _enabled; + + /// The number of code points from the beginning of the unformatted source + /// where the unformatted code should begin or end. + /// + /// If this piece is for `// dart format off`, then the offset is just past + /// the `off`. If this piece is for `// dart format on`, it points to just + /// before `//`. + final int _sourceOffset; + + _EnableFormattingCode(this._enabled, this._sourceOffset); +} + /// Which selection marker is pointed to by a [_MarkerCode]. enum _Marker { start, end } -/// Pre-calculated whitespace strings for various common levels of indentation. -/// -/// Generating these ahead of time is faster than concatenating multiple spaces -/// at runtime. -const _indents = { - 2: ' ', - 4: ' ', - 6: ' ', - 8: ' ', - 10: ' ', - 12: ' ', - 14: ' ', - 16: ' ', - 18: ' ', - 20: ' ', - 22: ' ', - 24: ' ', - 26: ' ', - 28: ' ', - 30: ' ', - 32: ' ', - 34: ' ', - 36: ' ', - 38: ' ', - 40: ' ', - 42: ' ', - 44: ' ', - 46: ' ', - 48: ' ', - 50: ' ', - 52: ' ', - 54: ' ', - 56: ' ', - 58: ' ', - 60: ' ', -}; +/// Traverses a [Code] tree and produces the final string of output code and +/// the selection markers, if any. +class _StringBuilder { + /// Pre-calculated whitespace strings for various common levels of + /// indentation. + /// + /// Generating these ahead of time is faster than concatenating multiple + /// spaces at runtime. + static const _indents = { + 2: ' ', + 4: ' ', + 6: ' ', + 8: ' ', + 10: ' ', + 12: ' ', + 14: ' ', + 16: ' ', + 18: ' ', + 20: ' ', + 22: ' ', + 24: ' ', + 26: ' ', + 28: ' ', + 30: ' ', + 32: ' ', + 34: ' ', + 36: ' ', + 38: ' ', + 40: ' ', + 42: ' ', + 44: ' ', + 46: ' ', + 48: ' ', + 50: ' ', + 52: ' ', + 54: ' ', + 56: ' ', + 58: ' ', + 60: ' ', + }; + + final SourceCode _source; + final String _lineEnding; + final StringBuffer _buffer = StringBuffer(); + + /// The offset from the beginning of the source to where the selection start + /// marker is, if there is one. + int? _selectionStart; + + /// The offset from the beginning of the source to where the selection end + /// marker is, if there is one. + int? _selectionEnd; + + /// How many spaces of indentation should be written before the next text. + int _indent = 0; + + /// If formatting has been disabled, then this is the offset from the + /// beginning of the source, to where the disabled formatting begins. + /// + /// Otherwise, -1 to indicate that formatting is enabled. + int _disableFormattingStart = -1; + + _StringBuilder(this._source, this._lineEnding); + + void traverse(Code code) { + switch (code) { + case _NewlineCode(): + // If formatting has been disabled, then don't write the formatted + // output. The unformatted output will be written when formatting is + // re-enabled. + if (_disableFormattingStart == -1) { + _buffer.write(_lineEnding); + if (code._blank) _buffer.write(_lineEnding); + _indent = code._indent; + } + + case _TextCode(): + // If formatting has been disabled, then don't write the formatted + // output. The unformatted output will be written when formatting is + // re-enabled. + if (_disableFormattingStart == -1) { + // Write any pending indentation. + _buffer.write(_indents[_indent] ?? (' ' * _indent)); + _indent = 0; + + _buffer.write(code._text); + } + + case GroupCode(): + _indent = code._indent; + for (var i = 0; i < code._children.length; i++) { + var child = code._children[i]; + traverse(child); + } + + case _MarkerCode(): + if (_disableFormattingStart == -1) { + // Calculate the absolute offset from the beginning of the formatted + // output where the selection marker will appear based on how much + // formatted output we've written, pending indentation, and then the + // relative offset of the marker into the subsequent [Code] we will + // write. + var absolutePosition = _buffer.length + _indent + code._offset; + switch (code._marker) { + case _Marker.start: + _selectionStart = absolutePosition; + case _Marker.end: + _selectionEnd = absolutePosition; + } + } else { + // The marker appears inside a region where formatting is disabled. + // In that case, calculating where the marker will end up in the + // final formatted output is more complicated because we haven't + // actually written any of the code between the `// dart format off` + // comment and this marker to [_buffer] yet. However, we do know the + // *absolute* position of the selection markers in the original + // source. + // + // Let's say the source file looks like: + // + // 1 2 3 + // 0123456789012345678901234567890123456789 + // bef + ore off code | inside on more + // + // Here, `bef + ore` is some amount of code appearing before + // formatting is disabled, `off` is the `// dart format off` comment, + // `code` is some code inside the unformatted region, `|` is the + // selection marker, `inside` is more code in the unformatted region, + // `on` turns formatting back on, and `more` is formatted code at the + // end. + // + // We know the beginning of the unformatted region is at offset 15 + // (just after the comment) in the original source. We know the + // selection marker is at offset 21 in the original source. From that, + // we know the selection marker should end up 6 code points after the + // beginning of the unformatted region in the resulting output. + switch (code._marker) { + case _Marker.start: + // Calculate how far into the unformatted code where the marker + // should appear. + var markerOffsetInUnformatted = + _source.selectionStart! - _disableFormattingStart; + _selectionStart = _buffer.length + markerOffsetInUnformatted; + + case _Marker.end: + var end = _source.selectionStart! + _source.selectionLength!; + + // Calculate how far into the unformatted code where the marker + // should appear. + var markerOffsetInUnformatted = end - _disableFormattingStart; + _selectionEnd = _buffer.length + markerOffsetInUnformatted; + } + } + + case _EnableFormattingCode(_enabled: false): + // Region markers don't nest. If we've already turned off formatting, + // then ignore any subsequent `// dart format off` comments until it's + // been turned back on. + if (_disableFormattingStart == -1) { + _disableFormattingStart = code._sourceOffset; + } + + case _EnableFormattingCode(_enabled: true): + // If we didn't disable formatting, then enabling it does nothing. + if (_disableFormattingStart != -1) { + // Write all of the unformatted text from the `// dart format off` + // comment to the end of the `// dart format on` comment. + _buffer.write(_source.text + .substring(_disableFormattingStart, code._sourceOffset)); + _disableFormattingStart = -1; + } + } + } + + SourceCode finish() { + if (_disableFormattingStart != -1) { + // Formatting was disabled and never re-enabled, so write the rest of the + // source file as unformatted text. + _buffer.write(_source.text.substring(_disableFormattingStart)); + } else if (_source.isCompilationUnit) { + // Be a good citizen, end with a newline. + _buffer.write(_lineEnding); + } + + var selectionStart = _selectionStart; + int? selectionLength; + if (_source.selectionStart != null) { + // If we haven't hit the beginning and/or end of the selection yet, they + // must be at the very end of the code. + selectionStart ??= _buffer.length; + var selectionEnd = _selectionEnd ?? _buffer.length; + selectionLength = selectionEnd - selectionStart; + } + + return SourceCode(_buffer.toString(), + uri: _source.uri, + isCompilationUnit: _source.isCompilationUnit, + selectionStart: selectionStart, + selectionLength: selectionLength); + } +} diff --git a/lib/src/back_end/code_writer.dart b/lib/src/back_end/code_writer.dart index e60f593e..c266dc17 100644 --- a/lib/src/back_end/code_writer.dart +++ b/lib/src/back_end/code_writer.dart @@ -30,7 +30,7 @@ class CodeWriter { final Solution _solution; /// The code being written. - final GroupCode _code = GroupCode(); + final GroupCode _code; /// What whitespace should be written before the next non-whitespace text. /// @@ -41,7 +41,7 @@ class CodeWriter { /// /// Initially [Whitespace.newline] so that we write the leading indentation /// before the first token. - Whitespace _pendingWhitespace = Whitespace.newline; + Whitespace _pendingWhitespace = Whitespace.none; /// The number of spaces of indentation that should be begin the next line /// when [_pendingWhitespace] is [Whitespace.newline] or @@ -100,11 +100,13 @@ class CodeWriter { /// [leadingIndent] is the number of spaces of leading indentation at the /// beginning of each line independent of indentation created by pieces being /// written. - CodeWriter(this._pageWidth, int leadingIndent, this._cache, this._solution) { + CodeWriter(this._pageWidth, int leadingIndent, this._cache, this._solution) + : _code = GroupCode(leadingIndent) { _indentStack.add(_Indent(leadingIndent, 0)); - // Write the leading indent before the first line. + // Track the leading indent before the first line. _pendingIndent = leadingIndent; + _column = _pendingIndent; } /// Returns the final formatted code and the next pieces that can be expanded @@ -331,6 +333,11 @@ class CodeWriter { _code.endSelection(end); } + /// Disables or re-enables formatting in a region of code. + void setFormattingEnabled(bool enabled, int sourceOffset) { + _code.setFormattingEnabled(enabled, sourceOffset); + } + /// Write any pending whitespace. /// /// This is called before non-whitespace text is about to be written, or diff --git a/lib/src/back_end/solver.dart b/lib/src/back_end/solver.dart index 948ec6ba..df087f89 100644 --- a/lib/src/back_end/solver.dart +++ b/lib/src/back_end/solver.dart @@ -103,7 +103,7 @@ class Solver { if (debug.traceSolver) { debug.log(debug.bold('Try #$attempts $solution')); - debug.log(solution.code.build().code); + debug.log(solution.code.toDebugString()); debug.log(''); } @@ -133,8 +133,8 @@ class Solver { // If we didn't find a solution without overflow, pick the least bad one. if (debug.traceSolver) { debug.unindent(); - debug.log(debug.bold('Solved $root to $best:')); - debug.log(solution.code.build().code); + debug.log(debug.bold('Solved $root to $best')); + debug.log(solution.code.toDebugString()); debug.log(''); } diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart index d814dde8..67a97b10 100644 --- a/lib/src/dart_formatter.dart +++ b/lib/src/dart_formatter.dart @@ -219,7 +219,7 @@ class DartFormatter { SourceCode output; if (experimentFlags.contains(tallStyleExperimentFlag)) { var visitor = AstNodeVisitor(this, lineInfo, unitSourceCode); - output = visitor.run(node); + output = visitor.run(unitSourceCode, node); } 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 36d5f4e0..b3cdf9a8 100644 --- a/lib/src/front_end/ast_node_visitor.dart +++ b/lib/src/front_end/ast_node_visitor.dart @@ -66,7 +66,7 @@ class AstNodeVisitor extends ThrowingAstVisitor with PieceFactory { /// /// This is the only method that should be called externally. Everything else /// is effectively private. - SourceCode run(AstNode node) { + SourceCode run(SourceCode source, AstNode node) { Profile.begin('AstNodeVisitor.run()'); Profile.begin('AstNodeVisitor build Piece tree'); @@ -123,7 +123,7 @@ class AstNodeVisitor extends ThrowingAstVisitor with PieceFactory { Profile.end('AstNodeVisitor build Piece tree'); // Finish writing and return the complete result. - var result = pieces.finish(unitPiece); + var result = pieces.finish(source, unitPiece); Profile.end('AstNodeVisitor.run()'); diff --git a/lib/src/front_end/piece_writer.dart b/lib/src/front_end/piece_writer.dart index 90e75951..e2e1f0bf 100644 --- a/lib/src/front_end/piece_writer.dart +++ b/lib/src/front_end/piece_writer.dart @@ -277,7 +277,18 @@ class PieceWriter { /// Creates a new [Piece] for [comment] and returns it. Piece commentPiece(SourceComment comment, [Whitespace trailingWhitespace = Whitespace.none]) { - var piece = CommentPiece(trailingWhitespace); + var piece = switch (comment.text) { + '// dart format off' => EnableFormattingCommentPiece( + enable: false, + comment.offset + comment.text.length, + trailingWhitespace), + '// dart format on' => EnableFormattingCommentPiece( + enable: true, + comment.offset + comment.text.length, + trailingWhitespace), + _ => CommentPiece(trailingWhitespace), + }; + _write(piece, comment.text, comment.offset, multiline: comment.type.mayBeMultiline); return piece; @@ -379,7 +390,7 @@ class PieceWriter { /// Finishes writing and returns a [SourceCode] containing the final output /// and updated selection, if any. - SourceCode finish(Piece rootPiece) { + SourceCode finish(SourceCode source, Piece rootPiece) { if (debug.tracePieceBuilder) { debug.log(debug.pieceTree(rootPiece)); } @@ -390,28 +401,11 @@ class PieceWriter { var solver = Solver(cache, pageWidth: _formatter.pageWidth, leadingIndent: _formatter.indent); var solution = solver.format(rootPiece); - var (:code, :selectionStart, :selectionEnd) = - solution.code.build(_formatter.lineEnding); + var output = solution.code.build(source, _formatter.lineEnding); Profile.end('PieceWriter.finish() format piece tree'); - // Be a good citizen, end with a newline. - if (_source.isCompilationUnit) code += _formatter.lineEnding!; - - int? selectionLength; - if (_source.selectionStart != null) { - // If we haven't hit the beginning and/or end of the selection yet, they - // must be at the very end of the code. - selectionStart ??= code.length; - selectionEnd ??= code.length; - selectionLength = selectionEnd - selectionStart; - } - - return SourceCode(code, - uri: _source.uri, - isCompilationUnit: _source.isCompilationUnit, - selectionStart: selectionStart, - selectionLength: selectionLength); + return output; } /// Returns the number of characters past [position] in the source where the diff --git a/lib/src/piece/text.dart b/lib/src/piece/text.dart index 274516cb..b5c159f4 100644 --- a/lib/src/piece/text.dart +++ b/lib/src/piece/text.dart @@ -59,18 +59,6 @@ sealed class TextPiece extends Piece { } } - /// Sets [selectionStart] to be [start] code units after the end of the - /// current text in this piece. - void startSelection(int start) { - _selectionStart = _adjustSelection(start); - } - - /// Sets [selectionEnd] to be [end] code units after the end of the - /// current text in this piece. - void endSelection(int end) { - _selectionEnd = _adjustSelection(end); - } - /// Adjust [offset] by the current length of this [TextPiece]. int _adjustSelection(int offset) { for (var line in _lines) { @@ -163,7 +151,7 @@ class CommentPiece extends TextPiece { /// Whitespace at the end of the comment. final Whitespace _trailingWhitespace; - CommentPiece([this._trailingWhitespace = Whitespace.none]); + CommentPiece(this._trailingWhitespace); @override void format(CodeWriter writer, State state) { @@ -180,6 +168,32 @@ class CommentPiece extends TextPiece { void forEachChild(void Function(Piece piece) callback) {} } +/// A piece for the special `// dart format off` and `// dart format on` +/// comments that are used to opt a region of code out of being formatted. +class EnableFormattingCommentPiece extends CommentPiece { + /// Whether this comment disables formatting (`format off`) or re-enables it + /// (`format on`). + final bool _enabled; + + /// The number of code points from the beginning of the unformatted source + /// where the unformatted code should begin or end. + /// + /// If this piece is for `// dart format off`, then the offset is just past + /// the `off`. If this piece is for `// dart format on`, it points to just + /// before `//`. + final int _sourceOffset; + + EnableFormattingCommentPiece(this._sourceOffset, super._trailingWhitespace, + {required bool enable}) + : _enabled = enable; + + @override + void format(CodeWriter writer, State state) { + super.format(writer, state); + writer.setFormattingEnabled(_enabled, _sourceOffset); + } +} + /// A piece that writes a single space. class SpacePiece extends Piece { @override diff --git a/test/tall/other/format_off.unit b/test/tall/other/format_off.unit new file mode 100644 index 00000000..fc96f283 --- /dev/null +++ b/test/tall/other/format_off.unit @@ -0,0 +1,196 @@ +40 columns | +### Tests for disabling formatting on regions of code. +>>> Simple unformatted region. +main() { + here + gets + formatted ; + // dart format off + here + does + not ; + // dart format on + but + here + does ; +} +<<< +main() { + here + gets + formatted; + // dart format off + here + does + not ; + // dart format on + but + here + does; +} +>>> Multiple unformatted regions. +main() { + here + gets + formatted ; + // dart format off + here + does + not ; + // dart format on + but + here + does ; + + { + more + formatted ; + // dart format off + more + unformatted ; + multiple + lines ; + // dart format on + } +} +<<< +main() { + here + gets + formatted; + // dart format off + here + does + not ; + // dart format on + but + here + does; + + { + more + formatted; + // dart format off + more + unformatted ; + multiple + lines ; + // dart format on + } +} +>>> Begin outside block and end inside. +main() { + before ; + // dart format off + inside + region + outside + block ; + { + inside + block + and + region ; + // dart format on + inside + block + outside + region ; + } + outside + block ; +} +<<< +main() { + before; + // dart format off + inside + region + outside + block ; + { + inside + block + and + region ; + // dart format on + inside + block + outside + region; + } + outside + block; +} +>>> Begin inside block and end outside. +main() { + before ; + { + inside + block + outside + region ; + // dart format off + inside + block + and + region ; + } + outside + block + inside + region ; + // dart format on + outside + block ; +} +<<< +main() { + before; + { + inside + block + outside + region; + // dart format off + inside + block + and + region ; + } + outside + block + inside + region ; + // dart format on + outside + block; +} +>>> Preserve indentation in region but not region marker comments. +main() { + too + much ; + // dart format off + still + too + much ; + // dart format on + again + too + much ; +} +<<< +main() { + too + much; + // dart format off + still + too + much ; + // dart format on + again + too + much; +} +>>> On without off does nothing. +main() { + before + marker ; + // dart format on + after + marker ; +} +<<< +main() { + before + marker; + // dart format on + after + marker; +} +>>> Off without on leaves rest of file unformatted. +main() { + before + marker ; + // dart format off + after + marker ; +} +<<< +main() { + before + marker; + // dart format off + after + marker ; +} +>>> Markers do not nest. +main() { + one ; + // dart format off + two ; + // dart format off + three ; + // dart format on + four ; + // dart format on + five ; +} +<<< +main() { + one; + // dart format off + two ; + // dart format off + three ; + // dart format on + four; + // dart format on + five; +} +>>> Formatted code on same line before format off comment. +main() { + long + code + before + comment + // dart format off + unformatted + code + + // dart format on + after + region; +} +<<< +main() { + long + + code + + before + + comment + // dart format off + unformatted + code + + // dart format on + after + + region; +} +>>> Unformatted code on same line before format on comment. +main() { + before + region + + // dart format off + unformatted + code + // dart format on + after + region; +} +<<< +main() { + before + + region + + // dart format off + unformatted + code + // dart format on + after + + region; +} \ No newline at end of file diff --git a/test/tall/other/format_off_selection.unit b/test/tall/other/format_off_selection.unit new file mode 100644 index 00000000..e0ff161c --- /dev/null +++ b/test/tall/other/format_off_selection.unit @@ -0,0 +1,65 @@ +40 columns | +>>> Selection start inside disabled formatting. +main() { + here + gets + format‹ted ; + // dart format off + here + does › + not ; + // dart format on + but + here + does ; +} +<<< +main() { + here + gets + format‹ted; + // dart format off + here + does › + not ; + // dart format on + but + here + does; +} +>>> Selection end inside disabled formatting. +main() { + here + gets + formatted ; + // dart format off + here + does ‹ + not ; + // dart format on + but + here + do›es ; +} +<<< +main() { + here + gets + formatted; + // dart format off + here + does ‹ + not ; + // dart format on + but + here + do›es; +} +>>> Selection inside disabled formatting. +main() { + here + gets + formatted ; + // dart format off + here ‹ + does + › not ; + // dart format on + but + here + does ; +} +<<< +main() { + here + gets + formatted; + // dart format off + here ‹ + does + › not ; + // dart format on + but + here + does; +} +>>> Selection around disabled formatting. +main() { + here + gets + format‹ted ; + // dart format off + here + does + not ; + // dart format on + but + here + do›es ; +} +<<< +main() { + here + gets + format‹ted; + // dart format off + here + does + not ; + // dart format on + but + here + do›es; +} \ No newline at end of file diff --git a/test/tall/selection/selection.stmt b/test/tall/other/selection.stmt similarity index 100% rename from test/tall/selection/selection.stmt rename to test/tall/other/selection.stmt diff --git a/test/tall/selection/selection.unit b/test/tall/other/selection.unit similarity index 100% rename from test/tall/selection/selection.unit rename to test/tall/other/selection.unit diff --git a/test/tall/selection/comma.stmt b/test/tall/other/selection_comma.stmt similarity index 100% rename from test/tall/selection/comma.stmt rename to test/tall/other/selection_comma.stmt diff --git a/test/tall/selection/comment.stmt b/test/tall/other/selection_comment.stmt similarity index 100% rename from test/tall/selection/comment.stmt rename to test/tall/other/selection_comment.stmt diff --git a/test/tall/top_level/trailing_whitespace.unit b/test/tall/other/trailing_whitespace.unit similarity index 100% rename from test/tall/top_level/trailing_whitespace.unit rename to test/tall/other/trailing_whitespace.unit diff --git a/test/tall/top_level/unicode.unit b/test/tall/other/unicode.unit similarity index 100% rename from test/tall/top_level/unicode.unit rename to test/tall/other/unicode.unit diff --git a/test/tall_format_test.dart b/test/tall_format_test.dart index d55f4a37..35404ba8 100644 --- a/test/tall_format_test.dart +++ b/test/tall_format_test.dart @@ -14,8 +14,8 @@ void main() async { await testDirectory('tall/expression'); await testDirectory('tall/function'); await testDirectory('tall/invocation'); + await testDirectory('tall/other'); await testDirectory('tall/pattern'); - await testDirectory('tall/selection'); await testDirectory('tall/statement'); await testDirectory('tall/top_level'); await testDirectory('tall/type');