blob: 152755b450ca5bd93d3a59afb2c28f348f9afe25 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:core' hide RegExp;
import 'patterns.dart';
void _stripFullLines(List<String> lines) {
// Strip full-line decorations, e.g. horizontal lines, and trailing whitespace.
for (int index = 0; index < lines.length; index += 1) {
lines[index] = lines[index].trimRight();
if (fullDecorations.matchAsPrefix(lines[index]) != null) {
if (index == 0 || lines[index].length != lines[index - 1].length) {
// (we leave the decorations if it's just underlining the previous line)
lines[index] = '';
}
}
}
// Remove leading and trailing blank lines
while (lines.isNotEmpty && lines.first == '') {
lines.removeAt(0);
}
while (lines.isNotEmpty && lines.last == '') {
lines.removeLast();
}
}
bool _stripIndentation(List<String> lines) {
// Try stripping leading indentation.
String? prefix;
bool removeTrailingBlockEnd = false;
if (lines.first.startsWith('/*') && lines.last.startsWith(' *')) {
// In addition to the leadingDecorations, we also support one specific
// kind of multiline decoration, the /*...*/ block comment.
prefix = ' *';
removeTrailingBlockEnd = true;
} else {
prefix = leadingDecorations.matchAsPrefix(lines.first)?.group(0);
}
if (prefix != null && lines.skip(1).every((String line) => line.startsWith(prefix!) || prefix.startsWith(line))) {
final int prefixLength = prefix.length;
for (int index = 0; index < lines.length; index += 1) {
final String line = lines[index];
if (line.length > prefixLength) {
lines[index] = line.substring(prefixLength);
} else {
lines[index] = '';
}
}
if (removeTrailingBlockEnd) {
if (lines.last == '/') {
// This removes the line with the trailing "*/" when we had a "/*" at the top.
lines.removeLast();
}
}
return true;
}
return false;
}
bool _unindentLeadingParagraphs(List<String> lines) {
// Try removing leading spaces
// (we know that this loop terminates before the end of the block because
// otherwise the previous section would have stripped a common prefix across
// the entire block)
//
// For example, this will change:
//
// foo
// bar
//
// ...into:
//
// foo
// bar
//
assert(' '.startsWith(leadingDecorations));
if (lines.first.startsWith(' ')) {
int lineCount = 0;
String line = lines.first;
int leadingBlockIndent = line.length; // arbitrarily big number
do {
int indentWidth = 1;
while (indentWidth < line.length && line[indentWidth] == ' ') {
indentWidth += 1;
}
if (indentWidth < leadingBlockIndent) {
leadingBlockIndent = indentWidth;
}
lineCount += 1;
assert(lineCount < lines.length);
line = lines[lineCount];
} while (line.startsWith(' '));
assert(leadingBlockIndent > 0);
for (int index = 0; index < lineCount; index += 1) {
lines[index] = lines[index].substring(leadingBlockIndent, lines[index].length);
}
return true;
}
return false;
}
bool _minorRemovals(List<String> lines) {
bool didEdits = false;
// Try removing stray leading spaces (but only one space).
for (int index = 0; index < lines.length; index += 1) {
if (lines[index].startsWith(' ') && !lines[index].startsWith(' ')) {
lines[index] = lines[index].substring(1, lines[index].length);
didEdits = true;
}
}
// Try stripping HTML leading and trailing block (<!--/-->) comment markers.
if (lines.first.contains('<!--') || lines.last.contains('-->')) {
lines.first = lines[0].replaceFirst('<!--', '');
lines.last = lines[0].replaceFirst('-->', '');
didEdits = true;
}
// Try stripping C-style leading block comment markers.
// We don't do this earlier because if it's a multiline block comment
// we want to be careful about stripping the trailing aligned "*/".
if (lines.first.startsWith('/*')) {
lines.first = lines.first.substring(2, lines.first.length);
didEdits = true;
}
// Try stripping trailing decorations (specifically, stray "*/"s).
for (int index = 0; index < lines.length; index += 1) {
if (lines[index].endsWith('*/')) {
lines[index] = lines[index].substring(0, lines[index].length - 2);
didEdits = true;
}
}
return didEdits;
}
/// This function takes a block of text potentially decorated with leading
/// prefix indents, horizontal lines, C-style comment blocks, blank lines, etc,
/// and removes all such incidental material leaving only the significant text.
String reformat(String body) {
final List<String> lines = body.split('\n');
while (true) {
// The order of the following checks is important. If any test changes
// something that could affect an earlier test, then we should "continue" back
// to the top of the loop.
_stripFullLines(lines);
if (lines.isEmpty) {
// We've stripped everything, give up.
return '';
}
if (_stripIndentation(lines)) {
continue; // Go back to top since we may have more blank lines to strip now.
}
if (_unindentLeadingParagraphs(lines)) {
continue; // Go back to the top since we may have more indentation to strip now.
}
if (_minorRemovals(lines)) {
continue; // Go back to the top since we may have new stuff to strip.
}
// If we get here, we could not find anything else to do to the text to clean it up.
break;
}
return lines.join('\n');
}
String stripAsciiArt(String input) {
// Look for images so that we can remove them.
final List<String> lines = input.split('\n');
for (final List<String> image in asciiArtImages) {
assert(image.isNotEmpty);
// Look for the image starting on each line.
search: for (int index = 0; index < lines.length - image.length; index += 1) {
final int x = lines[index].indexOf(image[0]);
if (x >= 0) {
int width = image[0].length;
// Found the first line, check to see if we have a complete image.
for (int imageLine = 1; imageLine < image.length; imageLine += 1) {
if (lines[index + imageLine].indexOf(image[imageLine]) != x) {
continue search; // Not a complete image.
}
if (image[imageLine].length > width) {
width = image[imageLine].length;
}
}
// Now remove the image.
for (int imageLine = 0; imageLine < image.length; imageLine += 1) {
final String text = lines[index + imageLine];
assert(text.length > x);
if (text.length >= x + width) {
lines[index + imageLine] = text.substring(0, x) + text.substring(x + width, text.length);
} else {
lines[index + imageLine] = text.substring(0, x);
}
}
}
}
}
return lines.join('\n');
}