formatSource method
Formats the given source
.
Returns a new SourceCode containing the formatted code and the resulting selection, if any.
Implementation
SourceCode formatSource(SourceCode source) {
var inputOffset = 0;
var text = source.text;
var unitSourceCode = source;
// If we're parsing a single statement, wrap the source in a fake function.
if (!source.isCompilationUnit) {
var prefix = 'void foo() { ';
inputOffset = prefix.length;
text = '$prefix$text }';
unitSourceCode = SourceCode(
text,
uri: source.uri,
isCompilationUnit: false,
selectionStart: source.selectionStart != null
? source.selectionStart! + inputOffset
: null,
selectionLength: source.selectionLength,
);
}
// Parse it.
var parseResult = _parse(text, source.uri, patterns: true);
// If we couldn't parse it with patterns enabled, it may be because of
// one of the breaking syntax changes to switch cases. Try parsing it
// again without patterns.
if (parseResult.errors.isNotEmpty) {
var withoutPatternsResult = _parse(text, source.uri, patterns: false);
// If we succeeded this time, use this parse instead.
if (withoutPatternsResult.errors.isEmpty) {
parseResult = withoutPatternsResult;
}
}
// Infer the line ending if not given one. Do it here since now we know
// where the lines start.
if (lineEnding == null) {
// If the first newline is "\r\n", use that. Otherwise, use "\n".
var lineStarts = parseResult.lineInfo.lineStarts;
if (lineStarts.length > 1 &&
lineStarts[1] >= 2 &&
text[lineStarts[1] - 2] == '\r') {
lineEnding = '\r\n';
} else {
lineEnding = '\n';
}
}
// Throw if there are syntactic errors.
var syntacticErrors = parseResult.errors.where((error) {
return error.errorCode.type == ErrorType.SYNTACTIC_ERROR;
}).toList();
if (syntacticErrors.isNotEmpty) {
throw FormatterException(syntacticErrors);
}
AstNode node;
if (source.isCompilationUnit) {
node = parseResult.unit;
} else {
var function = parseResult.unit.declarations[0] as FunctionDeclaration;
var body = function.functionExpression.body as BlockFunctionBody;
node = body.block.statements[0];
// Make sure we consumed all of the source.
var token = node.endToken.next!;
if (token.type != TokenType.CLOSE_CURLY_BRACKET) {
var stringSource = StringSource(text, source.uri);
var error = AnalysisError.tmp(
source: stringSource,
offset: token.offset - inputOffset,
length: math.max(token.length, 1),
errorCode: ParserErrorCode.UNEXPECTED_TOKEN,
arguments: [token.lexeme]);
throw FormatterException([error]);
}
}
// Format it.
var lineInfo = parseResult.lineInfo;
SourceCode output;
if (experimentFlags.contains(tallStyleExperimentFlag)) {
var visitor = AstNodeVisitor(this, lineInfo, unitSourceCode);
output = visitor.run(node);
} else {
var visitor = SourceVisitor(this, lineInfo, unitSourceCode);
output = visitor.run(node);
}
// Sanity check that only whitespace was changed if that's all we expect.
if (fixes.isEmpty &&
!string_compare.equalIgnoringWhitespace(source.text, output.text)) {
throw UnexpectedOutputException(source.text, output.text);
}
return output;
}