spoom 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -80,7 +80,7 @@ module Spoom
80
80
 
81
81
  private
82
82
 
83
- sig { params(node: FileTree::Node, collected_nodes: T::Array[Node]).returns(T::Array[String]) }
83
+ sig { params(node: FileTree::Node, collected_nodes: T::Array[Node]).returns(T::Array[Node]) }
84
84
  def collect_nodes(node, collected_nodes = [])
85
85
  collected_nodes << node
86
86
  node.children.values.each { |child| collect_nodes(child, collected_nodes) }
data/lib/spoom/git.rb CHANGED
@@ -14,11 +14,12 @@ module Spoom
14
14
  return "", "Error: `#{path}` is not a directory.", false unless File.directory?(path)
15
15
  opts = {}
16
16
  opts[:chdir] = path
17
- _, o, e, s = Open3.popen3(*T.unsafe([command, *T.unsafe(arg), opts]))
17
+ i, o, e, s = Open3.popen3(*T.unsafe([command, *T.unsafe(arg), opts]))
18
18
  out = o.read.to_s
19
19
  o.close
20
20
  err = e.read.to_s
21
21
  e.close
22
+ i.close
22
23
  [out, err, T.cast(s.value, Process::Status).success?]
23
24
  end
24
25
 
data/lib/spoom/printer.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "colorize"
5
4
  require "stringio"
6
5
 
7
6
  module Spoom
data/lib/spoom/sorbet.rb CHANGED
@@ -11,73 +11,112 @@ require "open3"
11
11
 
12
12
  module Spoom
13
13
  module Sorbet
