Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Unified Diff: observatory_pub_packages/analyzer/src/services/writer.dart

Issue 816693004: Add observatory_pub_packages snapshot to third_party (Closed) Base URL: http://dart.googlecode.com/svn/third_party/
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: observatory_pub_packages/analyzer/src/services/writer.dart
===================================================================
--- observatory_pub_packages/analyzer/src/services/writer.dart (revision 0)
+++ observatory_pub_packages/analyzer/src/services/writer.dart (working copy)
@@ -0,0 +1,518 @@
+// Copyright (c) 2013, 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.
+
+library source_writer;
+
+import 'dart:math' as math;
+
+class Line {
+
+ final List<LineToken> tokens = <LineToken>[];
+ final bool useTabs;
+ final int spacesPerIndent;
+ final int indentLevel;
+ final LinePrinter printer;
+
+ Line({this.indentLevel: 0, this.useTabs: false, this.spacesPerIndent: 2,
+ this.printer: const SimpleLinePrinter()}) {
+ if (indentLevel > 0) {
+ indent(indentLevel);
+ }
+ }
+
+ void addSpace() {
+ addSpaces(1);
+ }
+
+ void addSpaces(int n, {breakWeight: DEFAULT_SPACE_WEIGHT}) {
+ tokens.add(new SpaceToken(n, breakWeight: breakWeight));
+ }
+
+ void addToken(LineToken token) {
+ tokens.add(token);
+ }
+
+ void clear() {
+ tokens.clear();
+ }
+
+ bool isEmpty() => tokens.isEmpty;
+
+ bool isWhitespace() => tokens.every(
+ (tok) => tok is SpaceToken || tok is TabToken);
+
+ void indent(int n) {
+ tokens.insert(0,
+ useTabs ? new TabToken(n) : new SpaceToken(n * spacesPerIndent));
+ }
+
+ String toString() => printer.printLine(this);
+
+}
+
+
+/// Base class for line printers
+abstract class LinePrinter {
+
+ const LinePrinter();
+
+ /// Convert this [line] to a [String] representation.
+ String printLine(Line line);
+}
+
+
+typedef String Indenter(int n);
+
+
+/// A simple line breaking [LinePrinter]
+class SimpleLineBreaker extends LinePrinter {
+
+ static final NO_OP_INDENTER = (n) => '';
+
+ final chunks = <Chunk>[];
+ final int maxLength;
+ Indenter indenter;
+
+ SimpleLineBreaker(this.maxLength, [this.indenter]) {
+ if (indenter == null) {
+ indenter = NO_OP_INDENTER;
+ }
+ }
+
+ String printLine(Line line) {
+ var buf = new StringBuffer();
+ var chunks = breakLine(line);
+ for (var i = 0; i < chunks.length; ++i) {
+ var chunk = chunks[i];
+ if (i > 0) {
+ buf.write(indent(chunk, chunk.indent));
+ } else {
+ buf.write(chunk);
+ }
+ }
+ return buf.toString();
+ }
+
+ String indent(Chunk chunk, int level) {
+ return '\n' + indenter(level) + chunk.toString();
+ }
+
+ List<Chunk> breakLine(Line line) {
+ List<LineToken> tokens = preprocess(line.tokens);
+ List<Chunk> chunks = <Chunk>[new Chunk(line.indentLevel, maxLength, tokens)];
+ // try SINGLE_SPACE_WEIGHT
+ {
+ Chunk chunk = chunks[0];
+ if (chunk.length > maxLength) {
+ for (int i = 0; i < tokens.length; i++) {
+ LineToken token = tokens[i];
+ if (token is SpaceToken && token.breakWeight == SINGLE_SPACE_WEIGHT) {
+ var beforeChunk = chunk.subChunk(chunk.indent, 0, i);
+ var restChunk = chunk.subChunk(chunk.indent + 2, i + 1);
+ // check if 'init' in 'var v = init;' fits a line
+ if (restChunk.length < maxLength) {
+ return [beforeChunk, restChunk];
+ }
+ // check if 'var v = method(' in 'var v = method(args)' does not fit
+ int weight = chunk.findMinSpaceWeight();
+ if (chunk.getLengthToSpaceWithWeight(weight) > maxLength) {
+ chunks = [beforeChunk, restChunk];
+ }
+ // done anyway
+ break;
+ }
+ }
+ }
+ }
+ // other spaces
+ while (true) {
+ List<Chunk> newChunks = <Chunk>[];
+ bool hasChanges = false;
+ for (Chunk chunk in chunks) {
+ tokens = chunk.tokens;
+ if (chunk.length > maxLength) {
+ if (chunk.hasAnySpace()) {
+ int weight = chunk.findMinSpaceWeight();
+ int newIndent = chunk.indent;
+ if (weight == DEFAULT_SPACE_WEIGHT) {
+ int start = 0;
+ int length = 0;
+ for (int i = 0; i < tokens.length; i++) {
+ LineToken token = tokens[i];
+ if (token is SpaceToken && token.breakWeight == weight &&
+ i < tokens.length - 1) {
+ LineToken nextToken = tokens[i + 1];
+ if (length + token.length + nextToken.length > maxLength) {
+ newChunks.add(chunk.subChunk(newIndent, start, i));
+ newIndent = chunk.indent + 2;
+ start = i + 1;
+ length = 0;
+ continue;
+ }
+ }
+ length += token.length;
+ }
+ if (start < tokens.length) {
+ newChunks.add(chunk.subChunk(newIndent, start));
+ }
+ } else {
+ List<LineToken> part = [];
+ int start = 0;
+ for (int i = 0; i < tokens.length; i++) {
+ LineToken token = tokens[i];
+ if (token is SpaceToken && token.breakWeight == weight) {
+ newChunks.add(chunk.subChunk(newIndent, start, i));
+ newIndent = chunk.indent + 2;
+ start = i + 1;
+ }
+ }
+ if (start < tokens.length) {
+ newChunks.add(chunk.subChunk(newIndent, start));
+ }
+ }
+ } else {
+ newChunks.add(chunk);
+ }
+ } else {
+ newChunks.add(chunk);
+ }
+ if (newChunks.length > chunks.length) {
+ hasChanges = true;
+ }
+ }
+ if (!hasChanges) {
+ break;
+ }
+ chunks = newChunks;
+ }
+ return chunks;
+ }
+
+ static List<LineToken> preprocess(List<LineToken> tok) {
+
+ var tokens = <LineToken>[];
+ var curr;
+
+ tok.forEach((token) {
+ if (token is! SpaceToken) {
+ if (curr == null) {
+ curr = token;
+ } else {
+ curr = merge(curr, token);
+ }
+ } else {
+ if (isNonbreaking(token)) {
+ curr = merge(curr, token);
+ } else {
+ if (curr != null) {
+ tokens.add(curr);
+ curr = null;
+ }
+ tokens.add(token);
+ }
+ }
+ });
+
+ if (curr != null) {
+ tokens.add(curr);
+ }
+
+ return tokens;
+ }
+
+ static bool isNonbreaking(SpaceToken token) =>
+ token.breakWeight == UNBREAKABLE_SPACE_WEIGHT;
+
+ static LineToken merge(LineToken first, LineToken second) =>
+ new LineToken(first.value + second.value);
+}
+
+/// Test if this [string] contains only whitespace characters
+bool isWhitespace(String string) => string.codeUnits.every(
+ (c) => c == 0x09 || c == 0x20 || c == 0x0A || c == 0x0D);
+
+/// Special token indicating a line start
+final LINE_START = new SpaceToken(0);
+
+const DEFAULT_SPACE_WEIGHT = UNBREAKABLE_SPACE_WEIGHT - 1;
+/// The weight of a space after '=' in variable declaration or assignment
+const SINGLE_SPACE_WEIGHT = UNBREAKABLE_SPACE_WEIGHT - 2;
+const UNBREAKABLE_SPACE_WEIGHT = 100000000;
+
+/// Simple non-breaking printer
+class SimpleLinePrinter extends LinePrinter {
+
+ const SimpleLinePrinter();
+
+ String printLine(Line line) {
+ var buffer = new StringBuffer();
+ line.tokens.forEach((tok) => buffer.write(tok.toString()));
+ return buffer.toString();
+ }
+
+}
+
+
+/// Describes a piece of text in a [Line].
+abstract class LineText {
+ int get length;
+}
+
+
+/// A working piece of text used in calculating line breaks
+class Chunk {
+ final int indent;
+ final int maxLength;
+ final List<LineToken> tokens = <LineToken>[];
+
+ Chunk(this.indent, this.maxLength, [List<LineToken> tokens]) {
+ this.tokens.addAll(tokens);
+ }
+
+ int get length {
+ return tokens.fold(0, (len, token) => len + token.length);
+ }
+
+ int getLengthToSpaceWithWeight(int weight) {
+ int length = 0;
+ for (LineToken token in tokens) {
+ if (token is SpaceToken && token.breakWeight == weight) {
+ break;
+ }
+ length += token.length;
+ }
+ return length;
+ }
+
+ bool fits(LineToken a, LineToken b) {
+ return length + a.length + a.length <= maxLength;
+ }
+
+ void add(LineToken token) {
+ tokens.add(token);
+ }
+
+ bool hasInitializerSpace() {
+ return tokens.any((token) {
+ return token is SpaceToken && token.breakWeight == SINGLE_SPACE_WEIGHT;
+ });
+ }
+
+ bool hasAnySpace() {
+ return tokens.any((token) => token is SpaceToken);
+ }
+
+ int findMinSpaceWeight() {
+ int minWeight = UNBREAKABLE_SPACE_WEIGHT;
+ for (var token in tokens) {
+ if (token is SpaceToken) {
+ minWeight = math.min(minWeight, token.breakWeight);
+ }
+ }
+ return minWeight;
+ }
+
+ Chunk subChunk(int indentLevel, int start, [int end]) {
+ List<LineToken> subTokens = tokens.sublist(start, end);
+ return new Chunk(indentLevel, maxLength, subTokens);
+ }
+
+ String toString() => tokens.join();
+}
+
+
+class LineToken implements LineText {
+
+ final String value;
+
+ LineToken(this.value);
+
+ String toString() => value;
+
+ int get length => lengthLessNewlines(value);
+
+ int lengthLessNewlines(String str) =>
+ str.endsWith('\n') ? str.length - 1 : str.length;
+
+}
+
+
+class SpaceToken extends LineToken {
+
+ final int breakWeight;
+
+ SpaceToken(int n, {this.breakWeight: DEFAULT_SPACE_WEIGHT}) :
+ super(getSpaces(n));
+}
+
+
+class TabToken extends LineToken {
+
+ TabToken(int n) : super(getTabs(n));
+}
+
+
+class NewlineToken extends LineToken {
+
+ NewlineToken(String value) : super(value);
+}
+
+
+class SourceWriter {
+
+ final StringBuffer buffer = new StringBuffer();
+ Line currentLine;
+
+ final String lineSeparator;
+ int indentCount = 0;
+ final int spacesPerIndent;
+ final bool useTabs;
+
+ LinePrinter linePrinter;
+ LineToken _lastToken;
+
+ SourceWriter({this.indentCount: 0, this.lineSeparator: NEW_LINE,
+ this.useTabs: false, this.spacesPerIndent: 2, int maxLineLength: 80}) {
+ if (maxLineLength > 0) {
+ linePrinter = new SimpleLineBreaker(maxLineLength, (n) =>
+ getIndentString(n, useTabs: useTabs, spacesPerIndent: spacesPerIndent));
+ } else {
+ linePrinter = new SimpleLinePrinter();
+ }
+ currentLine = newLine();
+ }
+
+ LineToken get lastToken => _lastToken;
+
+ _addToken(LineToken token) {
+ _lastToken = token;
+ currentLine.addToken(token);
+ }
+
+ void indent() {
+ ++indentCount;
+ // Rather than fiddle with deletions/insertions just start fresh
+ if (currentLine.isWhitespace()) {
+ currentLine = newLine();
+ }
+ }
+
+ void newline() {
+ if (currentLine.isWhitespace()) {
+ currentLine.tokens.clear();
+ }
+ _addToken(new NewlineToken(this.lineSeparator));
+ buffer.write(currentLine.toString());
+ currentLine = newLine();
+ }
+
+ void newlines(int num) {
+ while (num-- > 0) {
+ newline();
+ }
+ }
+
+ void write(String string) {
+ var lines = string.split(lineSeparator);
+ var length = lines.length;
+ for (int i = 0; i < length; i++) {
+ var line = lines[i];
+ _addToken(new LineToken(line));
+ if (i != length - 1) {
+ newline();
+ // no indentation for multi-line strings
+ currentLine.clear();
+ }
+ }
+ }
+
+ void writeln(String s) {
+ write(s);
+ newline();
+ }
+
+ void space() {
+ spaces(1);
+ }
+
+ void spaces(n, {breakWeight: DEFAULT_SPACE_WEIGHT}) {
+ currentLine.addSpaces(n, breakWeight: breakWeight);
+ }
+
+ void unindent() {
+ --indentCount;
+ // Rather than fiddle with deletions/insertions just start fresh
+ if (currentLine.isWhitespace()) {
+ currentLine = newLine();
+ }
+ }
+
+ Line newLine() => new Line(indentLevel: indentCount, useTabs: useTabs,
+ spacesPerIndent: spacesPerIndent, printer: linePrinter);
+
+ String toString() {
+ var source = new StringBuffer(buffer.toString());
+ if (!currentLine.isWhitespace()) {
+ source.write(currentLine);
+ }
+ return source.toString();
+ }
+
+}
+
+const NEW_LINE = '\n';
+const SPACE = ' ';
+const SPACES = const [
+ '',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+];
+const TABS = const [
+ '',
+ '\t',
+ '\t\t',
+ '\t\t\t',
+ '\t\t\t\t',
+ '\t\t\t\t\t',
+ '\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t\t\t\t\t',
+ '\t\t\t\t\t\t\t\t\t\t\t\t\t\t',
+];
+
+
+String getIndentString(int indentWidth, {bool useTabs: false,
+ int spacesPerIndent: 2}) => useTabs ? getTabs(indentWidth) :
+ getSpaces(indentWidth * spacesPerIndent);
+
+String getSpaces(int n) => n < SPACES.length ? SPACES[n] : repeat(' ', n);
+
+String getTabs(int n) => n < TABS.length ? TABS[n] : repeat('\t', n);
+
+String repeat(String ch, int times) {
+ var sb = new StringBuffer();
+ for (var i = 0; i < times; ++i) {
+ sb.write(ch);
+ }
+ return sb.toString();
+}

Powered by Google App Engine
This is Rietveld 408576698