spoom 1.1.12 → 1.1.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed6d04e01efc1c6c8c00df77ad340f189a277aebf3afbd64edbd89f6145541f2
4
- data.tar.gz: 1935557277bcfdb54b4ab0d389ab5a72fb5b2d00a6631f6c787dae9d83dac7c4
3
+ metadata.gz: 5cad6017cf76302bc359d170fce6c2e33c4ff5923a564b26e2f270b42c102e3d
4
+ data.tar.gz: 734654483394f8520346d0ddab7872e1c7f42de4eed7523e503264f8045bf2c9
5
5
  SHA512:
6
- metadata.gz: 781d3bb6443c518fc1d4334e5c4ac17a09372ba9115f024bcdceb5cf15af4d4524d1e4cb465963ce5d23aaa07ebd5ab2114ebe81b4eea3fffe27942295e21b9f
7
- data.tar.gz: 3427fdfe5cd483609fbbe6618de2f8a536f8bf0727a6865e44c2c4c9b74f9303c0fdcfdefd44bdd547d16e6766f0f09fa49c1e8cd991d0efcffaa37f3c9c7609
6
+ metadata.gz: a67da9fa2811e15d5bfc5f554a2b1cb23bed4a0744617d1168b3d3d4c08f4f6828b207142996fb8cebc57ca8732d901e3e390c43939eaa504a2ed1cc1fe611b4
7
+ data.tar.gz: 1b98af8f37acd554f908fdde1257f9c53eaab0a37b21847e619caee5cdda44e9f06e248545ec247cb43c6ac434d5e00bce4b6731eb434c62bd3a60a72c67313f
@@ -71,7 +71,7 @@ module Spoom
71
71
  say("\n")
72
72
 
73
73
  if files_to_bump.empty?
74
- say("No file to bump from `#{from}` to `#{to}`")
74
+ say("No files to bump from `#{from}` to `#{to}`")
75
75
  exit(0)
76
76
  end
77
77
 
@@ -88,7 +88,7 @@ module Spoom
88
88
  "--error-url-base=#{error_url_base}",
89
89
  path: exec_path,
90
90
  capture_err: true,
91
- sorbet_bin: options[:sorbet]
91
+ sorbet_bin: options[:sorbet],
92
92
  )
93
93
 
94
94
  check_sorbet_segfault(result.exit_code) do
@@ -132,13 +132,14 @@ module Spoom
132
132
 
133
133
  no_commands do
134
134
  def print_changes(files, command:, from: "false", to: "true", dry: false, path: File.expand_path("."))
135
- if files.empty?
136
- say("No file to bump from `#{from}` to `#{to}`")
135
+ files_count = files.size
136
+ if files_count.zero?
137
+ say("No files to bump from `#{from}` to `#{to}`")
137
138
  return
138
139
  end
139
140
  message = StringIO.new
140
141
  message << (dry ? "Can bump" : "Bumped")
141
- message << " `#{files.size}` file#{"s" if files.size > 1}"
142
+ message << " `#{files_count}` file#{"s" if files_count > 1}"
142
143
  message << " from `#{from}` to `#{to}`:"
143
144
  say(message.string)
144
145
  files.each do |file|
@@ -146,7 +147,7 @@ module Spoom
146
147
  say(" + #{file_path}")
147
148
  end
148
149
  if dry && command
149
- say("\nRun `#{command}` to bump them")
150
+ say("\nRun `#{command}` to bump #{files_count > 1 ? "them" : "it"}")
150
151
  elsif dry
151
152
  say("\nRun `spoom bump --from #{from} --to #{to}` locally then `commit the changes` and `push them`")
152
153
  end
@@ -46,7 +46,7 @@ module Spoom
46
46
  sorbet = options[:sorbet]
47
47
 
48
48
  ref_before = Spoom::Git.current_branch
49
- ref_before = Spoom::Git.last_commit(path: path) unless ref_before
49
+ ref_before = Spoom::Git.last_commit(path: path)&.sha unless ref_before
50
50
  unless ref_before
51
51
  say_error("Not in a git repository")
52
52
  say_error("\nSpoom needs to checkout into your previous commits to build the timeline.", status: nil)
@@ -71,9 +71,9 @@ module Spoom
71
71
  to = parse_time(options[:to], "--to")
72
72
 
73
73
  unless from
74
- intro_sha = Spoom::Git.sorbet_intro_commit(path: path)
75
- intro_sha = T.must(intro_sha) # we know it's in there since in_sorbet_project!
76
- from = Spoom::Git.commit_time(intro_sha, path: path)
74
+ intro_commit = Spoom::Git.sorbet_intro_commit(path: path)
75
+ intro_commit = T.must(intro_commit) # we know it's in there since in_sorbet_project!
76
+ from = intro_commit.time
77
77
  end
78
78
 
79
79
  timeline = Spoom::Timeline.new(from, to, path: path)
