| // Copyright (c) 2015, 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. |
| |
| import 'dart:io'; |
| |
| import 'package:analyzer/dart/analysis/features.dart'; |
| import 'package:analyzer/dart/analysis/utilities.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/token.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| |
| final class Spelunker { |
| final StringSink _sink; |
| final FeatureSet _featureSet; |
| final String _source; |
| |
| Spelunker(this._source, {StringSink? sink, FeatureSet? featureSet}) |
| : _sink = sink ?? stdout, |
| _featureSet = featureSet ?? FeatureSet.latestLanguageVersion(); |
| |
| void spelunk() { |
| var parseResult = parseString(content: _source, featureSet: _featureSet); |
| |
| var visitor = _SourceVisitor(_sink); |
| parseResult.unit.accept(visitor); |
| } |
| } |
| |
| class _SourceVisitor extends GeneralizingAstVisitor { |
| int indent = 0; |
| |
| final StringSink sink; |
| |
| _SourceVisitor(this.sink); |
| |
| String asString(AstNode node) => '${typeInfo(node.runtimeType)} [$node]'; |
| |
| List<CommentToken> getPrecedingComments(Token token) { |
| var comments = <CommentToken>[]; |
| var comment = token.precedingComments; |
| while (comment is CommentToken) { |
| comments.add(comment); |
| comment = comment.next as CommentToken?; |
| } |
| return comments; |
| } |
| |
| String getTrailingComment(AstNode node) { |
| var successor = node.endToken.next; |
| if (successor != null) { |
| var precedingComments = successor.precedingComments; |
| if (precedingComments != null) { |
| return precedingComments.toString(); |
| } |
| } |
| return ''; |
| } |
| |
| String typeInfo(Type type) => type.toString(); |
| |
| @override |
| void visitNode(AstNode node) { |
| write(node); |
| |
| ++indent; |
| node.visitChildren(this); |
| --indent; |
| } |
| |
| void write(AstNode node) { |
| // EOL comments. |
| var comments = getPrecedingComments(node.beginToken); |
| for (var comment in comments) { |
| sink.writeln('${" " * indent}$comment'); |
| } |
| |
| sink.writeln( |
| '${" " * indent}${asString(node)} ${getTrailingComment(node)}', |
| ); |
| } |
| } |