-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfind_comments.rb
100 lines (86 loc) · 3.35 KB
/
find_comments.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# This script finds all of the comments within a given source file for a method.
require "prism"
class FindMethodComments < Prism::Visitor
def initialize(target, comments, nesting = [])
@target = target
@comments = comments
@nesting = nesting
end
# These visit methods are specific to each class. Defining a visitor allows
# you to group functionality that applies to all node types into a single
# class. You can find which method corresponds to which node type by looking
# at the class name, calling #type on the node, or by looking at the #accept
# method definition on the node.
def visit_module_node(node)
visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
node.compact_child_nodes.each { |child| child.accept(visitor) }
end
def visit_class_node(node)
# We could keep track of an internal state where we push the class name here
# and then pop it after the visit is complete. However, it is often simpler
# and cleaner to generate a new visitor instance when the state changes,
# because then the state is immutable and it's easier to reason about. This
# also provides for more debugging opportunity in the initializer.
visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
node.compact_child_nodes.each { |child| child.accept(visitor) }
end
def visit_def_node(node)
if [*@nesting, node.name] == @target
# Comments are always attached to locations (either inner locations on a
# node like the location of a keyword or the location on the node itself).
# Nodes are considered either "leading" or "trailing", which means that
# they occur before or after the location, respectively. In this case of
# documentation, we only want to consider leading comments. You can also
# fetch all of the comments on a location with #comments.
@comments.concat(node.location.leading_comments)
else
super
end
end
end
# Most of the time, the concept of "finding" something in the AST can be
# accomplished either with a queue or with a visitor. In this case we will use a
# visitor, but a queue would work just as well.
def find_comments(result, path)
target = path.split(/::|#/).map(&:to_sym)
comments = []
result.value.accept(FindMethodComments.new(target, comments))
comments
end
result = Prism.parse_stream(DATA)
result.attach_comments!
find_comments(result, "Foo#foo").each do |comment|
puts comment.inspect
puts comment.slice
end
# =>
# #<Prism::InlineComment @location=#<Prism::Location @start_offset=205 @length=27 start_line=13>>
# # This is the documentation
# #<Prism::InlineComment @location=#<Prism::Location @start_offset=235 @length=21 start_line=14>>
# # for the foo method.
find_comments(result, "Foo::Bar#bar").each do |comment|
puts comment.inspect
puts comment.slice
end
# =>
# #<Prism::InlineComment @location=#<Prism::Location @start_offset=126 @length=23 start_line=7>>
# # This is documentation
# #<Prism::InlineComment @location=#<Prism::Location @start_offset=154 @length=21 start_line=8>>
# # for the bar method.
__END__
# This is the documentation
# for the Foo module.
module Foo
# This is documentation
# for the Bar class.
class Bar
# This is documentation
# for the bar method.
def bar
end
end
# This is the documentation
# for the foo method.
def foo
end
end