yard_to_rbs_inline 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecc73716bfcc2a940e6d360e85a334ce640d97cfcf47c8d9f9ef64f182e8a8a3
4
- data.tar.gz: 992af7e460bd7456a16e718d79d7a993b6396a1b09a81a0aa64d20992e9959cf
3
+ metadata.gz: 86fdbb3d79a0b8fc087b0c561fde8b42a177d16354dc58dc44aa45df1c907c20
4
+ data.tar.gz: ecc100f64d44a665156193acba142037010703c9b11b943fa3868cad5f42a6ba
5
5
  SHA512:
6
- metadata.gz: 02a11e36ce83ada391d9fcc937d2eb54e3c1cb292d2107a9c350f801b23892e65da5f1c027a9207cd480b36ad4e2770967b02f736d722b573e62cb4f7422acb9
7
- data.tar.gz: 73a525cd918cf110c1e23a9b7b11cc8d1b58cb33849e6d80473727fd9d42600332a843a01cd1c06415a14876170d79d912c5fb9f266afdf62dd9d82bd10e183b
6
+ metadata.gz: 54d0d43ce951c23222a3eafc0164f24b15336dcc2e9bb7e707747c1f4d116a6a0cef87902a85ee2a31707a3bea7d4431717d3ff7b9e280115add7a7409e74362
7
+ data.tar.gz: 0ed86dd791e4806b68114796b1ccd26d66ca127923e0d99c6a3a323243c418aadaa99bd1ad6ff645c405fa8d4f522435158daa0b73d210be5ea4f298c7281208
data/.rubocop.yml CHANGED
@@ -1,5 +1,29 @@
1
+ require:
2
+ - rubocop-rbs_inline
3
+
1
4
  AllCops:
2
- TargetRubyVersion: 2.6
5
+ TargetRubyVersion: 3.2
6
+ Exclude:
7
+ - lib/yard_to_rbs_inline/yard_type/parser.rb # Generated file by racc
8
+
9
+ Lint/MissingSuper:
10
+ Enabled: false
11
+
12
+ Lint/NonLocalExitFromIterator:
13
+ Enabled: false
14
+
15
+ Lint/ShadowingOuterLocalVariable:
16
+ Enabled: false
17
+
18
+ Naming/FileName:
19
+ Exclude:
20
+ - examples/**
21
+
22
+ Style/Documentation:
23
+ Enabled: false
24
+
25
+ Style/StringConcatenation:
26
+ Mode: conservative
3
27
 
4
28
  Style/StringLiterals:
5
29
  Enabled: true
@@ -9,5 +33,12 @@ Style/StringLiteralsInInterpolation:
9
33
  Enabled: true
10
34
  EnforcedStyle: double_quotes
11
35
 
36
+ Layout/LeadingCommentSpace:
37
+ AllowRBSInlineAnnotation: true
38
+ AllowSteepAnnotation: true
39
+
12
40
  Layout/LineLength:
13
41
  Max: 120
42
+
43
+ Metrics:
44
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## [Unreleased]
2
2
 
3
+ - Write `#:` instead of `#::`.
4
+ - Configure Steep on this gem
5
+
3
6
  ## [0.1.0] - 2024-05-10
4
7
 
5
8
  - Initial release
data/README.md CHANGED
@@ -1,15 +1,25 @@
1
1
  # yard → rbs-inline
2
2
 
3
- Rewrite YARD type annotations in Your codes to rbs-inline.
3
+ Insert [rbs-inline](https://github.com/soutaro/rbs-inline) comments from [YARD type annotations](https://yardoc.org/types.html) in your codes.
4
4
 
5
5
  ## Usage
6
6
 
7
+ Install this gem with the following command.
8
+
7
9
  ```console
8
- bundle
10
+ gem install yard_to_rbs_inline
11
+ ```
12
+
13
+ Then, run the following command to insert rbs-inline comments to your codes.
9
14
 
15
+ ```console
10
16
  # Print rewrited code to stdout.
11
- ./exe/yard-rbs-inline --dry-run your-file.rb
17
+ yard_to_rbs_inline --dry-run your-file.rb
12
18
 
13
19
  # Overwrite files.
14
- ./exe/yard-rbs-inline your-file.rb
20
+ yard_to_rbs_inline your-file.rb
15
21
  ```
22
+
23
+ ## Examples
24
+
25
+ See: [examples](./examples)
data/Rakefile CHANGED
@@ -10,13 +10,51 @@ require "bump/tasks"
10
10
 
11
11
  RuboCop::RakeTask.new
12
12
 
13
- task default: %i[spec rubocop]
13
+ task default: %i[spec rubocop steep]
14
14
 
15
15
  desc "Build parser files generated by racc"
16
16
  task racc: [
17
17
  "lib/yard_to_rbs_inline/yard_type/parser.rb"
18
18
  ]
19
19
 
20
+ desc "Build rbs files"
21
+ task rbs: ["rbs:collection", "rbs:inline"]
22
+
23
+ namespace :rbs do
24
+ desc "Install collections"
25
+ task :collection do
26
+ sh "bundle exec rbs collection install"
27
+ end
28
+
29
+ desc "Generate RBS files"
30
+ task inline: "rbs:inline:build"
31
+
32
+ namespace :inline do
33
+ desc "Generate RBS files from rbs-inline"
34
+ task :build do
35
+ sh "bundle exec rbs-inline lib --opt-out --output=sig/inline"
36
+ end
37
+
38
+ desc "Watch files to build rbs-inline"
39
+ task :watch do
40
+ require "listen"
41
+
42
+ listener = Listen.to("lib", only: /\.rb$/) do
43
+ Rake::Task["rbs:inline:build"].execute
44
+ end
45
+ listener.start
46
+ sleep
47
+ ensure
48
+ listener&.stop
49
+ end
50
+ end
51
+ end
52
+
53
+ desc "Type check the code"
54
+ task steep: ["rbs:collection", "rbs:inline"] do
55
+ sh "bundle exec steep check"
56
+ end
57
+
20
58
  rule ".rb" => ".y" do |t|
21
59
  sh "bundle exec racc #{t.source} -o #{t.name}"
22
60
  end
data/Steepfile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ target :lib do
6
+ signature "sig"
7
+ signature "types"
8
+ ignore_signature "sig/test"
9
+
10
+ check "lib" # Directory name
11
+ # check "path/to/source.rb" # File name
12
+ # check "app/models/**/*.rb" # Glob
13
+ ignore "lib/yard_to_rbs_inline/yard_type/parser.rb" # Ignore generated files
14
+ # ignore "lib/templates/*.rb"
15
+
16
+ # library "pathname" # Standard libraries
17
+ # library "strong_json" # Gems
18
+
19
+ # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
20
+ # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
21
+ # configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
22
+ # configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
23
+ # configure_code_diagnostics do |hash| # You can setup everything yourself
24
+ # hash[D::Ruby::NoMethod] = :information
25
+ # end
26
+ end
27
+
28
+ # target :test do
29
+ # unreferenced! # Skip type checking the `lib` code when types in `test` target is changed
30
+ # signature "sig/test" # Put RBS files for tests under `sig/test`
31
+ # check "test" # Type check Ruby scripts under `test`
32
+ #
33
+ # configure_code_diagnostics(D::Ruby.lenient) # Weak type checking for test code
34
+ #
35
+ # # library "pathname" # Standard libraries
36
+ # end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rbs_inline: enabled
4
+
5
+ class Code
6
+ # @return [String]
7
+ attr_reader :source #: String
8
+
9
+ # @param source [String]
10
+ #: (String source) -> untyped
11
+ def initialize(source)
12
+ @source = source
13
+ end
14
+
15
+ # @param new_source [String]
16
+ # @return [Code]
17
+ #: (String new_source) -> Code
18
+ def rewrite(new_source)
19
+ @source = new_source
20
+
21
+ self
22
+ end
23
+
24
+ # @param recover [Boolean].
25
+ # @return [Array<Token>]
26
+ #: (?recover: bool) -> Array[Token]
27
+ def tokenize(recover: false)
28
+ parser.tokenize(source, recover: recover)
29
+ end
30
+ end
data/examples/example.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Code
2
4
  # @return [String]
3
5
  attr_reader :source
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) if Dir.exist?(File.join(__dir__, "..", ".git"))
4
+
5
+ require "yard_to_rbs_inline"
6
+ exit(YardToRbsInline::Cli.start(ARGV))
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
5
  require "optparse"
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
- require_relative 'subscriptable'
5
+ require_relative "subscriptable"
4
6
 
5
7
  module YardToRbsInline
6
8
  module Converter
@@ -10,20 +12,23 @@ module YardToRbsInline
10
12
  # @!rbs
11
13
  # type mod = PrependLine | AppendLineContent
12
14
 
13
- attr_reader :source #:: Prism::Source
15
+ # @rbs @source: Prism::Source
16
+ attr_reader :source #: Prism::Source
14
17
 
15
- attr_reader :comments #:: Array[Prism::InlineComment]
18
+ # @rbs @comments: Array[Prism::comment]
19
+ attr_reader :comments #: Array[Prism::comment]
16
20
 
17
- attr_reader :text_with_mod #:: TextWithMod
21
+ # @rbs @text_with_mod: TextWithMod
22
+ attr_reader :text_with_mod #: TextWithMod
18
23
 
19
- #:: (source: Prism::Source, comments: Array[Prism::InlineComment]) -> untyped
24
+ #: (source: Prism::Source, comments: Array[Prism::comment]) -> untyped
20
25
  def initialize(source:, comments:)
21
26
  @source = source
22
27
  @comments = comments
23
28
  @text_with_mod = TextWithMod.new(source.source)
24
29
  end
25
30
 
26
- #:: (Prism::CallNode) -> void
31
+ #: (Prism::CallNode) -> void
27
32
  def visit_call_node(node)
28
33
  parse_annotation = lambda do
29
34
  annotation_comments = annotations_for(node)
@@ -35,29 +40,29 @@ module YardToRbsInline
35
40
  when :attr_reader