@@ -84,16 +84,15 @@ module Spoom
84
84
  exit(1)
85
85
  end
86
86
 
87
- ticks.each_with_index do |sha, i|
88
- date = Spoom::Git.commit_time(sha, path: path)
89
- say("Analyzing commit `#{sha}` - #{date&.strftime("%F")} (#{i + 1} / #{ticks.size})")
87
+ ticks.each_with_index do |commit, i|
88
+ say("Analyzing commit `#{commit.sha}` - #{commit.time.strftime("%F")} (#{i + 1} / #{ticks.size})")
90
89
 
91
- Spoom::Git.checkout(sha, path: path)
90
+ Spoom::Git.checkout(commit.sha, path: path)
92
91
 
93
92
  snapshot = T.let(nil, T.nilable(Spoom::Coverage::Snapshot))
94
93
  if options[:bundle_install]
95
94
  Bundler.with_unbundled_env do
96
- next unless bundle_install(path, sha)
95
+ next unless bundle_install(path, commit.sha)
97
96
 
98
97
  snapshot = Spoom::Coverage.snapshot(path: path, sorbet_bin: sorbet)
99
98
  end
@@ -107,7 +106,7 @@ module Spoom
107
106
 
108
107
  next unless save_dir
109
108
 
110
- file = "#{save_dir}/#{sha}.json"
109
+ file = "#{save_dir}/#{commit.sha}.json"
111
110
  File.write(file, snapshot.to_json)
112
111
  say(" Snapshot data saved under `#{file}`\n\n")
113
112
  end
@@ -148,7 +147,7 @@ module Spoom
148
147
  false: options[:color_false],
149
148
  true: options[:color_true],
150
149
  strict: options[:color_strict],
151
- strong: options[:color_strong]
150
+ strong: options[:color_strong],
152
151
  )
153
152
 
154
153
  report = Spoom::Coverage.report(snapshots, palette: palette, path: exec_path)
@@ -33,7 +33,7 @@ module Spoom
33
33
  params(
34
34
  message: String,
35
35
  status: T.nilable(String),
36
- nl: T::Boolean
36
+ nl: T::Boolean,
37
37
  ).void
38
38
  end
39
39
  def say_error(message, status: "Error", nl: true)
@@ -61,7 +61,7 @@ module Spoom
61
61
  say_error(
62
62
  "not in a Sorbet project (`#{sorbet_config_file}` not found)\n\n" \
63
63
  "When running spoom from another path than the project's root, " \
64
- "use `--path PATH` to specify the path to the root."
64
+ "use `--path PATH` to specify the path to the root.",
65
65
  )
66
66
  Kernel.exit(1)
67
67
  end
data/lib/spoom/cli/lsp.rb CHANGED
@@ -118,7 +118,7 @@ module Spoom
118
118
  "--lsp",
119
119
  "--enable-all-experimental-lsp-features",
120
120
  "--disable-watchman",
121
- path: path
121
+ path: path,
122
122
  )
123
123
  client.open(File.expand_path(path))
124
124
  client
@@ -128,7 +128,7 @@ module Spoom
128
128
  Spoom::LSP::SymbolPrinter.new(
129
129
  indent_level: 2,
130
130
  colors: options[:color],
131
- prefix: "file://#{File.expand_path(exec_path)}"
131
+ prefix: "file://#{File.expand_path(exec_path)}",
132
132
  )
133
133
  end
134
134
 
data/lib/spoom/cli/run.rb CHANGED
@@ -40,7 +40,7 @@ module Spoom
40
40
  *options[:sorbet_options].split(" "),
41
41
  path: path,
42
42
  capture_err: false,
43
- sorbet_bin: sorbet
43
+ sorbet_bin: sorbet,
44
44
  )
45
45
 
46
46
  check_sorbet_segfault(result.code)
@@ -54,7 +54,7 @@ module Spoom
54
54
  "--error-url-base=#{error_url_base}",
55
55
  path: path,
56
56
  capture_err: true,
57
- sorbet_bin: sorbet
57
+ sorbet_bin: sorbet,
58
58
  )
59
59
 
60
60
  check_sorbet_segfault(result.exit_code)
@@ -125,14 +125,14 @@ module Spoom
125
125
 
126
126
  sig { params(id: String, title: String, snapshot: Snapshot).void }
127
127
  def initialize(id, title, snapshot)
128
- super(id, title, snapshot.sigils.select { |_k, v| v })
128
+ super(id, title, snapshot.sigils_excluding_rbis.select { |_k, v| v })
129
129
  end
130
130
 
131
131
  sig { override.returns(String) }
132
132
  def tooltip
133
133
  <<~JS
134
134
  function tooltip_#{id}(d) {
