ruby-lsp 0.23.17 → 0.23.18

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +5 -0
  4. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +6 -9
  5. data/lib/ruby_indexer/test/prefix_tree_test.rb +8 -8
  6. data/lib/ruby_lsp/client_capabilities.rb +6 -1
  7. data/lib/ruby_lsp/document.rb +6 -2
  8. data/lib/ruby_lsp/erb_document.rb +1 -4
  9. data/lib/ruby_lsp/internal.rb +6 -1
  10. data/lib/ruby_lsp/listeners/hover.rb +8 -5
  11. data/lib/ruby_lsp/listeners/test_discovery.rb +1 -1
  12. data/lib/ruby_lsp/listeners/test_style.rb +1 -1
  13. data/lib/ruby_lsp/rbs_document.rb +1 -4
  14. data/lib/ruby_lsp/requests/code_lens.rb +1 -1
  15. data/lib/ruby_lsp/requests/definition.rb +0 -2
  16. data/lib/ruby_lsp/requests/hover.rb +1 -4
  17. data/lib/ruby_lsp/requests/request.rb +1 -1
  18. data/lib/ruby_lsp/requests/support/test_item.rb +1 -1
  19. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +1 -4
  20. data/lib/ruby_lsp/response_builders/document_symbol.rb +1 -2
  21. data/lib/ruby_lsp/response_builders/hover.rb +1 -4
  22. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  23. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +1 -2
  24. data/lib/ruby_lsp/response_builders/signature_help.rb +1 -2
  25. data/lib/ruby_lsp/response_builders/test_collection.rb +9 -20
  26. data/lib/ruby_lsp/ruby_document.rb +1 -4
  27. data/lib/ruby_lsp/server.rb +40 -1
  28. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +16 -6
  29. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +6 -23
  30. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +5 -17
  31. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 783a43ad6688b46f4edcb4662a06d9fa9277ecc35475e64189c02547ab4f980b
4
- data.tar.gz: 3f4b78c3f7909886d8642d7608d4ef7d358c41ca55fa118380fa2082119864ff
3
+ metadata.gz: ccc13fa3f30ae54cbb92ce72ea8ae2c6707a94cbed29991c6df6eb424de1797e
4
+ data.tar.gz: d30bbc7515361249d332006aac49f73256912cedf5afd0690e6ac37970ed3bcb
5
5
  SHA512:
6
- metadata.gz: cb4be502edd93cd9ce01b505486f035733927076138447bd30607dda326bb40432c1ce0c8c50751252acf5098fbed24824a2c7e578b9914206411cbb7e869268
7
- data.tar.gz: ce0ec2cdf2311ae7fda5c5c53fd3abcc122a7c6bcf031dd2dd30d8d65555e51e722985fbc58f75cce94b8591e36f6e404283f67d17c8ad58438c8ba5d7b82b39
6
+ metadata.gz: 7667f639640708645275f8c8980d0da6a40943883729eb96f6c7d5ebbf26dbe66f167bcc0dccef77899a7f028db20c029a0c9808646b7113ce2cfed4cf48d689
7
+ data.tar.gz: 84f1da1bb1150b55e8667f2ca8aac9249e0d86cae5c4a9d9ac6494c79eba7c17d54bbed43aa8c4a263614e9cbec1d0e8a30876395295c86b3747d62020bf2a4e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.17
1
+ 0.23.18
@@ -661,6 +661,11 @@ module RubyIndexer
661
661
  @ancestors.clear if original_map.any? { |name, hash| updated_map[name] != hash }
662
662
  end
663
663
 
664
+ #: -> void
665
+ def clear_ancestors
666
+ @ancestors.clear
667
+ end
668
+
664
669
  #: -> bool
665
670
  def empty?
666
671
  @entries.empty?
@@ -32,14 +32,14 @@ module RubyIndexer
32
32
  # `Value` type.
33
33
  #
34
34
  # See https://en.wikipedia.org/wiki/Trie for more information
35
+ #: [Value]
35
36
  class PrefixTree
36
- extend T::Generic
37
-
38
- Value = type_member
39
-
40
37
  #: -> void
41
38
  def initialize