14
- extend T::Sig
14
+ CONFIG_PATH = "sorbet/config"
15
+ GEM_PATH = Gem::Specification.find_by_name("sorbet-static").full_gem_path
16
+ BIN_PATH = (Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s
15
17
 
16
- sig { params(arg: String, path: String, capture_err: T::Boolean).returns([String, T::Boolean]) }
17
- def self.srb(*arg, path: '.', capture_err: false)
18
- opts = {}
19
- opts[:chdir] = path
20
- out = T.let("", T.nilable(String))
21
- res = T.let(false, T::Boolean)
22
- if capture_err
23
- Open3.popen2e(["bundle", "exec", "srb", *arg].join(" "), opts) do |_, o, t|
24
- out = o.read
25
- res = T.cast(t.value, Process::Status).success?
26
- end
27
- else
28
- Open3.popen2(["bundle", "exec", "srb", *arg].join(" "), opts) do |_, o, t|
29
- out = o.read
30
- res = T.cast(t.value, Process::Status).success?
18
+ class << self
19
+ extend T::Sig
20
+
21
+ sig do
22
+ params(
23
+ arg: String,
24
+ path: String,
25
+ capture_err: T::Boolean,
26
+ sorbet_bin: T.nilable(String)
27
+ ).returns([String, T::Boolean])
28
+ end
29
+ def srb(*arg, path: '.', capture_err: false, sorbet_bin: nil)
30
+ if sorbet_bin
31
+ arg.prepend(sorbet_bin)
32
+ else
33
+ arg.prepend("bundle", "exec", "srb")
31
34
  end
35
+ T.unsafe(Spoom).exec(*arg, path: path, capture_err: capture_err)
32
36
  end
33
- [out || "", res]
34
- end
35
37
 
36
- sig { params(arg: String, path: String, capture_err: T::Boolean).returns([String, T::Boolean]) }
37
- def self.srb_tc(*arg, path: '.', capture_err: false)
38
- srb(*T.unsafe(["tc", *arg]), path: path, capture_err: capture_err)
39
- end
38
+ sig do
39
+ params(
40
+ arg: String,
41
+ path: String,
42
+ capture_err: T::Boolean,
43
+ sorbet_bin: T.nilable(String)
44
+ ).returns([String, T::Boolean])
45
+ end
46
+ def srb_tc(*arg, path: '.', capture_err: false, sorbet_bin: nil)
47
+ arg.prepend("tc") unless sorbet_bin
48
+ T.unsafe(self).srb(*arg, path: path, capture_err: capture_err, sorbet_bin: sorbet_bin)
49
+ end
40
50
 
41
- # List all files typechecked by Sorbet from its `config`
42
- sig { params(config: Config, path: String).returns(T::Array[String]) }
43
- def self.srb_files(config, path: '.')
44
- regs = config.ignore.map { |string| Regexp.new(Regexp.escape(string)) }
45
- exts = config.allowed_extensions.empty? ? ['.rb', '.rbi'] : config.allowed_extensions
46
- Dir.glob((Pathname.new(path) / "**/*{#{exts.join(',')}}").to_s).reject do |f|
47
- regs.any? { |re| re.match?(f) }
48
- end.sort
49
- end
51
+ # List all files typechecked by Sorbet from its `config`
52
+ sig { params(config: Config, path: String).returns(T::Array[String]) }
53
+ def srb_files(config, path: '.')
54
+ regs = config.ignore.map { |string| Regexp.new(Regexp.escape(string)) }
55
+ exts = config.allowed_extensions.empty? ? ['.rb', '.rbi'] : config.allowed_extensions
56
+ Dir.glob((Pathname.new(path) / "**/*{#{exts.join(',')}}").to_s).reject do |f|
57
+ regs.any? { |re| re.match?(f) }
58
+ end.sort
59
+ end
50
60
 
51
- sig { params(arg: String, path: String, capture_err: T::Boolean).returns(T.nilable(String)) }
52
- def self.srb_version(*arg, path: '.', capture_err: false)
53
- out, res = srb(*T.unsafe(["--version", *arg]), path: path, capture_err: capture_err)
54
- return nil unless res
55
- out.split(" ")[2]
56
- end
61
+ sig do
62
+ params(
63
+ arg: String,
64
+ path: String,
65
+ capture_err: T::Boolean,
66
+ sorbet_bin: T.nilable(String)
67
+ ).returns(T.nilable(String))
68
+ end
69
+ def srb_version(*arg, path: '.', capture_err: false, sorbet_bin: nil)
70
+ out, res = T.unsafe(self).srb_tc(
71
+ "--no-config",
72
+ "--version",
73
+ *arg,
74
+ path: path,
75
+ capture_err: capture_err,
76
+ sorbet_bin: sorbet_bin
77
+ )
78
+ return nil unless res
79
+ out.split(" ")[2]
80
+ end
57
81
 
58
- sig { params(arg: String, path: String, capture_err: T::Boolean).returns(T.nilable(T::Hash[String, Integer])) }
59
- def self.srb_metrics(*arg, path: '.', capture_err: false)
60
- metrics_file = "metrics.tmp"
61
- metrics_path = "#{path}/#{metrics_file}"
62
- srb_tc(*T.unsafe(["--metrics-file=#{metrics_file}", *arg]), path: path, capture_err: capture_err)
63
- if File.exist?(metrics_path)
64
- metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
65
- File.delete(metrics_path)
66
- return metrics
82
+ sig do
83
+ params(
84
+ arg: String,
85
+ path: String,
86
+ capture_err: T::Boolean,
87
+ sorbet_bin: T.nilable(String)
88
+ ).returns(T.nilable(T::Hash[String, Integer]))
89
+ end
90
+ def srb_metrics(*arg, path: '.', capture_err: false, sorbet_bin: nil)
91
+ metrics_file = "metrics.tmp"
92
+ metrics_path = "#{path}/#{metrics_file}"
93
+ T.unsafe(self).srb_tc(
94
+ "--metrics-file",
95
+ metrics_file,
96
+ *arg,
97
+ path: path,
98
+ capture_err: capture_err,
99
+ sorbet_bin: sorbet_bin
100
+ )
101
+ if File.exist?(metrics_path)
102
+ metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
103
+ File.delete(metrics_path)
104
+ return metrics
105
+ end
106
+ nil
67
107
  end
68
- nil
69
- end
70
108
 
71
- # Get `gem` version from the `Gemfile.lock` content
72
- #
73
- # Returns `nil` if `gem` cannot be found in the Gemfile.
74
- sig { params(gem: String, path: String).returns(T.nilable(String)) }
75
- def self.version_from_gemfile_lock(gem: 'sorbet', path: '.')
76
- gemfile_path = "#{path}/Gemfile.lock"
77
- return nil unless File.exist?(gemfile_path)
78
- content = File.read(gemfile_path).match(/^ #{gem} \(.*(\d+\.\d+\.\d+).*\)/)
79
- return nil unless content
80
- content[1]
109
+ # Get `gem` version from the `Gemfile.lock` content
110
+ #
111
+ # Returns `nil` if `gem` cannot be found in the Gemfile.
112
+ sig { params(gem: String, path: String).returns(T.nilable(String)) }
113
+ def version_from_gemfile_lock(gem: 'sorbet', path: '.')
114
+ gemfile_path = "#{path}/Gemfile.lock"
115
+ return nil unless File.exist?(gemfile_path)
116
+ content = File.read(gemfile_path).match(/^ #{gem} \(.*(\d+\.\d+\.\d+).*\)/)
117
+ return nil unless content
118
+ content[1]
119
+ end
81
120
  end
82
121
  end
83
122
  end
@@ -36,6 +36,36 @@ module Spoom
36
36
  @allowed_extensions = T.let([], T::Array[String])
37
37
  end
38
38
 
39
+ sig { returns(Config) }
40
+ def copy
41
+ new_config = Sorbet::Config.new
42
+ new_config.paths.concat(@paths)
43
+ new_config.ignore.concat(@ignore)
44
+ new_config.allowed_extensions.concat(@allowed_extensions)
45
+ new_config
46
+ end
47
+
48
+ # Returns self as a string of options that can be passed to Sorbet
49
+ #
50
+ # Example:
51
+ # ~~~rb
52
+ # config = Sorbet::Config.new
53
+ # config.paths << "/foo"
54
+ # config.paths << "/bar"
55
+ # config.ignore << "/baz"
56
+ # config.allowed_extensions << ".rb"
57
+ #
58
+ # puts config.options_string # "/foo /bar --ignore /baz --allowed-extension .rb"
59
+ # ~~~
60
+ sig { returns(String) }
61
+ def options_string
62
+ opts = []
63
+ opts.concat(paths)
64
+ opts.concat(ignore.map { |p| "--ignore #{p}" })
65
+ opts.concat(allowed_extensions.map { |ext| "--allowed-extension #{ext}" })
66
+ opts.join(" ")
67
+ end
68
+
39
69
  class << self
40
70
  extend T::Sig
41
71
 
@@ -4,6 +4,8 @@
4
4
  module Spoom
5
5
  module Sorbet
6
6
  module Errors
7
+ extend T::Sig
8
+
7
9
  # Parse errors from Sorbet output
8
10
  class Parser
9
11
  extend T::Sig
@@ -123,6 +125,7 @@ module Spoom
123
125
  @more = more
124
126
  end
125
127
 
128
+ # By default errors are sorted by location
126
129
  sig { params(other: T.untyped).returns(Integer) }
127
130
  def <=>(other)
128
131
  return 0 unless other.is_a?(Error)
@@ -134,6 +137,11 @@ module Spoom
134
137
  "#{file}:#{line}: #{message} (#{code})"
135
138
  end
136
139
  end
140
+
141
+ sig { params(errors: T::Array[Error]).returns(T::Array[Error]) }
142
+ def self.sort_errors_by_code(errors)
143
+ errors.sort_by { |e| [e.code, e.file, e.line, e.message] }
144
+ end
137
145
  end
138
146
  end
139
147
  end
@@ -11,13 +11,9 @@ require_relative 'lsp/errors'
11
11
  module Spoom
12
12
  module LSP
13
13
  class Client
14
- def initialize(sorbet_cmd, *sorbet_args, path: ".")
14
+ def initialize(sorbet_bin, *sorbet_args, path: ".")
15
15
  @id = 0
16
- Bundler.with_clean_env do
17
- opts = {}
18
- opts[:chdir] = path
19
- @in, @out, @err, @status = Open3.popen3([sorbet_cmd, *sorbet_args].join(" "), opts)
20
- end
16
+ @in, @out, @err, @status = T.unsafe(Open3).popen3(sorbet_bin, *sorbet_args, chdir: path)
21
17
  end
22
18
 
23
19
  def next_id
@@ -56,14 +56,14 @@ module Spoom
56
56
  sig { params(path: T.any(String, Pathname)).returns(T.nilable(String)) }
57
57
  def self.file_strictness(path)
58
58
  return nil unless File.exist?(path)
59
- content = File.read(path)
59
+ content = File.read(path, encoding: Encoding::ASCII_8BIT)
60
60
  strictness_in_content(content)
61
61
  end
62
62
 
63
63
  # changes the sigil in the file at the passed path to the specified new strictness
64
64
  sig { params(path: T.any(String, Pathname), new_strictness: String).returns(T::Boolean) }
65
65
  def self.change_sigil_in_file(path, new_strictness)
66
- content = File.read(path)
66
+ content = File.read(path, encoding: Encoding::ASCII_8BIT)
67
67
  new_content = update_sigil(content, new_strictness)
68
68
 
69
69
  File.write(path, new_content)
@@ -87,7 +87,7 @@ module Spoom
87
87
  extension: String
88
88
  ).returns(T::Array[String])
89
89
  end
90
- def self.files_with_sigil_strictness(directory, strictness, extension = ".rb")
90
+ def self.files_with_sigil_strictness(directory, strictness, extension: ".rb")
91
91
  paths = Dir.glob("#{File.expand_path(directory)}/**/*#{extension}").sort.uniq
92
92
  paths.filter do |path|
93
93
  file_strictness(path) == strictness
@@ -76,6 +76,15 @@ module Spoom
76
76
  Spoom::Git.exec("GIT_COMMITTER_DATE=\"#{date}\" git commit -m '#{message}' --date '#{date}'", path: path)
77
77
  end
78
78
 
79
+ # Run `bundle install` in this project
80
+ sig { returns([T.nilable(String), T.nilable(String), T::Boolean]) }
81
+ def bundle_install
82
+ opts = {}
83
+ opts[:chdir] = path
84
+ out, err, status = Open3.capture3("bundle", "install", opts)
85
+ [out, err, status.success?]
86
+ end
87
+
79
88
  # Run a command with `bundle exec` in this project
80
89
  sig { params(cmd: String, args: String).returns([T.nilable(String), T.nilable(String), T::Boolean]) }
81
90
  def bundle_exec(cmd, *args)
data/lib/spoom/version.rb CHANGED
@@ -1,6 +1,6 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.0.5"
5
+ VERSION = "1.1.0"
6
6
  end
@@ -0,0 +1,8 @@
1
+ <div class="card">
2
+ <% if title %>
3
+ <h5 class="card-header"><%= title %></h5>
4
+ <% end %>
5
+ <div class="card-body">
6
+ <%= body %>
7
+ </div>
8
+ </div>
@@ -0,0 +1,22 @@
1
+ <div class="card">
2
+ <% if title %>
3
+ <h5 class="card-header"><%= title %></h5>
4
+ <% end %>
5
+ <div class="card-body">
6
+ <div class="container-fluid">
7
+ <div class="row justify-content-md-center">
8
+ <div class="col-12 col-sm-4 col-xl-3">
9
+ <%= pie_sigils.html %>
10
+ </div>
11
+ <div class="d-none d-xl-block col-xl-1"></div>
12
+ <div class="col-12 col-sm-4 col-xl-3">
13
+ <%= pie_calls.html %>
14
+ </div>
15
+ <div class="d-none d-xl-block col-xl-1"></div>
16
+ <div class="col-12 col-sm-4 col-xl-3">
17
+ <%= pie_sigs.html %>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
@@ -0,0 +1,50 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta http-equiv="x-ua-compatible" content="ie=edge" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+
8
+ <title><%= title %></title>
9
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
10
+
11
+ <style>
12
+ .card {
13
+ margin: 30px 0 0 0;
14
+ }
15
+
16
+ .footer {
17
+ color: #6c757d;
18
+ text-align: center;
19
+ margin: 30px 0 0 0;
20
+ }
21
+
22
+ <%= header_style %>
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
27
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
28
+ <script src="https://d3js.org/d3.v4.min.js"></script>
29
+
30
+ <script>
31
+ <%= header_script %>
32
+ </script>
33
+
34
+ <div class="px-5 py-4 container-fluid">
35
+ <div class="row justify-content-center">
36
+ <div class="col-xs-12 col-md-12 col-lg-9 col-xl-8">
37
+ <div class="header">
38
+ <%= header_html %>
39
+ </div>
40
+ <div class="body">
41
+ <%= body_html %>
42
+ </div>
43
+ <div class="footer">
44
+ <%= footer_html %>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </body>
50
+ </html>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-19 00:00:00.000000000 Z
11
+ date: 2021-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: sorbet
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 0.5.5
75
+ version: 0.5.6347
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 0.5.5
82
+ version: 0.5.6347
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: thor
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -128,7 +128,6 @@ files:
128
128
  - lib/spoom/cli/helper.rb
129
129
  - lib/spoom/cli/lsp.rb
130
130
  - lib/spoom/cli/run.rb
131
- - lib/spoom/config.rb
132
131
  - lib/spoom/coverage.rb
133
132
  - lib/spoom/coverage/d3.rb
134
133
  - lib/spoom/coverage/d3/base.rb
@@ -152,6 +151,9 @@ files:
152
151
  - lib/spoom/test_helpers/project.rb
153
152
  - lib/spoom/timeline.rb
154
153
  - lib/spoom/version.rb
154
+ - templates/card.erb
155
+ - templates/card_snapshot.erb
156
+ - templates/page.erb
155
157
  homepage: https://github.com/Shopify/spoom
156
158
  licenses:
157
159
  - MIT