Skip to content

Commit 79ac260

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 79ac260

6 files changed

Lines changed: 63 additions & 44 deletions

File tree

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: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,63 @@ module Prototype
55
module Helpers
66
private
77

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

sig/prototype/helpers.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module RBS
33
module Helpers
44
type node = RubyVM::AbstractSyntaxTree::Node
55

6+
def parse_comments: (String, include_trailing: bool) -> Hash[Integer, AST::Comment]
7+
68
def block_from_body: (node) -> Types::Block?
79

810
def each_node: (Array[untyped] nodes) { (node) -> void } -> void

0 commit comments

Comments
 (0)