Skip to content

Commit 0f4fd2c

Browse files
committed
Add CommentAssociation
1 parent 15d2614 commit 0f4fd2c

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed

lib/rbs.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
require "rbs/ast/ruby/members"
3434
require "rbs/source"
3535
require "rbs/inline_parser"
36+
require "rbs/inline_parser/comment_association"
3637
require "rbs/environment"
3738
require "rbs/environment/use_map"
3839
require "rbs/environment/class_entry"
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
module RBS
2+
class InlineParser
3+
class CommentAssociation
4+
attr_reader :blocks, :associated_blocks, :start_line_map, :end_line_map
5+
6+
def initialize(blocks)
7+
@blocks = blocks.sort_by {|block| block.start_line }
8+
@associated_blocks = Set[].compare_by_identity
9+
10+
@start_line_map = {}
11+
@end_line_map = {}
12+
13+
blocks.each do |block|
14+
if block.leading?
15+
end_line_map[block.end_line] = block
16+
else
17+
start_line_map[block.start_line] = block
18+
end
19+
end
20+
end
21+
22+
def self.build(buffer, result)
23+
blocks = AST::Ruby::CommentBlock.build(buffer, result.comments)
24+
new(blocks)
25+
end
26+
27+
class Reference
28+
attr_reader :block
29+
30+
def initialize(block, association)
31+
@block = block
32+
@associated_blocks = association
33+
end
34+
35+
def associate!
36+
@associated_blocks << block
37+
self
38+
end
39+
40+
def associated?
41+
@associated_blocks.include?(block)
42+
end
43+
end
44+
45+
def leading_block(node)
46+
start_line = node.location.start_line
47+
48+
if block = end_line_map.fetch(start_line - 1, nil)
49+
Reference.new(block, associated_blocks)
50+
end
51+
end
52+
53+
def leading_block!(node)
54+
if ref = leading_block(node)
55+
unless ref.associated?
56+
ref.associate!.block
57+
end
58+
end
59+
end
60+
61+
def trailing_block(node)
62+
location =
63+
if node.is_a?(Prism::Node)
64+
node.location
65+
else
66+
node
67+
end #: Prism::Location
68+
end_line = location.end_line
69+
if block = start_line_map.fetch(end_line, nil)
70+
Reference.new(block, associated_blocks)
71+
end
72+
end
73+
74+
def trailing_block!(node)
75+
if ref = trailing_block(node)
76+
unless ref.associated?
77+
ref.associate!.block
78+
end
79+
end
80+
end
81+
82+
def each_enclosed_block(node)
83+
if block_given?
84+
start_line = node.location.start_line
85+
end_line = node.location.end_line
86+
87+
if start_line+1 < end_line
88+
((start_line + 1)...end_line).each do |line|
89+
if block = end_line_map.fetch(line, nil)
90+
unless associated_blocks.include?(block)
91+
associated_blocks << block
92+
yield block
93+
end
94+
end
95+
end
96+
end
97+
else
98+
enum_for :each_enclosed_block, node
99+
end
100+
end
101+
102+
def each_unassociated_block
103+
if block_given?
104+
blocks.each do |block|
105+
unless associated_blocks.include?(block)
106+
yield block
107+
end
108+
end
109+
else
110+
enum_for :each_unassociated_block
111+
end
112+
end
113+
end
114+
end
115+
end
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use RBS::AST::Ruby::CommentBlock
2+
3+
module RBS
4+
class InlineParser
5+
# CommentAssociation manages the association between `Prism::Node` and `CommentBlock`
6+
#
7+
class CommentAssociation
8+
attr_reader blocks: Array[CommentBlock]
9+
10+
attr_reader start_line_map: Hash[Integer, CommentBlock]
11+
12+
attr_reader end_line_map: Hash[Integer, CommentBlock]
13+
14+
# CommentBlocks that are already associated to a node, which cannot be associated to another node again
15+
#
16+
attr_reader associated_blocks: Set[CommentBlock]
17+
18+
def self.build: (Buffer, Prism::Result) -> instance
19+
20+
def initialize: (Array[CommentBlock]) -> void
21+
22+
class Reference
23+
attr_reader block: CommentBlock
24+
25+
@associated_blocks: Set[CommentBlock]
26+
27+
def initialize: (CommentBlock, Set[CommentBlock]) -> void
28+
29+
def associate!: () -> self
30+
31+
def associated?: () -> bool
32+
end
33+
34+
# Returns an unassociated CommentBlock that can be associated to given node
35+
#
36+
# Automatically updates association status.
37+
#
38+
def leading_block!: (Prism::Node) -> CommentBlock?
39+
40+
# Returns a Reference that is associated to given node
41+
#
42+
# Updates association explicitly through the reference.
43+
#
44+
def leading_block: (Prism::Node) -> Reference?
45+
46+
# Returns a CommentBlock that is associated to given node, or by its location
47+
#
48+
# Update association status.
49+
#
50+
def trailing_block!: (Prism::Node | Prism::Location) -> CommentBlock?
51+
52+
# Returns a Reference that is associated to given node, or by its location
53+
#
54+
# Updates association explicitly through the reference.
55+
#
56+
def trailing_block: (Prism::Node | Prism::Location) -> Reference?
57+
58+
# Yields leading CommentBlocks that is enclosed in the given node
59+
#
60+
# Note that `enclosed_blocks` works only after all of the *leading* blocks inside the node is associated.
61+
#
62+
# Update association status.
63+
#
64+
def each_enclosed_block: (Prism::Node) { (CommentBlock) -> void } -> void
65+
| (Prism::Node) -> Enumerator[CommentBlock]
66+
67+
def each_unassociated_block: () { (CommentBlock) -> void } -> void
68+
| () -> Enumerator[CommentBlock]
69+
end
70+
end
71+
end

0 commit comments

Comments
 (0)