ruby-lsp 0.13.4 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/VERSION +1 -1
  4. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +4 -8
  5. data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +5 -1
  6. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +4 -2
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -3
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +9 -0
  9. data/lib/ruby_indexer/test/index_test.rb +27 -0
  10. data/lib/ruby_lsp/addon.rb +21 -10
  11. data/lib/ruby_lsp/check_docs.rb +8 -8
  12. data/lib/ruby_lsp/executor.rb +28 -10
  13. data/lib/ruby_lsp/internal.rb +1 -1
  14. data/lib/ruby_lsp/listeners/code_lens.rb +54 -55
  15. data/lib/ruby_lsp/listeners/completion.rb +17 -16
  16. data/lib/ruby_lsp/listeners/definition.rb +10 -16
  17. data/lib/ruby_lsp/listeners/document_highlight.rb +6 -11
  18. data/lib/ruby_lsp/listeners/document_link.rb +6 -12
  19. data/lib/ruby_lsp/listeners/document_symbol.rb +95 -55
  20. data/lib/ruby_lsp/listeners/folding_ranges.rb +19 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +26 -30
  22. data/lib/ruby_lsp/listeners/inlay_hints.rb +7 -13
  23. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -124
  24. data/lib/ruby_lsp/listeners/signature_help.rb +11 -13
  25. data/lib/ruby_lsp/requests/code_lens.rb +9 -17
  26. data/lib/ruby_lsp/requests/completion.rb +7 -9
  27. data/lib/ruby_lsp/requests/definition.rb +10 -22
  28. data/lib/ruby_lsp/requests/document_highlight.rb +7 -5
  29. data/lib/ruby_lsp/requests/document_link.rb +7 -6
  30. data/lib/ruby_lsp/requests/document_symbol.rb +5 -11
  31. data/lib/ruby_lsp/requests/folding_ranges.rb +11 -6
  32. data/lib/ruby_lsp/requests/hover.rb +18 -24
  33. data/lib/ruby_lsp/requests/inlay_hints.rb +7 -8
  34. data/lib/ruby_lsp/requests/on_type_formatting.rb +12 -2
  35. data/lib/ruby_lsp/requests/semantic_highlighting.rb +10 -8
  36. data/lib/ruby_lsp/requests/signature_help.rb +53 -18
  37. data/lib/ruby_lsp/requests/support/common.rb +23 -10
  38. data/lib/ruby_lsp/requests/support/dependency_detector.rb +5 -1
  39. data/lib/ruby_lsp/requests.rb +0 -1
  40. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +29 -0
  41. data/lib/ruby_lsp/response_builders/document_symbol.rb +57 -0
  42. data/lib/ruby_lsp/response_builders/hover.rb +49 -0
  43. data/lib/ruby_lsp/response_builders/response_builder.rb +16 -0
  44. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +199 -0
  45. data/lib/ruby_lsp/response_builders/signature_help.rb +28 -0
  46. data/lib/ruby_lsp/response_builders.rb +13 -0
  47. data/lib/ruby_lsp/server.rb +3 -3
  48. data/lib/ruby_lsp/setup_bundler.rb +30 -5
  49. data/lib/ruby_lsp/store.rb +4 -4
  50. metadata +12 -7
  51. data/lib/ruby_lsp/listener.rb +0 -33
  52. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +0 -73
@@ -2,13 +2,12 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "shellwords"
5
- require_relative "../listener"
6
5
 
7
6
  module RubyLsp
8
7
  module Listeners
9
- class CodeLens < Listener
8
+ class CodeLens
10
9
  extend T::Sig
11
- extend T::Generic
10
+ include Requests::Support::Common
12
11
 
13
12
  BASE_COMMAND = T.let(
14
13
  begin
@@ -21,31 +20,25 @@ module RubyLsp
21
20
  )
22
21
  ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
23
22
  SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
24
-
25
- ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
26
-
27
- sig { override.returns(ResponseType) }
28
- attr_reader :_response
23
+ DESCRIBE_KEYWORD = T.let(:describe, Symbol)
24
+ IT_KEYWORD = T.let(:it, Symbol)
29
25
 
30
26
  sig do
31
27
  params(
28
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
32
29
  uri: URI::Generic,
33
- lenses_configuration: RequestConfig,
34
30
  dispatcher: Prism::Dispatcher,
35
31
  ).void
36
32
  end
37
- def initialize(uri, lenses_configuration, dispatcher)
33
+ def initialize(response_builder, uri, dispatcher)
34
+ @response_builder = response_builder
38
35
  @uri = T.let(uri, URI::Generic)
39
- @_response = T.let([], ResponseType)
40
36
  @path = T.let(uri.to_standardized_path, T.nilable(String))
41
37
  # visibility_stack is a stack of [current_visibility, previous_visibility]
42
38
  @visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
43
- @class_stack = T.let([], T::Array[String])
39
+ @group_stack = T.let([], T::Array[String])
44
40
  @group_id = T.let(1, Integer)
45
41
  @group_id_stack = T.let([], T::Array[Integer])
46
- @lenses_configuration = lenses_configuration
47
-
48
- super(dispatcher)
49
42
 
50
43
  dispatcher.register(
51
44
  self,
@@ -61,13 +54,13 @@ module RubyLsp
61
54
  def on_class_node_enter(node)
62
55
  @visibility_stack.push([:public, :public])
63
56
  class_name = node.constant_path.slice
64
- @class_stack.push(class_name)
57
+ @group_stack.push(class_name)
65
58
 
66
59
  if @path && class_name.end_with?("Test")
67
60
  add_test_code_lens(
68
61
  node,
69
62
  name: class_name,
70
- command: generate_test_command(class_name: class_name),
63
+ command: generate_test_command(group_name: class_name),
71
64
  kind: :group,
72
65
  )
73
66
  end
@@ -79,13 +72,13 @@ module RubyLsp
79
72
  sig { params(node: Prism::ClassNode).void }
80
73
  def on_class_node_leave(node)
81
74
  @visibility_stack.pop
82
- @class_stack.pop
75
+ @group_stack.pop
83
76
  @group_id_stack.pop
84
77
  end
85
78
 
86
79
  sig { params(node: Prism::DefNode).void }
87
80
  def on_def_node_enter(node)
88
- class_name = @class_stack.last
81
+ class_name = @group_stack.last
89
82
  return unless class_name&.end_with?("Test")
90
83
 
91
84
  visibility, _ = @visibility_stack.last
@@ -95,7 +88,7 @@ module RubyLsp
95
88
  add_test_code_lens(
96
89
  node,
97
90
  name: method_name,
98
- command: generate_test_command(method_name: method_name, class_name: class_name),
91
+ command: generate_test_command(method_name: method_name, group_name: class_name),
99
92
  kind: :example,
100
93
  )
101
94
  end
@@ -120,16 +113,15 @@ module RubyLsp
120
113
  return
121
114
  end
122
115
 
123
- if @path&.include?(GEMFILE_NAME) && name == :gem && arguments
124
- return unless @lenses_configuration.enabled?(:gemfileLinks)
125
-
126
- first_argument = arguments.arguments.first
127
- return unless first_argument.is_a?(Prism::StringNode)
128
-
129
- remote = resolve_gem_remote(first_argument)
130
- return unless remote
131
-
132
- add_open_gem_remote_code_lens(node, remote)
116
+ if [DESCRIBE_KEYWORD, IT_KEYWORD].include?(name)
117
+ case name
118
+ when DESCRIBE_KEYWORD
119
+ add_spec_code_lens(node, kind: :group)
120
+ @group_id_stack.push(@group_id)
121
+ @group_id += 1
122
+ when IT_KEYWORD
123
+ add_spec_code_lens(node, kind: :example)
124
+ end
133
125
  end
134
126
  end
135
127
 
@@ -137,6 +129,9 @@ module RubyLsp
137
129
  def on_call_node_leave(node)
138
130
  _, prev_visibility = @visibility_stack.pop
139
131
  @visibility_stack.push([prev_visibility, prev_visibility])
132
+ if node.name == DESCRIBE_KEYWORD
133
+ @group_id_stack.pop
134
+ end
140
135
  end
141
136
 
142
137
  private
@@ -161,7 +156,7 @@ module RubyLsp
161
156
  grouping_data = { group_id: @group_id_stack.last, kind: kind }
162
157
  grouping_data[:id] = @group_id if kind == :group
163
158
 
164
- @_response << create_code_lens(
159
+ @response_builder << create_code_lens(
165
160
  node,
166
161
  title: "Run",
167
162
  command_name: "rubyLsp.runTest",
@@ -169,7 +164,7 @@ module RubyLsp
169
164
  data: { type: "test", **grouping_data },
170
165
  )
171
166
 
172
- @_response << create_code_lens(
167
+ @response_builder << create_code_lens(
173
168
  node,
174
169
  title: "Run In Terminal",
175
170
  command_name: "rubyLsp.runTestInTerminal",
@@ -177,7 +172,7 @@ module RubyLsp
177
172
  data: { type: "test_in_terminal", **grouping_data },
178
173
  )
179
174
 
180
- @_response << create_code_lens(
175
+ @response_builder << create_code_lens(
181
176
  node,
182
177
  title: "Debug",
183
178
  command_name: "rubyLsp.debugTest",
@@ -186,29 +181,19 @@ module RubyLsp
186
181
  )
187
182
  end
188
183
 
189
- sig { params(gem_name: Prism::StringNode).returns(T.nilable(String)) }
190
- def resolve_gem_remote(gem_name)
191
- spec = Gem::Specification.stubs.find { |gem| gem.name == gem_name.content }&.to_spec
192
- return if spec.nil?
193
-
194
- [spec.homepage, spec.metadata["source_code_uri"]].compact.find do |page|
195
- page.start_with?("https://github.com", "https://gitlab.com")
196
- end
197
- end
198
-
199
- sig { params(class_name: String, method_name: T.nilable(String)).returns(String) }
200
- def generate_test_command(class_name:, method_name: nil)
184
+ sig { params(group_name: String, method_name: T.nilable(String)).returns(String) }
185
+ def generate_test_command(group_name:, method_name: nil)
201
186
  command = BASE_COMMAND + T.must(@path)
202
187
 
203
188
  case DependencyDetector.instance.detected_test_library
204
189
  when "minitest"
205
190
  command += if method_name
206
- " --name " + "/#{Shellwords.escape(class_name + "#" + method_name)}/"
191
+ " --name " + "/#{Shellwords.escape(group_name + "#" + method_name)}/"
207
192
  else
208
- " --name " + "/#{Shellwords.escape(class_name)}/"
193
+ " --name " + "/#{Shellwords.escape(group_name)}/"
209
194
  end
210
195
  when "test-unit"
211
- command += " --testcase " + "/#{Shellwords.escape(class_name)}/"
196
+ command += " --testcase " + "/#{Shellwords.escape(group_name)}/"
212
197
 
213
198
  if method_name
214
199
  command += " --name " + Shellwords.escape(method_name)
@@ -218,14 +203,28 @@ module RubyLsp
218
203
  command
219
204
  end
220
205
 
221
- sig { params(node: Prism::CallNode, remote: String).void }
222
- def add_open_gem_remote_code_lens(node, remote)
223
- @_response << create_code_lens(
206
+ sig { params(node: Prism::CallNode, kind: Symbol).void }
207
+ def add_spec_code_lens(node, kind:)
208
+ arguments = node.arguments
209
+ return unless arguments
210
+
211
+ first_argument = arguments.arguments.first
212
+ return unless first_argument
213
+
214
+ name = case first_argument
215
+ when Prism::StringNode
216
+ first_argument.content
217
+ when Prism::ConstantReadNode
218
+ first_argument.full_name
219
+ end
220
+
221
+ return unless name
222
+
223
+ add_test_code_lens(
224
224
  node,
225
- title: "Open remote",
226
- command_name: "rubyLsp.openLink",
227
- arguments: [remote],
228
- data: { type: "link" },
225
+ name: name,
226
+ command: generate_test_command(group_name: name),
227
+ kind: kind,
229
228
  )
230
229
  end
231
230
  end
@@ -3,26 +3,21 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class Completion < Listener
6
+ class Completion
7
7
  extend T::Sig
8
- extend T::Generic
9
-
10
- ResponseType = type_member { { fixed: T::Array[Interface::CompletionItem] } }
11
-
12
- sig { override.returns(ResponseType) }
13
- attr_reader :_response
8
+ include Requests::Support::Common
14
9
 
15
10
  sig do
16
11
  params(
12
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
17
13
  index: RubyIndexer::Index,
18
14
  nesting: T::Array[String],
19
15
  typechecker_enabled: T::Boolean,
20
16
  dispatcher: Prism::Dispatcher,
21
17
  ).void
22
18
  end
23
- def initialize(index, nesting, typechecker_enabled, dispatcher)
24
- super(dispatcher)
25
- @_response = T.let([], ResponseType)
19
+ def initialize(response_builder, index, nesting, typechecker_enabled, dispatcher)
20
+ @response_builder = response_builder
26
21
  @index = index
27
22
  @nesting = nesting
28
23
  @typechecker_enabled = typechecker_enabled
@@ -39,7 +34,7 @@ module RubyLsp
39
34
  sig { params(node: Prism::StringNode).void }
40
35
  def on_string_node_enter(node)
41
36
  @index.search_require_paths(node.content).map!(&:require_path).sort!.each do |path|
42
- @_response << build_completion(T.must(path), node)
37
+ @response_builder << build_completion(T.must(path), node)
43
38
  end
44
39
  end
45
40
 
@@ -52,7 +47,7 @@ module RubyLsp
52
47
  candidates = @index.prefix_search(name, @nesting)
53
48
  candidates.each do |entries|
54
49
  complete_name = T.must(entries.first).name
55
- @_response << build_entry_completion(
50
+ @response_builder << build_entry_completion(
56
51
  complete_name,
57
52
  name,
58
53
  node,
@@ -96,7 +91,7 @@ module RubyLsp
96
91
 
97
92
  full_name = aliased_namespace.empty? ? constant_name : "#{aliased_namespace}::#{constant_name}"
98
93
 
99
- @_response << build_entry_completion(
94
+ @response_builder << build_entry_completion(
100
95
  full_name,
101
96
  name,
102
97
  node,
@@ -123,7 +118,7 @@ module RubyLsp
123
118
  entry = entries.find { |e| e.is_a?(RubyIndexer::Entry::Member) && e.owner&.name == receiver.name }
124
119
  next unless entry
125
120
 
126
- @_response << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
121
+ @response_builder << build_method_completion(T.cast(entry, RubyIndexer::Entry::Member), node)
127
122
  end
128
123
  end
129
124
 
@@ -147,7 +142,10 @@ module RubyLsp
147
142
  detail: "(#{entry.parameters.map(&:decorated_name).join(", ")})",
148
143
  description: entry.file_name,
149
144
  ),
150
- documentation: markdown_from_index_entries(name, entry),
145
+ documentation: Interface::MarkupContent.new(
146
+ kind: "markdown",
147
+ value: markdown_from_index_entries(name, entry),
148
+ ),
151
149
  )
152
150
  end
153
151
 
@@ -240,7 +238,10 @@ module RubyLsp
240
238
  label_details: Interface::CompletionItemLabelDetails.new(
241
239
  description: entries.map(&:file_name).join(","),
242
240
  ),
243
- documentation: markdown_from_index_entries(real_name, entries),
241
+ documentation: Interface::MarkupContent.new(
242
+ kind: "markdown",
243
+ value: markdown_from_index_entries(real_name, entries),
244
+ ),
244
245
  )
245
246
  end
246
247
 
@@ -3,17 +3,13 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class Definition < Listener
6
+ class Definition
7
7
  extend T::Sig
8
- extend T::Generic
9
-
10
- ResponseType = type_member { { fixed: T.nilable(T.any(T::Array[Interface::Location], Interface::Location)) } }
11
-
12
- sig { override.returns(ResponseType) }
13
- attr_reader :_response
8
+ include Requests::Support::Common
14
9
 
15
10
  sig do
16
11
  params(
12
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::Location],
17
13
  uri: URI::Generic,
18
14
  nesting: T::Array[String],
19
15
  index: RubyIndexer::Index,
@@ -21,14 +17,12 @@ module RubyLsp
21
17
  typechecker_enabled: T::Boolean,
22
18
  ).void
23
19
  end
24
- def initialize(uri, nesting, index, dispatcher, typechecker_enabled)
20
+ def initialize(response_builder, uri, nesting, index, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
21
+ @response_builder = response_builder
25
22
  @uri = uri
26
23
  @nesting = nesting
27
24
  @index = index
28
25
  @typechecker_enabled = typechecker_enabled
29
- @_response = T.let(nil, ResponseType)
30
-
31
- super(dispatcher)
32
26
 
33
27
  dispatcher.register(
34
28
  self,
@@ -75,7 +69,7 @@ module RubyLsp
75
69
  file_path = target_method.file_path
76
70
  return if @typechecker_enabled && not_in_dependencies?(file_path)
77
71
 
78
- @_response = Interface::Location.new(
72
+ @response_builder << Interface::Location.new(
79
73
  uri: URI::Generic.from_path(path: file_path).to_s,
80
74
  range: Interface::Range.new(
81
75
  start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
@@ -102,7 +96,7 @@ module RubyLsp
102
96
  if entry
103
97
  candidate = entry.full_path
104
98
 
105
- @_response = Interface::Location.new(
99
+ @response_builder << Interface::Location.new(
106
100
  uri: URI::Generic.from_path(path: candidate).to_s,
107
101
  range: Interface::Range.new(
108
102
  start: Interface::Position.new(line: 0, character: 0),
@@ -116,7 +110,7 @@ module RubyLsp
116
110
  current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
117
111
  candidate = File.expand_path(File.join(current_folder, required_file))
118
112
 
119
- @_response = Interface::Location.new(
113
+ @response_builder << Interface::Location.new(
120
114
  uri: URI::Generic.from_path(path: candidate).to_s,
121
115
  range: Interface::Range.new(
122
116
  start: Interface::Position.new(line: 0, character: 0),
@@ -136,7 +130,7 @@ module RubyLsp
136
130
  first_entry = T.must(entries.first)
137
131
  return if first_entry.visibility == :private && first_entry.name != "#{@nesting.join("::")}::#{value}"
138
132
 
139
- @_response = entries.filter_map do |entry|
133
+ entries.each do |entry|
140
134
  location = entry.location
141
135
  # If the project has Sorbet, then we only want to handle go to definition for constants defined in gems, as an
142
136
  # additional behavior on top of jumping to RBIs. Sorbet can already handle go to definition for all constants
@@ -144,7 +138,7 @@ module RubyLsp
144
138
  file_path = entry.file_path
145
139
  next if @typechecker_enabled && not_in_dependencies?(file_path)
146
140
 
147
- Interface::Location.new(
141
+ @response_builder << Interface::Location.new(
148
142
  uri: URI::Generic.from_path(path: file_path).to_s,
149
143
  range: Interface::Range.new(
150
144
  start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
@@ -3,10 +3,9 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Listeners
6
- class DocumentHighlight < Listener
6
+ class DocumentHighlight
7
7
  extend T::Sig
8
-
9
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } }
8
+ include Requests::Support::Common
10
9
 
11
10
  GLOBAL_VARIABLE_NODES = T.let(
12
11
  [
@@ -87,20 +86,16 @@ module RubyLsp
87
86
  T::Array[T.class_of(Prism::Node)],
88
87
  )
89
88
 
90
- sig { override.returns(ResponseType) }
91
- attr_reader :_response
92
-
93
89
  sig do
94
90
  params(
91
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight],
95
92
  target: T.nilable(Prism::Node),
96
93
  parent: T.nilable(Prism::Node),
97
94
  dispatcher: Prism::Dispatcher,
98
95
  ).void
99
96
  end
100
- def initialize(target, parent, dispatcher)
101
- super(dispatcher)
102
-
103
- @_response = T.let([], T::Array[Interface::DocumentHighlight])
97
+ def initialize(response_builder, target, parent, dispatcher)
98
+ @response_builder = response_builder
104
99
 
105
100
  return unless target && parent
106
101
 
@@ -521,7 +516,7 @@ module RubyLsp
521
516
 
522
517
  sig { params(kind: Integer, location: Prism::Location).void }
523
518
  def add_highlight(kind, location)
524
- @_response << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind)
519
+ @response_builder << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind)
525
520
  end
526
521
 
527
522
  sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(String)) }
@@ -5,11 +5,9 @@ require "ruby_lsp/requests/support/source_uri"
5
5
 
6
6
  module RubyLsp
7
7
  module Listeners
8
- class DocumentLink < Listener
8
+ class DocumentLink
9
9
  extend T::Sig
10
- extend T::Generic
11
-
12
- ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
10
+ include Requests::Support::Common
13
11
 
14
12
  GEM_TO_VERSION_MAP = T.let(
15
13
  [*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
@@ -59,25 +57,21 @@ module RubyLsp
59
57
  end
60
58
  end
61
59
 
62
- sig { override.returns(ResponseType) }
63
- attr_reader :_response
64
-
65
60
  sig do
66
61
  params(
62
+ response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::DocumentLink],
67
63
  uri: URI::Generic,
68
64
  comments: T::Array[Prism::Comment],
69
65
  dispatcher: Prism::Dispatcher,
70
66
  ).void
71
67
  end
72
- def initialize(uri, comments, dispatcher)
73
- super(dispatcher)
74
-
68
+ def initialize(response_builder, uri, comments, dispatcher)
75
69
  # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
76
70
  # in the URI
71
+ @response_builder = response_builder
77
72
  path = uri.to_standardized_path
78
73
  version_match = path ? /(?<=%40)[\d.]+(?=\.rbi$)/.match(path) : nil
79
74
  @gem_version = T.let(version_match && version_match[0], T.nilable(String))
80
- @_response = T.let([], T::Array[Interface::DocumentLink])
81
75
  @lines_to_comments = T.let(
82
76
  comments.to_h do |comment|
83
77
  [comment.location.end_line, comment]
@@ -137,7 +131,7 @@ module RubyLsp
137
131
  file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(uri.path))
138
132
  return if file_path.nil?
139
133
 
140
- @_response << Interface::DocumentLink.new(
134
+ @response_builder << Interface::DocumentLink.new(
141
135
  range: range_from_location(comment.location),
142
136
  target: "file://#{file_path}##{uri.line_number}",
143
137
  tooltip: "Jump to #{file_path}##{uri.line_number}",