135
- tooltipPie(d, "typed: " + d.data.key, "files", sum_#{id});
135
+ tooltipPie(d, "typed: " + d.data.key, "files excluding RBIs", sum_#{id});
136
136
  }
137
137
  JS
138
138
  end
@@ -161,14 +161,21 @@ module Spoom
161
161
 
162
162
  sig { params(id: String, title: String, snapshot: Snapshot).void }
163
163
  def initialize(id, title, snapshot)
164
- super(id, title, { true: snapshot.methods_with_sig, false: snapshot.methods_without_sig })
164
+ super(
165
+ id,
166
+ title,
167
+ { true: snapshot.methods_with_sig_excluding_rbis, false: snapshot.methods_without_sig_excluding_rbis }
168
+ )
165
169
  end
166
170
 
167
171
  sig { override.returns(String) }
168
172
  def tooltip
169
173
  <<~JS
170
174
  function tooltip_#{id}(d) {
171
- tooltipPie(d, (d.data.key == "true" ? " with" : " without") + " a signature", "methods", sum_#{id})
175
+ tooltipPie(
176
+ d,
177
+ (d.data.key == "true" ? " with" : " without") + " a signature", "methods excluding RBIs", sum_#{id}
178
+ )
172
179
  }
173
180
  JS
174
181
  end
@@ -268,7 +268,7 @@ module Spoom
268
268
  d3.min(data_#{id}, (d) => parseVersion(d.runtime))]) - 0.01",
269
269
  max: "d3.max([d3.max(data_#{id}, (d) => parseVersion(d.static)),
270
270
  d3.max(data_#{id}, (d) => parseVersion(d.runtime))]) + 0.01",
271
- ticks: "ticks(8)"
271
+ ticks: "ticks(8)",
272
272
  )}
273
273
  #{line(y: "parseVersion(d.runtime)", color: "#e83e8c", curve: "curveStepAfter")}
274
274
  #{line(y: "parseVersion(d.static)", color: "#007bff", curve: "curveStepAfter")}
@@ -314,7 +314,7 @@ module Spoom
314
314
  #{y_scale(
315
315
  min: "0",
316
316
  max: "d3.max(data_#{id}, (d) => d.runtime)",
317
- ticks: "ticks(10)"
317
+ ticks: "ticks(10)",
318
318
  )}
319
319
  #{area(y: "d.runtime")}
320
320
  #{line(y: "d.runtime")}
@@ -428,8 +428,8 @@ module Spoom
428
428
  {
429
429
  timestamp: snapshot.commit_timestamp,
430
430
  commit: snapshot.commit_sha,
431
- total: snapshot.files,
432
- values: snapshot.sigils,
431
+ total: snapshot.files - snapshot.rbi_files,
432
+ values: snapshot.sigils_excluding_rbis,
433
433
  }
434
434
  end
435
435
  super(id, data, keys)
@@ -439,7 +439,7 @@ module Spoom
439
439
  def tooltip
440
440
  <<~JS
441
441
  function tooltip_#{id}(d) {
442
- tooltipTimeline(d, "files");
442
+ tooltipTimeline(d, "files excluding RBIs");
443
443
  }
444
444
  JS
445
445
  end
@@ -482,8 +482,11 @@ module Spoom
482
482
  {
483
483
  timestamp: snapshot.commit_timestamp,
484
484
  commit: snapshot.commit_sha,
485
- total: snapshot.methods_with_sig + snapshot.methods_without_sig,
486
- values: { true: snapshot.methods_with_sig, false: snapshot.methods_without_sig },
485
+ total: snapshot.methods_with_sig_excluding_rbis + snapshot.methods_without_sig_excluding_rbis,
486
+ values: {
487
+ true: snapshot.methods_with_sig_excluding_rbis,
488
+ false: snapshot.methods_without_sig_excluding_rbis,
489
+ },
487
490
  }
488
491
  end
489
492
  super(id, data, keys)
@@ -493,7 +496,7 @@ module Spoom
493
496
  def tooltip
494
497
  <<~JS
495
498
  function tooltip_#{id}(d) {
496
- tooltipTimeline(d, "methods");
499
+ tooltipTimeline(d, "methods excluding RBIs");
497
500
  }
498
501
  JS
499
502
  end
@@ -22,6 +22,9 @@ module Spoom
22
22
  prop :calls_untyped, Integer, default: 0
23
23
  prop :calls_typed, Integer, default: 0
24
24
  prop :sigils, T::Hash[String, Integer], default: Hash.new(0)
25
+ prop :methods_with_sig_excluding_rbis, Integer, default: 0
26
+ prop :methods_without_sig_excluding_rbis, Integer, default: 0
27
+ prop :sigils_excluding_rbis, T::Hash[String, Integer], default: Hash.new(0)
25
28
 
26
29
  # The strictness name as found in the Sorbet metrics file
27
30
  STRICTNESSES = T.let(["ignore", "false", "true", "strict", "strong", "stdlib"].freeze, T::Array[String])
