spoom 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +64 -55
- data/lib/spoom/backtrace_filter/minitest.rb +21 -0
- data/lib/spoom/cli/deadcode.rb +172 -0
- data/lib/spoom/cli/helper.rb +20 -0
- data/lib/spoom/cli/srb/bump.rb +200 -0
- data/lib/spoom/cli/srb/coverage.rb +224 -0
- data/lib/spoom/cli/srb/lsp.rb +159 -0
- data/lib/spoom/cli/srb/tc.rb +150 -0
- data/lib/spoom/cli/srb.rb +27 -0
- data/lib/spoom/cli.rb +72 -32
- data/lib/spoom/context/git.rb +2 -2
- data/lib/spoom/context/sorbet.rb +2 -2
- data/lib/spoom/deadcode/definition.rb +11 -0
- data/lib/spoom/deadcode/erb.rb +4 -4
- data/lib/spoom/deadcode/indexer.rb +266 -200
- data/lib/spoom/deadcode/location.rb +30 -2
- data/lib/spoom/deadcode/plugins/action_mailer.rb +21 -0
- data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
- data/lib/spoom/deadcode/plugins/actionpack.rb +59 -0
- data/lib/spoom/deadcode/plugins/active_job.rb +13 -0
- data/lib/spoom/deadcode/plugins/active_model.rb +46 -0
- data/lib/spoom/deadcode/plugins/active_record.rb +108 -0
- data/lib/spoom/deadcode/plugins/active_support.rb +32 -0
- data/lib/spoom/deadcode/plugins/base.rb +165 -12
- data/lib/spoom/deadcode/plugins/graphql.rb +47 -0
- data/lib/spoom/deadcode/plugins/minitest.rb +28 -0
- data/lib/spoom/deadcode/plugins/namespaces.rb +32 -0
- data/lib/spoom/deadcode/plugins/rails.rb +31 -0
- data/lib/spoom/deadcode/plugins/rake.rb +12 -0
- data/lib/spoom/deadcode/plugins/rspec.rb +19 -0
- data/lib/spoom/deadcode/plugins/rubocop.rb +41 -0
- data/lib/spoom/deadcode/plugins/ruby.rb +10 -18
- data/lib/spoom/deadcode/plugins/sorbet.rb +40 -0
- data/lib/spoom/deadcode/plugins/thor.rb +21 -0
- data/lib/spoom/deadcode/plugins.rb +91 -0
- data/lib/spoom/deadcode/remover.rb +651 -0
- data/lib/spoom/deadcode/send.rb +27 -6
- data/lib/spoom/deadcode/visitor.rb +755 -0
- data/lib/spoom/deadcode.rb +41 -10
- data/lib/spoom/file_tree.rb +0 -16
- data/lib/spoom/sorbet/errors.rb +1 -1
- data/lib/spoom/sorbet/lsp/structures.rb +2 -2
- data/lib/spoom/version.rb +1 -1
- metadata +36 -15
- data/lib/spoom/cli/bump.rb +0 -198
- data/lib/spoom/cli/coverage.rb +0 -222
- data/lib/spoom/cli/lsp.rb +0 -168
- data/lib/spoom/cli/run.rb +0 -148
data/lib/spoom/cli/coverage.rb
DELETED
@@ -1,222 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative "../coverage"
|
5
|
-
require_relative "../timeline"
|
6
|
-
|
7
|
-
module Spoom
|
8
|
-
module Cli
|
9
|
-
class Coverage < Thor
|
10
|
-
include Helper
|
11
|
-
|
12
|
-
DATA_DIR = "spoom_data"
|
13
|
-
|
14
|
-
default_task :snapshot
|
15
|
-
|
16
|
-
desc "snapshot", "Run srb tc and display metrics"
|
17
|
-
option :save, type: :string, lazy_default: DATA_DIR, desc: "Save snapshot data as json"
|
18
|
-
option :rbi, type: :boolean, default: true, desc: "Include RBI files in metrics"
|
19
|
-
option :sorbet, type: :string, desc: "Path to custom Sorbet bin"
|
20
|
-
def snapshot
|
21
|
-
context = context_requiring_sorbet!
|
22
|
-
sorbet = options[:sorbet]
|
23
|
-
|
24
|
-
snapshot = Spoom::Coverage.snapshot(context, rbi: options[:rbi], sorbet_bin: sorbet)
|
25
|
-
snapshot.print
|
26
|
-
|
27
|
-
save_dir = options[:save]
|
28
|
-
return unless save_dir
|
29
|
-
|
30
|
-
FileUtils.mkdir_p(save_dir)
|
31
|
-
file = "#{save_dir}/#{snapshot.commit_sha || snapshot.timestamp}.json"
|
32
|
-
File.write(file, snapshot.to_json)
|
33
|
-
say("\nSnapshot data saved under `#{file}`")
|
34
|
-
end
|
35
|
-
|
36
|
-
desc "timeline", "Replay a project and collect metrics"
|
37
|
-
option :from, type: :string, desc: "From commit date"
|
38
|
-
option :to, type: :string, default: Time.now.strftime("%F"), desc: "To commit date"
|
39
|
-
option :save, type: :string, lazy_default: DATA_DIR, desc: "Save snapshot data as json"
|
40
|
-
option :bundle_install, type: :boolean, desc: "Execute `bundle install` before collecting metrics"
|
41
|
-
option :sorbet, type: :string, desc: "Path to custom Sorbet bin"
|
42
|
-
def timeline
|
43
|
-
context = context_requiring_sorbet!
|
44
|
-
path = exec_path
|
45
|
-
sorbet = options[:sorbet]
|
46
|
-
|
47
|
-
ref_before = context.git_current_branch
|
48
|
-
ref_before = context.git_last_commit&.sha unless ref_before
|
49
|
-
unless ref_before
|
50
|
-
say_error("Not in a git repository")
|
51
|
-
say_error("\nSpoom needs to checkout into your previous commits to build the timeline.", status: nil)
|
52
|
-
exit(1)
|
53
|
-
end
|
54
|
-
|
55
|
-
unless context.git_workdir_clean?
|
56
|
-
say_error("Uncommited changes")
|
57
|
-
say_error(<<~ERR, status: nil)
|
58
|
-
|
59
|
-
Spoom needs to checkout into your previous commits to build the timeline."
|
60
|
-
|
61
|
-
Please `git commit` or `git stash` your changes then try again
|
62
|
-
ERR
|
63
|
-
exit(1)
|
64
|
-
end
|
65
|
-
|
66
|
-
save_dir = options[:save]
|
67
|
-
FileUtils.mkdir_p(save_dir) if save_dir
|
68
|
-
|
69
|
-
from = parse_time(options[:from], "--from")
|
70
|
-
to = parse_time(options[:to], "--to")
|
71
|
-
|
72
|
-
unless from
|
73
|
-
intro_commit = context.sorbet_intro_commit
|
74
|
-
intro_commit = T.must(intro_commit) # we know it's in there since in_sorbet_project!
|
75
|
-
from = intro_commit.time
|
76
|
-
end
|
77
|
-
|
78
|
-
timeline = Spoom::Timeline.new(context, from, to)
|
79
|
-
ticks = timeline.ticks
|
80
|
-
|
81
|
-
if ticks.empty?
|
82
|
-
say_error("No commits to replay, try different `--from` and `--to` options")
|
83
|
-
exit(1)
|
84
|
-
end
|
85
|
-
|
86
|
-
ticks.each_with_index do |commit, i|
|
87
|
-
say("Analyzing commit `#{commit.sha}` - #{commit.time.strftime("%F")} (#{i + 1} / #{ticks.size})")
|
88
|
-
|
89
|
-
context.git_checkout!(ref: commit.sha)
|
90
|
-
|
91
|
-
snapshot = T.let(nil, T.nilable(Spoom::Coverage::Snapshot))
|
92
|
-
if options[:bundle_install]
|
93
|
-
Bundler.with_unbundled_env do
|
94
|
-
next unless bundle_install(path, commit.sha)
|
95
|
-
|
96
|
-
snapshot = Spoom::Coverage.snapshot(context, sorbet_bin: sorbet)
|
97
|
-
end
|
98
|
-
else
|
99
|
-
snapshot = Spoom::Coverage.snapshot(context, sorbet_bin: sorbet)
|
100
|
-
end
|
101
|
-
next unless snapshot
|
102
|
-
|
103
|
-
snapshot.print(indent_level: 2)
|
104
|
-
say("\n")
|
105
|
-
|
106
|
-
next unless save_dir
|
107
|
-
|
108
|
-
file = "#{save_dir}/#{commit.sha}.json"
|
109
|
-
File.write(file, snapshot.to_json)
|
110
|
-
say(" Snapshot data saved under `#{file}`\n\n")
|
111
|
-
end
|
112
|
-
context.git_checkout!(ref: ref_before)
|
113
|
-
end
|
114
|
-
|
115
|
-
desc "report", "Produce a typing coverage report"
|
116
|
-
option :data, type: :string, default: DATA_DIR, desc: "Snapshots JSON data"
|
117
|
-
option :file,
|
118
|
-
type: :string,
|
119
|
-
default: "spoom_report.html",
|
120
|
-
aliases: :f,
|
121
|
-
desc: "Save report to file"
|
122
|
-
option :color_ignore,
|
123
|
-
type: :string,
|
124
|
-
default: Spoom::Coverage::D3::COLOR_IGNORE,
|
125
|
-
desc: "Color used for typed: ignore"
|
126
|
-
option :color_false,
|
127
|
-
type: :string,
|
128
|
-
default: Spoom::Coverage::D3::COLOR_FALSE,
|
129
|
-
desc: "Color used for typed: false"
|
130
|
-
option :color_true,
|
131
|
-
type: :string,
|
132
|
-
default: Spoom::Coverage::D3::COLOR_TRUE,
|
133
|
-
desc: "Color used for typed: true"
|
134
|
-
option :color_strict,
|
135
|
-
type: :string,
|
136
|
-
default: Spoom::Coverage::D3::COLOR_STRICT,
|
137
|
-
desc: "Color used for typed: strict"
|
138
|
-
option :color_strong,
|
139
|
-
type: :string,
|
140
|
-
default: Spoom::Coverage::D3::COLOR_STRONG,
|
141
|
-
desc: "Color used for typed: strong"
|
142
|
-
def report
|
143
|
-
context = context_requiring_sorbet!
|
144
|
-
|
145
|
-
data_dir = options[:data]
|
146
|
-
files = Dir.glob("#{data_dir}/*.json")
|
147
|
-
if files.empty?
|
148
|
-
message_no_data(data_dir)
|
149
|
-
exit(1)
|
150
|
-
end
|
151
|
-
|
152
|
-
snapshots = files.sort.map do |file|
|
153
|
-
json = File.read(file)
|
154
|
-
Spoom::Coverage::Snapshot.from_json(json)
|
155
|
-
end.filter(&:commit_timestamp).sort_by!(&:commit_timestamp)
|
156
|
-
|
157
|
-
palette = Spoom::Coverage::D3::ColorPalette.new(
|
158
|
-
ignore: options[:color_ignore],
|
159
|
-
false: options[:color_false],
|
160
|
-
true: options[:color_true],
|
161
|
-
strict: options[:color_strict],
|
162
|
-
strong: options[:color_strong],
|
163
|
-
)
|
164
|
-
|
165
|
-
report = Spoom::Coverage.report(context, snapshots, palette: palette)
|
166
|
-
file = options[:file]
|
167
|
-
File.write(file, report.html)
|
168
|
-
say("Report generated under `#{file}`")
|
169
|
-
say("\nUse `spoom coverage open` to open it.")
|
170
|
-
end
|
171
|
-
|
172
|
-
desc "open", "Open the typing coverage report"
|
173
|
-
def open(file = "spoom_report.html")
|
174
|
-
unless File.exist?(file)
|
175
|
-
say_error("No report file to open `#{file}`")
|
176
|
-
say_error(<<~ERR, status: nil)
|
177
|
-
|
178
|
-
If you already generated a report under another name use #{blue("spoom coverage open PATH")}.
|
179
|
-
|
180
|
-
To generate a report run #{blue("spoom coverage report")}.
|
181
|
-
ERR
|
182
|
-
exit(1)
|
183
|
-
end
|
184
|
-
|
185
|
-
exec("open #{file}")
|
186
|
-
end
|
187
|
-
|
188
|
-
no_commands do
|
189
|
-
def parse_time(string, option)
|
190
|
-
return unless string
|
191
|
-
|
192
|
-
Time.parse(string)
|
193
|
-
rescue ArgumentError
|
194
|
-
say_error("Invalid date `#{string}` for option `#{option}` (expected format `YYYY-MM-DD`)")
|
195
|
-
exit(1)
|
196
|
-
end
|
197
|
-
|
198
|
-
def bundle_install(path, sha)
|
199
|
-
opts = {}
|
200
|
-
opts[:chdir] = path
|
201
|
-
out, status = Open3.capture2e("bundle install", opts)
|
202
|
-
unless status.success?
|
203
|
-
say_error("Can't run `bundle install` for commit `#{sha}`. Skipping snapshot")
|
204
|
-
say_error(out, status: nil)
|
205
|
-
return false
|
206
|
-
end
|
207
|
-
true
|
208
|
-
end
|
209
|
-
|
210
|
-
def message_no_data(file)
|
211
|
-
say_error("No snapshot files found in `#{file}`")
|
212
|
-
say_error(<<~ERR, status: nil)
|
213
|
-
|
214
|
-
If you already generated snapshot files under another directory use #{blue("spoom coverage report PATH")}.
|
215
|
-
|
216
|
-
To generate snapshot files run #{blue("spoom coverage timeline --save")}.
|
217
|
-
ERR
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
data/lib/spoom/cli/lsp.rb
DELETED
@@ -1,168 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "shellwords"
|
5
|
-
|
6
|
-
require_relative "../sorbet/lsp"
|
7
|
-
|
8
|
-
module Spoom
|
9
|
-
module Cli
|
10
|
-
class LSP < Thor
|
11
|
-
include Helper
|
12
|
-
|
13
|
-
default_task :show
|
14
|
-
|
15
|
-
desc "interactive", "Interactive LSP mode"
|
16
|
-
def show
|
17
|
-
context_requiring_sorbet!
|
18
|
-
|
19
|
-
lsp = lsp_client
|
20
|
-
# TODO: run interactive mode
|
21
|
-
puts lsp
|
22
|
-
end
|
23
|
-
|
24
|
-
desc "list", "List all known symbols"
|
25
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
26
|
-
def list
|
27
|
-
run do |client|
|
28
|
-
printer = symbol_printer
|
29
|
-
Dir["**/*.rb"].each do |file|
|
30
|
-
res = client.document_symbols(to_uri(file))
|
31
|
-
next if res.empty?
|
32
|
-
|
33
|
-
say("Symbols from `#{file}`:")
|
34
|
-
printer.print_objects(res)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
desc "hover", "Request hover informations"
|
40
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
41
|
-
def hover(file, line, col)
|
42
|
-
run do |client|
|
43
|
-
res = client.hover(to_uri(file), line.to_i, col.to_i)
|
44
|
-
say("Hovering `#{file}:#{line}:#{col}`:")
|
45
|
-
if res
|
46
|
-
symbol_printer.print_object(res)
|
47
|
-
else
|
48
|
-
say("<no data>")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
desc "defs", "List definitions of a symbol"
|
54
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
55
|
-
def defs(file, line, col)
|
56
|
-
run do |client|
|
57
|
-
res = client.definitions(to_uri(file), line.to_i, col.to_i)
|
58
|
-
say("Definitions for `#{file}:#{line}:#{col}`:")
|
59
|
-
symbol_printer.print_list(res)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
desc "find", "Find symbols matching a query"
|
64
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
65
|
-
def find(query)
|
66
|
-
run do |client|
|
67
|
-
res = client.symbols(query).reject { |symbol| symbol.location.uri.start_with?("https") }
|
68
|
-
say("Symbols matching `#{query}`:")
|
69
|
-
symbol_printer.print_objects(res)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
desc "symbols", "List symbols from a file"
|
74
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
75
|
-
def symbols(file)
|
76
|
-
run do |client|
|
77
|
-
res = client.document_symbols(to_uri(file))
|
78
|
-
say("Symbols from `#{file}`:")
|
79
|
-
symbol_printer.print_objects(res)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
desc "refs", "List references to a symbol"
|
84
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
85
|
-
def refs(file, line, col)
|
86
|
-
run do |client|
|
87
|
-
res = client.references(to_uri(file), line.to_i, col.to_i)
|
88
|
-
say("References to `#{file}:#{line}:#{col}`:")
|
89
|
-
symbol_printer.print_list(res)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
desc "sigs", "List signatures for a symbol"
|
94
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
95
|
-
def sigs(file, line, col)
|
96
|
-
run do |client|
|
97
|
-
res = client.signatures(to_uri(file), line.to_i, col.to_i)
|
98
|
-
say("Signature for `#{file}:#{line}:#{col}`:")
|
99
|
-
symbol_printer.print_list(res)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
desc "types", "Display type of a symbol"
|
104
|
-
# TODO: options, filter, limit, kind etc.. filter rbi
|
105
|
-
def types(file, line, col)
|
106
|
-
run do |client|
|
107
|
-
res = client.type_definitions(to_uri(file), line.to_i, col.to_i)
|
108
|
-
say("Type for `#{file}:#{line}:#{col}`:")
|
109
|
-
symbol_printer.print_list(res)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
no_commands do
|
114
|
-
def lsp_client
|
115
|
-
context_requiring_sorbet!
|
116
|
-
|
117
|
-
path = exec_path
|
118
|
-
client = Spoom::LSP::Client.new(
|
119
|
-
Spoom::Sorbet::BIN_PATH,
|
120
|
-
"--lsp",
|
121
|
-
"--enable-all-experimental-lsp-features",
|
122
|
-
"--disable-watchman",
|
123
|
-
path: path,
|
124
|
-
)
|
125
|
-
client.open(File.expand_path(path))
|
126
|
-
client
|
127
|
-
end
|
128
|
-
|
129
|
-
def symbol_printer
|
130
|
-
Spoom::LSP::SymbolPrinter.new(
|
131
|
-
indent_level: 2,
|
132
|
-
colors: options[:color],
|
133
|
-
prefix: "file://#{File.expand_path(exec_path)}",
|
134
|
-
)
|
135
|
-
end
|
136
|
-
|
137
|
-
def run(&block)
|
138
|
-
client = lsp_client
|
139
|
-
block.call(client)
|
140
|
-
rescue Spoom::LSP::Error::Diagnostics => err
|
141
|
-
say_error("Sorbet returned typechecking errors for `#{symbol_printer.clean_uri(err.uri)}`")
|
142
|
-
err.diagnostics.each do |d|
|
143
|
-
say_error("#{d.message} (#{d.code})", status: " #{d.range}")
|
144
|
-
end
|
145
|
-
exit(1)
|
146
|
-
rescue Spoom::LSP::Error::BadHeaders => err
|
147
|
-
say_error("Sorbet didn't answer correctly (#{err.message})")
|
148
|
-
exit(1)
|
149
|
-
rescue Spoom::LSP::Error => err
|
150
|
-
say_error(err.message)
|
151
|
-
exit(1)
|
152
|
-
ensure
|
153
|
-
begin
|
154
|
-
client&.close
|
155
|
-
rescue
|
156
|
-
# We can't do much if Sorbet refuse to close.
|
157
|
-
# We kill the parent process and let the child be killed.
|
158
|
-
exit(1)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def to_uri(path)
|
163
|
-
"file://" + File.join(File.expand_path(exec_path), path)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
data/lib/spoom/cli/run.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
# typed: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Spoom
|
5
|
-
module Cli
|
6
|
-
class Run < Thor
|
7
|
-
include Helper
|
8
|
-
|
9
|
-
default_task :tc
|
10
|
-
|
11
|
-
SORT_CODE = "code"
|
12
|
-
SORT_LOC = "loc"
|
13
|
-
SORT_ENUM = [SORT_CODE, SORT_LOC]
|
14
|
-
|
15
|
-
DEFAULT_FORMAT = "%C - %F:%L: %M"
|
16
|
-
|
17
|
-
desc "tc", "Run `srb tc`"
|
18
|
-
option :limit, type: :numeric, aliases: :l, desc: "Limit displayed errors"
|
19
|
-
option :code, type: :numeric, aliases: :c, desc: "Filter displayed errors by code"
|
20
|
-
option :sort, type: :string, aliases: :s, desc: "Sort errors", enum: SORT_ENUM, default: SORT_LOC
|
21
|
-
option :format, type: :string, aliases: :f, desc: "Format line output"
|
22
|
-
option :uniq, type: :boolean, aliases: :u, desc: "Remove duplicated lines"
|
23
|
-
option :count, type: :boolean, default: true, desc: "Show errors count"
|
24
|
-
option :sorbet, type: :string, desc: "Path to custom Sorbet bin"
|
25
|
-
option :sorbet_options, type: :string, default: "", desc: "Pass options to Sorbet"
|
26
|
-
def tc(*paths_to_select)
|
27
|
-
context = context_requiring_sorbet!
|
28
|
-
limit = options[:limit]
|
29
|
-
sort = options[:sort]
|
30
|
-
code = options[:code]
|
31
|
-
uniq = options[:uniq]
|
32
|
-
format = options[:format]
|
33
|
-
count = options[:count]
|
34
|
-
sorbet = options[:sorbet]
|
35
|
-
|
36
|
-
unless limit || code || sort
|
37
|
-
result = T.unsafe(context).srb_tc(
|
38
|
-
*options[:sorbet_options].split(" "),
|
39
|
-
capture_err: false,
|
40
|
-
sorbet_bin: sorbet,
|
41
|
-
)
|
42
|
-
|
43
|
-
say_error(result.err, status: nil, nl: false)
|
44
|
-
exit(result.status)
|
45
|
-
end
|
46
|
-
|
47
|
-
error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
|
48
|
-
result = T.unsafe(context).srb_tc(
|
49
|
-
*options[:sorbet_options].split(" "),
|
50
|
-
"--error-url-base=#{error_url_base}",
|
51
|
-
capture_err: true,
|
52
|
-
sorbet_bin: sorbet,
|
53
|
-
)
|
54
|
-
|
55
|
-
if result.status
|
56
|
-
say_error(result.err, status: nil, nl: false)
|
57
|
-
exit(0)
|
58
|
-
end
|
59
|
-
|
60
|
-
unless result.exit_code == 100
|
61
|
-
# Sorbet will return exit code 100 if there are type checking errors.
|
62
|
-
# If Sorbet returned something else, it means it didn't terminate normally.
|
63
|
-
say_error(result.err, status: nil, nl: false)
|
64
|
-
exit(1)
|
65
|
-
end
|
66
|
-
|
67
|
-
errors = Spoom::Sorbet::Errors::Parser.parse_string(result.err, error_url_base: error_url_base)
|
68
|
-
errors_count = errors.size
|
69
|
-
|
70
|
-
errors = errors.select { |e| e.code == code } if code
|
71
|
-
|
72
|
-
unless paths_to_select.empty?
|
73
|
-
errors.select! do |error|
|
74
|
-
paths_to_select.any? { |path_to_select| error.file&.start_with?(path_to_select) }
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
errors = case sort
|
79
|
-
when SORT_CODE
|
80
|
-
Spoom::Sorbet::Errors.sort_errors_by_code(errors)
|
81
|
-
when SORT_LOC
|
82
|
-
errors.sort
|
83
|
-
else
|
84
|
-
errors # preserve natural sort
|
85
|
-
end
|
86
|
-
|
87
|
-
errors = T.must(errors.slice(0, limit)) if limit
|
88
|
-
|
89
|
-
lines = errors.map { |e| format_error(e, format || DEFAULT_FORMAT) }
|
90
|
-
lines = lines.uniq if uniq
|
91
|
-
|
92
|
-
lines.each do |line|
|
93
|
-
say_error(line, status: nil)
|
94
|
-
end
|
95
|
-
|
96
|
-
if count
|
97
|
-
if errors_count == errors.size
|
98
|
-
say_error("Errors: #{errors_count}", status: nil)
|
99
|
-
else
|
100
|
-
say_error("Errors: #{errors.size} shown, #{errors_count} total", status: nil)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
exit(1)
|
105
|
-
rescue Spoom::Sorbet::Error::Segfault => error
|
106
|
-
say_error(<<~ERR, status: nil)
|
107
|
-
#{red("!!! Sorbet exited with code #{error.result.exit_code} - SEGFAULT !!!")}
|
108
|
-
|
109
|
-
This is most likely related to a bug in Sorbet.
|
110
|
-
ERR
|
111
|
-
|
112
|
-
exit(error.result.exit_code)
|
113
|
-
rescue Spoom::Sorbet::Error::Killed => error
|
114
|
-
say_error(<<~ERR, status: nil)
|
115
|
-
#{red("!!! Sorbet exited with code #{error.result.exit_code} - KILLED !!!")}
|
116
|
-
ERR
|
117
|
-
|
118
|
-
exit(error.result.exit_code)
|
119
|
-
end
|
120
|
-
|
121
|
-
no_commands do
|
122
|
-
def format_error(error, format)
|
123
|
-
line = format
|
124
|
-
line = line.gsub(/%C/, yellow(error.code.to_s))
|
125
|
-
line = line.gsub(/%F/, error.file)
|
126
|
-
line = line.gsub(/%L/, error.line.to_s)
|
127
|
-
line = line.gsub(/%M/, colorize_message(error.message))
|
128
|
-
line
|
129
|
-
end
|
130
|
-
|
131
|
-
def colorize_message(message)
|
132
|
-
return message unless color?
|
133
|
-
|
134
|
-
cyan = T.let(false, T::Boolean)
|
135
|
-
word = StringIO.new
|
136
|
-
message.chars.each do |c|
|
137
|
-
if c == "`"
|
138
|
-
cyan = !cyan
|
139
|
-
next
|
140
|
-
end
|
141
|
-
word << (cyan ? cyan(c) : red(c))
|
142
|
-
end
|
143
|
-
word.string
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|