42
- @root = Node.new("", "") #: Node[Value]
39
+ @root = Node.new(
40
+ "",
41
+ "", #: as untyped
42
+ ) #: Node[Value]
43
43
  end
44
44
 
45
45
  # Search the PrefixTree based on a given `prefix`. If `foo` is an entry in the tree, then searching for `fo` will
@@ -106,11 +106,8 @@ module RubyIndexer
106
106
  node
107
107
  end
108
108
 
109
+ #: [Value]
109
110
  class Node
110
- extend T::Generic
111
-
112
- Value = type_member
113
-
114
111
  #: Hash[String, Node[Value]]
115
112
  attr_reader :children
116
113
 
@@ -22,7 +22,7 @@ module RubyIndexer
22
22
  end
23
23
 
24
24
  def test_multiple_items
25
- tree = PrefixTree[String].new
25
+ tree = PrefixTree.new #: PrefixTree[String]
26
26
  ["foo", "bar", "baz"].each { |item| tree.insert(item, item) }
27
27
 
28
28
  assert_equal(["baz", "bar", "foo"], tree.search(""))
@@ -34,7 +34,7 @@ module RubyIndexer
34
34
  end
35
35
 
36
36
  def test_multiple_prefixes
37
- tree = PrefixTree[String].new
37
+ tree = PrefixTree.new #: PrefixTree[String]
38
38
  ["fo", "foo"].each { |item| tree.insert(item, item) }
39
39
 
40
40
  assert_equal(["fo", "foo"], tree.search(""))
@@ -45,7 +45,7 @@ module RubyIndexer
45
45
  end
46
46
 
47
47
  def test_multiple_prefixes_with_shuffled_order
