+
+ # Do a deep search and return an array of tokens that match a given text
+ fun collect_tokens_by_text(text: String): Array[Token]
+ do
+ var v = new CollectTokensByTextVisitor(text)
+ v.enter_visit(self)
+ return v.result
+ end
+
+ # Do a deep search and return an array of node that are annotated
+ # The attached node can be retrieved by two invocations of parent
+ fun collect_annotations_by_name(name: String): Array[AAnnotation]
+ do
+ var v = new CollectAnnotationsByNameVisitor(name)
+ v.enter_visit(self)
+ return v.result
+ end
+end
+
+private class CollectTokensByTextVisitor
+ super Visitor
+ var text: String
+ var result = new Array[Token]
+ redef fun visit(node)
+ do
+ node.visit_all(self)
+ if node isa Token and node.text == text then result.add(node)
+ end
+end
+
+private class CollectAnnotationsByNameVisitor
+ super Visitor
+ var name: String
+ var result = new Array[AAnnotation]
+ redef fun visit(node)
+ do
+ node.visit_all(self)
+ if node isa AAnnotation and node.n_atid.n_id.text == name then result.add(node)
+ end
+end
+
+# A helper class to handle (print) Nit AST as an OrderedTree
+class ASTDump
+ super Visitor
+ super OrderedTree[ANode]
+
+ # Reference to the last parent in the Ordered Tree
+ # Is used to handle the initial node parent and workaround possible inconsistent `ANode::parent`
+ private var last_parent: nullable ANode = null
+
+ # Display tokens and structural production?
+ #
+ # Should tokens (and structural production like AQId) be displayed?
+ var display_structural: Bool
+
+ # Display lines?
+ #
+ # Should each new line be displayed (numbered and in gray)?
+ var display_line: Bool
+
+ # Current line number (when printing lines)
+ private var line = 0
+
+ redef fun visit(n)
+ do
+ if not display_structural and n.is_structural then return
+ var p = last_parent
+ add(p, n)
+ last_parent = n
+ n.visit_all(self)
+ last_parent = p
+ end
+
+ redef fun write_line(o, n, p)
+ do
+ if display_line then
+ var ls = n.location.line_start
+ var file = n.location.file
+ var line = self.line
+ if ls > line and file != null then
+ if line == 0 then line = ls - 1
+ while line < ls do
+ line += 1
+ o.write "{line}\t{file.get_line(line)}\n".light_gray
+ end
+ self.line = ls
+ end
+ end
+
+ super
+ end
+
+ redef fun display(n)
+ do
+ return "{n.class_name} {n.dump_info(self)} @{n.location}"
+ end
+
+ # `s` as yellow
+ fun yellow(s: String): String do return s.yellow
+
+ # `s` as red
+ fun red(s: String): String do return s.red