1f8aea0c81
Changes: ``` > git log --format="%C(auto) %h %s" af68bf0..a97011b https://dart.googlesource.com/lints.git/+/a97011b add unintended_html_in_doc_comment; rev to 5.1.0 (211) ``` Diff: https://dart.googlesource.com/lints.git/+/af68bf0cbad88002a2ae19ee8db37b64eb120c32..a97011b4b031b1df94765dbacd7d92b0b765c8b9/ Change-Id: I998d73f5f72e94d5f2922b0751d31620a837555e Tested: analysis only change Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/387601 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Devon Carew <devoncarew@google.com>
558 lines
21 KiB
Dart
Executable File
558 lines
21 KiB
Dart
Executable File
#!/usr/bin/env dart
|
|
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// 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.
|
|
|
|
// ----------------------------------------------------------------------
|
|
// This is a very specialized tool which was created in order to support
|
|
// adding hash values used as location markers in the LaTeX source of the
|
|
// language specification. It is intended to take its input file as the
|
|
// first argument, an output file name as the second argument, and a
|
|
// hash listing file name as the third argument. From docs/language a
|
|
// typical usage would be as follows:
|
|
//
|
|
// dart ../../tools/addlatexhash.dart dartLangSpec.tex out.tex hash.txt
|
|
//
|
|
// This will produce a normalized variant out.tex of the language
|
|
// specification with hash values filled in, and a listing hash.txt of
|
|
// all the hash values along with the label of their textual context
|
|
// (section, subsection, subsubsection, paragraph) . For more details,
|
|
// please check the language specification source itself.
|
|
//
|
|
// NB: This utility assumes UN*X style line endings, \n, in the LaTeX
|
|
// source file received as input; it will not work with other styles.
|
|
|
|
// ignore_for_file: constant_identifier_names, only_throw_errors
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:convert/convert.dart';
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Normalization of the text: removal or normalization of parts that
|
|
// do not affect the output from latex, such as white space.
|
|
|
|
final commentRE = RegExp(r'[^\\]%.*'); // NB: . does not match \n.
|
|
final whitespaceAllRE = RegExp(r'^\s+$');
|
|
final whitespaceRE = RegExp(r'(?:(?=\s).){2,}'); // \s except end-of-line
|
|
|
|
/// Removes [match]ing part of [line], adjusting that part with the
|
|
/// given [startOffset] and [endOffset], bounded to be valid indices
|
|
/// into the string if needed, then inserts [glue] where text was
|
|
/// removed. If there is no match then [line] is returned.
|
|
String cutMatch(String line, RegExpMatch? match,
|
|
{int startOffset = 0, int endOffset = 0, String glue = ''}) {
|
|
if (match == null) return line;
|
|
var start = match.start + startOffset;
|
|
var end = match.end + endOffset;
|
|
var len = line.length;
|
|
if (start < 0) start = 0;
|
|
if (end > len) end = len;
|
|
return line.substring(0, start) + glue + line.substring(end);
|
|
}
|
|
|
|
String cutRegexp(String line, RegExp re,
|
|
{int startOffset = 0, int endOffset = 0, String glue = ''}) {
|
|
return cutMatch(line, re.firstMatch(line),
|
|
startOffset: startOffset, endOffset: endOffset, glue: glue);
|
|
}
|
|
|
|
bool isWsOnly(String line) => line.contains(whitespaceAllRE);
|
|
bool isCommentOnly(String line) => line.startsWith('%');
|
|
|
|
/// Returns the end-of-line character at the end of [line], if any,
|
|
/// otherwise returns the empty string.
|
|
String justEol(String line) => line.endsWith('\n') ? '\n' : '';
|
|
|
|
/// Removes the contents of the comment at the end of [line],
|
|
/// leaving the "%" in place. If no comment is present,
|
|
/// return [line].
|
|
///
|
|
/// NB: it is tempting to remove everything from the '%' and out,
|
|
/// including the final newline, if any, but this does not work.
|
|
/// The problem is that TeX will do exactly this, but then it will
|
|
/// add back a character that depends on its state (S, M, or N),
|
|
/// and it is tricky to maintain a similar state that matches the
|
|
/// state of TeX faithfully. Hence, we remove the content of
|
|
/// comments but do not remove the comments themselves, we just
|
|
/// leave the '%' at the end of the line and let TeX manage its
|
|
/// states in a way that does not differ from the file from before
|
|
/// stripComment.
|
|
String stripComment(String line) {
|
|
if (isCommentOnly(line)) return '%\n';
|
|
return cutRegexp(line, commentRE, startOffset: 2);
|
|
}
|
|
|
|
/// Reduces a white-space-only [line] to its eol character,
|
|
/// removes leading ws entirely, and reduces multiple
|
|
/// white-space chars to one.
|
|
String normalizeWhitespace(String line) {
|
|
var trimLine = line.trimLeft();
|
|
if (trimLine.isEmpty) return justEol(line);
|
|
return trimLine.replaceAll(whitespaceRE, ' ');
|
|
}
|
|
|
|
/// Reduces sequences of >1 white-space-only lines in [lines] to 1,
|
|
/// and sequences of >1 comment-only lines to 1. Treats comment-only
|
|
/// lines as white-space-only when they occur in white-space-only
|
|
/// line blocks.
|
|
List<String> multilineNormalize(List<String> lines) {
|
|
var afterBlankLines = false; // Does [line] succeed >0 empty lines?
|
|
var afterCommentLines = false; // Does [line] succeed >0 commentOnly lines?
|
|
var newLines = <String>[];
|
|
for (var line in lines) {
|
|
if (afterBlankLines && afterCommentLines) {
|
|
// Previous line was both blank and a comment: not possible.
|
|
throw 'Bug, please report to eernst@';
|
|
} else if (afterBlankLines && !afterCommentLines) {
|
|
// At least one line before [line] is wsOnly.
|
|
if (!isWsOnly(line)) {
|
|
// Blank line block ended.
|
|
afterCommentLines = isCommentOnly(line);
|
|
// Special case: It seems to be safe to remove commentOnly lines
|
|
// after wsOnly lines, so the TeX state must be predictably right;
|
|
// next line will then be afterCommentLines and be dropped, so
|
|
// we drop the entire comment block---which is very useful. We can
|
|
// also consider this comment line to be an empty line, such that
|
|
// subsequent empty lines can be considered to be in a block of
|
|
// empty lines. Note that almost all variants of this breaks.
|
|
if (afterCommentLines) {
|
|
// _Current_ 'line' is a commentOnly here.
|
|
afterBlankLines = true;
|
|
afterCommentLines = false;
|
|
// Omit addition of [line].
|
|
} else {
|
|
// After blanks, but current 'line' is neither blank nor comment.
|
|
afterBlankLines = false;
|
|
newLines.add(line);
|
|
}
|
|
} else {
|
|
// Blank line block continues, omit addition of [line].
|
|
}
|
|
} else if (!afterBlankLines && afterCommentLines) {
|
|
// At least one line before [line] is commentOnly.
|
|
if (!isCommentOnly(line)) {
|
|
// Comment block ended.
|
|
afterBlankLines = isWsOnly(line);
|
|
afterCommentLines = false;
|
|
newLines.add(line);
|
|
} else {
|
|
// Comment block continues, do not add [line].
|
|
}
|
|
} else {
|
|
assert(!afterBlankLines && !afterCommentLines);
|
|
// No wsOnly or commentOnly lines precede [line].
|
|
afterBlankLines = isWsOnly(line);
|
|
afterCommentLines = isCommentOnly(line);
|
|
if (!afterCommentLines) {
|
|
newLines.add(line);
|
|
} else {
|
|
// skip commentOnly line after nonWs/nonComment text.
|
|
}
|
|
}
|
|
}
|
|
return newLines;
|
|
}
|
|
|
|
/// Selects the elements in the normalization pipeline.
|
|
String normalize(String line) => normalizeWhitespace(stripComment(line));
|
|
|
|
/// Selects the elements in the significant-spacing block
|
|
/// normalization pipeline.
|
|
String sispNormalize(String line) => stripComment(line);
|
|
|
|
// Managing fragments with significant spacing.
|
|
|
|
final dartCodeBeginRE = RegExp(r'^\s*\\begin\s*\{dartCode\}');
|
|
final dartCodeEndRE = RegExp(r'^\s*\\end\s*\{dartCode\}');
|
|
|
|
/// Recognizes beginning of dartCode block.
|
|
bool sispIsDartBegin(String line) => line.contains(dartCodeBeginRE);
|
|
|
|
/// Recognizes end of dartCode block.
|
|
bool sispIsDartEnd(String line) => line.contains(dartCodeEndRE);
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Analyzing the input to point out "interesting" lines
|
|
|
|
/// Returns the event information for [lines] as determined by the
|
|
/// given [analyzer]. The method [HashAnalyzer.analyze] indicates that a
|
|
/// line is "uninteresting" by returning null (i.e., no events here).
|
|
List<HashEvent> findEvents(List<String> lines, HashAnalyzer analyzer) {
|
|
var events = <HashEvent>[];
|
|
for (var line in lines) {
|
|
var event = analyzer.analyze(line);
|
|
if (event != null) events.add(event);
|
|
}
|
|
return events;
|
|
}
|
|
|
|
/// Returns RegExp text for recognizing a command occupying a line
|
|
/// of its own, given the part of the RegExp that recognizes the
|
|
/// command name, [cmdNameRE]
|
|
RegExp lineCommandRE(String cmdNameRE) =>
|
|
RegExp(r'^\s*\\' + cmdNameRE + r'\s*\{.*\}%?\s*$');
|
|
|
|
final hashLabelStartRE = RegExp(r'^\s*\\LMLabel\s*\{');
|
|
final hashLabelEndRE = RegExp(r'\}\s*$');
|
|
|
|
final hashMarkRE = lineCommandRE('LMHash');
|
|
final hashLabelRE = lineCommandRE('LMLabel');
|
|
final sectioningRE = lineCommandRE('((|sub(|sub))section|paragraph)');
|
|
final sectionRE = lineCommandRE('section');
|
|
final subsectionRE = lineCommandRE('subsection');
|
|
final subsubsectionRE = lineCommandRE('subsubsection');
|
|
final paragraphRE = lineCommandRE('paragraph');
|
|
|
|
/// Returns true iff [line] begins a block of lines that gets a hash value.
|
|
bool isHashMarker(String line) => line.contains(hashMarkRE);
|
|
|
|
/// Returns true iff [line] defines a sectioning label.
|
|
bool isHashLabel(String line) => line.contains(hashLabelRE);
|
|
|
|
/// Returns true iff [line] is a sectioning command resp. one of its
|
|
/// more specific forms; note that it is assumed that sectioning commands
|
|
/// do not contain a newline between the command name and the '{'.
|
|
bool isSectioningCommand(String line) => line.contains(sectioningRE);
|
|
bool isSectionCommand(String line) => line.contains(sectionRE);
|
|
bool isSubsectionCommand(String line) => line.contains(subsectionRE);
|
|
bool isSubsubsectionCommand(String line) => line.contains(subsubsectionRE);
|
|
bool isParagraphCommand(String line) => line.contains(paragraphRE);
|
|
|
|
/// Returns true iff [line] does not end a block of lines that gets
|
|
/// a hash value.
|
|
bool isntHashBlockTerminator(String line) => !isSectioningCommand(line);
|
|
|
|
/// Returns the label text part from [line], based on the assumption
|
|
/// that isHashLabel(line) returns true.
|
|
String extractHashLabel(String line) {
|
|
var startMatch = hashLabelStartRE.firstMatch(line);
|
|
var endMatch = hashLabelEndRE.firstMatch(line);
|
|
if (startMatch != null && endMatch != null) {
|
|
return line.substring(startMatch.end, endMatch.start);
|
|
} else {
|
|
throw 'Assertion failure (so this file is both valid nnbd and not)';
|
|
}
|
|
}
|
|
|
|
// Event classes: Keep track of relevant information about the LaTeX
|
|
// source code lines, such as where \LMHash and \LMLabel commands are
|
|
// used, and how they are embedded in the sectioning structure.
|
|
|
|
/// Abstract events, enabling us to [setEndLineNumber] on all events.
|
|
abstract class HashEvent {
|
|
/// For events that have an endLineNumber, set it; otherwise ignore.
|
|
/// The endLineNumber specifies the end of the block of lines
|
|
/// associated with a given event, for event types concerned with
|
|
/// blocks of lines rather than single lines.
|
|
void setEndLineNumber(int n) {}
|
|
|
|
/// Returns null except for \LMHash{} events, where it returns
|
|
/// the startLineNumber. This serves to specify a boundary because
|
|
/// the preceding \LMHash{} block should stop before the line of
|
|
/// this \LMHash{} command. Note that hash blocks may stop earlier,
|
|
/// because they cannot contain sectioning commands.
|
|
int? getStartLineNumber() => null;
|
|
}
|
|
|
|
class HashMarkerEvent extends HashEvent {
|
|
// Line number of first line in block that gets hashed.
|
|
int startLineNumber;
|
|
|
|
// Highest possible number of first line after block that gets
|
|
// hashed (where the next \LMHash{} occurs). Note that this value
|
|
// is not known initially (because that line has not yet been
|
|
// reached), so [endLineNumber] will be initialized in a separate
|
|
// scan. Also note that the block may end earlier, because a block
|
|
// ends if it would otherwise include a sectioning command.
|
|
int? endLineNumber;
|
|
|
|
HashMarkerEvent(this.startLineNumber);
|
|
|
|
@override
|
|
void setEndLineNumber(int n) {
|
|
endLineNumber = n;
|
|
}
|
|
|
|
@override
|
|
int getStartLineNumber() => startLineNumber;
|
|
}
|
|
|
|
class HashLabelEvent extends HashEvent {
|
|
String labelText;
|
|
HashLabelEvent(this.labelText);
|
|
}
|
|
|
|
class HashAnalyzer {
|
|
// List of kinds of pending (= most recently seen) sectioning command.
|
|
// When updating this list, also update sectioningPrefix below.
|
|
static const PENDING_IS_NONE = 0;
|
|
static const PENDING_IS_SECTION = 1;
|
|
static const PENDING_IS_SUBSECTION = 2;
|
|
static const PENDING_IS_SUBSUBSECTION = 3;
|
|
static const PENDING_IS_PARAGRAPH = 1;
|
|
|
|
int lineNumber = 0;
|
|
int pendingSectioning = PENDING_IS_NONE;
|
|
|
|
HashAnalyzer();
|
|
|
|
void setPendingToSection() {
|
|
pendingSectioning = PENDING_IS_SECTION;
|
|
}
|
|
|
|
void setPendingToSubsection() {
|
|
pendingSectioning = PENDING_IS_SUBSECTION;
|
|
}
|
|
|
|
void setPendingToSubsubsection() {
|
|
pendingSectioning = PENDING_IS_SUBSUBSECTION;
|
|
}
|
|
|
|
void setPendingToParagraph() {
|
|
pendingSectioning = PENDING_IS_PARAGRAPH;
|
|
}
|
|
|
|
String sectioningPrefix() {
|
|
switch (pendingSectioning) {
|
|
case PENDING_IS_SECTION:
|
|
return 'sec:';
|
|
case PENDING_IS_SUBSECTION:
|
|
return 'subsec:';
|
|
case PENDING_IS_SUBSUBSECTION:
|
|
return 'subsubsec:';
|
|
case PENDING_IS_NONE:
|
|
throw '\\LMHash{..} should only be used after a sectioning command '
|
|
'(\\section, \\subsection, \\subsubsection, \\paragraph)';
|
|
default:
|
|
// set of PENDING_IS_.. was extended, but updates here omitted
|
|
throw 'Bug, please report to eernst@';
|
|
}
|
|
}
|
|
|
|
HashEvent? analyze(String line) {
|
|
var currentLineNumber = lineNumber++;
|
|
if (isHashMarker(line)) {
|
|
return HashMarkerEvent(currentLineNumber);
|
|
} else if (isHashLabel(line)) {
|
|
var labelText = sectioningPrefix() + extractHashLabel(line);
|
|
return HashLabelEvent(labelText);
|
|
} else {
|
|
// No events to emit, but we may need to note state changes
|
|
if (isSectionCommand(line)) {
|
|
setPendingToSection();
|
|
} else if (isSubsectionCommand(line)) {
|
|
setPendingToSubsection();
|
|
} else if (isSubsubsectionCommand(line)) {
|
|
setPendingToSubsubsection();
|
|
} else if (isParagraphCommand(line)) {
|
|
setPendingToParagraph();
|
|
} else {
|
|
// No state changes.
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<HashEvent> findHashEvents(List<String> lines) {
|
|
// Create the list of events, omitting endLineNumbers.
|
|
var events = findEvents(lines, HashAnalyzer());
|
|
// Set the endLineNumbers.
|
|
var currentEndLineNumber = lines.length;
|
|
for (var event in events.reversed) {
|
|
event.setEndLineNumber(currentEndLineNumber);
|
|
var nextEndLineNumber = event.getStartLineNumber();
|
|
if (nextEndLineNumber != null) currentEndLineNumber = nextEndLineNumber;
|
|
}
|
|
return events;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Removal of non-normative elements of the text (rationale, commentary).
|
|
|
|
/// Returns [line] without the command [cmdName] (based on a match
|
|
/// on "\\cmdName\s*{..}") starting at [startIndex]; note that it is
|
|
/// assumed but not checked that [line] contains "\\cmdType\s*{..",
|
|
/// and note that the end of the {..} block is found via brace matching
|
|
/// (i.e., nested {..} blocks are handled), but it may break if '{' is
|
|
/// made an active character etc.etc.
|
|
String removeCommand(String line, String cmdName, int startIndex) {
|
|
const BACKSLASH = 92; // char code for '\\'.
|
|
const BRACE_BEGIN = 123; // char code for '{'.
|
|
const BRACE_END = 125; // char code for '}'.
|
|
|
|
var blockStartIndex = startIndex + cmdName.length + 1;
|
|
while (blockStartIndex < line.length &&
|
|
line.codeUnitAt(blockStartIndex) != BRACE_BEGIN) {
|
|
blockStartIndex++;
|
|
}
|
|
blockStartIndex++;
|
|
if (blockStartIndex > line.length) {
|
|
throw 'Bug, please report to eernst@';
|
|
}
|
|
// [blockStartIndex] has index just after '{'.
|
|
|
|
var afterEscape = false; // Is true iff [index] is just after '{'.
|
|
var braceLevel = 1; // Have seen so many '{'s minus so many '}'s.
|
|
|
|
for (var index = blockStartIndex; index < line.length; index++) {
|
|
switch (line.codeUnitAt(index)) {
|
|
case BRACE_BEGIN:
|
|
if (afterEscape) {
|
|
afterEscape = false;
|
|
} else {
|
|
braceLevel++;
|
|
}
|
|
break;
|
|
case BRACE_END:
|
|
if (afterEscape) {
|
|
afterEscape = false;
|
|
} else {
|
|
braceLevel--;
|
|
}
|
|
break;
|
|
case BACKSLASH:
|
|
afterEscape = true;
|
|
break;
|
|
default:
|
|
afterEscape = false;
|
|
}
|
|
if (braceLevel == 0) {
|
|
return line.substring(0, startIndex) + line.substring(index + 1);
|
|
}
|
|
}
|
|
// Removal failed; we consider this to mean that the input is ill-formed.
|
|
throw 'Unmatched braces';
|
|
}
|
|
|
|
final commentaryRE = RegExp(r'\\commentary\s*\{');
|
|
final rationaleRE = RegExp(r'\\rationale\s*\{');
|
|
|
|
/// Removes {}-balanced '\commentary{..}' commands from [line].
|
|
String removeCommentary(String line) {
|
|
var match = commentaryRE.firstMatch(line);
|
|
if (match == null) return line;
|
|
return removeCommentary(removeCommand(line, r'commentary', match.start));
|
|
}
|
|
|
|
/// Removes {}-balanced '\rationale{..}' commands from [line].
|
|
String removeRationale(String line) {
|
|
var match = rationaleRE.firstMatch(line);
|
|
if (match == null) return line;
|
|
return removeRationale(removeCommand(line, r'rationale', match.start));
|
|
}
|
|
|
|
/// Removes {}-balanced '\commentary{..}' and '\rationale{..}'
|
|
/// commands from [line], then normalizes its white-space.
|
|
String simplifyLine(String line) {
|
|
var simplerLine = removeCommentary(line);
|
|
simplerLine = removeRationale(simplerLine);
|
|
simplerLine = normalizeWhitespace(simplerLine);
|
|
return simplerLine;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// Recognition of line blocks, insertion of block hash into \LMHash{}.
|
|
|
|
final latexArgumentRE = RegExp(r'\{.*\}');
|
|
|
|
String cleanupLine(String line) =>
|
|
cutRegexp(line, commentRE, startOffset: 1).trimRight();
|
|
|
|
/// Returns concatenation of all lines from [startIndex] in [lines] until
|
|
/// a hash block terminator is encountered or [nextIndex] reached (if so,
|
|
/// the line lines[nextIndex] itself is not included); each line is cleaned
|
|
/// up using [cleanupLine], and " " is inserted between the lines gathered.
|
|
String gatherLines(List<String> lines, int startIndex, int nextIndex) => lines
|
|
.getRange(startIndex, nextIndex)
|
|
.takeWhile(isntHashBlockTerminator)
|
|
.map(cleanupLine)
|
|
.join(' ');
|
|
|
|
/// Computes the hash value for the line block starting at [startIndex]
|
|
/// in [lines], stopping just before [nextIndex]. SIDE EFFECT:
|
|
/// Outputs the simplified text and its hash value to [listSink].
|
|
List<int> computeHashValue(
|
|
List<String> lines, int startIndex, int nextIndex, IOSink listSink) {
|
|
final gatheredLine = gatherLines(lines, startIndex, nextIndex);
|
|
final simplifiedLine = simplifyLine(gatheredLine);
|
|
listSink.write(' % $simplifiedLine\n');
|
|
var digest = sha1.convert(utf8.encode(simplifiedLine));
|
|
return digest.bytes;
|
|
}
|
|
|
|
String computeHashString(
|
|
List<String> lines, int startIndex, int nextIndex, IOSink listSink) =>
|
|
hex.encode(computeHashValue(lines, startIndex, nextIndex, listSink));
|
|
|
|
/// Computes and adds hashes to \LMHash{} lines in [lines] (which must be on the
|
|
/// line numbers specified in [hashEvents]), and emits sectioning markers and
|
|
/// hash values to [listSink], along with "comments" containing the simplified
|
|
/// text (using the format `' % <text>'`, where the text is one, long line, for
|
|
/// easy grepping etc.).
|
|
void addHashMarks(
|
|
List<String> lines, List<HashEvent> hashEvents, IOSink listSink) {
|
|
for (var hashEvent in hashEvents) {
|
|
if (hashEvent is HashMarkerEvent) {
|
|
var start = hashEvent.startLineNumber;
|
|
var end = hashEvent.endLineNumber;
|
|
final hashValue = computeHashString(lines, start + 1, end!, listSink);
|
|
lines[start] = lines[start].replaceAll(latexArgumentRE, '{$hashValue}');
|
|
listSink.write(' $hashValue\n');
|
|
} else if (hashEvent is HashLabelEvent) {
|
|
listSink.write('${hashEvent.labelText}\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Transforms LaTeX input to LaTeX output plus hash value list file.
|
|
void main(List<String> args) {
|
|
if (args.length != 3) {
|
|
print('Usage: addlatexhash.dart <input-file> <output-file> <list-file>');
|
|
throw 'Received ${args.length} arguments, expected three';
|
|
}
|
|
|
|
// Get LaTeX source.
|
|
var inputFile = File(args[0]);
|
|
assert(inputFile.existsSync());
|
|
var lines = inputFile.readAsLinesSync();
|
|
|
|
// Will hold LaTeX source with normalized spacing etc., plus hash values.
|
|
var outputFile = File(args[1]);
|
|
|
|
// Will hold hierarchical list of hash values.
|
|
var listFile = File(args[2]);
|
|
var listSink = listFile.openWrite();
|
|
|
|
// Perform single-line normalization.
|
|
var inDartCode = false;
|
|
var normalizedLines = <String>[];
|
|
|
|
for (var line in lines) {
|
|
if (sispIsDartBegin(line)) {
|
|
inDartCode = true;
|
|
} else if (sispIsDartEnd(line)) {
|
|
inDartCode = false;
|
|
}
|
|
if (inDartCode) {
|
|
normalizedLines.add(sispNormalize('$line\n'));
|
|
} else {
|
|
normalizedLines.add(normalize('$line\n'));
|
|
}
|
|
}
|
|
|
|
// Perform multi-line normalization.
|
|
normalizedLines = multilineNormalize(normalizedLines);
|
|
|
|
// Insert hash values.
|
|
var hashEvents = findHashEvents(normalizedLines);
|
|
addHashMarks(normalizedLines, hashEvents, listSink);
|
|
|
|
// Produce/finalize output.
|
|
outputFile.writeAsStringSync(normalizedLines.join());
|
|
listSink.close();
|
|
}
|