36
41
  annotation = parse_annotation.call
37
42
 
38
- unless annotation.match?(/^\s*#(::|\s*@rbs)/)
39
- docstring = YARD::DocstringParser.new.parse(annotation.gsub(/^\s*#\s*/, '')).to_docstring
43
+ unless annotation.match?(/^\s*#(:|\s*@rbs)/)
44
+ docstring = YARD::DocstringParser.new.parse(annotation.gsub(/^\s*#\s*/, "")).to_docstring
40
45
 
41
46
  sig = capture_error_to_write_comment(node) { build_return_sig(docstring) }
42
- text_with_mod.mods << AppendLineContent.from_node_and_content(node, "#:: #{sig}")
47
+ text_with_mod.mods << AppendLineContent.from_node_and_content(node, "#: #{sig}")
43
48
  end
44
49
  end
45
50
 
46
51
  super
47
52
  end
48
53
 
49
- #:: (Prism::DefNode) -> void
54
+ #: (Prism::DefNode) -> void
50
55
  def visit_def_node(node)
51
56
  annotation_comments = annotations_for(node)
52
57
  annotation_strs = annotation_comments.map(&method(:source_for_comment))
53
58
  annotation = annotation_strs.map(&:chomp).join("\n")
54
59
 
55
- unless annotation.match?(/^\s*#(::|\s*@rbs)/)
56
- docstring = YARD::DocstringParser.new.parse(annotation.gsub(/^\s*#\s*/, '')).to_docstring
60
+ unless annotation.match?(/^\s*#(:|\s*@rbs)/)
61
+ docstring = YARD::DocstringParser.new.parse(annotation.gsub(/^\s*#\s*/, "")).to_docstring
57
62
 
58
63
  sig = capture_error_to_write_comment(node) { build_signature(node.parameters, docstring) }
59
64
 
60
- text_with_mod.mods << PrependLine.from_node_and_content(node, "#:: #{sig}")
65
+ text_with_mod.mods << PrependLine.from_node_and_content(node, "#: #{sig}")
61
66
 
62
67
  # STDERR.puts "#{node.name_loc.inspect} #{node.name}: #{sig}"
63
68
  end
@@ -67,18 +72,18 @@ module YardToRbsInline
67
72
 
68
73
  private
69
74
 
70
- #:: (YARD::Docstring) -> String
75
+ #: (YARD::Docstring) -> String
71
76
  def build_return_sig(docstring)
72
- return_tag = docstring.tag("return") #:: YARD::Tags::Tag | nil
77
+ return_tag = docstring.tag("return") #: YARD::Tags::Tag | nil
73
78
 
74
- if return_tag&.types
75
- convert_types(return_tag.types)
79
+ if (types = return_tag&.types)
80
+ convert_types(types)
76
81
  else
77
82
  "untyped"
78
83
  end
79
84
  end
80
85
 
81
- #:: (Prism::ParametersNode | nil, YARD::Docstring) -> String
86
+ #: (Prism::ParametersNode | nil, YARD::Docstring) -> String
82
87
  def build_signature(params, docstring)
83
88
  params_sig = build_params_signature(params, docstring)
84
89
  return_sig = build_return_sig(docstring)
@@ -86,59 +91,61 @@ module YardToRbsInline
86
91
  "(#{params_sig}) -> #{return_sig}"
87
92
  end
88
93
 
89
- #:: (Prism::ParametersNode | nil, YARD::Docstring) -> String
94
+ #: (Prism::ParametersNode | nil, YARD::Docstring) -> String
90
95
  def build_params_signature(params, docstring)
91
96
  return "" unless params
92
97
 
93
- param_tags = docstring.tags("param") #:: Array[YARD::Tags::Tag]
98
+ param_tags = docstring.tags("param") #: Array[YARD::Tags::Tag]
94
99
 
95
100
  find_tag = proc do |name|
96
- if name
97
- param_tags.find { |tag| tag.name == name.to_s }
98
- else
99
- nil
100
- end
101
+ param_tags.find { |tag| tag.name == name.to_s } if name
101
102
  end
102
103
 
103
- param_sigs = []
104
+ param_sigs = [] #: Array[String]
104
105
  params.requireds.each do |param|
106
+ param = TypeUtils.narrow_with_name(param)
107
+ next unless param
108
+
105
109
  tag = find_tag.call(param.name)
106
110
 
107
- if tag&.types
108
- param_sigs << "#{convert_types(tag.types)} #{param.name}"
109
- else
110
- param_sigs << "untyped #{param.name}"
111
- end
111
+ param_sigs << if tag&.types
112
+ "#{convert_types(tag.types)} #{param.name}"
113
+ else
114
+ "untyped #{param.name}"
115
+ end
112
116
  end
113
117
 
114
118
  params.optionals.each do |param|
115
119
  tag = find_tag.call(param.name)
116
120
 
117
- if tag&.types
118
- param_sigs << "?#{convert_types(tag.types)} #{param.name}"
119
- else
120
- param_sigs << "?untyped #{param.name}"
121
- end
121
+ param_sigs << if tag&.types
122
+ "?#{convert_types(tag.types)} #{param.name}"
123
+ else
124
+ "?untyped #{param.name}"
125
+ end
122
126
  end
123
127
 
124
- if (param = params.rest)
128
+ if (param = TypeUtils.narrow_with_name(params.rest))
125
129
  tag = find_tag.call(param.name)
126
130
 
127
- if tag&.types
128
- param_sigs << "*#{convert_types(tag.types)} #{param.name}"
129
- else
130
- param_sigs << "*untyped #{param.name}"
131
- end
131
+ param_sigs << if tag&.types
132
+ "*#{convert_types(tag.types)} #{param.name}"
133
+ else
134
+ "*untyped #{param.name}"
135
+ end
132
136
  end
133
137
 
134
138
  params.posts.each do |param|
139
+ param = TypeUtils.narrow_with_name(param)
140
+ next unless param
141
+
135
142
  tag = find_tag.call(param.name)
136
143
 
137
- if tag&.types
138
- param_sigs << "#{convert_types(tag.types)} #{param.name}"
139
- else
140
- param_sigs << "untyped #{param.name}"
141
- end
144
+ param_sigs << if tag&.types
145
+ "#{convert_types(tag.types)} #{param.name}"
146
+ else
147
+ "untyped #{param.name}"
148
+ end
142
149
  end
143
150
 
144
151
  params.keywords.each do |param|
@@ -152,43 +159,44 @@ module YardToRbsInline
152
159
  "?"
153
160
  end
154
161
 
155
- if tag && tag.types
156
- param_sigs << "#{prefix}#{param.name}: #{convert_types(tag.types)}"
157
- else
158
- param_sigs << "#{prefix}#{param.name}: untyped"
159
- end
162
+ param_sigs << if tag&.types
163
+ "#{prefix}#{param.name}: #{convert_types(tag.types)}"
164
+ else
165
+ "#{prefix}#{param.name}: untyped"
166
+ end
160
167
  end
161
168
 
162
- if (param = params.keyword_rest)
169
+ if (param = TypeUtils.narrow_with_name(params.keyword_rest))
170
+
163
171
  tag = find_tag.call(param.name)
164
172
 
165
- if tag&.types
166
- param_sigs << "**#{convert_types(tag.types)} #{param.name}"
167
- else
168
- param_sigs << "**untyped #{param.name}"
169
- end
173
+ param_sigs << if tag&.types
174
+ "**#{convert_types(tag.types)} #{param.name}"
175
+ else
176
+ "**untyped #{param.name}"
177
+ end
170
178
  end
171
179
 
172
180
  # Remove trailing spaces for unnamed params
173
181
  param_sigs.map(&:strip).join(", ")
174
182
  end
175
183
 
176
- #:: (Array[String], node: Prism::Node) -> String
184
+ #: (Array[String]) -> String
177
185
  def convert_types(yard_type_literals)
178
186
  new_types = yard_type_literals.map do |literal|
179
187
  yard_type = YardToRbsInline::YardType::Parser.new.parse(literal)
180
188
  convert_yard_type(yard_type)
181
- rescue Racc::ParseError, Yoda::Parsing::YardType::Scanner::ScanError => e
189
+ rescue Racc::ParseError, YardType::Scanner::ScanError => e
182
190
  # STDERR.puts "Cannot parse #{literal}: #{e}"
183
191
  emit_error_message("Cannot parse #{literal}: #{e}")
184
192
 
185
- 'untyped'
193
+ "untyped"
186
194
  end.uniq
187
195
 
188
- new_types.empty? ? 'untyped' : new_types.join(' | ')
196
+ new_types.empty? ? "untyped" : new_types.join(" | ")
189
197
  end
190
198
 
191
- #:: (YardToRbsInline::YardType::Ast::node, node: Prism::Node) -> String
199
+ #: (YardToRbsInline::YardType::Ast::node) -> String
192
200
  def convert_yard_type(yard_type)
193
201
  case yard_type
194
202
  in YardType::Ast::Type(name:)
@@ -212,19 +220,19 @@ module YardToRbsInline
212
220
  content
213
221
  in YardType::Ast::GenericType(container_type:, types:)
214
222
  container_sig = container_type ? convert_yard_type(container_type) : "Array"
215
- "#{container_sig}[#{types.map(&method(:convert_yard_type)).join(' | ')}]"
223
+ "#{container_sig}[#{types.map(&method(:convert_yard_type)).join(" | ")}]"
216
224
  in YardType::Ast::TupleType(container_type:, types:)
217
225
  container_sig = container_type ? convert_yard_type(container_type) : "Array"
218
- "#{container_sig} & [#{types.map(&method(:convert_yard_type)).join(', ')}]"
226
+ "#{container_sig} & [#{types.map(&method(:convert_yard_type)).join(", ")}]"
219
227
  in YardType::Ast::HashType(container_type:, type_pair: [key_type, value_type])
220
228
  container_sig = container_type ? convert_yard_type(container_type) : "Hash"
221
229
  "#{container_sig}[#{convert_yard_type(key_type)}, #{convert_yard_type(value_type)}]"
222
230
  in YardType::Ast::UnionType(types:)
223
- types.map(&method(:convert_yard_type)).join(' | ')
231
+ types.map(&method(:convert_yard_type)).join(" | ")
224
232
  end
225
233
  end
226
234
 
227
- #:: (Prism::Node) -> Integer
235
+ #: (Prism::Node) -> Integer
228
236
  def start_line_of_node(node)
229
237
  gather_comment_targets = proc do |l|
230
238
  if l.respond_to?(:comment_targets)
@@ -237,34 +245,53 @@ module YardToRbsInline
237
245
  gather_comment_targets.call(node).map(&:start_line).min || 0
238
246
  end
239
247
 
240
- #:: (Prism::Node) -> Array[Prism::InlineComment]
248
+ #: (Prism::Node) -> Array[Prism::comment]
241
249
  def annotations_for(node)
242
250
  start_line = start_line_of_node(node)
243
251
 
244
- matched_comments = []
252
+ matched_comments = [] #: Array[Prism::comment]
245
253
  matched_comments += comments_by_line[start_line] if comments_by_line[start_line]
246
254
 
247
255
  (start_line - 1).downto(1) do |line|
248
256
  current_comments = comments_by_line[line]
249
- if current_comments
250
- matched_comments += current_comments
251
- else
252
- break
253
- end
257
+ break unless current_comments
258
+
259
+ matched_comments += current_comments
254
260
  end
255
261
 
256
262
  matched_comments
257
263
  end
258
264
 
259
- #:: (Prism::InlineComment) -> String
265
+ #: (Prism::comment) -> String
260
266
  def source_for_comment(comment)
261
267
  source.slice(comment.location.start_offset, comment.location.length)
262
268
  end
263
269
 
264
- #:: () -> Hash[Integer, Array[Prism::InlineComment]]
270
+ # @rbs @comments_by_line: Hash[Integer, Array[Prism::comment]]?
271
+
272
+ #: () -> Hash[Integer, Array[Prism::comment]]
265
273
  def comments_by_line
266
274
  @comments_by_line ||= comments.group_by { |comment| comment.location.start_line }
267
275
  end
276
+
277
+ module TypeUtils
278
+ module_function
279
+
280
+ # @rbs!
281
+ # interface _WithName
282
+ # def name: () -> String
283
+ # end
284
+
285
+ # @rbs!
286
+ # interface _RespondTo
287
+ # def respond_to?: (String | Symbol) -> boolish
288
+ # end
289
+
290
+ #: [T < _RespondTo] (T) -> (T & _WithName)?
291
+ def narrow_with_name(node)
292
+ node.respond_to?(:name) && node #: untyped
293
+ end
294
+ end
268
295
  end
269
296
  end
270
297
  end
@@ -1,33 +1,45 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
5
  module YardToRbsInline
4
6
  module Converter
7
+ # @rbs!
8
+ # interface _TextWithModContainer
9
+ # def text_with_mod: () -> TextWithMod
10
+ # end
11
+
12
+ # @rbs module-self _TextWithModContainer
5
13
  module Subscriptable
6
14
  # @rbs!
7
15
  # type subscriber = ^(String) -> void
8
16
 
9
- #:: (String) -> void
17
+ #: (String) -> void
10
18
  def emit_error_message(error_message)
11
19
  (@subscribers || []).each { |subscriber| subscriber.call(error_message) }
12
20
  end
13
21
 
14
- #:: [X] (subscriber) { () -> X } -> X
22
+ # @rbs @subscribers: Array[subscriber]
23
+
24
+ #: [X] (subscriber) { () -> X } -> X
15
25
  def capture_error_message(subscriber)
16
- @subscribers ||= []
26
+ @subscribers ||= [] #: Array[subscriber]
17
27
  @subscribers.push(subscriber)
18
28
  yield
19
29
  ensure
20
30
  @subscribers.pop
21
31
  end
22
32
 
23
- #:: (Prism::Node) -> subscriber
33
+ #: (Prism::Node) -> subscriber
24
34
  def subscriber_to_prepend_error(node)
25
- lambda { |message| text_with_mod.mods << PrependLine.from_node_and_content(node, "# YARD to RBS Error: #{message}") }
35
+ lambda { |message|
36
+ text_with_mod.mods << PrependLine.from_node_and_content(node, "# YARD to RBS Error: #{message}")
37
+ }
26
38
  end
27
39
 
28
- #:: [X] (Prism::Node) { () -> X } -> X
29
- def capture_error_to_write_comment(node)
30
- capture_error_message(subscriber_to_prepend_error(node)) { yield }
40
+ #: [X] (Prism::Node) { () -> X } -> X
41
+ def capture_error_to_write_comment(node, &block)
42
+ capture_error_message(subscriber_to_prepend_error(node), &block)
31
43
  end
32
44
  end
33
45
  end
@@ -1,43 +1,57 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
5
  module YardToRbsInline
4
6
  module Converter
5
- PrependLine = Data.define(:line_num, :content) do
6
- #:: (Prism::Node, String) -> PrependLine
7
+ class PrependLine < Data.define(:line_num, :content)
8
+ # @rbs!
9
+ # def initialize: (line_num: Integer, content: String) -> void
10
+ # attr_reader line_num: Integer
11
+ # attr_reader content: String
12
+
13
+ #: (Prism::Node, String) -> PrependLine
7
14
  def self.from_node_and_content(node, comment_content)
8
15
  start_line = node.location.start_line
9
16
  start_column = node.location.start_column
10
17
 
11
- line = ' ' * start_column + comment_content
18
+ line = " " * start_column + comment_content
12
19
 
13
20
  PrependLine.new(line_num: start_line, content: line)
14
21
  end
15
22
  end
16
23
 
17
- AppendLineContent = Data.define(:line_num, :content) do
18
- #:: (Prism::Node, String) -> PrependLine
24
+ class AppendLineContent < Data.define(:line_num, :content)
25
+ # @rbs!
26
+ # def initialize: (line_num: Integer, content: String) -> void
27
+ # attr_reader line_num: Integer
28
+ # attr_reader content: String
29
+
30
+ #: (Prism::Node, String) -> AppendLineContent
19
31
  def self.from_node_and_content(node, comment_content)
20
32
  start_line = node.location.start_line
21
33
 
22
- AppendLineContent.new(line_num: start_line, content: ' ' + comment_content)
34
+ AppendLineContent.new(line_num: start_line, content: " #{comment_content}")
23
35
  end
24
36
  end
25
37
 
38
+ # @rbs!
39
+ # type mod = PrependLine | AppendLineContent
40
+
26
41
  class TextWithMod
27
- attr_reader :original_text #:: Prism::Source
42
+ attr_reader :original_text #: Prism::Source
28
43
 
29
- attr_reader :mods #:: Array[mod]
44
+ attr_reader :mods #: Array[mod]
30
45
 
31
- #:: (String) -> untyped
46
+ #: (String) -> untyped
32
47
  def initialize(original_text)
33
48
  @original_text = original_text
34
49
  @mods = []
35
50
  end
36
51
 
37
- #:: () -> String
38
- def modified_text
39
- prepended_line_nums = []
40
- mods.reduce(original_text.lines(chomp: true)) do |source_lines, mod|
52
+ def modified_text #: String
53
+ prepended_line_nums = [] #: Array[String]
54
+ mods.each_with_object(original_text.lines.map(&:chomp)) do |mod, source_lines|
41
55
  real_line_num = mod.line_num + prepended_line_nums.map { |i| i <= mod.line_num }.length # 1-index
42
56
 
43
57
  case mod
@@ -47,8 +61,6 @@ module YardToRbsInline
47
61
  in AppendLineContent(content:)
48
62
  source_lines[real_line_num - 1] += content
49
63
  end
50
-
51
- source_lines
52
64
  end.join("\n") + "\n"
53
65
  end
54
66
  end
@@ -1,5 +1,7 @@
1
- require 'prism'
2
- require 'yard'
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+ require "yard"
3
5
 
4
6
  module YardToRbsInline
5
7
  module Converter
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YardToRbsInline
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
5
  module YardToRbsInline
@@ -6,14 +8,19 @@ module YardToRbsInline
6
8
  # @rbs!
7
9
  # type node = Type | LiteralType | DuckType | GenericType | TupleType | HashType | UnionType
8
10
 
9
- Type = Struct.new(:name, keyword_init: true)
10
- LiteralType = Struct.new(:content, keyword_init: true)
11
- DuckType = Struct.new(:name, keyword_init: true)
12
- GenericType = Struct.new(:container_type, :types, keyword_init: true)
13
- TupleType = Struct.new(:container_type, :types, keyword_init: true)
14
- HashType = Struct.new(:container_type, :type_pair, keyword_init: true)
15
- UnionType = Struct.new(:types, keyword_init: true) do
16
- #:: (untyped types) -> untyped
11
+ Type = Data.define(:name)
12
+ LiteralType = Data.define(:content)
13
+ DuckType = Data.define(:name)
14
+ GenericType = Data.define(:container_type, :types)
15
+ TupleType = Data.define(:container_type, :types)
16
+ HashType = Data.define(:container_type, :type_pair)
17
+
18
+ class UnionType < Data.define(:types)
19
+ # @rbs!
20
+ # def initialize: (types: Array[node]) -> void
21
+ # attr_reader types: Array[node]
22
+
23
+ #: (untyped types) -> untyped
17
24
  def self.build(types)
18
25
  if types.length == 1
19
26
  types.first
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.7.1
4
- # from Racc grammar file "".
3
+ # This file is automatically generated by Racc 1.8.1
4
+ # from Racc grammar file "parser.y".
5
5
  #
6
6
 
7
7
  require 'racc/parser.rb'
@@ -14,14 +14,14 @@ module YardToRbsInline
14
14
 
15
15
  module_eval(<<'...end parser.y/module_eval...', 'parser.y', 34)
16
16
 
17
- #:: (String) -> Ast::node
17
+ #: (String) -> Ast::node
18
18
  def parse(string)
19
19
  @tokens = Scanner.new(string).to_racc_tokens
20
20
 
21
21
  do_parse()
22
22
  end
23
23
 
24
- #:: () -> untyped
24
+ #: () -> untyped
25
25
  def next_token
26
26
  @tokens.shift
27
27
  end
@@ -294,14 +294,12 @@ end
294
294
  end # module YardToRbsInline
295
295
 
296
296
 
297
- module Yoda
298
- module Parsing
299
- module YardType
300
- class Parser
301
- # @rbs!
302
- # def parse: (String) -> Ast::node
303
- # def next_token: () -> untyped
304
- end
297
+ module YardToRbsInline
298
+ module YardType
299
+ class Parser
300
+ # @rbs!
301
+ # def parse: (String) -> Ast::node
302
+ # def next_token: () -> untyped
305
303
  end
306
304
  end
307
305
  end
@@ -32,28 +32,26 @@ end
32
32
 
33
33
  ---- inner
34
34
 
35
- #:: (String) -> Ast::node
35
+ #: (String) -> Ast::node
36
36
  def parse(string)
37
37
  @tokens = Scanner.new(string).to_racc_tokens
38
38
 
39
39
  do_parse()
40
40
  end
41
41
 
42
- #:: () -> untyped
42
+ #: () -> untyped
43
43
  def next_token
44
44
  @tokens.shift
45
45
  end
46
46
 
47
47
  ---- footer
48
48
 
49
- module Yoda
50
- module Parsing
51
- module YardType
52
- class Parser
53
- # @rbs!
54
- # def parse: (String) -> Ast::node
55
- # def next_token: () -> untyped
56
- end
49
+ module YardToRbsInline
50
+ module YardType
51
+ class Parser
52
+ # @rbs!
53
+ # def parse: (String) -> Ast::node
54
+ # def next_token: () -> untyped
57
55
  end
58
56
  end
59
57
  end
@@ -1,69 +1,76 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
- require 'strscan'
5
+ require "strscan"
4
6
 
5
- module YardToRbsInline::YardType
6
- class Scanner
7
- class ScanError < StandardError; end
7
+ module YardToRbsInline
8
+ module YardType
9
+ class Scanner
10
+ class ScanError < StandardError; end
8
11
 
9
- Token = Struct.new(:kind, :content, keyword_init: true)
12
+ Token = Struct.new(:kind, :content, keyword_init: true)
10
13
 
11
- TOKEN_PATTERNS = {
12
- generic_start: /</,
13
- generic_end: />/,
14
- tuple_start: /\(/,
15
- tuple_end: /\)/,
16
- duck_symbol: /#/,
17
- separator: /[,;]/,
18
- arrow: /\=>/,
19
- hash_start: /\{/,
20
- hash_end: /\}/,
21
- name: /(::|\w)+/,
22
- symbol: /:\w+/,
23
- string: [/\"[^"]*\"/, /\'[^']*\'/],
24
- integer: /\d+/,
25
- spaces: /\s+/,
26
- }
14
+ TOKEN_PATTERNS = {
15
+ generic_start: /</,
16
+ generic_end: />/,
17
+ tuple_start: /\(/,
18
+ tuple_end: /\)/,
19
+ duck_symbol: /#/,
20
+ separator: /[,;]/,
21
+ arrow: /=>/,
22
+ hash_start: /\{/,
23
+ hash_end: /\}/,
24
+ name: /(::|\w)+/,
25
+ symbol: /:\w+/,
26
+ string: [/"[^"]*"/, /'[^']*'/],
27
+ integer: /\d+/,
28
+ spaces: /\s+/
29
+ }.freeze
27
30
 
28
- attr_reader :text #:: String
31
+ attr_reader :text #: String
29
32
 
30
- #:: (String text) -> untyped
31
- def initialize(text)
32
- @text = text
33
- end
33
+ #: (String text) -> untyped
34
+ def initialize(text)
35
+ @text = text
36
+ end
34
37
 
35
- #:: () -> Array[[Symbol | bool, String]]
36
- def to_racc_tokens
37
- tokens.map { |token| [token.kind.to_s.upcase.to_sym, token.content] } + [[false, "EOS"]]
38
- end
38
+ #: () -> Array[[Symbol | boolish, String]]
39
+ def to_racc_tokens
40
+ tokens.map do |token| #$ [Symbol | bool, String]
41
+ [token.kind.to_s.upcase.to_sym, token.content]
42
+ end + [
43
+ [false, "EOS"] #: [bool, String]
44
+ ]
45
+ end
46
+
47
+ # @rbs @tokens: Array[Token]?
39
48
 
40
- #:: () -> Array[Token]
41
- def tokens
42
- @tokens ||= begin
43
- scanner = StringScanner.new(text)
44
- tokens = []
49
+ #: () -> Array[Token]
50
+ def tokens
51
+ @tokens ||= begin
52
+ scanner = StringScanner.new(text)
53
+ tokens = [] #: Array[Token]
54
+
55
+ scan_step = lambda do
56
+ TOKEN_PATTERNS.each do |kind, pattern_or_patterns|
57
+ Array(pattern_or_patterns).each do |pattern|
58
+ next unless (matched = scanner.scan(pattern))
45
59
 
46
- scan_step = lambda do
47
- TOKEN_PATTERNS.each do |kind, pattern_or_patterns|
48
- Array(pattern_or_patterns).each do |pattern|
49
- if (matched = scanner.scan(pattern))
50
60
  tokens << Token.new(kind: kind, content: matched) unless kind == :spaces
51
- return
61
+ # For now, steep parse this as a return of the whole method
62
+ return # steep:ignore
52
63
  end
53
64
  end
65
+
66
+ raise ScanError, "Unknown keyword #{scanner.rest} (in #{text})"
54
67
  end
55
68
 
56
- raise ScanError, "Unknown keyword #{scanner.rest} (in #{text})"
57
- end
69
+ scan_step.call until scanner.eos?
58
70
 
59
- until scanner.eos? do
60
- scan_step.call
71
+ tokens
61
72
  end
62
-
63
- tokens
64
73
  end
65
74
  end
66
75
  end
67
-
68
-
69
76
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # rbs_inline: enabled
2
4
 
3
5
  module YardToRbsInline
4
6
  module YardType
5
- require_relative 'yard_type/ast'
6
- require_relative 'yard_type/parser'
7
- require_relative 'yard_type/scanner'
7
+ require_relative "yard_type/ast"
8
+ require_relative "yard_type/parser"
9
+ require_relative "yard_type/scanner"
8
10
  end
9
11
  end
@@ -0,0 +1,19 @@
1
+ # Download sources
2
+ sources:
3
+ - type: git
4
+ name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
8
+
9
+ # You can specify local directories as sources also.
10
+ # - type: local
11
+ # path: path/to/your/local/repository
12
+
13
+ # A directory to install the downloaded RBSs
14
+ path: .gem_rbs_collection
15
+
16
+ # gems:
17
+ # # If you want to avoid installing rbs files for gems, you can specify them here.
18
+ # - name: GEM_NAME
19
+ # ignore: true
@@ -1,4 +1,3 @@
1
1
  module YardToRbsInline
2
- VERSION: String
3
2
  # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
3
  end
@@ -0,0 +1,4 @@
1
+ module Racc
2
+ class ParseError < StandardError
3
+ end
4
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yard_to_rbs_inline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomoya Chiba
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-05-16 00:00:00.000000000 Z
10
+ date: 2025-04-09 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: yard
@@ -24,25 +23,12 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: bump
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
26
  description: Converter of yard to rbs-inline
42
27
  email:
43
28
  - tomo.asleep@gmail.com
44
29
  executables:
45
30
  - yard-to-rbs-inline
31
+ - yard_to_rbs_inline
46
32
  extensions: []
47
33
  extra_rdoc_files: []
48
34
  files:
@@ -53,8 +39,11 @@ files:
53
39
  - LICENSE
54
40
  - README.md
55
41
  - Rakefile
42
+ - Steepfile
43
+ - examples/example-converted.rb
56
44
  - examples/example.rb
57
45
  - exe/yard-to-rbs-inline
46
+ - exe/yard_to_rbs_inline
58
47
  - lib/yard_to_rbs_inline.rb
59
48
  - lib/yard_to_rbs_inline/cli.rb
60
49
  - lib/yard_to_rbs_inline/converter.rb
@@ -67,14 +56,15 @@ files:
67
56
  - lib/yard_to_rbs_inline/yard_type/parser.rb
68
57
  - lib/yard_to_rbs_inline/yard_type/parser.y
69
58
  - lib/yard_to_rbs_inline/yard_type/scanner.rb
59
+ - rbs_collection.yaml
70
60
  - sig/yard_to_rbs_inline_example.rbs
61
+ - types/gems/racc.rbs
71
62
  homepage: https://github.com/tomoasleep/yard_to_rbs_inline
72
63
  licenses: []
73
64
  metadata:
74
65
  homepage_uri: https://github.com/tomoasleep/yard_to_rbs_inline
75
66
  source_code_uri: https://github.com/tomoasleep/yard_to_rbs_inline
76
67
  changelog_uri: https://github.com/tomoasleep/yard_to_rbs_inline/blob/main/CHANGELOG.md
77
- post_install_message:
78
68
  rdoc_options: []
79
69
  require_paths:
80
70
  - lib
@@ -82,15 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
82
72
  requirements:
83
73
  - - ">="
84
74
  - !ruby/object:Gem::Version
85
- version: 2.6.0
75
+ version: 3.2.0
86
76
  required_rubygems_version: !ruby/object:Gem::Requirement
87
77
  requirements:
88
78
  - - ">="
89
79
  - !ruby/object:Gem::Version
90
80
  version: '0'
91
81
  requirements: []
92
- rubygems_version: 3.5.9
93
- signing_key:
82
+ rubygems_version: 3.6.3
94
83
  specification_version: 4
95
84
  summary: Converter of yard to rbs-inline
96
85
  test_files: []