spoom 1.2.4 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -55
  3. data/lib/spoom/cli/deadcode.rb +172 -0
  4. data/lib/spoom/cli/helper.rb +20 -0
  5. data/lib/spoom/cli/srb/bump.rb +200 -0
  6. data/lib/spoom/cli/srb/coverage.rb +224 -0
  7. data/lib/spoom/cli/srb/lsp.rb +159 -0
  8. data/lib/spoom/cli/srb/tc.rb +150 -0
  9. data/lib/spoom/cli/srb.rb +27 -0
  10. data/lib/spoom/cli.rb +72 -32
  11. data/lib/spoom/context/git.rb +2 -2
  12. data/lib/spoom/context/sorbet.rb +2 -2
  13. data/lib/spoom/deadcode/definition.rb +11 -0
  14. data/lib/spoom/deadcode/indexer.rb +222 -224
  15. data/lib/spoom/deadcode/location.rb +2 -2
  16. data/lib/spoom/deadcode/plugins/action_mailer.rb +2 -2
  17. data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
  18. data/lib/spoom/deadcode/plugins/actionpack.rb +4 -6
  19. data/lib/spoom/deadcode/plugins/active_model.rb +8 -8
  20. data/lib/spoom/deadcode/plugins/active_record.rb +9 -12
  21. data/lib/spoom/deadcode/plugins/active_support.rb +11 -0
  22. data/lib/spoom/deadcode/plugins/base.rb +1 -1
  23. data/lib/spoom/deadcode/plugins/graphql.rb +4 -4
  24. data/lib/spoom/deadcode/plugins/namespaces.rb +2 -4
  25. data/lib/spoom/deadcode/plugins/ruby.rb +8 -17
  26. data/lib/spoom/deadcode/plugins/sorbet.rb +4 -10
  27. data/lib/spoom/deadcode/plugins.rb +1 -0
  28. data/lib/spoom/deadcode/remover.rb +209 -174
  29. data/lib/spoom/deadcode/send.rb +9 -10
  30. data/lib/spoom/deadcode/visitor.rb +755 -0
  31. data/lib/spoom/deadcode.rb +40 -10
  32. data/lib/spoom/file_tree.rb +0 -16
  33. data/lib/spoom/sorbet/errors.rb +1 -1
  34. data/lib/spoom/sorbet/lsp/structures.rb +2 -2
  35. data/lib/spoom/version.rb +1 -1
  36. metadata +19 -15
  37. data/lib/spoom/cli/bump.rb +0 -198
  38. data/lib/spoom/cli/coverage.rb +0 -222
  39. data/lib/spoom/cli/lsp.rb +0 -168
  40. 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