Skip to content

Commit c67fe7e

Browse files
committed
Use prism to parse comments when available
The long-term goal is to deprecate ripper: https://bugs.ruby-lang.org/issues/21827 So, this starts using prism to parse. Prism already knows if a comment is preceeded by code via `trailing?`, so that makes the `RB` case a bit simpler. Ripper is still used when running as ruby 3.2 because prism can't parse 3.2 syntax. When runtime support for 3.2 is dropped, the fallback code can be dropped as well.
1 parent 67563ca commit c67fe7e

File tree

5 files changed

+63
-44
lines changed

5 files changed

+63
-44
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ PATH
33
specs:
44
rbs (4.0.0.dev.5)
55
logger
6-
prism (>= 1.3.0)
6+
prism (>= 1.6.0)
77
tsort
88

99
PATH

lib/rbs/prototype/helpers.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,65 @@ module Prototype
55
module Helpers
66
private
77

8+
# Prism can't parse Ruby 3.2 code
9+
CAN_USE_PRISM = RUBY_VERSION > "3.3.0"
10+
11+
if CAN_USE_PRISM
12+
def parse_comments(string, include_trailing:)
13+
Prism.parse_comments(string, version: "current").yield_self do |prism_comments|
14+
prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
15+
# Skip EmbDoc comments
16+
next unless comment.is_a?(Prism::InlineComment)
17+
# skip like `module Foo # :nodoc:`
18+
next if comment.trailing? && !include_trailing
19+
20+
line = comment.location.start_line
21+
body = "#{comment.location.slice}\n"
22+
body = body[2..-1] or raise
23+
body = "\n" if body.empty?
24+
25+
comment = AST::Comment.new(string: body, location: nil)
26+
if prev_comment = hash.delete(line - 1)
27+
hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
28+
location: nil)
29+
else
30+
hash[line] = comment
31+
end
32+
end
33+
end
34+
end
35+
else
36+
require "ripper"
37+
def parse_comments(string, include_trailing:)
38+
Ripper.lex(string).yield_self do |tokens|
39+
code_lines = {} #: Hash[Integer, bool]
40+
tokens.each.with_object({}) do |token, hash| #$ Hash[Integer, AST::Comment]
41+
case token[1]
42+
when :on_sp, :on_ignored_nl
43+
# skip
44+
when :on_comment
45+
line = token[0][0]
46+
# skip like `module Foo # :nodoc:`
47+
next if code_lines[line] && !include_trailing
48+
body = token[2][2..-1] or raise
49+
50+
body = "\n" if body.empty?
51+
52+
comment = AST::Comment.new(string: body, location: nil)
53+
if prev_comment = hash.delete(line - 1)
54+
hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
55+
location: nil)
56+
else
57+
hash[line] = comment
58+
end
59+
else
60+
code_lines[token[0][0]] = true
61+
end
62+
end
63+
end
64+
end
65+
end
66+
867
def block_from_body(node)
968
_, args_node, body_node = node.children
1069
_pre_num, _pre_init, _opt, _first_post, _post_num, _post_init, _rest, _kw, _kwrest, block_var = args_from_node(args_node)

lib/rbs/prototype/rb.rb

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,27 +74,7 @@ def decls
7474

7575
def parse(string)
7676
# @type var comments: Hash[Integer, AST::Comment]
77-
comments = Prism.parse_comments(string).yield_self do |prism_comments|
78-
prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
79-
# Skip EmbDoc comments
80-
next unless comment.is_a?(Prism::InlineComment)
81-
# skip like `module Foo # :nodoc:`
82-
next if comment.trailing?
83-
84-
line = comment.location.start_line
85-
body = "#{comment.location.slice}\n"
86-
body = body[2..-1] or raise
87-
body = "\n" if body.empty?
88-
89-
comment = AST::Comment.new(string: body, location: nil)
90-
if prev_comment = hash.delete(line - 1)
91-
hash[line] = AST::Comment.new(string: prev_comment.string + comment.string,
92-
location: nil)
93-
else
94-
hash[line] = comment
95-
end
96-
end
97-
end
77+
comments = parse_comments(string, include_trailing: false)
9878

9979
process RubyVM::AbstractSyntaxTree.parse(string), decls: source_decls, comments: comments, context: Context.initial
10080
end

lib/rbs/prototype/rbi.rb

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,7 @@ def initialize
1616
end
1717

1818
def parse(string)
19-
comments = Prism.parse_comments(string).yield_self do |prism_comments|
20-
prism_comments.each_with_object({}) do |comment, hash| #$ Hash[Integer, AST::Comment]
21-
# Skip EmbDoc comments
22-
next unless comment.is_a?(Prism::InlineComment)
23-
24-
line = comment.location.start_line
25-
body = "#{comment.location.slice}\n"
26-
body = body[2..-1] or raise
27-
body = "\n" if body.empty?
28-
29-
comment = AST::Comment.new(string: body, location: nil)
30-
if (prev_comment = hash.delete(line - 1))
31-
hash[line] = AST::Comment.new(
32-
string: prev_comment.string + comment.string,
33-
location: nil
34-
)
35-
else
36-
hash[line] = comment
37-
end
38-
end
39-
end
19+
comments = parse_comments(string, include_trailing: true)
4020
process RubyVM::AbstractSyntaxTree.parse(string), comments: comments
4121
end
4222

rbs.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ Gem::Specification.new do |spec|
4646
spec.require_paths = ["lib"]
4747
spec.required_ruby_version = ">= 3.2"
4848
spec.add_dependency "logger"
49-
spec.add_dependency "prism", ">= 1.3.0"
49+
spec.add_dependency "prism", ">= 1.6.0"
5050
spec.add_dependency "tsort"
5151
end

0 commit comments

Comments
 (0)