@@ -63,6 +66,8 @@ module Spoom
63
66
  snapshot.methods_without_sig = obj.fetch("methods_without_sig", 0)
64
67
  snapshot.calls_typed = obj.fetch("calls_typed", 0)
65
68
  snapshot.calls_untyped = obj.fetch("calls_untyped", 0)
69
+ snapshot.methods_with_sig_excluding_rbis = obj.fetch("methods_with_sig_excluding_rbis", 0)
70
+ snapshot.methods_without_sig_excluding_rbis = obj.fetch("methods_without_sig_excluding_rbis", 0)
66
71
 
67
72
  sigils = obj.fetch("sigils", {})
68
73
  if sigils
@@ -73,6 +78,15 @@ module Spoom
73
78
  end
74
79
  end
75
80
 
81
+ sigils_excluding_rbis = obj.fetch("sigils_excluding_rbis", {})
82
+ if sigils_excluding_rbis
83
+ Snapshot::STRICTNESSES.each do |strictness|
84
+ next unless sigils_excluding_rbis.key?(strictness)
85
+
86
+ snapshot.sigils_excluding_rbis[strictness] = sigils_excluding_rbis[strictness]
87
+ end
88
+ end
89
+
76
90
  snapshot
77
91
  end
78
92
  end
@@ -84,6 +98,7 @@ module Spoom
84
98
  sig { params(snapshot: Snapshot).void }
85
99
  def print_snapshot(snapshot)
86
100
  methods = snapshot.methods_with_sig + snapshot.methods_without_sig
101
+ methods_excluding_rbis = snapshot.methods_with_sig_excluding_rbis + snapshot.methods_without_sig_excluding_rbis
87
102
  calls = snapshot.calls_typed + snapshot.calls_untyped
88
103
 
89
104
  if snapshot.version_static || snapshot.version_runtime
@@ -93,10 +108,12 @@ module Spoom
93
108
  end
94
109
  printl("Content:")
95
110
  indent
96
- printl("files: #{snapshot.files} (including #{snapshot.rbi_files} RBIs)")
111
+ printl("files: #{snapshot.files}")
112
+ printl("files excluding rbis: #{snapshot.files - snapshot.rbi_files}")
97
113
  printl("modules: #{snapshot.modules}")
98
114
  printl("classes: #{snapshot.classes - snapshot.singleton_classes}")
99
115
  printl("methods: #{methods}")
116
+ printl("methods excluding rbis: #{methods_excluding_rbis}")
100
117
  dedent
101
118
  printn
102
119
  printl("Sigils:")
@@ -109,6 +126,13 @@ module Spoom
109
126
  }
110
127
  print_map(methods_map, methods)
111
128
  printn
129
+ printl("Methods excluding RBIs")
130
+ methods_excluding_rbis_map = {
131
+ "with signature" => snapshot.methods_with_sig_excluding_rbis,
132
+ "without signature" => snapshot.methods_without_sig_excluding_rbis,
133
+ }
134
+ print_map(methods_excluding_rbis_map, methods_excluding_rbis)
135
+ printn
112
136
  printl("Calls:")
