blob: f16a4ac32a865e22c07d0d4278068c9ac5d68a6c [file] [log] [blame]
// 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.
/// Indentation utility class. Should be used as a mixin in most cases.
class Indentation {
/// The current indentation string.
String get indentation {
// Lazily add new indentation strings as required.
for (int i = _indentList.length; i <= _indentLevel; i++) {
_indentList.add(_indentList[i - 1] + indentationUnit);
}
return _indentList[_indentLevel];
}
/// The current indentation level.
int _indentLevel = 0;
/// A cache of all indentation strings used so far.
/// Always at least of length 1.
List<String> _indentList = <String>[""];
/// The indentation unit, defaulting to two spaces. May be overwritten.
String _indentationUnit = " ";
String get indentationUnit => _indentationUnit;
set indentationUnit(String value) {
if (value != _indentationUnit) {
_indentationUnit = value;
_indentList = <String>[""];
}
}
/// Increases the current level of indentation.
void indentMore() {
_indentLevel++;
}
/// Decreases the current level of indentation.
void indentLess() {
_indentLevel--;
}
/// Calls [f] with one more indentation level, restoring indentation context
/// upon return of [f] and returning its result.
void indentBlock(Function f) {
indentMore();
dynamic result = f();
indentLess();
return result;
}
}
abstract class Tagging<N> implements Indentation {
StringBuffer sb = new StringBuffer();
List<String> tagStack = [];
void pushTag(String tag) {
tagStack.add(tag);
indentMore();
}
String popTag() {
assert(!tagStack.isEmpty);
indentLess();
return tagStack.removeLast();
}
/// Adds given string to result string.
void add(String string) {
sb.write(string);
}
/// Adds default parameters for [node] into [params].
void addDefaultParameters(N node, Map params) {}
/// Adds given node type to result string.
/// The method "opens" the node, meaning that all output after calling
/// this method and before calling closeNode() will represent contents
/// of given node.
void openNode(N node, String type, [Map? params]) {
if (params == null) params = new Map();
addCurrentIndent();
sb.write("<");
addDefaultParameters(node, params);
addTypeWithParams(type, params);
sb.write(">\n");
pushTag(type);
}
/// Adds given node to result string.
void openAndCloseNode(N node, String type, [Map? params]) {
if (params == null) params = {};
addCurrentIndent();
sb.write("<");
addDefaultParameters(node, params);
addTypeWithParams(type, params);
sb.write("/>\n");
}
/// Closes current node type.
void closeNode() {
String tag = popTag();
addCurrentIndent();
sb.write("</");
addTypeWithParams(tag);
sb.write(">\n");
}
void addTypeWithParams(String type, [Map? params]) {
if (params == null) params = new Map();
sb.write("${type}");
params.forEach((k, v) {
String value;
if (v != null) {
String str = valueToString(v);
value = str
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "'");
} else {
value = "[null]";
}
sb.write(' $k="$value"');
});
}
void addCurrentIndent() {
sb.write(indentation);
}
/// Converts a parameter value into a string.
String valueToString(dynamic value) => value;
}