48
- tree = PrefixTree[String].new
48
+ tree = PrefixTree.new #: PrefixTree[String]
49
49
  [
50
50
  "foo/bar/base",
51
51
  "foo/bar/on",
@@ -97,7 +97,7 @@ module RubyIndexer
97
97
  end
98
98
 
99
99
  def test_deletion
100
- tree = PrefixTree[String].new
100
+ tree = PrefixTree.new #: PrefixTree[String]
101
101
  ["foo/bar", "foo/baz"].each { |item| tree.insert(item, item) }
102
102
  assert_equal(["foo/baz", "foo/bar"], tree.search("foo"))
103
103
 
@@ -107,7 +107,7 @@ module RubyIndexer
107
107
  end
108
108
 
109
109
  def test_delete_does_not_impact_other_keys_with_the_same_value
110
- tree = PrefixTree[String].new
110
+ tree = PrefixTree.new #: PrefixTree[String]
111
111
  tree.insert("key1", "value")
112
112
  tree.insert("key2", "value")
113
113
  assert_equal(["value", "value"], tree.search("key"))
@@ -118,7 +118,7 @@ module RubyIndexer
118
118
  end
119
119
 
120
120
  def test_deleted_node_is_removed_from_the_tree
121
- tree = PrefixTree[String].new
121
+ tree = PrefixTree.new #: PrefixTree[String]
122
122
  tree.insert("foo/bar", "foo/bar")
123
123
  assert_equal(["foo/bar"], tree.search("foo"))
124
124
 
@@ -128,7 +128,7 @@ module RubyIndexer
128
128
  end
129
129
 
130
130
  def test_deleting_non_terminal_nodes
131
- tree = PrefixTree[String].new
131
+ tree = PrefixTree.new #: PrefixTree[String]
132
132
  tree.insert("abc", "value1")
133
133
  tree.insert("abcdef", "value2")
134
134
 
@@ -138,7 +138,7 @@ module RubyIndexer
138
138
  end
139
139
 
140
140
  def test_overriding_values
141
- tree = PrefixTree[Integer].new
141
+ tree = PrefixTree.new #: PrefixTree[Integer]
142
142
 
143
143
  tree.insert("foo/bar", 123)
144
144
  assert_equal([123], tree.search("foo/bar"))
@@ -10,7 +10,8 @@ module RubyLsp
10
10
  :supports_request_delegation,
11
11
  :window_show_message_supports_extra_properties,
12
12
  :supports_progress,
13
- :supports_diagnostic_refresh
13
+ :supports_diagnostic_refresh,
14
+ :supports_code_lens_refresh
14
15
 
15
16
  #: -> void
16
17
  def initialize
@@ -34,6 +35,9 @@ module RubyLsp
34
35
 
35
36
  # The editor supports server initiated refresh for diagnostics
36
37
  @supports_diagnostic_refresh = false #: bool
38
+
39
+ # The editor supports server initiated refresh for code lenses
40
+ @supports_code_lens_refresh = false #: bool
37
41
  end
38
42
 
39
43
  #: (Hash[Symbol, untyped] capabilities) -> void
@@ -61,6 +65,7 @@ module RubyLsp
61
65
  @supports_progress = progress if progress
62
66
 
63
67
  @supports_diagnostic_refresh = workspace_capabilities.dig(:diagnostics, :refreshSupport) || false
68
+ @supports_code_lens_refresh = workspace_capabilities.dig(:codeLens, :refreshSupport) || false
64
69
  end
65
70
 
66
71
  #: -> bool
@@ -2,13 +2,12 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ #: [ParseResultType]
5
6
  class Document
6
7
  extend T::Sig
7
8
  extend T::Helpers
8
- extend T::Generic
9
9
 
10
10
  class LocationNotFoundError < StandardError; end
11
- ParseResultType = type_member
12
11
 
13
12
  # This maximum number of characters for providing expensive features, like semantic highlighting and diagnostics.
14
13
  # This is the same number used by the TypeScript extension in VS Code
@@ -86,6 +85,11 @@ module RubyLsp
86
85
  @cache[request_name]
87
86
  end
88
87
 
88
+ #: (String request_name) -> void
89
+ def clear_cache(request_name)
90
+ @cache[request_name] = EMPTY_CACHE
91
+ end
92
+
89
93
  #: (Array[Hash[Symbol, untyped]] edits, version: Integer) -> void
90
94
  def push_edits(edits, version:)
91
95
  edits.each do |edit|
@@ -2,11 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ #: [ParseResultType = Prism::ParseResult]
5
6
  class ERBDocument < Document
6
- extend T::Generic
7
-
8
- ParseResultType = type_member { { fixed: Prism::ParseResult } }
9
-
10
7
  #: String
11
8
  attr_reader :host_language_source
12
9
 
@@ -14,7 +14,12 @@ Bundler.ui.level = :silent
14
14
 
15
15
  require "json"
16
16
  require "uri"
17
- require "cgi"
17
+ require "cgi/escape"
18
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.5")
19
+ # Just requiring `cgi/escape` leaves CGI.unescape broken on older rubies
20
+ # Some background on why this is necessary: https://bugs.ruby-lang.org/issues/21258
21
+ require "cgi/util"
22
+ end
18
23
  require "set"
19
24
  require "strscan"
20
25
  require "prism"
@@ -86,6 +86,14 @@ module RubyLsp
86
86
 
87
87
  #: (Prism::StringNode node) -> void
88
88
  def on_string_node_enter(node)
89
+ if @path && File.basename(@path) == GEMFILE_NAME
90
+ call_node = @node_context.call_node
91
+ if call_node && call_node.name == :gem && call_node.arguments&.arguments&.first == node
92
+ generate_gem_hover(call_node)
93
+ return
94
+ end
95
+ end
96
+
89
97
  generate_heredoc_hover(node)
90
98
  end
91
99
 
@@ -123,11 +131,6 @@ module RubyLsp
123
131
 
124
132
  #: (Prism::CallNode node) -> void
125
133
  def on_call_node_enter(node)
126
- if @path && File.basename(@path) == GEMFILE_NAME && node.name == :gem
127
- generate_gem_hover(node)
128
- return
129
- end
130
-
131
134
  return if @sorbet_level.true_or_higher? && self_receiver?(node)
132
135
 
133
136
  message = node.message
@@ -71,7 +71,7 @@ module RubyLsp
71
71
  slice.gsub(/((?<=::)|^)[a-z]\w*/, DYNAMIC_REFERENCE_MARKER)
72
72
  end
73
73
 
74
- #: (Prism::ClassNode node, ^(String name, Array[String] ancestors) -> void block) -> void
74
+ #: (Prism::ClassNode node) { (String name, Array[String] ancestors) -> void } -> void
75
75
  def with_test_ancestor_tracking(node, &block)
76
76
  @visibility_stack << :public
77
77
  name = constant_name(node.constant_path)
@@ -202,7 +202,7 @@ module RubyLsp
202
202
  framework: @framework,
203
203
  )
204
204
  test_item.add(example_item)
205
- @response_builder.add_code_lens(test_item)
205
+ @response_builder.add_code_lens(example_item)
206
206
  end
207
207
 
208
208
  #: (Prism::CallNode node) -> void
@@ -2,11 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ #: [ParseResultType = Array[RBS::AST::Declarations::Base]]
5
6
  class RBSDocument < Document
6
- extend T::Generic
7
-
8
- ParseResultType = type_member { { fixed: T::Array[RBS::AST::Declarations::Base] } }
9
-
10
7
  #: (source: String, version: Integer, uri: URI::Generic, global_state: GlobalState) -> void
11
8
  def initialize(source:, version:, uri:, global_state:)
12
9
  @syntax_error = false #: bool
@@ -14,7 +14,7 @@ module RubyLsp
14
14
  class << self
15
15
  #: -> Interface::CodeLensOptions
16
16
  def provider
17
- Interface::CodeLensOptions.new(resolve_provider: false)
17
+ Interface::CodeLensOptions.new(resolve_provider: true)
18
18
  end
19
19
  end
20
20
 
@@ -9,8 +9,6 @@ module RubyLsp
9
9
  # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) jumps to the
10
10
  # definition of the symbol under the cursor.
11
11
  class Definition < Request
12
- extend T::Generic
13
-
14
12
  #: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
15
13
  def initialize(document, global_state, position, dispatcher, sorbet_level)
16
14
  super()
@@ -7,9 +7,8 @@ module RubyLsp
7
7
  module Requests
8
8
  # The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
9
9
  # displays the documentation for the symbol currently under the cursor.
10
+ #: [ResponseType = Interface::Hover?]
10
11
  class Hover < Request
11
- extend T::Generic
12
-
13
12
  class << self
14
13
  #: -> Interface::HoverOptions
15
14
  def provider
@@ -17,8 +16,6 @@ module RubyLsp
17
16
  end
18
17
  end
19
18
 
20
- ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }
21
-
22
19
  #: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
23
20
  def initialize(document, global_state, position, dispatcher, sorbet_level)
24
21
  super()
@@ -4,7 +4,7 @@
4
4
  module RubyLsp
5
5
  module Requests
6
6
  class Request
7
- extend T::Generic
7
+ extend T::Helpers
8
8
  extend T::Sig
9
9
 
10
10
  class InvalidFormatter < StandardError; end
@@ -19,7 +19,7 @@ module RubyLsp
19
19
  #: Interface::Range
20
20
  attr_reader :range
21
21
 
22
- #: (String id, String label, URI::Generic uri, Interface::Range range, Symbol framework) -> void
22
+ #: (String id, String label, URI::Generic uri, Interface::Range range, framework: Symbol) -> void
23
23
  def initialize(id, label, uri, range, framework:)
24
24
  @id = id
25
25
  @label = label
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType < Object]
6
7
  class CollectionResponseBuilder < ResponseBuilder
7
- extend T::Generic
8
-
9
- ResponseType = type_member { { upper: Object } }
10
-
11
8
  #: -> void
12
9
  def initialize
13
10
  super
@@ -3,9 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = Array[Interface::DocumentSymbol]]
6
7
  class DocumentSymbol < ResponseBuilder
7
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
8
-
9
8
  class SymbolHierarchyRoot
10
9
  #: Array[Interface::DocumentSymbol]
11
10
  attr_reader :children
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = String]
6
7
  class Hover < ResponseBuilder
7
- extend T::Generic
8
-
9
- ResponseType = type_member { { fixed: String } }
10
-
11
8
  #: -> void
12
9
  def initialize
13
10
  super
@@ -4,8 +4,8 @@
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
6
  class ResponseBuilder
7
- extend T::Generic
8
7
  extend T::Sig
8
+ extend T::Helpers
9
9
 
10
10
  abstract!
11
11
 
@@ -3,6 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = Interface::SemanticTokens]
6
7
  class SemanticHighlighting < ResponseBuilder
7
8
  class UndefinedTokenType < StandardError; end
8
9
 
@@ -45,8 +46,6 @@ module RubyLsp
45
46
  default_library: 9,
46
47
  }.freeze #: Hash[Symbol, Integer]
47
48
 
48
- ResponseType = type_member { { fixed: Interface::SemanticTokens } }
49
-
50
49
  #: ((^(Integer arg0) -> Integer | Prism::CodeUnitsCache) code_units_cache) -> void
51
50
  def initialize(code_units_cache)
52
51
  super()
@@ -3,9 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = Interface::SignatureHelp?]
6
7
  class SignatureHelp < ResponseBuilder
7
- ResponseType = type_member { { fixed: T.nilable(Interface::SignatureHelp) } }
8
-
9
8
  #: -> void
10
9
  def initialize
11
10
  super
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
+ #: [ResponseType = Requests::Support::TestItem]
6
7
  class TestCollection < ResponseBuilder
7
- extend T::Generic
8
-
9
- ResponseType = type_member { { fixed: Requests::Support::TestItem } }
10
-
11
8
  #: Array[Interface::CodeLens]
12
9
  attr_reader :code_lens
13
10
 
@@ -25,34 +22,26 @@ module RubyLsp
25
22
 
26
23
  #: (ResponseType item) -> void
27
24
  def add_code_lens(item)
28
- range = item.range
29
25
  arguments = [item.uri.to_standardized_path, item.id]
26
+ start = item.range.start
27
+ range = Interface::Range.new(
28
+ start: start,
29
+ end: Interface::Position.new(line: start.line, character: start.character + 1),
30
+ )
30
31
 
31
32
  @code_lens << Interface::CodeLens.new(
32
33
  range: range,
33
- command: Interface::Command.new(
34
- title: "▶ Run",
35
- command: "rubyLsp.runTest",
36
- arguments: arguments,
37
- ),
34
+ data: { arguments: arguments, kind: "run_test" },
38
35
  )
39
36
 
40
37
  @code_lens << Interface::CodeLens.new(
41
38
  range: range,
42
- command: Interface::Command.new(
43
- title: "▶ Run In Terminal",
44
- command: "rubyLsp.runTestInTerminal",
45
- arguments: arguments,
46
- ),
39
+ data: { arguments: arguments, kind: "run_test_in_terminal" },
47
40
  )
48
41
 
49
42
  @code_lens << Interface::CodeLens.new(
50
43
  range: range,
51
- command: Interface::Command.new(
52
- title: "⚙ Debug",
53
- command: "rubyLsp.debugTest",
54
- arguments: arguments,
55
- ),
44
+ data: { arguments: arguments, kind: "debug_test" },
56
45
  )
57
46
  end
58
47
 
@@ -2,11 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ #: [ParseResultType = Prism::ParseResult]
5
6
  class RubyDocument < Document
6
- extend T::Generic
7
-
8
- ParseResultType = type_member { { fixed: Prism::ParseResult } }
9
-
10
7
  METHODS_THAT_CHANGE_DECLARATIONS = [
11
8
  :private_constant,
12
9
  :attr_reader,
@@ -32,6 +32,8 @@ module RubyLsp
32
32
  text_document_document_link(message)
33
33
  when "textDocument/codeLens"
34
34
  text_document_code_lens(message)
35
+ when "codeLens/resolve"
36
+ code_lens_resolve(message)
35
37
  when "textDocument/semanticTokens/full"
36
38
  text_document_semantic_tokens_full(message)
37
39
  when "textDocument/semanticTokens/full/delta"
@@ -1124,7 +1126,7 @@ module RubyLsp
1124
1126
  # Clear all document caches for pull diagnostics
1125
1127
  @global_state.synchronize do
1126
1128
  @store.each do |_uri, document|
1127
- document.cache_set("textDocument/diagnostic", Document::EMPTY_CACHE)
1129
+ document.clear_cache("textDocument/diagnostic")
1128
1130
  end
1129
1131
  end
1130
1132
 
@@ -1280,9 +1282,25 @@ module RubyLsp
1280
1282
  # allocations and garbage collections are faster
1281
1283
  GC.compact unless @test_mode
1282
1284
 
1285
+ @global_state.synchronize do
1286
+ # If we linearize ancestors while the index is not fully populated, we may end up caching incorrect results
1287
+ # that were missing namespaces. After indexing is complete, we need to clear the ancestors cache and start
1288
+ # again
1289
+ @global_state.index.clear_ancestors
1290
+
1291
+ # The results for code lens depend on ancestor linearization, so we need to clear any previously computed
1292
+ # responses
1293
+ @store.each { |_uri, document| document.clear_cache("textDocument/codeLens") }
1294
+ end
1295
+
1283
1296
  # Always end the progress notification even if indexing failed or else it never goes away and the user has no
1284
1297
  # way of dismissing it
1285
1298
  end_progress("indexing-progress")
1299
+
1300
+ # Request a code lens refresh if we populated them before all test parent classes were indexed
1301
+ if @global_state.client_capabilities.supports_code_lens_refresh
1302
+ send_message(Request.new(id: @current_request_id, method: "workspace/codeLens/refresh", params: nil))
1303
+ end
1286
1304
  end
1287
1305
  end
1288
1306
 
@@ -1497,5 +1515,26 @@ module RubyLsp
1497
1515
  },
1498
1516
  ))
1499
1517
  end
1518
+
1519
+ #: (Hash[Symbol, untyped] message) -> void
1520
+ def code_lens_resolve(message)
1521
+ code_lens = message[:params]
1522
+ args = code_lens.dig(:data, :arguments)
1523
+
1524
+ case code_lens.dig(:data, :kind)
1525
+ when "run_test"
1526
+ code_lens[:command] = Interface::Command.new(title: "▶ Run", command: "rubyLsp.runTest", arguments: args)
1527
+ when "run_test_in_terminal"
1528
+ code_lens[:command] =
1529
+ Interface::Command.new(title: "▶ Run in terminal", command: "rubyLsp.runTestInTerminal", arguments: args)
1530
+ when "debug_test"
1531
+ code_lens[:command] = Interface::Command.new(title: "⚙ Debug", command: "rubyLsp.debugTest", arguments: args)
1532
+ end
1533
+
1534
+ send_message(Result.new(
1535
+ id: message[:id],
1536
+ response: code_lens,
1537
+ ))
1538
+ end
1500
1539
  end
1501
1540
  end
@@ -59,9 +59,9 @@ module RubyLsp
59
59
  @io.close
60
60
  end
61
61
 
62
- #: (id: String, uri: URI::Generic) -> void
63
- def start_test(id:, uri:)
64
- send_message("start", id: id, uri: uri.to_s)
62
+ #: (id: String, uri: URI::Generic, ?line: Integer?) -> void
63
+ def start_test(id:, uri:, line: nil)
64
+ send_message("start", id: id, uri: uri.to_s, line: line)
65
65
  end
66
66
 
67
67
  #: (id: String, uri: URI::Generic) -> void
@@ -84,6 +84,17 @@ module RubyLsp
84
84
  send_message("error", id: id, message: message, uri: uri.to_s)
85
85
  end
86
86
 
87
+ #: (Method | UnboundMethod) -> [URI::Generic, Integer?]?
88
+ def uri_and_line_for(method_object)
89
+ file_path, line = method_object.source_location
90
+ return unless file_path
91
+ return if file_path.start_with?("(eval at ")
92
+
93
+ uri = URI::Generic.from_path(path: File.expand_path(file_path))
94
+ zero_based_line = line ? line - 1 : nil
95
+ [uri, zero_based_line]
96
+ end
97
+
87
98
  # Gather the results returned by Coverage.result and format like the VS Code test explorer expects
88
99
  #
89
100
  # Coverage result format:
@@ -115,10 +126,9 @@ module RubyLsp
115
126
  def gather_coverage_results
116
127
  # Ignore coverage results inside dependencies
117
128
  bundle_path = Bundler.bundle_path.to_s
118
- default_gems_path = File.dirname(RbConfig::CONFIG["rubylibdir"])
119
129
 
120
130
  result = Coverage.result.reject do |file_path, _coverage_info|
121
- file_path.start_with?(bundle_path, default_gems_path, "eval")
131
+ file_path.start_with?(bundle_path) || !file_path.start_with?(Dir.pwd)
122
132
  end
123
133
 
124
134
  result.to_h do |file_path, coverage_info|
@@ -163,7 +173,7 @@ module RubyLsp
163
173
 
164
174
  private
165
175
 
166
- #: (method_name: String?, params: untyped) -> void
176
+ #: (String?, **untyped) -> void
167
177
  def send_message(method_name, **params)
168
178
  json_message = { method: method_name, params: params }.to_json
169
179
  @io.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
@@ -51,16 +51,19 @@ module RubyLsp
51
51
 
52
52
  #: (singleton(Minitest::Test) test_class, String method_name) -> void
53
53
  def prerecord(test_class, method_name)
54
- uri = uri_from_test_class(test_class, method_name)
54
+ uri, line = LspReporter.instance.uri_and_line_for(test_class.instance_method(method_name))
55
55
  return unless uri
56
56
 
57
- LspReporter.instance.start_test(id: "#{test_class.name}##{method_name}", uri: uri)
57
+ LspReporter.instance.start_test(id: "#{test_class.name}##{method_name}", uri: uri, line: line)
58
58
  end
59
59
 
60
60
  #: (Minitest::Result result) -> void
61
61
  def record(result)
62
62
  id = "#{result.klass}##{result.name}"
63
- uri = uri_from_result(result)
63
+ file_path, _line = result.source_location
64
+ return unless file_path
65
+
66
+ uri = URI::Generic.from_path(path: File.expand_path(file_path))
64
67
 
65
68
  if result.error?
66
69
  message = result.failures.first.message
@@ -79,26 +82,6 @@ module RubyLsp
79
82
  def report
80
83
  LspReporter.instance.shutdown
81
84
  end
82
-
83
- private
84
-
85
- #: (Minitest::Result result) -> URI::Generic
86
- def uri_from_result(result)
87
- file = result.source_location[0]
88
- absolute_path = File.expand_path(file, Dir.pwd)
89
- URI::Generic.from_path(path: absolute_path)
90
- end
91
-
92
- #: (singleton(Minitest::Test) test_class, String method_name) -> URI::Generic?
93
- def uri_from_test_class(test_class, method_name)
94
- file, _line = test_class.instance_method(method_name).source_location
95
- return unless file
96
-
97
- return if file.start_with?("(eval at ") # test is dynamically defined
98
-
99
- absolute_path = File.expand_path(file, Dir.pwd)
100
- URI::Generic.from_path(path: absolute_path)
101
- end
102
85
  end
103
86
  end
104
87
 
@@ -26,12 +26,12 @@ module RubyLsp
26
26
  def test_started(test)
27
27
  super
28
28
 
29
- current_test = test
30
- @current_uri = uri_for_test(current_test)
31
- return unless @current_uri
29
+ uri, line = LspReporter.instance.uri_and_line_for(test.method(test.method_name))
30
+ return unless uri
32
31
 
33
- @current_test_id = "#{current_test.class.name}##{current_test.method_name}"
34
- LspReporter.instance.start_test(id: @current_test_id, uri: @current_uri)
32
+ @current_uri = uri
33
+ @current_test_id = "#{test.class.name}##{test.method_name}"
34
+ LspReporter.instance.start_test(id: @current_test_id, uri: @current_uri, line: line)
35
35
  end
36
36
 
37
37
  #: (::Test::Unit::TestCase test) -> void
@@ -62,18 +62,6 @@ module RubyLsp
62
62
  LspReporter.instance.shutdown
63
63
  end
64
64
 
65
- #: (::Test::Unit::TestCase test) -> URI::Generic?
66
- def uri_for_test(test)
67
- location = test.method(test.method_name).source_location
68
- return unless location
69
-
70
- file, _line = location
71
- return if file.start_with?("(eval at ")
72
-
73
- absolute_path = File.expand_path(file, Dir.pwd)
74
- URI::Generic.from_path(path: absolute_path)
75
- end
76
-
77
65
  #: -> void
78
66
  def attach_to_mediator
79
67
  # Events we care about
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.17
4
+ version: 0.23.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify