SourceCode formatSource()

in lib/src/dart_formatter.dart [85:182]


  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.fromEnableFlags2(
      sdkLanguageVersion: Version(2, 17, 0),
      flags: [
        'enhanced-enums',
        'named-arguments-anywhere',
        'super-parameters',
      ],
    );

    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;
  }