formatSource method

SourceCode formatSource (
  1. SourceCode source
)

Formats the given source.

Returns a new SourceCode containing the formatted code and the resulting selection, if any.

Implementation

SourceCode formatSource(SourceCode source) {
  // Enable all features that are enabled by default in the current analyzer
  // version.
  // TODO(paulberry): consider plumbing in experiment enable flags from the
  // command line.
  var featureSet = FeatureSet.fromEnableFlags([
    'extension-methods',
    'non-nullable',
  ]);

  var inputOffset = 0;
  var text = source.text;
  var unitSourceCode = source;
  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 = parseString(
    content: text,
    featureSet: featureSet,
    path: source.uri,
    throwIfDiagnostics: false,
  );

  // 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(
          stringSource,
          token.offset - inputOffset,
          math.max(token.length, 1),
          ParserErrorCode.UNEXPECTED_TOKEN,
          [token.lexeme]);

      throw FormatterException([error]);
    }
  }

  // Format it.
  var lineInfo = parseResult.lineInfo;
  var visitor = SourceVisitor(this, lineInfo, unitSourceCode);
  var 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;
}