blob: 96ca93ff3287f35d66f995c08616155381fa77da [file] [log] [blame]
// Copyright (c) 2023, 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.
/// Class to write string literals for bytes or words.
///
/// The string will be `'` delimited.
/// Escapes as necessary, and performs line breaks to stay within 80
/// characters.
class StringLiteralWriter {
final StringSink buffer;
final String _padding;
final int _lineLength;
final bool Function(int) _escape;
int _currentLineLength = 0;
static final Map<int, String> _escapeCache = {};
StringLiteralWriter(
this.buffer, {
int padding = 0,
int lineLength = 80,
bool Function(int)? escape,
}) : _padding = ' ' * padding,
_lineLength = lineLength,
_escape = escape ?? _defaultEscape;
static bool _defaultEscape(int codeUnit) {
return codeUnit < 0x20 || codeUnit >= 0x7f && codeUnit <= 0xa0;
}
void start([int initialOffset = 0]) {
if (initialOffset >= _lineLength - 2) {
buffer
..write('\n')
..write(_padding);
initialOffset = _padding.length;
}
buffer.write("'");
_currentLineLength = initialOffset + 1;
}
/// Adds a single UTF-16 code unit.
void add(int codeUnit) {
// Always escape: `\n`, `\r`, `'`, `$` and `\`, plus anything the user wants.
if (_escape(codeUnit) || // Anything the user wants encoded.
codeUnit == 0x24 /* $ */ ||
codeUnit == 0x27 /* ' */ ||
codeUnit == 0x5c /* \ */ ||
codeUnit == 0x0a /* \n */ ||
codeUnit == 0x0d /* \r */) {
_writeEscape(codeUnit);
return;
}
if (_currentLineLength >= _lineLength - 1) {
_wrap();
}
_currentLineLength++;
buffer.writeCharCode(codeUnit);
}
/// Writes an escape for the [codeUnit].
///
/// Is only called for characters that need escaping.
void _writeEscape(int codeUnit) {
var replacement = _escapeCache[codeUnit];
if (replacement == null) {
if (codeUnit < 0x10) {
if (codeUnit == '\b'.codeUnitAt(0)) {
replacement = r'\b';
} else if (codeUnit == '\t'.codeUnitAt(0)) {
replacement = r'\t';
} else if (codeUnit == '\n'.codeUnitAt(0)) {
replacement = r'\n';
} else if (codeUnit == '\v'.codeUnitAt(0)) {
replacement = r'\v';
} else if (codeUnit == '\f'.codeUnitAt(0)) {
replacement = r'\f';
} else if (codeUnit == '\r'.codeUnitAt(0)) {
replacement = r'\r';
} else {
replacement = r'\x0' + codeUnit.toRadixString(16);
}
} else if (codeUnit < 0x100) {
if (codeUnit == r'$'.codeUnitAt(0)) {
replacement = r'\$';
} else if (codeUnit == "'".codeUnitAt(0)) {
replacement = r"\'";
} else if (codeUnit == r'\'.codeUnitAt(0)) {
replacement = r'\\';
} else {
replacement = r'\x' + codeUnit.toRadixString(16);
}
} else if (codeUnit < 0x1000) {
replacement = r'\u0' + codeUnit.toRadixString(16);
} else if (codeUnit < 0x10000) {
replacement = r'\u' + codeUnit.toRadixString(16);
} else {
replacement = '\\u{${codeUnit.toRadixString(16)}}';
}
_escapeCache[codeUnit] = replacement;
}
if (_currentLineLength + replacement.length + 1 > _lineLength) {
_wrap();
}
buffer.write(replacement);
_currentLineLength += replacement.length;
}
void _wrap() {
buffer
..write("'\n")
..write(_padding)
..write("'");
_currentLineLength = _padding.length + 1;
}
void end() {
buffer.write("'");
}
}