113
137
  calls_map = {
114
138
  "typed" => snapshot.calls_typed,
@@ -19,24 +19,35 @@ module Spoom
19
19
 
20
20
  new_config = config.copy
21
21
  new_config.allowed_extensions.reject! { |ext| !rbi && ext == ".rbi" }
22
-
23
- metrics = Spoom::Sorbet.srb_metrics(
22
+ flags = [
24
23
  "--no-config",
25
24
  "--no-error-sections",
26
25
  "--no-error-count",
27
26
  "--isolate-error-code=0",
28
27
  new_config.options_string,
28
+ ]
29
+
30
+ metrics = Spoom::Sorbet.srb_metrics(
31
+ *flags,
32
+ path: path,
33
+ capture_err: true,
34
+ sorbet_bin: sorbet_bin,
35
+ )
36
+ # Collect extra information using a different configuration
37
+ flags << "--ignore sorbet/rbi/"
38
+ metrics_without_rbis = Spoom::Sorbet.srb_metrics(
39
+ *flags,
29
40
  path: path,
30
41
  capture_err: true,
31
- sorbet_bin: sorbet_bin
42
+ sorbet_bin: sorbet_bin,
32
43
  )
33
44
 
34
45
  snapshot = Snapshot.new
35
46
  return snapshot unless metrics
36
47
 
37
- sha = Spoom::Git.last_commit(path: path)
38
- snapshot.commit_sha = sha
39
- snapshot.commit_timestamp = Spoom::Git.commit_timestamp(sha, path: path).to_i if sha
48
+ last_commit = Spoom::Git.last_commit(path: path)
49
+ snapshot.commit_sha = last_commit&.sha
50
+ snapshot.commit_timestamp = last_commit&.timestamp
40
51
 
41
52
  snapshot.files = metrics.fetch("types.input.files", 0)
42
53
  snapshot.modules = metrics.fetch("types.input.modules.total", 0)
@@ -50,10 +61,20 @@ module Spoom
50
61
  snapshot.duration += metrics.fetch("run.utilization.system_time.us", 0)
51
62
  snapshot.duration += metrics.fetch("run.utilization.user_time.us", 0)
52
63
 
53
- Snapshot::STRICTNESSES.each do |strictness|
54
- next unless metrics.key?("types.input.files.sigil.#{strictness}")
64
+ if metrics_without_rbis
65
+ snapshot.methods_with_sig_excluding_rbis = metrics_without_rbis.fetch("types.sig.count", 0)
66
+ snapshot.methods_without_sig_excluding_rbis = metrics_without_rbis.fetch("types.input.methods.total",
67
+ 0) - snapshot.methods_with_sig_excluding_rbis
68
+ end
55
69
 
56
- snapshot.sigils[strictness] = T.must(metrics["types.input.files.sigil.#{strictness}"])
70
+ Snapshot::STRICTNESSES.each do |strictness|
71
+ if metrics.key?("types.input.files.sigil.#{strictness}")
72
+ snapshot.sigils[strictness] = T.must(metrics["types.input.files.sigil.#{strictness}"])
73
+ end
74
+ if metrics_without_rbis&.key?("types.input.files.sigil.#{strictness}")
75
+ snapshot.sigils_excluding_rbis[strictness] =
76
+ T.must(metrics_without_rbis["types.input.files.sigil.#{strictness}"])
77
+ end
57
78
  end
58
79
 
59
80
  snapshot.version_static = Spoom::Sorbet.version_from_gemfile_lock(gem: "sorbet-static", path: path)
@@ -68,15 +89,14 @@ module Spoom
68
89
  sig { params(snapshots: T::Array[Snapshot], palette: D3::ColorPalette, path: String).returns(Report) }
69
90
  def report(snapshots, palette:, path: ".")
70
91
  intro_commit = Git.sorbet_intro_commit(path: path)
71
- intro_date = intro_commit ? Git.commit_time(intro_commit, path: path) : nil
72
92
 
73
93
  Report.new(
74
94
  project_name: File.basename(File.expand_path(path)),
75
95
  palette: palette,
76
96
  snapshots: snapshots,
77
97
  sigils_tree: sigils_tree(path: path),
78
- sorbet_intro_commit: intro_commit,
79
- sorbet_intro_date: intro_date,
98
+ sorbet_intro_commit: intro_commit&.sha,
99
+ sorbet_intro_date: intro_commit&.time,
80
100
  )
81
101
  end
82
102
 
@@ -65,7 +65,7 @@ module Spoom
65
65
  out: T.any(IO, StringIO),
66
66
  show_strictness: T::Boolean,
67
67
  colors: T::Boolean,
68
- indent_level: Integer
68
+ indent_level: Integer,
69
69
  ).void
70
70
  end
71
71
  def print(out: $stdout, show_strictness: true, colors: true, indent_level: 0)
@@ -74,7 +74,7 @@ module Spoom
74
74
  out: out,
75
75
  show_strictness: show_strictness,
76
76
  colors: colors,
77
- indent_level: indent_level
77
+ indent_level: indent_level,
78
78
  )
79
79
  printer.print_tree
80
80
  end
@@ -126,7 +126,7 @@ module Spoom
126
126
  out: T.any(IO, StringIO),
127
127
  show_strictness: T::Boolean,
128
128
  colors: T::Boolean,
129
- indent_level: Integer
129
+ indent_level: Integer,
130
130
  ).void
131
131
  end
132
132
  def initialize(tree:, out: $stdout, show_strictness: true, colors: true, indent_level: 0)
data/lib/spoom/git.rb CHANGED
@@ -6,6 +6,18 @@ require "time"
6
6
  module Spoom
7
7
  # Execute git commands
8
8
  module Git
9
+ class Commit < T::Struct
10
+ extend T::Sig
11
+
12
+ const :sha, String
13
+ const :time, Time
14
+
15
+ sig { returns(Integer) }
16
+ def timestamp
17
+ time.to_i
18
+ end
19
+ end
20
+
9
21
  class << self
10
22
  extend T::Sig
11
23
 
@@ -16,7 +28,7 @@ module Spoom
16
28
  out: "",
17
29
  err: "Error: `#{path}` is not a directory.",
18
30
  status: false,
19
- exit_code: 1
31
+ exit_code: 1,
20
32
  ) unless File.directory?(path)
21
33
 
22
34
  T.unsafe(Open3).popen3(command, *arg, chdir: path) do |_, stdout, stderr, thread|
@@ -25,7 +37,7 @@ module Spoom
25
37
  out: stdout.read,
26
38
  err: stderr.read,
27
39
  status: T.must(status.success?),
28
- exit_code: T.must(status.exitstatus)
40
+ exit_code: T.must(status.exitstatus),
29
41
  )
