spoom 1.2.4 → 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 +54 -55
- 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/indexer.rb +222 -224
- data/lib/spoom/deadcode/location.rb +2 -2
- data/lib/spoom/deadcode/plugins/action_mailer.rb +2 -2
- data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
- data/lib/spoom/deadcode/plugins/actionpack.rb +4 -6
- data/lib/spoom/deadcode/plugins/active_model.rb +8 -8
- data/lib/spoom/deadcode/plugins/active_record.rb +9 -12
- data/lib/spoom/deadcode/plugins/active_support.rb +11 -0
- data/lib/spoom/deadcode/plugins/base.rb +1 -1
- data/lib/spoom/deadcode/plugins/graphql.rb +4 -4
- data/lib/spoom/deadcode/plugins/namespaces.rb +2 -4
- data/lib/spoom/deadcode/plugins/ruby.rb +8 -17
- data/lib/spoom/deadcode/plugins/sorbet.rb +4 -10
- data/lib/spoom/deadcode/plugins.rb +1 -0
- data/lib/spoom/deadcode/remover.rb +209 -174
- data/lib/spoom/deadcode/send.rb +9 -10
- data/lib/spoom/deadcode/visitor.rb +755 -0
- data/lib/spoom/deadcode.rb +40 -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 +19 -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/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
|