spoom 1.7.3 → 1.7.5

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: c8447b66e12a52fef3147761f8331749ba52cebaaba146888a7ca35a253acdf9
4
- data.tar.gz: d6a84c3091e71c56d26aceebb40f3a6486589266c0b9b984268bf32036b98a26
3
+ metadata.gz: 8704c33b8976616ddeb6d404521a47ab654b6bb652212f35ea9b2c92fa3a9469
4
+ data.tar.gz: c85e35bac3a99847a82c8234f4303434fede477317fd99ffc79dfcd66789d14b
5
5
  SHA512:
6
- metadata.gz: 9827d000700721b1e30028eedcaf41c5e5e45811e1b62693dbaa2d859b5adf379c5f5da4acbaac8e0a0a78a8bb4928fbd5d8a395b5527a7c1f3fc0a72f207cc3
7
- data.tar.gz: a7c46380dc2e26c622b8b96290964ede0b5f455d060a176d1336db3aa09ca3e7dbccb656d7a6c1451e43406b0e9cf7847b9a985418b905ae8114c20dae294240
6
+ metadata.gz: d83c8596f8e10ccb36256d42ed97c3b6eeb69bbb50b922e4b09d088d5a2decadee5103583fa2ab638a359c7f97cb48eec9be695886d11755b24f66ca3c10a538
7
+ data.tar.gz: 990bcdfb1851ccd8808f4dfd02739cfb96cc35f65ac8b4167849d03b4c08b95904b2cda2c6ef3ec6173df0f7348bac3d1e404c57c39cffa863d83acaf1573160
@@ -7,13 +7,10 @@ require "stringio"
7
7
 
8
8
  module Spoom
9
9
  module Cli
10
+ # @requires_ancestor: Thor
10
11
  module Helper
11
- extend T::Helpers
12
-
13
12
  include Colorize
14
13
 
15
- requires_ancestor { Thor }
16
-
17
14
  # Print `message` on `$stdout`
18
15
  #: (String message) -> void
19
16
  def say(message)
@@ -10,6 +10,11 @@ module Spoom
10
10
  desc "translate", "Translate type assertions from/to RBI and RBS"
11
11
  option :from, type: :string, aliases: :f, desc: "From format", enum: ["rbi"], default: "rbi"
12
12
  option :to, type: :string, aliases: :t, desc: "To format", enum: ["rbs"], default: "rbs"
13
+ option :translate_t_let, type: :boolean, aliases: :t_let, desc: "Translate `T.let`", default: true
14
+ option :translate_t_cast, type: :boolean, aliases: :t_cast, desc: "Translate `T.cast`", default: true
15
+ option :translate_t_bind, type: :boolean, aliases: :t_bind, desc: "Translate `T.bind`", default: true
16
+ option :translate_t_must, type: :boolean, aliases: :t_must, desc: "Translate `T.must`", default: true
17
+ option :translate_t_unsafe, type: :boolean, aliases: :t_unsafe, desc: "Translate `T.unsafe`", default: true
13
18
  def translate(*paths)
14
19
  from = options[:from]
15
20
  to = options[:to]
@@ -19,7 +24,15 @@ module Spoom
19
24
  "in `#{files.size}` file#{files.size == 1 ? "" : "s"}...\n\n")
20
25
 
21
26
  transformed_files = transform_files(files) do |file, contents|
22
- Spoom::Sorbet::Translate.sorbet_assertions_to_rbs_comments(contents, file: file)
27
+ Spoom::Sorbet::Translate.sorbet_assertions_to_rbs_comments(
28
+ contents,
29
+ file: file,
30
+ translate_t_let: options[:translate_t_let],
31
+ translate_t_cast: options[:translate_t_cast],
32
+ translate_t_bind: options[:translate_t_bind],
33
+ translate_t_must: options[:translate_t_must],
34
+ translate_t_unsafe: options[:translate_t_unsafe],
35
+ )
23
36
  end
24
37
 
25
38
  say("Translated type assertions in `#{transformed_files}` file#{transformed_files == 1 ? "" : "s"}.")
@@ -16,15 +16,29 @@ module Spoom
16
16
  desc: "Use positional names when translating from RBI to RBS",
17
17
  default: true
18
18
  option :include_rbi_files, type: :boolean, desc: "Include RBI files", default: false
19
+ option :max_line_length, type: :numeric, desc: "Max line length (pass 0 to disable)", default: 120
20
+ option :translate_generics, type: :boolean, desc: "Translate generics", default: false
21
+ option :translate_helpers, type: :boolean, desc: "Translate helpers", default: false
22
+ option :translate_abstract_methods, type: :boolean, desc: "Translate abstract methods", default: false
19
23
  def translate(*paths)
20
24
  from = options[:from]
21
25
  to = options[:to]
26
+ max_line_length = options[:max_line_length]
22
27
 
23
28
  if from == to
24
29
  say_error("Can't translate signatures from `#{from}` to `#{to}`")
25
30
  exit(1)
26
31
  end
27
32
 
33
+ if max_line_length.nil? || max_line_length.zero?
34
+ max_line_length = nil
35
+ elsif max_line_length.negative?
36
+ say_error("--max-line-length can't be negative")
37
+ exit(1)
38
+ else
39
+ max_line_length = max_line_length.to_i
40
+ end
41
+
28
42
  files = collect_files(paths, include_rbi_files: options[:include_rbi_files])
29
43
 