30
42
  end
31
43
  end
@@ -47,11 +59,6 @@ module Spoom
47
59
  exec("git log #{arg.join(" ")}", path: path)
48
60
  end
49
61
 
50
- sig { params(arg: String, path: String).returns(ExecResult) }
51
- def rev_parse(*arg, path: ".")
52
- exec("git rev-parse --short #{arg.join(" ")}", path: path)
53
- end
54
-
55
62
  sig { params(arg: String, path: String).returns(ExecResult) }
56
63
  def show(*arg, path: ".")
57
64
  exec("git show #{arg.join(" ")}", path: path)
@@ -67,37 +74,16 @@ module Spoom
67
74
 
68
75
  # Utils
69
76
 
70
- # Get the commit epoch timestamp for a `sha`
71
- sig { params(sha: String, path: String).returns(T.nilable(Integer)) }
72
- def commit_timestamp(sha, path: ".")
73
- result = show("--no-notes --no-patch --pretty=%at #{sha}", path: path)
74
- return nil unless result.status
75
-
76
- result.out.strip.to_i
77
- end
78
-
79
- # Get the commit Time for a `sha`
80
- sig { params(sha: String, path: String).returns(T.nilable(Time)) }
81
- def commit_time(sha, path: ".")
82
- timestamp = commit_timestamp(sha, path: path)
83
- return nil unless timestamp
84
-
85
- epoch_to_time(timestamp.to_s)
86
- end
87
-
88
- # Get the last commit sha
89
- sig { params(path: String).returns(T.nilable(String)) }
77
+ # Get the last commit in the currently checked out branch
78
+ sig { params(path: String).returns(T.nilable(Commit)) }
90
79
  def last_commit(path: ".")
91
- result = rev_parse("HEAD", path: path)
80
+ result = log("HEAD --format='%h %at' -1", path: path)
92
81
  return nil unless result.status
93
82
 
94
- result.out.strip
95
- end
83
+ out = result.out.strip
84
+ return nil if out.empty?
96
85
 
97
- # Translate a git epoch timestamp into a Time
98
- sig { params(timestamp: String).returns(Time) }
99
- def epoch_to_time(timestamp)
100
- Time.strptime(timestamp, "%s")
86
+ parse_commit(out)
101
87
  end
102
88
 
103
89
  # Is there uncommited changes in `path`?
@@ -106,28 +92,38 @@ module Spoom
106
92
  diff("HEAD", path: path).out.empty?
107
93
  end
108
94
 
109
- # Get the hash of the commit introducing the `sorbet/config` file
110
- sig { params(path: String).returns(T.nilable(String)) }
95
+ # Get the commit introducing the `sorbet/config` file
96
+ sig { params(path: String).returns(T.nilable(Commit)) }
111
97
  def sorbet_intro_commit(path: ".")
112
- result = Spoom::Git.log("--diff-filter=A --format='%h' -1 -- sorbet/config", path: path)
98
+ result = log("--diff-filter=A --format='%h %at' -1 -- sorbet/config", path: path)
113
99
  return nil unless result.status
114
100
 
115
101
  out = result.out.strip
116
102
  return nil if out.empty?
117
103
 
118
- out
104
+ parse_commit(out)
119
105
  end
120
106
 
121
- # Get the hash of the commit removing the `sorbet/config` file
122
- sig { params(path: String).returns(T.nilable(String)) }
107
+ # Get the commit removing the `sorbet/config` file
108
+ sig { params(path: String).returns(T.nilable(Commit)) }
123
109
  def sorbet_removal_commit(path: ".")
124
- result = Spoom::Git.log("--diff-filter=D --format='%h' -1 -- sorbet/config", path: path)
110
+ result = log("--diff-filter=D --format='%h %at' -1 -- sorbet/config", path: path)
125
111
  return nil unless result.status
126
112
 
127
113
  out = result.out.strip
128
114
  return nil if out.empty?
129
115
 
130
- out
116
+ parse_commit(out)
117
+ end
118
+
119
+ # Parse a line formated as `%h %at` into a `Commit`
120
+ sig { params(string: String).returns(T.nilable(Commit)) }
121
+ def parse_commit(string)
122
+ sha, epoch = string.split(" ", 2)
123
+ return nil unless sha && epoch
124
+
125
+ time = Time.strptime(epoch, "%s")
126
+ Commit.new(sha: sha, time: time)
131
127
  end
132
128
  end
133
129
  end
@@ -142,7 +142,7 @@ module Spoom
142
142
  line: T.nilable(Integer),
143
143
  message: T.nilable(String),
144
144
  code: T.nilable(Integer),
145
- more: T::Array[String]
145
+ more: T::Array[String],
146
146
  ).void
147
147
  end
148
148
  def initialize(file, line, message, code, more = [])
@@ -23,7 +23,7 @@ module Spoom
23
23
  def from_json(json)
