spoom 1.5.0 → 1.5.2
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 +4 -4
- data/lib/spoom/cli/srb/sigs.rb +90 -0
- data/lib/spoom/cli/srb.rb +4 -0
- data/lib/spoom/sorbet/sigs.rb +173 -0
- data/lib/spoom/version.rb +1 -1
- metadata +19 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f26cb1e2c5649be363ee1319b601c2abe2c61d8ca2bcd335bec6ab91dba4195
|
4
|
+
data.tar.gz: 2284b21b3cec3a02d49d9b5fcb160c4c38c27af131addc7b2d63194f3e5a4041
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3695fa07844fcf1d2829ebfbfa030e514842a41edd7903c46b697ff60f0e76ddb9c4c8ce9098ac299c34434d4b720f32da88047c6884c67be59a02b04f3da57
|
7
|
+
data.tar.gz: 2e338e995b073cb7dedbbce7e8cd0f976cca8fd3cd9cee4adaac1d0eabeb24ba43d1e362b449f0f701026327c0d7bd3e4984089eadc51e26f991627d4c08c663
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "spoom/sorbet/sigs"
|
5
|
+
|
6
|
+
module Spoom
|
7
|
+
module Cli
|
8
|
+
module Srb
|
9
|
+
class Sigs < Thor
|
10
|
+
extend T::Sig
|
11
|
+
include Helper
|
12
|
+
|
13
|
+
desc "translate", "Translate signatures from/to RBI and RBS"
|
14
|
+
option :from, type: :string, aliases: :f, desc: "From format", enum: ["rbi"], default: "rbi"
|
15
|
+
option :to, type: :string, aliases: :t, desc: "To format", enum: ["rbs"], default: "rbs"
|
16
|
+
def translate(*paths)
|
17
|
+
from = options[:from]
|
18
|
+
to = options[:to]
|
19
|
+
files = collect_files(paths)
|
20
|
+
|
21
|
+
say("Translating signatures from `#{from}` to `#{to}` " \
|
22
|
+
"in `#{files.size}` file#{files.size == 1 ? "" : "s"}...\n\n")
|
23
|
+
|
24
|
+
transformed_files = transform_files(files) do |_file, contents|
|
25
|
+
Spoom::Sorbet::Sigs.rbi_to_rbs(contents)
|
26
|
+
end
|
27
|
+
|
28
|
+
say("Translated signatures in `#{transformed_files}` file#{transformed_files == 1 ? "" : "s"}.")
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "strip", "Strip all the signatures from the files"
|
32
|
+
def strip(*paths)
|
33
|
+
files = collect_files(paths)
|
34
|
+
|
35
|
+
say("Stripping signatures from `#{files.size}` file#{files.size == 1 ? "" : "s"}...\n\n")
|
36
|
+
|
37
|
+
transformed_files = transform_files(files) do |_file, contents|
|
38
|
+
Spoom::Sorbet::Sigs.strip(contents)
|
39
|
+
end
|
40
|
+
|
41
|
+
say("Stripped signatures from `#{transformed_files}` file#{transformed_files == 1 ? "" : "s"}.")
|
42
|
+
end
|
43
|
+
|
44
|
+
no_commands do
|
45
|
+
def collect_files(paths)
|
46
|
+
paths << "." if paths.empty?
|
47
|
+
|
48
|
+
files = paths.flat_map do |path|
|
49
|
+
if File.file?(path)
|
50
|
+
[path]
|
51
|
+
else
|
52
|
+
Dir.glob("#{path}/**/*.rb")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if files.empty?
|
57
|
+
say_error("No files to transform")
|
58
|
+
exit(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
files
|
62
|
+
end
|
63
|
+
|
64
|
+
def transform_files(files, &block)
|
65
|
+
transformed_count = 0
|
66
|
+
|
67
|
+
files.each do |file|
|
68
|
+
contents = File.read(file)
|
69
|
+
first_line = contents.lines.first
|
70
|
+
|
71
|
+
if first_line&.start_with?("# encoding:")
|
72
|
+
encoding = T.must(first_line).gsub(/^#\s*encoding:\s*/, "").strip
|
73
|
+
contents = contents.force_encoding(encoding)
|
74
|
+
end
|
75
|
+
|
76
|
+
contents = block.call(file, contents)
|
77
|
+
File.write(file, contents)
|
78
|
+
transformed_count += 1
|
79
|
+
rescue RBI::Error => error
|
80
|
+
say_warning("Can't parse #{file}: #{error.message}")
|
81
|
+
next
|
82
|
+
end
|
83
|
+
|
84
|
+
transformed_count
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/spoom/cli/srb.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
require_relative "srb/bump"
|
5
5
|
require_relative "srb/coverage"
|
6
6
|
require_relative "srb/lsp"
|
7
|
+
require_relative "srb/sigs"
|
7
8
|
require_relative "srb/tc"
|
8
9
|
|
9
10
|
module Spoom
|
@@ -19,6 +20,9 @@ module Spoom
|
|
19
20
|
desc "bump", "Change Sorbet sigils from one strictness to another when no errors"
|
20
21
|
subcommand "bump", Spoom::Cli::Srb::Bump
|
21
22
|
|
23
|
+
desc "sigs", "Translate signatures from/to RBI and RBS"
|
24
|
+
subcommand "sigs", Spoom::Cli::Srb::Sigs
|
25
|
+
|
22
26
|
desc "tc", "Run typechecking with advanced options"
|
23
27
|
subcommand "tc", Spoom::Cli::Srb::Tc
|
24
28
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rbi"
|
5
|
+
|
6
|
+
module Spoom
|
7
|
+
module Sorbet
|
8
|
+
class Sigs
|
9
|
+
class << self
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(ruby_contents: String).returns(String) }
|
13
|
+
def strip(ruby_contents)
|
14
|
+
sigs = collect_sigs(ruby_contents)
|
15
|
+
lines_to_strip = sigs.flat_map { |sig, _| (sig.loc&.begin_line..sig.loc&.end_line).to_a }
|
16
|
+
|
17
|
+
lines = []
|
18
|
+
ruby_contents.lines.each_with_index do |line, index|
|
19
|
+
lines << line unless lines_to_strip.include?(index + 1)
|
20
|
+
end
|
21
|
+
lines.join
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { params(ruby_contents: String).returns(String) }
|
25
|
+
def rbi_to_rbs(ruby_contents)
|
26
|
+
ruby_contents = ruby_contents.dup
|
27
|
+
sigs = collect_sigs(ruby_contents)
|
28
|
+
|
29
|
+
sigs.each do |sig, node|
|
30
|
+
scanner = Scanner.new(ruby_contents)
|
31
|
+
start_index = scanner.find_char_position(
|
32
|
+
T.must(sig.loc&.begin_line&.pred),
|
33
|
+
T.must(sig.loc).begin_column,
|
34
|
+
)
|
35
|
+
end_index = scanner.find_char_position(
|
36
|
+
sig.loc&.end_line&.pred,
|
37
|
+
T.must(sig.loc).end_column,
|
38
|
+
)
|
39
|
+
ruby_contents[start_index...end_index] = SigTranslator.translate(sig, node)
|
40
|
+
end
|
41
|
+
|
42
|
+
ruby_contents
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
sig { params(ruby_contents: String).returns(T::Array[[RBI::Sig, T.any(RBI::Method, RBI::Attr)]]) }
|
48
|
+
def collect_sigs(ruby_contents)
|
49
|
+
tree = RBI::Parser.parse_string(ruby_contents)
|
50
|
+
visitor = SigsLocator.new
|
51
|
+
visitor.visit(tree)
|
52
|
+
visitor.sigs.sort_by { |sig, _rbs_string| -T.must(sig.loc&.begin_line) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class SigsLocator < RBI::Visitor
|
57
|
+
extend T::Sig
|
58
|
+
|
59
|
+
sig { returns(T::Array[[RBI::Sig, T.any(RBI::Method, RBI::Attr)]]) }
|
60
|
+
attr_reader :sigs
|
61
|
+
|
62
|
+
sig { void }
|
63
|
+
def initialize
|
64
|
+
super
|
65
|
+
@sigs = T.let([], T::Array[[RBI::Sig, T.any(RBI::Method, RBI::Attr)]])
|
66
|
+
end
|
67
|
+
|
68
|
+
sig { override.params(node: T.nilable(RBI::Node)).void }
|
69
|
+
def visit(node)
|
70
|
+
return unless node
|
71
|
+
|
72
|
+
case node
|
73
|
+
when RBI::Method, RBI::Attr
|
74
|
+
node.sigs.each do |sig|
|
75
|
+
@sigs << [sig, node]
|
76
|
+
end
|
77
|
+
when RBI::Tree
|
78
|
+
visit_all(node.nodes)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class SigTranslator
|
84
|
+
class << self
|
85
|
+
extend T::Sig
|
86
|
+
|
87
|
+
sig { params(sig: RBI::Sig, node: T.any(RBI::Method, RBI::Attr)).returns(String) }
|
88
|
+
def translate(sig, node)
|
89
|
+
case node
|
90
|
+
when RBI::Method
|
91
|
+
translate_method_sig(sig, node)
|
92
|
+
when RBI::Attr
|
93
|
+
translate_attr_sig(sig, node)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
sig { params(sig: RBI::Sig, node: RBI::Method).returns(String) }
|
100
|
+
def translate_method_sig(sig, node)
|
101
|
+
out = StringIO.new
|
102
|
+
p = RBI::RBSPrinter.new(out: out, indent: sig.loc&.begin_column)
|
103
|
+
|
104
|
+
if node.sigs.any?(&:is_final)
|
105
|
+
p.printn("# @final")
|
106
|
+
p.printt
|
107
|
+
end
|
108
|
+
|
109
|
+
if node.sigs.any?(&:is_abstract)
|
110
|
+
p.printn("# @abstract")
|
111
|
+
p.printt
|
112
|
+
end
|
113
|
+
|
114
|
+
if node.sigs.any?(&:is_override)
|
115
|
+
if node.sigs.any?(&:allow_incompatible_override)
|
116
|
+
p.printn("# @override(allow_incompatible: true)")
|
117
|
+
else
|
118
|
+
p.printn("# @override")
|
119
|
+
end
|
120
|
+
p.printt
|
121
|
+
end
|
122
|
+
|
123
|
+
if node.sigs.any?(&:is_overridable)
|
124
|
+
p.printn("# @overridable")
|
125
|
+
p.printt
|
126
|
+
end
|
127
|
+
|
128
|
+
p.print("#: ")
|
129
|
+
p.send(:print_method_sig, node, sig)
|
130
|
+
|
131
|
+
out.string
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(sig: RBI::Sig, node: RBI::Attr).returns(String) }
|
135
|
+
def translate_attr_sig(sig, node)
|
136
|
+
out = StringIO.new
|
137
|
+
p = RBI::RBSPrinter.new(out: out)
|
138
|
+
p.print_attr_sig(node, sig)
|
139
|
+
"#: #{out.string}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# From https://github.com/Shopify/ruby-lsp/blob/9154bfc6ef/lib/ruby_lsp/document.rb#L127
|
145
|
+
class Scanner
|
146
|
+
extend T::Sig
|
147
|
+
|
148
|
+
LINE_BREAK = T.let(0x0A, Integer)
|
149
|
+
|
150
|
+
sig { params(source: String).void }
|
151
|
+
def initialize(source)
|
152
|
+
@current_line = T.let(0, Integer)
|
153
|
+
@pos = T.let(0, Integer)
|
154
|
+
@source = T.let(source.codepoints, T::Array[Integer])
|
155
|
+
end
|
156
|
+
|
157
|
+
# Finds the character index inside the source string for a given line and column
|
158
|
+
sig { params(line: Integer, character: Integer).returns(Integer) }
|
159
|
+
def find_char_position(line, character)
|
160
|
+
# Find the character index for the beginning of the requested line
|
161
|
+
until @current_line == line
|
162
|
+
@pos += 1 until LINE_BREAK == @source[@pos]
|
163
|
+
@pos += 1
|
164
|
+
@current_line += 1
|
165
|
+
end
|
166
|
+
|
167
|
+
# The final position is the beginning of the line plus the requested column
|
168
|
+
@pos + character
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/lib/spoom/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spoom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexandre Terrasa
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-21 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bundler
|
@@ -94,6 +93,20 @@ dependencies:
|
|
94
93
|
- - ">="
|
95
94
|
- !ruby/object:Gem::Version
|
96
95
|
version: 0.28.0
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: rbi
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.2.3
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.2.3
|
97
110
|
- !ruby/object:Gem::Dependency
|
98
111
|
name: sorbet-static-and-runtime
|
99
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,7 +135,6 @@ dependencies:
|
|
122
135
|
- - ">="
|
123
136
|
- !ruby/object:Gem::Version
|
124
137
|
version: 0.19.2
|
125
|
-
description:
|
126
138
|
email:
|
127
139
|
- ruby@shopify.com
|
128
140
|
executables:
|
@@ -144,6 +156,7 @@ files:
|
|
144
156
|
- lib/spoom/cli/srb/bump.rb
|
145
157
|
- lib/spoom/cli/srb/coverage.rb
|
146
158
|
- lib/spoom/cli/srb/lsp.rb
|
159
|
+
- lib/spoom/cli/srb/sigs.rb
|
147
160
|
- lib/spoom/cli/srb/tc.rb
|
148
161
|
- lib/spoom/colors.rb
|
149
162
|
- lib/spoom/context.rb
|
@@ -207,6 +220,7 @@ files:
|
|
207
220
|
- lib/spoom/sorbet/lsp/structures.rb
|
208
221
|
- lib/spoom/sorbet/metrics.rb
|
209
222
|
- lib/spoom/sorbet/sigils.rb
|
223
|
+
- lib/spoom/sorbet/sigs.rb
|
210
224
|
- lib/spoom/timeline.rb
|
211
225
|
- lib/spoom/version.rb
|
212
226
|
- lib/spoom/visitor.rb
|
@@ -218,7 +232,6 @@ licenses:
|
|
218
232
|
- MIT
|
219
233
|
metadata:
|
220
234
|
allowed_push_host: https://rubygems.org
|
221
|
-
post_install_message:
|
222
235
|
rdoc_options: []
|
223
236
|
require_paths:
|
224
237
|
- lib
|
@@ -233,8 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
233
246
|
- !ruby/object:Gem::Version
|
234
247
|
version: '0'
|
235
248
|
requirements: []
|
236
|
-
rubygems_version: 3.
|
237
|
-
signing_key:
|
249
|
+
rubygems_version: 3.6.2
|
238
250
|
specification_version: 4
|
239
251
|
summary: Useful tools for Sorbet enthusiasts.
|
240
252
|
test_files: []
|