ruby-lsp 0.19.1 → 0.20.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +7 -1
  4. data/lib/core_ext/uri.rb +2 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +63 -12
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +85 -36
  7. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +5 -1
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +39 -98
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +12 -19
  10. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +22 -0
  11. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -7
  12. data/lib/ruby_indexer/test/enhancements_test.rb +5 -7
  13. data/lib/ruby_indexer/test/global_variable_test.rb +49 -0
  14. data/lib/ruby_indexer/test/index_test.rb +89 -0
  15. data/lib/ruby_lsp/erb_document.rb +15 -2
  16. data/lib/ruby_lsp/listeners/definition.rb +20 -0
  17. data/lib/ruby_lsp/listeners/folding_ranges.rb +3 -3
  18. data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -2
  19. data/lib/ruby_lsp/requests/completion.rb +1 -1
  20. data/lib/ruby_lsp/requests/definition.rb +2 -1
  21. data/lib/ruby_lsp/requests/document_highlight.rb +5 -1
  22. data/lib/ruby_lsp/requests/hover.rb +1 -1
  23. data/lib/ruby_lsp/requests/range_formatting.rb +4 -2
  24. data/lib/ruby_lsp/requests/references.rb +3 -1
  25. data/lib/ruby_lsp/requests/rename.rb +3 -1
  26. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  27. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  28. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  29. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -6
  30. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -2
  31. data/lib/ruby_lsp/response_builders/hover.rb +2 -2
  32. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +14 -8
  33. data/lib/ruby_lsp/response_builders/signature_help.rb +2 -2
  34. data/lib/ruby_lsp/ruby_document.rb +33 -12
  35. data/lib/ruby_lsp/setup_bundler.rb +30 -8
  36. data/lib/ruby_lsp/type_inferrer.rb +1 -1
  37. metadata +6 -5
@@ -39,6 +39,7 @@ module RubyLsp
39
39
  :on_block_argument_node_enter,
40
40
  :on_constant_read_node_enter,
41
41
  :on_constant_path_node_enter,
42
+ :on_global_variable_read_node_enter,
42
43
  :on_instance_variable_read_node_enter,
43
44
  :on_instance_variable_write_node_enter,
44
45
  :on_instance_variable_and_write_node_enter,
@@ -120,6 +121,25 @@ module RubyLsp
120
121
  find_in_index(name)
121
122
  end
122
123
 
124
+ sig { params(node: Prism::GlobalVariableReadNode).void }
125
+ def on_global_variable_read_node_enter(node)
126
+ entries = @index[node.name.to_s]
127
+
128
+ return unless entries
129
+
130
+ entries.each do |entry|
131
+ location = entry.location
132
+
133
+ @response_builder << Interface::Location.new(
134
+ uri: URI::Generic.from_path(path: entry.file_path).to_s,
135
+ range: Interface::Range.new(
136
+ start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
137
+ end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
138
+ ),
139
+ )
140
+ end
141
+ end
142
+
123
143
  sig { params(node: Prism::InstanceVariableReadNode).void }
124
144
  def on_instance_variable_read_node_enter(node)
125
145
  handle_instance_variable_definition(node.name.to_s)
@@ -239,10 +239,10 @@ module RubyLsp
239
239
  statements = node.statements
240
240
  return unless statements
241
241
 
242
- body = statements.body
243
- return if body.empty?
242
+ statement = statements.body.last
243
+ return unless statement
244
244
 
245
- add_lines_range(node.location.start_line, body.last.location.end_line)
245
+ add_lines_range(node.location.start_line, statement.location.end_line)
246
246
  end
247
247
 
248
248
  sig { params(node: Prism::Node).void }
@@ -99,7 +99,13 @@ module RubyLsp
99
99
 
100
100
  # Find the closest statements node, so that we place the refactor in a valid position
101
101
  node_context = RubyDocument
102
- .locate(@document.parse_result.value, start_index, node_types: [Prism::StatementsNode, Prism::BlockNode])
102
+ .locate(@document.parse_result.value,
103
+ start_index,
104
+ node_types: [
105
+ Prism::StatementsNode,
106
+ Prism::BlockNode,
107
+ ],
108
+ code_units_cache: @document.code_units_cache)
103
109
 
104
110
  closest_statements = node_context.node
105
111
  parent_statements = node_context.parent
@@ -196,7 +202,7 @@ module RubyLsp
196
202
  @document.parse_result.value,
197
203
  start_index,
198
204
  node_types: [Prism::DefNode],
199
- encoding: @global_state.encoding,
205
+ code_units_cache: @document.code_units_cache,
200
206
  )
201
207
  closest_node = node_context.node
202
208
  return Error::InvalidTargetRange unless closest_node
@@ -57,7 +57,7 @@ module RubyLsp
57
57
  Prism::InstanceVariableTargetNode,
58
58
  Prism::InstanceVariableWriteNode,
59
59
  ],
60
- encoding: global_state.encoding,
60
+ code_units_cache: document.code_units_cache,
61
61
  )
62
62
  @response_builder = T.let(
63
63
  ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem].new,
@@ -46,6 +46,7 @@ module RubyLsp
46
46
  Prism::ConstantReadNode,
47
47
  Prism::ConstantPathNode,
48
48
  Prism::BlockArgumentNode,
49
+ Prism::GlobalVariableReadNode,
49
50
  Prism::InstanceVariableReadNode,
50
51
  Prism::InstanceVariableAndWriteNode,
51
52
  Prism::InstanceVariableOperatorWriteNode,
@@ -57,7 +58,7 @@ module RubyLsp
57
58
  Prism::SuperNode,
58
59
  Prism::ForwardingSuperNode,
59
60
  ],
60
- encoding: global_state.encoding,
61
+ code_units_cache: document.code_units_cache,
61
62
  )
62
63
 
63
64
  target = node_context.node
@@ -28,7 +28,11 @@ module RubyLsp
28
28
  char_position = document.create_scanner.find_char_position(position)
29
29
  delegate_request_if_needed!(global_state, document, char_position)
30
30
 
31
- node_context = RubyDocument.locate(document.parse_result.value, char_position, encoding: global_state.encoding)
31
+ node_context = RubyDocument.locate(
32
+ document.parse_result.value,
33
+ char_position,
34
+ code_units_cache: document.code_units_cache,
35
+ )
32
36
 
33
37
  @response_builder = T.let(
34
38
  ResponseBuilders::CollectionResponseBuilder[Interface::DocumentHighlight].new,
@@ -41,7 +41,7 @@ module RubyLsp
41
41
  document.parse_result.value,
42
42
  char_position,
43
43
  node_types: Listeners::Hover::ALLOWED_TARGETS,
44
- encoding: global_state.encoding,
44
+ code_units_cache: document.code_units_cache,
45
45
  )
46
46
  target = node_context.node
47
47
  parent = node_context.parent
@@ -34,16 +34,18 @@ module RubyLsp
34
34
  )
35
35
  return unless formatted_text
36
36
 
37
+ code_units_cache = @document.code_units_cache
38
+
37
39
  [
38
40
  Interface::TextEdit.new(
39
41
  range: Interface::Range.new(
40
42
  start: Interface::Position.new(
41
43
  line: location.start_line - 1,
42
- character: location.start_code_units_column(@document.encoding),
44
+ character: location.cached_start_code_units_column(code_units_cache),
43
45
  ),
44
46
  end: Interface::Position.new(
45
47
  line: location.end_line - 1,
46
- character: location.end_code_units_column(@document.encoding),
48
+ character: location.cached_end_code_units_column(code_units_cache),
47
49
  ),
48
50
  ),
49
51
  new_text: formatted_text.strip,
@@ -42,7 +42,7 @@ module RubyLsp
42
42
  Prism::CallNode,
43
43
  Prism::DefNode,
44
44
  ],
45
- encoding: @global_state.encoding,
45
+ code_units_cache: @document.code_units_cache,
46
46
  )
47
47
  target = node_context.node
48
48
  parent = node_context.parent
@@ -78,6 +78,8 @@ module RubyLsp
78
78
 
79
79
  parse_result = Prism.parse_file(path)
80
80
  collect_references(reference_target, parse_result, uri)
81
+ rescue Errno::EISDIR, Errno::ENOENT
82
+ # If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
81
83
  end
82
84
 
83
85
  @store.each do |_uri, document|
@@ -37,7 +37,7 @@ module RubyLsp
37
37
  @document.parse_result.value,
38
38
  char_position,
39
39
  node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
40
- encoding: @global_state.encoding,
40
+ code_units_cache: @document.code_units_cache,
41
41
  )
42
42
  target = node_context.node
43
43
  parent = node_context.parent
@@ -147,6 +147,8 @@ module RubyLsp
147
147
  parse_result = Prism.parse_file(path)
148
148
  edits = collect_changes(target, parse_result, name, uri)
149
149
  changes[uri.to_s] = edits unless edits.empty?
150
+ rescue Errno::EISDIR, Errno::ENOENT
151
+ # If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
150
152
  end
151
153
 
152
154
  @store.each do |uri, document|
@@ -101,7 +101,7 @@ module RubyLsp
101
101
  @range = range
102
102
  @result_id = T.let(SemanticHighlighting.next_result_id.to_s, String)
103
103
  @response_builder = T.let(
104
- ResponseBuilders::SemanticHighlighting.new(global_state.encoding),
104
+ ResponseBuilders::SemanticHighlighting.new(document.code_units_cache),
105
105
  ResponseBuilders::SemanticHighlighting,
106
106
  )
107
107
  Listeners::SemanticHighlighting.new(dispatcher, @response_builder)
@@ -43,7 +43,7 @@ module RubyLsp
43
43
  document.parse_result.value,
44
44
  char_position,
45
45
  node_types: [Prism::CallNode],
46
- encoding: global_state.encoding,
46
+ code_units_cache: document.code_units_cache,
47
47
  )
48
48
 
49
49
  target = adjust_for_nested_target(node_context.node, node_context.parent, position)
@@ -159,7 +159,7 @@ module RubyLsp
159
159
  def namespace_constant_name(node)
160
160
  path = node.constant_path
161
161
  case path
162
- when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode
162
+ when Prism::ConstantPathNode, Prism::ConstantReadNode
163
163
  constant_name(path)
164
164
  end
165
165
  end
@@ -47,12 +47,6 @@ module RubyLsp
47
47
 
48
48
  class ConfigurationError < StandardError; end
49
49
 
50
- sig { returns(T::Array[RuboCop::Cop::Offense]) }
51
- attr_reader :offenses
52
-
53
- sig { returns(::RuboCop::Config) }
54
- attr_reader :config_for_working_directory
55
-
56
50
  DEFAULT_ARGS = T.let(
57
51
  [
58
52
  "--stderr", # Print any output to stderr so that our stdout does not get polluted
@@ -63,6 +57,12 @@ module RubyLsp
63
57
  T::Array[String],
64
58
  )
65
59
 
60
+ sig { returns(T::Array[RuboCop::Cop::Offense]) }
61
+ attr_reader :offenses
62
+
63
+ sig { returns(::RuboCop::Config) }
64
+ attr_reader :config_for_working_directory
65
+
66
66
  begin
67
67
  RuboCop::Options.new.parse(["--raise-cop-error"])
68
68
  DEFAULT_ARGS << "--raise-cop-error"
@@ -4,6 +4,8 @@
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
6
  class DocumentSymbol < ResponseBuilder
7
+ extend T::Sig
8
+
7
9
  ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
8
10
 
9
11
  class SymbolHierarchyRoot
@@ -18,8 +20,6 @@ module RubyLsp
18
20
  end
19
21
  end
20
22
 
21
- extend T::Sig
22
-
23
23
  sig { void }
24
24
  def initialize
25
25
  super
@@ -4,11 +4,11 @@
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
6
  class Hover < ResponseBuilder
7
- ResponseType = type_member { { fixed: String } }
8
-
9
7
  extend T::Sig
10
8
  extend T::Generic
11
9
 
10
+ ResponseType = type_member { { fixed: String } }
11
+
12
12
  sig { void }
13
13
  def initialize
14
14
  super
@@ -6,6 +6,8 @@ module RubyLsp
6
6
  class SemanticHighlighting < ResponseBuilder
7
7
  class UndefinedTokenType < StandardError; end
8
8
 
9
+ extend T::Sig
10
+
9
11
  TOKEN_TYPES = T.let(
10
12
  {
11
13
  namespace: 0,
@@ -51,25 +53,29 @@ module RubyLsp
51
53
  T::Hash[Symbol, Integer],
52
54
  )
53
55
 
54
- extend T::Sig
55
-
56
56
  ResponseType = type_member { { fixed: Interface::SemanticTokens } }
57
57
 
58
- sig { params(encoding: Encoding).void }
59
- def initialize(encoding)
58
+ sig do
59
+ params(code_units_cache: T.any(
60
+ T.proc.params(arg0: Integer).returns(Integer),
61
+ Prism::CodeUnitsCache,
62
+ )).void
63
+ end
64
+ def initialize(code_units_cache)
60
65
  super()
61
- @encoding = encoding
66
+ @code_units_cache = code_units_cache
62
67
  @stack = T.let([], T::Array[SemanticToken])
63
68
  end
64
69
 
65
70
  sig { params(location: Prism::Location, type: Symbol, modifiers: T::Array[Symbol]).void }
66
71
  def add_token(location, type, modifiers = [])
67
- length = location.end_code_units_offset(@encoding) - location.start_code_units_offset(@encoding)
72
+ end_code_unit = location.cached_end_code_units_offset(@code_units_cache)
73
+ length = end_code_unit - location.cached_start_code_units_offset(@code_units_cache)
68
74
  modifiers_indices = modifiers.filter_map { |modifier| TOKEN_MODIFIERS[modifier] }
69
75
  @stack.push(
70
76
  SemanticToken.new(
71
77
  start_line: location.start_line,
72
- start_code_unit_column: location.start_code_units_column(@encoding),
78
+ start_code_unit_column: location.cached_start_code_units_column(@code_units_cache),
73
79
  length: length,
74
80
  type: T.must(TOKEN_TYPES[type]),
75
81
  modifier: modifiers_indices,
@@ -83,7 +89,7 @@ module RubyLsp
83
89
  return false unless token
84
90
 
85
91
  token.start_line == location.start_line &&
86
- token.start_code_unit_column == location.start_code_units_column(@encoding)
92
+ token.start_code_unit_column == location.cached_start_code_units_column(@code_units_cache)
87
93
  end
88
94
 
89
95
  sig { returns(T.nilable(SemanticToken)) }
@@ -4,10 +4,10 @@
4
4
  module RubyLsp
5
5
  module ResponseBuilders
6
6
  class SignatureHelp < ResponseBuilder
7
- ResponseType = type_member { { fixed: T.nilable(Interface::SignatureHelp) } }
8
-
9
7
  extend T::Sig
10
8
 
9
+ ResponseType = type_member { { fixed: T.nilable(Interface::SignatureHelp) } }
10
+
11
11
  sig { void }
12
12
  def initialize
13
13
  super
@@ -25,11 +25,14 @@ module RubyLsp
25
25
  params(
26
26
  node: Prism::Node,
27
27
  char_position: Integer,
28
+ code_units_cache: T.any(
29
+ T.proc.params(arg0: Integer).returns(Integer),
30
+ Prism::CodeUnitsCache,
31
+ ),
28
32
  node_types: T::Array[T.class_of(Prism::Node)],
29
- encoding: Encoding,
30
33
  ).returns(NodeContext)
31
34
  end
32
- def locate(node, char_position, node_types: [], encoding: Encoding::UTF_8)
35
+ def locate(node, char_position, code_units_cache:, node_types: [])
33
36
  queue = T.let(node.child_nodes.compact, T::Array[T.nilable(Prism::Node)])
34
37
  closest = node
35
38
  parent = T.let(nil, T.nilable(Prism::Node))
@@ -62,8 +65,8 @@ module RubyLsp
62
65
 
63
66
  # Skip if the current node doesn't cover the desired position
64
67
  loc = candidate.location
65
- loc_start_offset = loc.start_code_units_offset(encoding)
66
- loc_end_offset = loc.end_code_units_offset(encoding)
68
+ loc_start_offset = loc.cached_start_code_units_offset(code_units_cache)
69
+ loc_end_offset = loc.cached_end_code_units_offset(code_units_cache)
67
70
  next unless (loc_start_offset...loc_end_offset).cover?(char_position)
68
71
 
69
72
  # If the node's start character is already past the position, then we should've found the closest node
@@ -74,7 +77,7 @@ module RubyLsp
74
77
  # and need to pop the stack
75
78
  previous_level = nesting_nodes.last
76
79
  if previous_level &&
77
- (loc_start_offset > previous_level.location.end_code_units_offset(encoding))
80
+ (loc_start_offset > previous_level.location.cached_end_code_units_offset(code_units_cache))
78
81
  nesting_nodes.pop
79
82
  end
80
83
 
@@ -89,10 +92,10 @@ module RubyLsp
89
92
  if candidate.is_a?(Prism::CallNode)
90
93
  arg_loc = candidate.arguments&.location
91
94
  blk_loc = candidate.block&.location
92
- if (arg_loc && (arg_loc.start_code_units_offset(encoding)...
93
- arg_loc.end_code_units_offset(encoding)).cover?(char_position)) ||
94
- (blk_loc && (blk_loc.start_code_units_offset(encoding)...
95
- blk_loc.end_code_units_offset(encoding)).cover?(char_position))
95
+ if (arg_loc && (arg_loc.cached_start_code_units_offset(code_units_cache)...
96
+ arg_loc.cached_end_code_units_offset(code_units_cache)).cover?(char_position)) ||
97
+ (blk_loc && (blk_loc.cached_start_code_units_offset(code_units_cache)...
98
+ blk_loc.cached_end_code_units_offset(code_units_cache)).cover?(char_position))
96
99
  call_node = candidate
97
100
  end
98
101
  end
@@ -102,8 +105,8 @@ module RubyLsp
102
105
 
103
106
  # If the current node is narrower than or equal to the previous closest node, then it is more precise
104
107
  closest_loc = closest.location
105
- closest_node_start_offset = closest_loc.start_code_units_offset(encoding)
106
- closest_node_end_offset = closest_loc.end_code_units_offset(encoding)
108
+ closest_node_start_offset = closest_loc.cached_start_code_units_offset(code_units_cache)
109
+ closest_node_end_offset = closest_loc.cached_end_code_units_offset(code_units_cache)
107
110
  if loc_end_offset - loc_start_offset <= closest_node_end_offset - closest_node_start_offset
108
111
  parent = closest
109
112
  closest = candidate
@@ -131,12 +134,30 @@ module RubyLsp
131
134
  end
132
135
  end
133
136
 
137
+ sig do
138
+ returns(T.any(
139
+ T.proc.params(arg0: Integer).returns(Integer),
140
+ Prism::CodeUnitsCache,
141
+ ))
142
+ end
143
+ attr_reader :code_units_cache
144
+
145
+ sig { params(source: String, version: Integer, uri: URI::Generic, encoding: Encoding).void }
146
+ def initialize(source:, version:, uri:, encoding: Encoding::UTF_8)
147
+ super
148
+ @code_units_cache = T.let(@parse_result.code_units_cache(@encoding), T.any(
149
+ T.proc.params(arg0: Integer).returns(Integer),
150
+ Prism::CodeUnitsCache,
151
+ ))
152
+ end
153
+
134
154
  sig { override.returns(T::Boolean) }
135
155
  def parse!
136
156
  return false unless @needs_parsing
137
157
 
138
158
  @needs_parsing = false
139
159
  @parse_result = Prism.parse(@source)
160
+ @code_units_cache = @parse_result.code_units_cache(@encoding)
140
161
  true
141
162
  end
142
163
 
@@ -214,8 +235,8 @@ module RubyLsp
214
235
  RubyDocument.locate(
215
236
  @parse_result.value,
216
237
  create_scanner.find_char_position(position),
238
+ code_units_cache: @code_units_cache,
217
239
  node_types: node_types,
218
- encoding: @encoding,
219
240
  )
220
241
  end
221
242
  end
@@ -48,7 +48,9 @@ module RubyLsp
48
48
  @lockfile_hash_path = T.let(@custom_dir + "main_lockfile_hash", Pathname)
49
49
  @last_updated_path = T.let(@custom_dir + "last_updated", Pathname)
50
50
 
51
- @dependencies = T.let(load_dependencies, T::Hash[String, T.untyped])
51
+ dependencies, bundler_version = load_dependencies
52
+ @dependencies = T.let(dependencies, T::Hash[String, T.untyped])
53
+ @bundler_version = T.let(bundler_version, T.nilable(Gem::Version))
52
54
  @rails_app = T.let(rails_app?, T::Boolean)
53
55
  @retry = T.let(false, T::Boolean)
54
56
  end
@@ -156,14 +158,15 @@ module RubyLsp
156
158
  @custom_gemfile.write(content) unless @custom_gemfile.exist? && @custom_gemfile.read == content
157
159
  end
158
160
 
159
- sig { returns(T::Hash[String, T.untyped]) }
161
+ sig { returns([T::Hash[String, T.untyped], T.nilable(Gem::Version)]) }
160
162
  def load_dependencies
161
- return {} unless @lockfile&.exist?
163
+ return [{}, nil] unless @lockfile&.exist?
162
164
 
163
165
  # We need to parse the Gemfile.lock manually here. If we try to do `bundler/setup` to use something more
164
166
  # convenient, we may end up with issues when the globally installed `ruby-lsp` version mismatches the one included
165
167
  # in the `Gemfile`
166
- dependencies = Bundler::LockfileParser.new(@lockfile.read).dependencies
168
+ lockfile_parser = Bundler::LockfileParser.new(@lockfile.read)
169
+ dependencies = lockfile_parser.dependencies
167
170
 
168
171
  # When working on a gem, the `ruby-lsp` might be listed as a dependency in the gemspec. We need to make sure we
169
172
  # check those as well or else we may get version mismatch errors. Notice that bundler allows more than one
@@ -172,7 +175,7 @@ module RubyLsp
172
175
  dependencies.merge!(Bundler.load_gemspec(path).dependencies.to_h { |dep| [dep.name, dep] })
173
176
  end
174
177
 
175
- dependencies
178
+ [dependencies, lockfile_parser.bundler_version]
176
179
  end
177
180
 
178
181
  sig { params(bundle_gemfile: T.nilable(Pathname)).returns(T::Hash[String, String]) }
@@ -188,6 +191,16 @@ module RubyLsp
188
191
  env["BUNDLE_PATH"] = File.expand_path(env["BUNDLE_PATH"], @project_path)
189
192
  end
190
193
 
194
+ # If there's a Bundler version locked, then we need to use that one to run bundle commands, so that the composed
195
+ # lockfile is also locked to the same version. This avoids Bundler restarts on version mismatches
196
+ base_bundle = if @bundler_version
197
+ env["BUNDLER_VERSION"] = @bundler_version.to_s
198
+ install_bundler_if_needed
199
+ "bundle _#{@bundler_version}_"
200
+ else
201
+ "bundle"
202
+ end
203
+
191
204
  # If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
192
205
  # to upgrade them or else we'll produce undesired source control changes. If the custom bundle was just created
193
206
  # and any of `ruby-lsp`, `ruby-lsp-rails` or `debug` weren't a part of the Gemfile, then we need to run `bundle
@@ -196,13 +209,13 @@ module RubyLsp
196
209
 
197
210
  # When not updating, we run `(bundle check || bundle install)`
198
211
  # When updating, we run `((bundle check && bundle update ruby-lsp debug) || bundle install)`
199
- command = +"(bundle check"
212
+ command = +"(#{base_bundle} check"
200
213
 
201
214
  if should_bundle_update?
202
215
  # If any of `ruby-lsp`, `ruby-lsp-rails` or `debug` are not in the Gemfile, try to update them to the latest
203
216
  # version
204
217
  command.prepend("(")
205
- command << " && bundle update "
218
+ command << " && #{base_bundle} update "
206
219
  command << "ruby-lsp " unless @dependencies["ruby-lsp"]
207
220
  command << "debug " unless @dependencies["debug"]
208
221
  command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
@@ -212,7 +225,7 @@ module RubyLsp
212
225
  @last_updated_path.write(Time.now.iso8601)
213
226
  end
214
227
 
215
- command << " || bundle install) "
228
+ command << " || #{base_bundle} install) "
216
229
 
217
230
  # Redirect stdout to stderr to prevent going into an infinite loop. The extension might confuse stdout output with
218
231
  # responses
@@ -259,6 +272,15 @@ module RubyLsp
259
272
  end
260
273
  end
261
274
 
275
+ sig { void }
276
+ def install_bundler_if_needed
277
+ # Try to find the bundler version specified in the lockfile in installed gems. If not found, install it
278
+ requirement = Gem::Requirement.new(@bundler_version.to_s)
279
+ return if Gem::Specification.any? { |s| s.name == "bundler" && requirement =~ s.version }
280
+
281
+ Gem.install("bundler", @bundler_version.to_s)
282
+ end
283
+
262
284
  sig { returns(T::Boolean) }
263
285
  def should_bundle_update?
264
286
  # If `ruby-lsp`, `ruby-lsp-rails` and `debug` are in the Gemfile, then we shouldn't try to upgrade them or else it
@@ -93,7 +93,7 @@ module RubyLsp
93
93
  raw_receiver = if receiver.is_a?(Prism::CallNode)
94
94
  receiver.message
95
95
  else
96
- receiver&.slice
96
+ receiver.slice
97
97
  end
98
98
 
99
99
  if raw_receiver
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.1
4
+ version: 0.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-04 00:00:00.000000000 Z
11
+ date: 2024-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: '1.2'
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '2.0'
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: '1.1'
43
+ version: '1.2'
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '2.0'
@@ -111,6 +111,7 @@ files:
111
111
  - lib/ruby_indexer/test/configuration_test.rb
112
112
  - lib/ruby_indexer/test/constant_test.rb
113
113
  - lib/ruby_indexer/test/enhancements_test.rb
114
+ - lib/ruby_indexer/test/global_variable_test.rb
114
115
  - lib/ruby_indexer/test/index_test.rb
115
116
  - lib/ruby_indexer/test/instance_variables_test.rb
116
117
  - lib/ruby_indexer/test/method_test.rb
@@ -211,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
212
  - !ruby/object:Gem::Version
212
213
  version: '0'
213
214
  requirements: []
214
- rubygems_version: 3.5.20
215
+ rubygems_version: 3.5.21
215
216
  signing_key:
216
217
  specification_version: 4
217
218
  summary: An opinionated language server for Ruby