part of mustache; | |
class _Template implements Template { | |
_Template.fromSource(String source, | |
{bool lenient: false, | |
bool htmlEscapeValues : true, | |
String name, | |
PartialResolver partialResolver}) | |
: source = source, | |
_root = _parse(source, lenient, name, '{{ }}'), | |
_lenient = lenient, | |
_htmlEscapeValues = htmlEscapeValues, | |
_name = name, | |
_partialResolver = partialResolver; | |
final String source; | |
final _Node _root; | |
final bool _lenient; | |
final bool _htmlEscapeValues; | |
final String _name; | |
final PartialResolver _partialResolver; | |
String get name => _name; | |
String renderString(values) { | |
var buf = new StringBuffer(); | |
render(values, buf); | |
return buf.toString(); | |
} | |
void render(values, StringSink sink) { | |
var renderer = new _Renderer(_root, sink, values, [values], | |
_lenient, _htmlEscapeValues, _partialResolver, _name, '', source, | |
'{{ }}'); | |
renderer.render(); | |
} | |
} | |
class _TemplateException implements TemplateException { | |
_TemplateException(this.message, this.templateName, this.source, this.offset); | |
final String message; | |
final String templateName; | |
final String source; | |
final int offset; | |
bool _isUpdated = false; | |
int _line; | |
int _column; | |
String _context; | |
int get line { | |
_update(); | |
return _line; | |
} | |
int get column { | |
_update(); | |
return _column; | |
} | |
String get context { | |
_update(); | |
return _context; | |
} | |
String toString() { | |
var list = []; | |
if (templateName != null) list.add(templateName); | |
if (line != null) list.add(line); | |
if (column != null) list.add(column); | |
var location = list.isEmpty ? '' : ' (${list.join(':')})'; | |
return '$message$location\n$context'; | |
} | |
// This source code is a modified version of FormatException.toString(). | |
void _update() { | |
if (_isUpdated) return; | |
_isUpdated = true; | |
if (source == null | |
|| offset == null | |
|| (offset < 0 || offset > source.length)) | |
return; | |
// Find line and character column. | |
int lineNum = 1; | |
int lineStart = 0; | |
bool lastWasCR; | |
for (int i = 0; i < offset; i++) { | |
int char = source.codeUnitAt(i); | |
if (char == 0x0a) { | |
if (lineStart != i || !lastWasCR) { | |
lineNum++; | |
} | |
lineStart = i + 1; | |
lastWasCR = false; | |
} else if (char == 0x0d) { | |
lineNum++; | |
lineStart = i + 1; | |
lastWasCR = true; | |
} | |
} | |
_line = lineNum; | |
_column = offset - lineStart + 1; | |
// Find context. | |
int lineEnd = source.length; | |
for (int i = offset; i < source.length; i++) { | |
int char = source.codeUnitAt(i); | |
if (char == 0x0a || char == 0x0d) { | |
lineEnd = i; | |
break; | |
} | |
} | |
int length = lineEnd - lineStart; | |
int start = lineStart; | |
int end = lineEnd; | |
String prefix = ""; | |
String postfix = ""; | |
if (length > 78) { | |
// Can't show entire line. Try to anchor at the nearest end, if | |
// one is within reach. | |
int index = offset - lineStart; | |
if (index < 75) { | |
end = start + 75; | |
postfix = "..."; | |
} else if (end - offset < 75) { | |
start = end - 75; | |
prefix = "..."; | |
} else { | |
// Neither end is near, just pick an area around the offset. | |
start = offset - 36; | |
end = offset + 36; | |
prefix = postfix = "..."; | |
} | |
} | |
String slice = source.substring(start, end); | |
int markOffset = offset - start + prefix.length; | |
_context = "$prefix$slice$postfix\n${" " * markOffset}^\n"; | |
} | |
} |