24
24
  Diagnostics.new(
25
25
  json["uri"],
26
- json["diagnostics"].map { |d| Diagnostic.from_json(d) }
26
+ json["diagnostics"].map { |d| Diagnostic.from_json(d) },
27
27
  )
28
28
  end
29
29
  end
@@ -54,7 +54,7 @@ module Spoom
54
54
  ResponseError.new(
55
55
  json["code"],
56
56
  json["message"],
57
- json["data"]
57
+ json["data"],
58
58
  )
59
59
  end
60
60
  end
@@ -30,7 +30,7 @@ module Spoom
30
30
  def from_json(json)
31
31
  Hover.new(
32
32
  contents: json["contents"]["value"],
33
- range: json["range"] ? Range.from_json(json["range"]) : nil
33
+ range: json["range"] ? Range.from_json(json["range"]) : nil,
34
34
  )
35
35
  end
36
36
  end
@@ -61,7 +61,7 @@ module Spoom
61
61
  def from_json(json)
62
62
  Position.new(
63
63
  line: json["line"].to_i,
64
- char: json["character"].to_i
64
+ char: json["character"].to_i,
65
65
  )
66
66
  end
67
67
  end
@@ -91,7 +91,7 @@ module Spoom
91
91
  def from_json(json)
92
92
  Range.new(
93
93
  start: Position.from_json(json["start"]),
94
- end: Position.from_json(json["end"])
94
+ end: Position.from_json(json["end"]),
95
95
  )
96
96
  end
97
97
  end
@@ -123,7 +123,7 @@ module Spoom
123
123
  def from_json(json)
124
124
  Location.new(
125
125
  uri: json["uri"],
126
- range: Range.from_json(json["range"])
126
+ range: Range.from_json(json["range"]),
127
127
  )
128
128
  end
129
129
  end
@@ -193,7 +193,7 @@ module Spoom
193
193
  range: Range.from_json(json["range"]),
194
194
  code: json["code"].to_i,
195
195
  message: json["message"],
196
- informations: json["relatedInformation"]
196
+ informations: json["relatedInformation"],
197
197
  )
198
198
  end
199
199
  end
@@ -317,7 +317,7 @@ module Spoom
317
317
  out: T.any(IO, StringIO),
318
318
  colors: T::Boolean,
319
319
  indent_level: Integer,
320
- prefix: T.nilable(String)
320
+ prefix: T.nilable(String),
321
321
  ).void
322
322
  end
323
323
  def initialize(out: $stdout, colors: true, indent_level: 0, prefix: nil)
@@ -98,7 +98,7 @@ module Spoom
98
98
  "line" => line,
99
99
  "character" => column,
100
100
  },
101
- }
101
+ },
102
102
  ))
103
103
 
104
104
  return nil unless json && json["result"]
@@ -119,7 +119,7 @@ module Spoom
119
119
  "line" => line,
120
120
  "character" => column,
121
121
  },
122
- }
122
+ },
123
123
  ))
124
124
 
125
125
  return [] unless json && json["result"] && json["result"]["signatures"]
@@ -140,7 +140,7 @@ module Spoom
140
140
  "line" => line,
141
141
  "character" => column,
142
142
  },
143
- }
143
+ },
144
144
  ))
145
145
 
146
146
  return [] unless json && json["result"]
@@ -161,7 +161,7 @@ module Spoom
161
161
  "line" => line,
162
162
  "character" => column,
163
163
  },
164
- }
164
+ },
165
165
  ))
166
166
 
167
167
  return [] unless json && json["result"]
@@ -185,7 +185,7 @@ module Spoom
185
185
  "context" => {
186
186
  "includeDeclaration" => include_decl,
187
187
  },
188
- }
188
+ },
189
189
  ))
190
190
 
191
191
  return [] unless json && json["result"]
@@ -200,7 +200,7 @@ module Spoom
200
200
  "workspace/symbol",
201
201
  {
202
202
  "query" => query,
203
- }
203
+ },
204
204
  ))
205
205
 
206
206
  return [] unless json && json["result"]
@@ -217,7 +217,7 @@ module Spoom
217
217
  "textDocument" => {
218
218
  "uri" => uri,
219
219
  },
220
- }
220
+ },
221
221
  ))
222
222
 
223
223
  return [] unless json && json["result"]
@@ -88,7 +88,7 @@ module Spoom
88
88
  params(
89
89
  directory: T.any(String, Pathname),
90
90
  strictness: String,
91
- extension: String
91
+ extension: String,
92
92
  ).returns(T::Array[String])
93
93
  end
94
94
  def files_with_sigil_strictness(directory, strictness, extension: ".rb")
data/lib/spoom/sorbet.rb CHANGED
@@ -25,7 +25,7 @@ module Spoom
25
25
  arg: String,
26
26
  path: String,
27
27
  capture_err: T::Boolean,