30
44
  say("Translating signatures from `#{from}` to `#{to}` " \
@@ -33,11 +47,23 @@ module Spoom
33
47
  case from
34
48
  when "rbi"
35
49
  transformed_files = transform_files(files) do |file, contents|
36
- Spoom::Sorbet::Translate.sorbet_sigs_to_rbs_comments(contents, file: file, positional_names: options[:positional_names])
50
+ Spoom::Sorbet::Translate.sorbet_sigs_to_rbs_comments(
51
+ contents,
52
+ file: file,
53
+ positional_names: options[:positional_names],
54
+ max_line_length: max_line_length,
55
+ translate_generics: options[:translate_generics],
56
+ translate_helpers: options[:translate_helpers],
57
+ translate_abstract_methods: options[:translate_abstract_methods],
58
+ )
37
59
  end
38
60
  when "rbs"
39
61
  transformed_files = transform_files(files) do |file, contents|
40
- Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(contents, file: file)
62
+ Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(
63
+ contents,
64
+ file: file,
65
+ max_line_length: max_line_length,
66
+ )
41
67
  end
42
68
  end
43
69
 
@@ -136,7 +162,7 @@ module Spoom
136
162
 
137
163
  # DO NOT EDIT MANUALLY
138
164
  # This is an autogenerated file for types exported from the `#{spec.name}` gem.
139
- # Please instead update this file by running `spoom srb sigs export`.
165
+ # Please instead update this file by running `bundle exec spoom srb sigs export`.
140
166
  RB
141
167
 
142
168
  output_path ||= "rbi/#{spec.name}.rbi"
@@ -149,7 +175,7 @@ module Spoom
149
175
  say_error(<<~ERR, status: "\nError")
150
176
  The RBI file at `#{output_path}` is not up to date
151
177
 
152
- Please run `spoom srb sigs export` to update it.
178
+ Please run `bundle exec spoom srb sigs export` to update it.
153
179
  ERR
154
180
  exit(1)
155
181
  end
@@ -4,11 +4,8 @@
4
4
  module Spoom
5
5
  class Context
6
6
  # Bundle features for a context
7
+ # @requires_ancestor: Context
7
8
  module Bundle
8
- extend T::Helpers
9
-
10
- requires_ancestor { Context }
11
-
12
9
  # Read the contents of the Gemfile in this context directory
13
10
  #: -> String?
14
11
  def read_gemfile
@@ -22,11 +22,8 @@ module Spoom
22
22
 
23
23
  class Context
24
24
  # Execution features for a context
25
+ # @requires_ancestor: Context
25
26
  module Exec
26
- extend T::Helpers
27
-
28
- requires_ancestor { Context }
29
-
30
27
  # Run a command in this context directory
31
28
  #: (String command, ?capture_err: bool) -> ExecResult
32
29
  def exec(command, capture_err: true)
@@ -4,11 +4,8 @@
4
4
  module Spoom
5
5
  class Context
6
6
  # File System features for a context
7
+ # @requires_ancestor: Context
7
8
  module FileSystem
8
- extend T::Helpers
9
-
10
- requires_ancestor { Context }
11
-
12
9
  # Returns the absolute path to `relative_path` in the context's directory
13
10
  #: (String relative_path) -> String
14
11
  def absolute_path_to(relative_path)
@@ -28,11 +28,8 @@ module Spoom
28
28
 
29
29
  class Context
30
30
  # Git features for a context
31
+ # @requires_ancestor: Context
31
32
  module Git
32
- extend T::Helpers
33
-
34
- requires_ancestor { Context }
35
-
36
33
  # Run a command prefixed by `git` in this context directory
37
34
  #: (String command) -> ExecResult
38
35
  def git(command)
@@ -4,11 +4,8 @@
4
4
  module Spoom
5
5
  class Context
6
6
  # Sorbet features for a context
7
+ # @requires_ancestor: Context
7
8
  module Sorbet
8
- extend T::Helpers
9
-
10
- requires_ancestor { Context }
11
-
12
9
  # Run `bundle exec srb` in this context directory
13
10
  #: (*String arg, ?sorbet_bin: String?, ?capture_err: bool) -> ExecResult
14
11
  def srb(*arg, sorbet_bin: nil, capture_err: true)
@@ -4,12 +4,8 @@
4
4
  module Spoom
5
5
  module Coverage
6
6
  module D3
7
+ # @abstract
7
8
  class Base
8
- extend T::Sig
9
- extend T::Helpers
10
-
11
- abstract!
12
-
13
9
  #: String
14
10
  attr_reader :id
15
11
 
@@ -44,8 +40,9 @@ module Spoom
44
40
  ""
45
41
  end
46
42
 
47
- sig { abstract.returns(String) }
48
- def script; end
43
+ # @abstract
44
+ #: -> String
45
+ def script = raise NotImplementedError, "Abstract method called"
49
46
  end
50
47
  end
51
48
  end
@@ -6,11 +6,8 @@ require_relative "base"
6
6
  module Spoom
7
7
  module Coverage
8
8
  module D3
9
+ # @abstract
9
10
  class Pie < Base
10
- extend T::Helpers
11
-
12
- abstract!
13
-
14
11
  #: (String id, String title, untyped data) -> void
15
12
  def initialize(id, title, data)
16
13
  super(id, data)
@@ -6,11 +6,8 @@ require_relative "base"
6
6
  module Spoom
7
7
  module Coverage
8
8
  module D3
9
+ # @abstract
9
10
  class Timeline < Base
10
- extend T::Helpers
11
-
12
- abstract!
13
-
14
11
  #: (String id, untyped data, Array[String] keys) -> void
15
12
  def initialize(id, data, keys)
16
13
  super(id, data)
@@ -120,8 +117,9 @@ module Spoom
120
117
  HTML
121
118
  end
122
119
 
123
- sig { abstract.returns(String) }
124
- def plot; end
120
+ # @abstract
121
+ #: -> String
122
+ def plot = raise NotImplementedError, "Abstract method called"
125
123
 
126
124
  #: -> String
127
125
  def x_scale
@@ -324,11 +322,8 @@ module Spoom
324
322
  end
325
323
  end
326
324
 
325
+ # @abstract
327
326
  class Stacked < Timeline
328
- extend T::Helpers
329
-
330
- abstract!
331
-
332
327
  # @override
333
328
  #: -> String
334
329
  def script
@@ -7,11 +7,8 @@ require "erb"
7
7
 
8
8
  module Spoom
9
9
  module Coverage
10
+ # @abstract
10
11
  class Template
11
- extend T::Helpers
12
-
13
- abstract!
14
-
15
12
  # Create a new template from an Erb file path
16
13
  #: (template: String) -> void
17
14
  def initialize(template:)
@@ -34,12 +31,8 @@ module Spoom
34
31
  end
35
32
  end
36
33
 
34
+ # @abstract
37
35
  class Page < Template
38
- extend T::Sig
39
- extend T::Helpers
40
-
41
- abstract!
42
-
43
36
  TEMPLATE = "#{Spoom::SPOOM_PATH}/templates/page.erb" #: String
44
37
 
45
38
  #: String
@@ -75,8 +68,9 @@ module Spoom
75
68
  cards.map(&:html).join("\n")
76
69
  end
77
70
 
78
- sig { abstract.returns(T::Array[Cards::Card]) }
79
- def cards; end
71
+ # @abstract
72
+ #: -> Array[Cards::Card]
73
+ def cards = raise NotImplementedError, "Abstract method called"
80
74
 
81
75
  #: -> String
82
76
  def footer_html
@@ -86,8 +80,6 @@ module Spoom
86
80
 
87
81
  module Cards
88
82
  class Card < Template
89
- extend T::Sig
90
-
91
83
  TEMPLATE = "#{Spoom::SPOOM_PATH}/templates/card.erb" #: String
92
84
 
93
85
  #: String?
@@ -101,11 +93,8 @@ module Spoom
101
93
  end
102
94
  end
103
95
 
96
+ # @abstract
104
97
  class Erb < Card
105
- extend T::Helpers
106
-
107
- abstract!
108
-
109
98
  #: -> void
110
99
  def initialize; end # rubocop:disable Lint/MissingSuper
111
100
 
@@ -115,8 +104,9 @@ module Spoom
115
104
  ERB.new(erb).result(get_binding)
116
105
  end
117
106
 
118
- sig { abstract.returns(String) }
119
- def erb; end
107
+ # @abstract
108
+ #: -> String
109
+ def erb = raise NotImplementedError, "Abstract method called"
120
110
  end
121
111
 
122
112
  class Snapshot < Card
@@ -6,11 +6,8 @@ require "set"
6
6
  module Spoom
7
7
  module Deadcode
8
8
  module Plugins
9
+ # @abstract
9
10
  class Base
10
- extend T::Helpers
11
-
12
- abstract!
13
-
14
11
  class << self
15
12
  # Plugins DSL
16
13
 
@@ -94,11 +94,8 @@ module Spoom
94
94
  end
95
95
 
96
96
  # An abstract visitor for FileTree
97
+ # @abstract
97
98
  class Visitor
98
- extend T::Helpers
99
-
100
- abstract!
101
-
102
99
  #: (FileTree tree) -> void
103
100
  def visit_tree(tree)
104
101
  visit_nodes(tree.roots)
@@ -63,11 +63,8 @@ module Spoom
63
63
  #
64
64
  # It can be a class, module, constant, method, etc.
65
65
  # A SymbolDef has a location pointing to the actual code that defines the symbol.
66
+ # @abstract
66
67
  class SymbolDef
67
- extend T::Helpers
68
-
69
- abstract!
70
-
71
68
  # The symbol this definition belongs to
72
69
  #: Symbol
73
70
  attr_reader :symbol
@@ -109,9 +106,8 @@ module Spoom
109
106
  end
110
107
 
111
108
  # A class or module
109
+ # @abstract
112
110
  class Namespace < SymbolDef
113
- abstract!
114
-
115
111
  #: Array[SymbolDef]
116
112
  attr_reader :children
117
113
 
@@ -156,9 +152,8 @@ module Spoom
156
152
  end
157
153
 
158
154
  # A method or an attribute accessor
155
+ # @abstract
159
156
  class Property < SymbolDef
160
- abstract!
161
-
162
157
  #: Visibility
163
158
  attr_reader :visibility
164
159
 
@@ -176,8 +171,8 @@ module Spoom
176
171
 
177
172
  class Method < Property; end
178
173
 
174
+ # @abstract
179
175
  class Attr < Property
180
- abstract!
181
176
  end
182
177
 
183
178
  class AttrReader < Attr; end
@@ -193,11 +188,8 @@ module Spoom
193
188
  end
194
189
 
195
190
  # A mixin (include, prepend, extend) to a namespace
191
+ # @abstract
196
192
  class Mixin
197
- extend T::Helpers
198
-
199
- abstract!
200
-
201
193
  #: String
202
194
  attr_reader :name
203
195
 
@@ -234,7 +226,7 @@ module Spoom
234
226
  #: -> void
235
227
  def initialize
236
228
  @symbols = {} #: Hash[String, Symbol]
237
- @symbols_hierarchy = Poset[Symbol].new #: Poset[Symbol]
229
+ @symbols_hierarchy = Poset.new #: Poset[Symbol]
238
230
  end
239
231
 
240
232
  # Get a symbol by it's full name
@@ -3,11 +3,8 @@
3
3
 
4
4
  module Spoom
5
5
  class Model
6
+ # @abstract
6
7
  class NamespaceVisitor < Visitor
7
- extend T::Helpers
8
-
9
- abstract!
10
-
11
8
  #: -> void
12
9
  def initialize
13
10
  super()
data/lib/spoom/poset.rb CHANGED
@@ -6,13 +6,10 @@ module Spoom
6
6
  #
7
7
  # The partial order relation is a binary relation that is reflexive, antisymmetric, and transitive.
8
8
  # It can be used to represent a hierarchy of classes or modules, the dependencies between gems, etc.
9
+ #: [E < Object]
9
10
  class Poset
10
- extend T::Generic
11
-
12
11
  class Error < Spoom::Error; end
13
12
 
14
- E = type_member { { upper: Object } }
15
-
16
13
  #: -> void
17
14
  def initialize
18
15
  @elements = {} #: Hash[E, Element[E]]
@@ -35,7 +32,7 @@ module Spoom
35
32
  element = @elements[value]
36
33
  return element if element
37
34
 
38
- @elements[value] = Element[E].new(value)
35
+ @elements[value] = Element.new(value) #: Element[E]
39
36
  end
40
37
 
41
38
  # Is the given value a element in the POSet?
@@ -132,12 +129,10 @@ module Spoom
132
129
  end
133
130
 
134
131
  # An element in a POSet
132
+ #: [E < Object]
135
133
  class Element
136
- extend T::Generic
137
134
  include Comparable
138
135
 
139
- E = type_member { { upper: Object } }
140
-
141
136
  # The value held by this element
142
137
  #: E
143
138
  attr_reader :value
@@ -6,14 +6,11 @@ require "set"
6
6
 
7
7
  module Spoom
8
8
  module LSP
9
+ # @interface
9
10
  module PrintableSymbol
10
- extend T::Sig
11
- extend T::Helpers
12
-
13
- interface!
14
-
15
- sig { abstract.params(printer: SymbolPrinter).void }
16
- def accept_printer(printer); end
11
+ # @abstract
12
+ #: (SymbolPrinter printer) -> void
13
+ def accept_printer(printer) = raise NotImplementedError, "Abstract method called"
17
14
  end
18
15
 
19
16
  class Hover < T::Struct
@@ -7,6 +7,13 @@ module Spoom
7
7
  class RBSCommentsToSorbetSigs < Translator
8
8
  include Spoom::RBS::ExtractRBSComments
9
9
 
10
+ #: (String, file: String, ?max_line_length: Integer?) -> void
11
+ def initialize(ruby_contents, file:, max_line_length: nil)
12
+ super(ruby_contents, file: file)
13
+
14
+ @max_line_length = max_line_length
15
+ end
16
+
10
17
  # @override
11
18
  #: (Prism::ClassNode node) -> void
12
19
  def visit_class_node(node)
@@ -34,31 +41,7 @@ module Spoom
34
41
  # @override
35
42
  #: (Prism::DefNode node) -> void
36
43
  def visit_def_node(node)
37
- comments = node_rbs_comments(node)
38
- return if comments.empty?
39
-
40
- return if comments.signatures.empty?
41
-
42
- builder = RBI::Parser::TreeBuilder.new(@ruby_contents, comments: [], file: @file)
43
- builder.visit(node)
44
- rbi_node = builder.tree.nodes.first #: as RBI::Method
45
-
46
- comments.signatures.each do |signature|
47
- method_type = ::RBS::Parser.parse_method_type(signature.string)
48
- translator = RBI::RBS::MethodTypeTranslator.new(rbi_node)
49
- translator.visit(method_type)
50
- sig = translator.result
51
- apply_member_annotations(comments.method_annotations, sig)
52
-
53
- @rewriter << Source::Replace.new(
54
- signature.location.start_offset,
55
- signature.location.end_offset,
56
- sig.string,
57
- )
58
- rescue ::RBS::ParsingError, ::RBI::Error
59
- # Ignore signatures with errors
60
- next
61
- end
44
+ rewrite_def(node, node_rbs_comments(node))
62
45
  end
63
46
 
64
47
  # @override
@@ -68,6 +51,12 @@ module Spoom
68
51
  when "attr_reader", "attr_writer", "attr_accessor"
69
52
  visit_attr(node)
70
53
  else
54
+ def_node = node.arguments&.arguments&.first
55
+ if def_node&.is_a?(Prism::DefNode)
56
+ rewrite_def(def_node, node_rbs_comments(node))
57
+ return
58
+ end
59
+
71
60
  super
72
61
  end
73
62
  end
@@ -104,7 +93,7 @@ module Spoom
104
93
  @rewriter << Source::Replace.new(
105
94
  signature.location.start_offset,
106
95
  signature.location.end_offset,
107
- sig.string,
96
+ sig.string(max_line_length: @max_line_length),
108
97
  )
109
98
  rescue ::RBS::ParsingError, ::RBI::Error
110
99
  # Ignore signatures with errors
@@ -112,6 +101,42 @@ module Spoom
112
101
  end
113
102
  end
114
103
 
104
+ #: (Prism::DefNode, RBS::Comments) -> void
105
+ def rewrite_def(def_node, comments)
106
+ return if comments.empty?
107
+ return if comments.signatures.empty?
108
+
109
+ builder = RBI::Parser::TreeBuilder.new(@ruby_contents, comments: [], file: @file)
110
+ builder.visit(def_node)
111
+ rbi_node = builder.tree.nodes.first #: as RBI::Method
112
+
113
+ comments.signatures.each do |signature|
114
+ begin
115
+ method_type = ::RBS::Parser.parse_method_type(signature.string)
116
+ rescue ::RBS::ParsingError
117
+ next
118
+ end
119
+
120
+ translator = RBI::RBS::MethodTypeTranslator.new(rbi_node)
121
+
122
+ begin
123
+ translator.visit(method_type)
124
+ rescue ::RBI::Error
125
+ next
126
+ end
127
+
128
+ sig = translator.result
129
+
130
+ apply_member_annotations(comments.method_annotations, sig)
131
+
132
+ @rewriter << Source::Replace.new(
133
+ signature.location.start_offset,
134
+ signature.location.end_offset,
135
+ sig.string(max_line_length: @max_line_length),
136
+ )
137
+ end
138
+ end
139
+
115
140
  #: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode) -> void
116
141
  def apply_class_annotations(node)
117
142
  comments = node_rbs_comments(node)
@@ -8,6 +8,33 @@ module Spoom
8
8
  class SorbetAssertionsToRBSComments < Translator
9
9
  LINE_BREAK = "\n".ord #: Integer
10
10
 
11
+ #: (
12
+ #| String,
13
+ #| file: String,
14
+ #| ?translate_t_let: bool,
15
+ #| ?translate_t_cast: bool,
16
+ #| ?translate_t_bind: bool,
17
+ #| ?translate_t_must: bool,
18
+ #| ?translate_t_unsafe: bool,
19
+ #| ) -> void
20
+ def initialize(
21
+ ruby_contents,
22
+ file:,
23
+ translate_t_let: true,
24
+ translate_t_cast: true,
25
+ translate_t_bind: true,
26
+ translate_t_must: true,
27
+ translate_t_unsafe: true
28
+ )
29
+ super(ruby_contents, file: file)
30
+
31
+ @translate_t_let = translate_t_let
32
+ @translate_t_cast = translate_t_cast
33
+ @translate_t_bind = translate_t_bind
34
+ @translate_t_must = translate_t_must
35
+ @translate_t_unsafe = translate_t_unsafe
36
+ end
37
+
11
38
  # @override
12
39
  #: (Prism::StatementsNode) -> void
13
40
  def visit_statements_node(node)
@@ -59,7 +86,7 @@ module Spoom
59
86
  end
60
87
 
61
88
  return false unless node.is_a?(Prism::CallNode)
62
- return false unless t_annotation?(node)
89
+ return false unless translatable_annotation?(node)
63
90
  return false unless at_end_of_line?(node)
64
91
 
65
92
  value = T.must(node.arguments&.arguments&.first)
@@ -116,16 +143,20 @@ module Spoom
116
143
 
117
144
  # Is this node a `T.let` or `T.cast`?
118
145
  #: (Prism::CallNode) -> bool
119
- def t_annotation?(node)
146
+ def translatable_annotation?(node)
120
147
  return false unless t?(node.receiver)
121
148
 
122
149
  case node.name
123
- when :let, :cast
124
- return node.arguments&.arguments&.size == 2
150
+ when :let
151
+ return @translate_t_let && node.arguments&.arguments&.size == 2
152
+ when :cast
153
+ return @translate_t_cast && node.arguments&.arguments&.size == 2
125
154
  when :bind
126
- return node.arguments&.arguments&.size == 2 && node.arguments&.arguments&.first.is_a?(Prism::SelfNode)
127
- when :must, :unsafe
128
- return node.arguments&.arguments&.size == 1
155
+ return @translate_t_bind && node.arguments&.arguments&.size == 2 && node.arguments&.arguments&.first.is_a?(Prism::SelfNode)
156
+ when :must
157
+ return @translate_t_must && node.arguments&.arguments&.size == 1
158
+ when :unsafe
159
+ return @translate_t_unsafe && node.arguments&.arguments&.size == 1
129
160
  end
130
161
 
131
162
  false
@@ -7,8 +7,24 @@ module Spoom
7
7
  # Converts all `sig` nodes to RBS comments in the given Ruby code.
8
8
  # It also handles type members and class annotations.
9
9
  class SorbetSigsToRBSComments < Translator
10
- #: (String, file: String, positional_names: bool) -> void
11
- def initialize(ruby_contents, file:, positional_names:)
10
+ #: (
11
+ #| String,
12
+ #| file: String,
13
+ #| positional_names: bool,
14
+ #| ?max_line_length: Integer?,
15
+ #| ?translate_generics: bool,
16
+ #| ?translate_helpers: bool,
17
+ #| ?translate_abstract_methods: bool
18
+ #| ) -> void
19
+ def initialize(
20
+ ruby_contents,
21
+ file:,
22
+ positional_names:,
23
+ max_line_length: nil,
24
+ translate_generics: true,
25
+ translate_helpers: true,
26
+ translate_abstract_methods: true
27
+ )
12
28
  super(ruby_contents, file: file)
13
29
 
14
30
  @positional_names = positional_names #: bool
@@ -18,6 +34,11 @@ module Spoom
18
34
  @extend_t_helpers = [] #: Array[Prism::CallNode]
19
35
  @extend_t_generics = [] #: Array[Prism::CallNode]
20
36
  @seen_mixes_in_class_methods = false #: bool
37
+ @max_line_length = max_line_length #: Integer?
38
+
39
+ @translate_generics = translate_generics #: bool
40
+ @translate_helpers = translate_helpers #: bool
41
+ @translate_abstract_methods = translate_abstract_methods #: bool
21
42
  end
22
43
 
23
44
  # @override
@@ -43,7 +64,6 @@ module Spoom
43
64
  def visit_def_node(node)
44
65
  last_sigs = collect_last_sigs
45
66
  return if last_sigs.empty?
46
- return if last_sigs.any? { |_, sig| sig.is_abstract }
47
67
 
48
68
  apply_member_annotations(last_sigs)
49
69
 
@@ -53,12 +73,25 @@ module Spoom
53
73
  rbi_node = builder.tree.nodes.first #: as RBI::Method
54
74
 
55
75
  last_sigs.each do |node, sig|
56
- out = StringIO.new
57
- p = RBI::RBSPrinter.new(out: out, indent: node.location.start_column, positional_names: @positional_names)
58
- p.print("#: ")
59
- p.send(:print_method_sig, rbi_node, sig)
60
- p.print("\n")
61
- @rewriter << Source::Replace.new(node.location.start_offset, node.location.end_offset, out.string)
76
+ next if sig.is_abstract && !@translate_abstract_methods
77
+
78
+ out = rbs_print(node.location.start_column) do |printer|
79
+ printer.print_method_sig(rbi_node, sig)
80
+ end
81
+ @rewriter << Source::Replace.new(node.location.start_offset, node.location.end_offset, out)
82
+ end
83
+
84
+ if @translate_abstract_methods && last_sigs.any? { |_, sig| sig.is_abstract }
85
+ @rewriter << Source::Replace.new(
86
+ node.rparen_loc&.end_offset || node.name_loc.end_offset,
87
+ node.location.end_offset - 1,
88
+ if node.name.end_with?("=")
89
+ indent = " " * node.location.start_column
90
+ "\n#{indent} raise NotImplementedError, \"Abstract method called\"\n#{indent}end"
91
+ else
92
+ " = raise NotImplementedError, \"Abstract method called\""
93
+ end,
94
+ )
62
95
  end
63
96
  end
64
97
 
@@ -84,6 +117,8 @@ module Spoom
84
117
  # @override
85
118
  #: (Prism::ConstantWriteNode) -> void
86
119
  def visit_constant_write_node(node)
120
+ return super unless @translate_generics
121
+
87
122
  call = node.value
88
123
  return super unless call.is_a?(Prism::CallNode)
89
124
  return super unless call.message == "type_member"
@@ -114,19 +149,23 @@ module Spoom
114
149
 
115
150
  yield
116
151
 
117
- delete_extend_t_generics
152
+ if @translate_generics
153
+ delete_extend_t_generics
118
154
 
119
- if @type_members.any?
120
- indent = " " * node.location.start_column
121
- @rewriter << Source::Insert.new(node.location.start_offset, "#: [#{@type_members.join(", ")}]\n#{indent}")
155
+ if @type_members.any?
156
+ indent = " " * node.location.start_column
157
+ @rewriter << Source::Insert.new(node.location.start_offset, "#: [#{@type_members.join(", ")}]\n#{indent}")
158
+ end
122
159
  end
123
160
 
124
- unless @seen_mixes_in_class_methods
125
- delete_extend_t_helpers
126
- end
161
+ if @translate_helpers
162
+ unless @seen_mixes_in_class_methods
163
+ delete_extend_t_helpers
164
+ end
127
165
 
128
- @class_annotations.each do |call|
129
- apply_class_annotation(node, call)
166
+ @class_annotations.each do |call|
167
+ apply_class_annotation(node, call)
168
+ end
130
169
  end
131
170
 
132
171
  @class_annotations = old_class_annotations
@@ -165,12 +204,10 @@ module Spoom
165
204
  rbi_node = builder.tree.nodes.first #: as RBI::Attr
166
205
 
167
206
  last_sigs.each do |node, sig|
168
- out = StringIO.new
169
- p = RBI::RBSPrinter.new(out: out, indent: node.location.start_column, positional_names: @positional_names)
170
- p.print("#: ")
171
- p.print_attr_sig(rbi_node, sig)
172
- p.print("\n")
173
- @rewriter << Source::Replace.new(node.location.start_offset, node.location.end_offset, out.string)
207
+ out = rbs_print(node.location.start_column) do |printer|
208
+ printer.print_attr_sig(rbi_node, sig)
209
+ end
210
+ @rewriter << Source::Replace.new(node.location.start_offset, node.location.end_offset, out)
174
211
  end
175
212
  end
176
213
 
@@ -240,23 +277,25 @@ module Spoom
240
277
  node, _sig = sigs.first #: as [Prism::CallNode, RBI::Sig]
241
278
  insert_pos = node.location.start_offset
242
279
 
280
+ indent = " " * node.location.start_column
281
+
243
282
  if sigs.any? { |_, sig| sig.without_runtime }
244
- @rewriter << Source::Insert.new(insert_pos, "# @without_runtime\n")
283
+ @rewriter << Source::Insert.new(insert_pos, "# @without_runtime\n#{indent}")
245
284
  end
246
285
 
247
286
  if sigs.any? { |_, sig| sig.is_final }
248
- @rewriter << Source::Insert.new(insert_pos, "# @final\n")
287
+ @rewriter << Source::Insert.new(insert_pos, "# @final\n#{indent}")
249
288
  end
250
289
 
251
- if sigs.any? { |_, sig| sig.is_abstract }
252
- @rewriter << Source::Insert.new(insert_pos, "# @abstract\n")
290
+ if sigs.any? { |_, sig| sig.is_abstract } && @translate_abstract_methods
291
+ @rewriter << Source::Insert.new(insert_pos, "# @abstract\n#{indent}")
253
292
  end
254
293
 
255
294
  if sigs.any? { |_, sig| sig.is_override }
256
295
  @rewriter << if sigs.any? { |_, sig| sig.allow_incompatible_override }
257
- Source::Insert.new(insert_pos, "# @override(allow_incompatible: true)\n")
296
+ Source::Insert.new(insert_pos, "# @override(allow_incompatible: true)\n#{indent}")
258
297
  else
259
- Source::Insert.new(insert_pos, "# @override\n")
298
+ Source::Insert.new(insert_pos, "# @override\n#{indent}")
260
299
  end
261
300
  end
262
301
 
@@ -342,6 +381,22 @@ module Spoom
342
381
  @last_sigs = []
343
382
  last_sigs
344
383
  end
384
+
385
+ #: (Integer) { (RBI::RBSPrinter) -> void } -> String
386
+ def rbs_print(indent, &block)
387
+ out = StringIO.new
388
+ p = RBI::RBSPrinter.new(out: out, positional_names: @positional_names, max_line_length: @max_line_length)
389
+ block.call(p)
390
+ string = out.string
391
+
392
+ string.lines.map.with_index do |line, index|
393
+ if index == 0
394
+ "#: #{line}"
395
+ else
396
+ "#{" " * indent}#| #{line}"
397
+ end
398
+ end.join + "\n"
399
+ end
345
400
  end
346
401
  end
347
402
  end
@@ -25,23 +25,67 @@ module Spoom
25
25
 
26
26
  # Converts all `sig` nodes to RBS comments in the given Ruby code.
27
27
  # It also handles type members and class annotations.
28
- #: (String ruby_contents, file: String, ?positional_names: bool) -> String
29
- def sorbet_sigs_to_rbs_comments(ruby_contents, file:, positional_names: true)
30
- SorbetSigsToRBSComments.new(ruby_contents, file: file, positional_names: positional_names).rewrite
28
+ #: (
29
+ #| String,
30
+ #| file: String,
31
+ #| ?positional_names: bool,
32
+ #| ?max_line_length: Integer?,
33
+ #| ?translate_generics: bool,
34
+ #| ?translate_helpers: bool,
35
+ #| ?translate_abstract_methods: bool
36
+ #| ) -> String
37
+ def sorbet_sigs_to_rbs_comments(
38
+ ruby_contents, file:, positional_names: true, max_line_length: nil,
39
+ translate_generics: true,
40
+ translate_helpers: true,
41
+ translate_abstract_methods: true
42
+ )
43
+ SorbetSigsToRBSComments.new(
44
+ ruby_contents,
45
+ file: file,
46
+ positional_names: positional_names,
47
+ max_line_length: max_line_length,
48
+ translate_generics: translate_generics,
49
+ translate_helpers: translate_helpers,
50
+ translate_abstract_methods: translate_abstract_methods,
51
+ ).rewrite
31
52
  end
32
53
 
33
54
  # Converts all the RBS comments in the given Ruby code to `sig` nodes.
34
55
  # It also handles type members and class annotations.
35
- #: (String ruby_contents, file: String) -> String
36
- def rbs_comments_to_sorbet_sigs(ruby_contents, file:)
37
- RBSCommentsToSorbetSigs.new(ruby_contents, file: file).rewrite
56
+ #: (String ruby_contents, file: String, ?max_line_length: Integer?) -> String
57
+ def rbs_comments_to_sorbet_sigs(ruby_contents, file:, max_line_length: nil)
58
+ RBSCommentsToSorbetSigs.new(ruby_contents, file: file, max_line_length: max_line_length).rewrite
38
59
  end
39
60
 
40
61
  # Converts all `T.let` and `T.cast` nodes to RBS comments in the given Ruby code.
41
62
  # It also handles type members and class annotations.
42
- #: (String ruby_contents, file: String) -> String
43
- def sorbet_assertions_to_rbs_comments(ruby_contents, file:)
44
- SorbetAssertionsToRBSComments.new(ruby_contents, file: file).rewrite
63
+ #: (
64
+ #| String,
65
+ #| file: String,
66
+ #| ?translate_t_let: bool,
67
+ #| ?translate_t_cast: bool,
68
+ #| ?translate_t_bind: bool,
69
+ #| ?translate_t_must: bool,
70
+ #| ?translate_t_unsafe: bool
71
+ #| ) -> String
72
+ def sorbet_assertions_to_rbs_comments(
73
+ ruby_contents, file:,
74
+ translate_t_let: true,
75
+ translate_t_cast: true,
76
+ translate_t_bind: true,
77
+ translate_t_must: true,
78
+ translate_t_unsafe: true
79
+ )
80
+ SorbetAssertionsToRBSComments.new(
81
+ ruby_contents,
82
+ file: file,
83
+ translate_t_let: translate_t_let,
84
+ translate_t_cast: translate_t_cast,
85
+ translate_t_bind: translate_t_bind,
86
+ translate_t_must: translate_t_must,
87
+ translate_t_unsafe: translate_t_unsafe,
88
+ ).rewrite
45
89
  end
46
90
  end
47
91
  end
@@ -29,11 +29,11 @@ module Spoom
29
29
  class Edit
30
30
  # @abstract
31
31
  #: (Array[Integer]) -> void
32
- def apply(bytes); end
32
+ def apply(bytes) = raise NotImplementedError, "Abstract method called"
33
33
 
34
34
  # @abstract
35
35
  #: -> [Integer, Integer]
36
- def range; end
36
+ def range = raise NotImplementedError, "Abstract method called"
37
37
  end
38
38
 
39
39
  class Insert < Edit
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.7.3"
5
+ VERSION = "1.7.5"
6
6
  end
data/rbi/spoom.rbi CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # DO NOT EDIT MANUALLY
4
4
  # This is an autogenerated file for types exported from the `spoom` gem.
5
- # Please instead update this file by running `spoom srb sigs export`.
5
+ # Please instead update this file by running `bundle exec spoom srb sigs export`.
6
6
 
7
7
  module Spoom
8
8
  class << self
@@ -2839,14 +2839,34 @@ Spoom::Sorbet::Sigils::VALID_STRICTNESS = T.let(T.unsafe(nil), Array)
2839
2839
 
2840
2840
  module Spoom::Sorbet::Translate
2841
2841
  class << self
2842
- sig { params(ruby_contents: ::String, file: ::String).returns(::String) }
2843
- def rbs_comments_to_sorbet_sigs(ruby_contents, file:); end
2842
+ sig { params(ruby_contents: ::String, file: ::String, max_line_length: T.nilable(::Integer)).returns(::String) }
2843
+ def rbs_comments_to_sorbet_sigs(ruby_contents, file:, max_line_length: T.unsafe(nil)); end
2844
2844
 
2845
- sig { params(ruby_contents: ::String, file: ::String).returns(::String) }
2846
- def sorbet_assertions_to_rbs_comments(ruby_contents, file:); end
2845
+ sig do
2846
+ params(
2847
+ ruby_contents: ::String,
2848
+ file: ::String,
2849
+ translate_t_let: T::Boolean,
2850
+ translate_t_cast: T::Boolean,
2851
+ translate_t_bind: T::Boolean,
2852
+ translate_t_must: T::Boolean,
2853
+ translate_t_unsafe: T::Boolean
2854
+ ).returns(::String)
2855
+ end
2856
+ def sorbet_assertions_to_rbs_comments(ruby_contents, file:, translate_t_let: T.unsafe(nil), translate_t_cast: T.unsafe(nil), translate_t_bind: T.unsafe(nil), translate_t_must: T.unsafe(nil), translate_t_unsafe: T.unsafe(nil)); end
2847
2857
 
2848
- sig { params(ruby_contents: ::String, file: ::String, positional_names: T::Boolean).returns(::String) }
2849
- def sorbet_sigs_to_rbs_comments(ruby_contents, file:, positional_names: T.unsafe(nil)); end
2858
+ sig do
2859
+ params(
2860
+ ruby_contents: ::String,
2861
+ file: ::String,
2862
+ positional_names: T::Boolean,
2863
+ max_line_length: T.nilable(::Integer),
2864
+ translate_generics: T::Boolean,
2865
+ translate_helpers: T::Boolean,
2866
+ translate_abstract_methods: T::Boolean
2867
+ ).returns(::String)
2868
+ end
2869
+ def sorbet_sigs_to_rbs_comments(ruby_contents, file:, positional_names: T.unsafe(nil), max_line_length: T.unsafe(nil), translate_generics: T.unsafe(nil), translate_helpers: T.unsafe(nil), translate_abstract_methods: T.unsafe(nil)); end
2850
2870
 
2851
2871
  sig { params(ruby_contents: ::String, file: ::String).returns(::String) }
2852
2872
  def strip_sorbet_sigs(ruby_contents, file:); end
@@ -2858,6 +2878,9 @@ class Spoom::Sorbet::Translate::Error < ::Spoom::Error; end
2858
2878
  class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Translate::Translator
2859
2879
  include ::Spoom::RBS::ExtractRBSComments
2860
2880
 
2881
+ sig { params(ruby_contents: ::String, file: ::String, max_line_length: T.nilable(::Integer)).void }
2882
+ def initialize(ruby_contents, file:, max_line_length: T.unsafe(nil)); end
2883
+
2861
2884
  sig { override.params(node: ::Prism::CallNode).void }
2862
2885
  def visit_call_node(node); end
2863
2886
 
@@ -2889,11 +2912,27 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Trans
2889
2912
  sig { params(annotations: T::Array[::Spoom::RBS::Annotation], sig: ::RBI::Sig).void }
2890
2913
  def apply_member_annotations(annotations, sig); end
2891
2914
 
2915
+ sig { params(def_node: ::Prism::DefNode, comments: ::Spoom::RBS::Comments).void }
2916
+ def rewrite_def(def_node, comments); end
2917
+
2892
2918
  sig { params(node: ::Prism::CallNode).void }
2893
2919
  def visit_attr(node); end
2894
2920
  end
2895
2921
 
2896
2922
  class Spoom::Sorbet::Translate::SorbetAssertionsToRBSComments < ::Spoom::Sorbet::Translate::Translator
2923
+ sig do
2924
+ params(
2925
+ ruby_contents: ::String,
2926
+ file: ::String,
2927
+ translate_t_let: T::Boolean,
2928
+ translate_t_cast: T::Boolean,
2929
+ translate_t_bind: T::Boolean,
2930
+ translate_t_must: T::Boolean,
2931
+ translate_t_unsafe: T::Boolean
2932
+ ).void
2933
+ end
2934
+ def initialize(ruby_contents, file:, translate_t_let: T.unsafe(nil), translate_t_cast: T.unsafe(nil), translate_t_bind: T.unsafe(nil), translate_t_must: T.unsafe(nil), translate_t_unsafe: T.unsafe(nil)); end
2935
+
2897
2936
  sig { override.params(node: ::Prism::IfNode).void }
2898
2937
  def visit_if_node(node); end
2899
2938
 
@@ -2918,14 +2957,24 @@ class Spoom::Sorbet::Translate::SorbetAssertionsToRBSComments < ::Spoom::Sorbet:
2918
2957
  def t?(node); end
2919
2958
 
2920
2959
  sig { params(node: ::Prism::CallNode).returns(T::Boolean) }
2921
- def t_annotation?(node); end
2960
+ def translatable_annotation?(node); end
2922
2961
  end
2923
2962
 
2924
2963
  Spoom::Sorbet::Translate::SorbetAssertionsToRBSComments::LINE_BREAK = T.let(T.unsafe(nil), Integer)
2925
2964
 
2926
2965
  class Spoom::Sorbet::Translate::SorbetSigsToRBSComments < ::Spoom::Sorbet::Translate::Translator
2927
- sig { params(ruby_contents: ::String, file: ::String, positional_names: T::Boolean).void }
2928
- def initialize(ruby_contents, file:, positional_names:); end
2966
+ sig do
2967
+ params(
2968
+ ruby_contents: ::String,
2969
+ file: ::String,
2970
+ positional_names: T::Boolean,
2971
+ max_line_length: T.nilable(::Integer),
2972
+ translate_generics: T::Boolean,
2973
+ translate_helpers: T::Boolean,
2974
+ translate_abstract_methods: T::Boolean
2975
+ ).void
2976
+ end
2977
+ def initialize(ruby_contents, file:, positional_names:, max_line_length: T.unsafe(nil), translate_generics: T.unsafe(nil), translate_helpers: T.unsafe(nil), translate_abstract_methods: T.unsafe(nil)); end
2929
2978
 
2930
2979
  sig { override.params(node: ::Prism::CallNode).void }
2931
2980
  def visit_call_node(node); end
@@ -2970,6 +3019,9 @@ class Spoom::Sorbet::Translate::SorbetSigsToRBSComments < ::Spoom::Sorbet::Trans
2970
3019
  sig { void }
2971
3020
  def delete_extend_t_helpers; end
2972
3021
 
3022
+ sig { params(indent: ::Integer, block: T.proc.params(arg0: ::RBI::RBSPrinter).void).returns(::String) }
3023
+ def rbs_print(indent, &block); end
3024
+
2973
3025
  sig { params(node: ::Prism::CallNode).void }
2974
3026
  def visit_attr(node); end
2975
3027
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.3
4
+ version: 1.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa