ruby-lsp 0.19.1 → 0.20.1

Sign up to get free protection for your applications and to get access to all the features.
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