28
- sorbet_bin: T.nilable(String)
28
+ sorbet_bin: T.nilable(String),
29
29
  ).returns(ExecResult)
30
30
  end
31
31
  def srb(*arg, path: ".", capture_err: false, sorbet_bin: nil)
@@ -42,7 +42,7 @@ module Spoom
42
42
  arg: String,
43
43
  path: String,
44
44
  capture_err: T::Boolean,
45
- sorbet_bin: T.nilable(String)
45
+ sorbet_bin: T.nilable(String),
46
46
  ).returns(ExecResult)
47
47
  end
48
48
  def srb_tc(*arg, path: ".", capture_err: false, sorbet_bin: nil)
@@ -65,7 +65,7 @@ module Spoom
65
65
  arg: String,
66
66
  path: String,
67
67
  capture_err: T::Boolean,
68
- sorbet_bin: T.nilable(String)
68
+ sorbet_bin: T.nilable(String),
69
69
  ).returns(T.nilable(String))
70
70
  end
71
71
  def srb_version(*arg, path: ".", capture_err: false, sorbet_bin: nil)
@@ -75,7 +75,7 @@ module Spoom
75
75
  *arg,
76
76
  path: path,
77
77
  capture_err: capture_err,
78
- sorbet_bin: sorbet_bin
78
+ sorbet_bin: sorbet_bin,
79
79
  ), ExecResult)
80
80
  return nil unless result.status
81
81
 
@@ -87,7 +87,7 @@ module Spoom
87
87
  arg: String,
88
88
  path: String,
89
89
  capture_err: T::Boolean,
90
- sorbet_bin: T.nilable(String)
90
+ sorbet_bin: T.nilable(String),
91
91
  ).returns(T.nilable(T::Hash[String, Integer]))
92
92
  end
93
93
  def srb_metrics(*arg, path: ".", capture_err: false, sorbet_bin: nil)
@@ -99,7 +99,7 @@ module Spoom
99
99
  *arg,
100
100
  path: path,
101
101
  capture_err: capture_err,
102
- sorbet_bin: sorbet_bin
102
+ sorbet_bin: sorbet_bin,
103
103
  )
104
104
  if File.exist?(metrics_path)
105
105
  metrics = Spoom::Sorbet::MetricsParser.parse_file(metrics_path)
@@ -15,7 +15,7 @@ module Spoom
15
15
  end
16
16
 
17
17
  # Return one commit for each month between `from` and `to`
18
- sig { returns(T::Array[String]) }
18
+ sig { returns(T::Array[Git::Commit]) }
19
19
  def ticks
20
20
  commits_for_dates(months)
21
21
  end
@@ -34,21 +34,21 @@ module Spoom
34
34
  end
35
35
 
36
36
  # Return one commit for each date in `dates`
37
- sig { params(dates: T::Array[Time]).returns(T::Array[String]) }
37
+ sig { params(dates: T::Array[Time]).returns(T::Array[Git::Commit]) }
38
38
  def commits_for_dates(dates)
39
39
  dates.map do |t|
40
40
  result = Spoom::Git.log(
41
41
  "--since='#{t}'",
42
42
  "--until='#{t.to_date.next_month}'",
43
- "--format='format:%h'",
43
+ "--format='format:%h %at'",
44
44
  "--author-date-order",
45
45
  "-1",
46
46
  path: @path,
47
47
  )
48
48
  next if result.out.empty?
49
49
 
50
- result.out
51
- end.compact.uniq
50
+ Git.parse_commit(result.out.strip)
51
+ end.compact.uniq(&:sha)
52
52
  end
53
53
  end
54
54
  end
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.1.12"
5
+ VERSION = "1.1.13"
6
6
  end
data/lib/spoom.rb CHANGED
@@ -39,7 +39,7 @@ module Spoom
39
39
  cmd: String,
40
40
  arg: String,
41
41
  path: String,
42
- capture_err: T::Boolean
42
+ capture_err: T::Boolean,
43
43
  ).returns(ExecResult)
44
44
  end
45
45
  def exec(cmd, *arg, path: ".", capture_err: false)
@@ -49,7 +49,7 @@ module Spoom
49
49
  out: stdout,
50
50
  err: stderr,
51
51
  status: status.success?,
52
- exit_code: status.exitstatus
52
+ exit_code: status.exitstatus,
53
53
  )
54
54
  else
55
55
  stdout, status = T.unsafe(Open3).capture2([cmd, *arg].join(" "), chdir: path)
@@ -57,7 +57,7 @@ module Spoom
57
57
  out: stdout,
58
58
  err: "",
59
59
  status: status.success?,
60
- exit_code: status.exitstatus
60
+ exit_code: status.exitstatus,
61
61
  )
62
62
  end
63
63
  end
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.1.12
4
+ version: 1.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-18 00:00:00.000000000 Z
11
+ date: 2022-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler