ruby-lsp 0.12.2 → 0.13.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-check +20 -4
  5. data/exe/ruby-lsp-doctor +2 -2
  6. data/lib/ruby_indexer/lib/ruby_indexer/{visitor.rb → collector.rb} +144 -61
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +9 -4
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +89 -12
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +22 -4
  10. data/lib/ruby_indexer/ruby_indexer.rb +1 -1
  11. data/lib/ruby_indexer/test/configuration_test.rb +10 -0
  12. data/lib/ruby_indexer/test/index_test.rb +64 -0
  13. data/lib/ruby_indexer/test/method_test.rb +80 -0
  14. data/lib/ruby_lsp/addon.rb +9 -13
  15. data/lib/ruby_lsp/document.rb +7 -9
  16. data/lib/ruby_lsp/executor.rb +54 -51
  17. data/lib/ruby_lsp/internal.rb +4 -0
  18. data/lib/ruby_lsp/listener.rb +4 -5
  19. data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -4
  20. data/lib/ruby_lsp/requests/code_lens.rb +16 -7
  21. data/lib/ruby_lsp/requests/completion.rb +60 -8
  22. data/lib/ruby_lsp/requests/definition.rb +55 -29
  23. data/lib/ruby_lsp/requests/diagnostics.rb +0 -5
  24. data/lib/ruby_lsp/requests/document_highlight.rb +20 -11
  25. data/lib/ruby_lsp/requests/document_link.rb +2 -3
  26. data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
  27. data/lib/ruby_lsp/requests/folding_ranges.rb +12 -15
  28. data/lib/ruby_lsp/requests/formatting.rb +0 -5
  29. data/lib/ruby_lsp/requests/hover.rb +23 -4
  30. data/lib/ruby_lsp/requests/inlay_hints.rb +42 -4
  31. data/lib/ruby_lsp/requests/on_type_formatting.rb +18 -4
  32. data/lib/ruby_lsp/requests/semantic_highlighting.rb +41 -16
  33. data/lib/ruby_lsp/requests/support/common.rb +22 -2
  34. data/lib/ruby_lsp/requests/support/dependency_detector.rb +0 -1
  35. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +3 -8
  36. data/lib/ruby_lsp/requests/workspace_symbol.rb +6 -11
  37. data/lib/ruby_lsp/ruby_document.rb +14 -0
  38. data/lib/ruby_lsp/setup_bundler.rb +2 -0
  39. data/lib/ruby_lsp/store.rb +5 -3
  40. data/lib/ruby_lsp/utils.rb +8 -3
  41. metadata +8 -7
@@ -107,20 +107,15 @@ module RubyLsp
107
107
  sig { override.returns(ResponseType) }
108
108
  attr_reader :_response
109
109
 
110
- sig do
111
- params(
112
- dispatcher: Prism::Dispatcher,
113
- message_queue: Thread::Queue,
114
- range: T.nilable(T::Range[Integer]),
115
- ).void
116
- end
117
- def initialize(dispatcher, message_queue, range: nil)
118
- super(dispatcher, message_queue)
110
+ sig { params(dispatcher: Prism::Dispatcher, range: T.nilable(T::Range[Integer])).void }
111
+ def initialize(dispatcher, range: nil)
112
+ super(dispatcher)
119
113
 
120
114
  @_response = T.let([], ResponseType)
121
115
  @range = range
122
116
  @special_methods = T.let(nil, T.nilable(T::Array[String]))
123
117
  @current_scope = T.let(ParameterScope.new, ParameterScope)
118
+ @inside_regex_capture = T.let(false, T::Boolean)
124
119
 
125
120
  dispatcher.register(
126
121
  self,
@@ -135,7 +130,8 @@ module RubyLsp
135
130
  :on_local_variable_write_node_enter,
136
131
  :on_local_variable_read_node_enter,
137
132
  :on_block_parameter_node_enter,
138
- :on_keyword_parameter_node_enter,
133
+ :on_required_keyword_parameter_node_enter,
134
+ :on_optional_keyword_parameter_node_enter,
139
135
  :on_keyword_rest_parameter_node_enter,
140
136
  :on_optional_parameter_node_enter,
141
137
  :on_required_parameter_node_enter,
@@ -151,6 +147,8 @@ module RubyLsp
151
147
  :on_local_variable_or_write_node_enter,
152
148
  :on_local_variable_target_node_enter,
153
149
  :on_block_local_variable_node_enter,
150
+ :on_match_write_node_enter,
151
+ :on_match_write_node_leave,
154
152
  )
155
153
  end
156
154
 
@@ -164,14 +162,28 @@ module RubyLsp
164
162
  # We can't push a semantic token for [] and []= because the argument inside the brackets is a part of
165
163
  # the message_loc
166
164
  return if message.start_with?("[") && (message.end_with?("]") || message.end_with?("]="))
167
-
168
- return process_regexp_locals(node) if message == "=~"
165
+ return if message == "=~"
169
166
  return if special_method?(message)
170
167
 
171
168
  type = Support::Sorbet.annotation?(node) ? :type : :method
172
169
  add_token(T.must(node.message_loc), type)
173
170
  end
174
171
 
172
+ sig { params(node: Prism::MatchWriteNode).void }
173
+ def on_match_write_node_enter(node)
174
+ call = node.call
175
+
176
+ if call.message == "=~"
177
+ @inside_regex_capture = true
178
+ process_regexp_locals(call)
179
+ end
180
+ end
181
+
182
+ sig { params(node: Prism::MatchWriteNode).void }
183
+ def on_match_write_node_leave(node)
184
+ @inside_regex_capture = true if node.call.message == "=~"
185
+ end
186
+
175
187
  sig { params(node: Prism::ConstantReadNode).void }
176
188
  def on_constant_read_node_enter(node)
177
189
  return unless visible?(node, @range)
@@ -252,11 +264,18 @@ module RubyLsp
252
264
  @current_scope << name.to_sym if name
253
265
  end
254
266
 
255
- sig { params(node: Prism::KeywordParameterNode).void }
256
- def on_keyword_parameter_node_enter(node)
257
- name = node.name
258
- @current_scope << name.to_s.delete_suffix(":").to_sym if name
267
+ sig { params(node: Prism::RequiredKeywordParameterNode).void }
268
+ def on_required_keyword_parameter_node_enter(node)
269
+ @current_scope << node.name
270
+ return unless visible?(node, @range)
271
+
272
+ location = node.name_loc
273
+ add_token(location.copy(length: location.length - 1), :parameter)
274
+ end
259
275
 
276
+ sig { params(node: Prism::OptionalKeywordParameterNode).void }
277
+ def on_optional_keyword_parameter_node_enter(node)
278
+ @current_scope << node.name
260
279
  return unless visible?(node, @range)
261
280
 
262
281
  location = node.name_loc
@@ -351,6 +370,12 @@ module RubyLsp
351
370
 
352
371
  sig { params(node: Prism::LocalVariableTargetNode).void }
353
372
  def on_local_variable_target_node_enter(node)
373
+ # If we're inside a regex capture, Prism will add LocalVariableTarget nodes for each captured variable.
374
+ # Unfortunately, if the regex contains a backslash, the location will be incorrect and we'll end up highlighting
375
+ # the entire regex as a local variable. We process these captures in process_regexp_locals instead and then
376
+ # prevent pushing local variable target tokens. See https://github.com/ruby/prism/issues/1912
377
+ return if @inside_regex_capture
378
+
354
379
  return unless visible?(node, @range)
355
380
 
356
381
  add_token(node.location, @current_scope.type_for(node.name))
@@ -9,6 +9,9 @@ module RubyLsp
9
9
  # https://github.com/Shopify/ruby-lsp-rails, or addons by created by developers outside of Shopify, so be
10
10
  # cautious of changing anything.
11
11
  extend T::Sig
12
+ extend T::Helpers
13
+
14
+ requires_ancestor { Kernel }
12
15
 
13
16
  sig { params(node: Prism::Node).returns(Interface::Range) }
14
17
  def range_from_node(node)
@@ -66,12 +69,29 @@ module RubyLsp
66
69
  )
67
70
  end
68
71
 
69
- sig { params(title: String, entries: T::Array[RubyIndexer::Entry]).returns(Interface::MarkupContent) }
72
+ sig { params(file_path: String).returns(T.nilable(T::Boolean)) }
73
+ def defined_in_gem?(file_path)
74
+ DependencyDetector.instance.typechecker && BUNDLE_PATH && !file_path.start_with?(T.must(BUNDLE_PATH)) &&
75
+ !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
76
+ end
77
+
78
+ sig { params(node: Prism::CallNode).returns(T::Boolean) }
79
+ def self_receiver?(node)
80
+ receiver = node.receiver
81
+ receiver.nil? || receiver.is_a?(Prism::SelfNode)
82
+ end
83
+
84
+ sig do
85
+ params(
86
+ title: String,
87
+ entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
88
+ ).returns(Interface::MarkupContent)
89
+ end
70
90
  def markdown_from_index_entries(title, entries)
71
91
  markdown_title = "```ruby\n#{title}\n```"
72
92
  definitions = []
73
93
  content = +""
74
- entries.each do |entry|
94
+ Array(entries).each do |entry|
75
95
  loc = entry.location
76
96
 
77
97
  # We always handle locations as zero based. However, for file links in Markdown we need them to be one
@@ -66,7 +66,6 @@ module RubyLsp
66
66
 
67
67
  sig { returns(T::Array[String]) }
68
68
  def dependencies
69
- # NOTE: If changing this behaviour, it's likely that the VS Code extension will also need changed.
70
69
  @dependencies ||= T.let(
71
70
  begin
72
71
  Bundler.with_original_env { Bundler.default_gemfile }
@@ -34,15 +34,10 @@ module RubyLsp
34
34
 
35
35
  sig { override.params(uri: URI::Generic, document: Document).returns(T.nilable(String)) }
36
36
  def run(uri, document)
37
- relative_path = Pathname.new(T.must(uri.to_standardized_path || uri.opaque))
38
- .relative_path_from(T.must(WORKSPACE_URI.to_standardized_path))
39
- return if @options.ignore_files.any? { |pattern| File.fnmatch(pattern, relative_path) }
37
+ path = uri.to_standardized_path
38
+ return if path && @options.ignore_files.any? { |pattern| File.fnmatch?("*/#{pattern}", path) }
40
39
 
41
- SyntaxTree.format(
42
- document.source,
43
- @options.print_width,
44
- options: @options.formatter_options,
45
- )
40
+ SyntaxTree.format(document.source, @options.print_width, options: @options.formatter_options)
46
41
  end
47
42
  end
48
43
  end
@@ -20,6 +20,7 @@ module RubyLsp
20
20
  #
21
21
  class WorkspaceSymbol
22
22
  extend T::Sig
23
+ include Support::Common
23
24
 
24
25
  sig { params(query: T.nilable(String), index: RubyIndexer::Index).void }
25
26
  def initialize(query, index)
@@ -29,21 +30,11 @@ module RubyLsp
29
30
 
30
31
  sig { returns(T::Array[Interface::WorkspaceSymbol]) }
31
32
  def run
32
- bundle_path = begin
33
- Bundler.bundle_path.to_s
34
- rescue Bundler::GemfileNotFound
35
- nil
36
- end
37
-
38
33
  @index.fuzzy_search(@query).filter_map do |entry|
39
34
  # If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
40
35
  # we still return entries defined in gems to allow developers to jump directly to the source
41
36
  file_path = entry.file_path
42
- if DependencyDetector.instance.typechecker && bundle_path && !file_path.start_with?(bundle_path) &&
43
- !file_path.start_with?(RbConfig::CONFIG["rubylibdir"])
44
-
45
- next
46
- end
37
+ next if defined_in_gem?(file_path)
47
38
 
48
39
  # We should never show private symbols when searching the entire workspace
49
40
  next if entry.visibility == :private
@@ -82,6 +73,10 @@ module RubyLsp
82
73
  Constant::SymbolKind::NAMESPACE
83
74
  when RubyIndexer::Entry::Constant
84
75
  Constant::SymbolKind::CONSTANT
76
+ when RubyIndexer::Entry::Method
77
+ entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
78
+ when RubyIndexer::Entry::Accessor
79
+ Constant::SymbolKind::PROPERTY
85
80
  end
86
81
  end
87
82
  end
@@ -0,0 +1,14 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ class RubyDocument < Document
6
+ sig { override.returns(Prism::ParseResult) }
7
+ def parse
8
+ return @parse_result unless @needs_parsing
9
+
10
+ @needs_parsing = false
11
+ @parse_result = Prism.parse(@source)
12
+ end
13
+ end
14
+ end
@@ -12,6 +12,8 @@ require "time"
12
12
  # the Ruby LSP without including the gem in their application's Gemfile while at the same time giving us access to the
13
13
  # exact locked versions of dependencies.
14
14
 
15
+ Bundler.ui.level = :silent
16
+
15
17
  module RubyLsp
16
18
  class SetupBundler
17
19
  extend T::Sig
@@ -1,8 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "ruby_lsp/document"
5
-
6
4
  module RubyLsp
7
5
  class Store
8
6
  extend T::Sig
@@ -19,6 +17,9 @@ module RubyLsp
19
17
  sig { returns(T::Boolean) }
20
18
  attr_accessor :experimental_features
21
19
 
20
+ sig { returns(URI::Generic) }
21
+ attr_accessor :workspace_uri
22
+
22
23
  sig { void }
23
24
  def initialize
24
25
  @state = T.let({}, T::Hash[String, Document])
@@ -26,6 +27,7 @@ module RubyLsp
26
27
  @formatter = T.let("auto", String)
27
28
  @supports_progress = T.let(true, T::Boolean)
28
29
  @experimental_features = T.let(false, T::Boolean)
30
+ @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
29
31
  end
30
32
 
31
33
  sig { params(uri: URI::Generic).returns(Document) }
@@ -40,7 +42,7 @@ module RubyLsp
40
42
 
41
43
  sig { params(uri: URI::Generic, source: String, version: Integer).void }
42
44
  def set(uri:, source:, version:)
43
- document = Document.new(source: source, version: version, uri: uri, encoding: @encoding)
45
+ document = RubyDocument.new(source: source, version: version, uri: uri, encoding: @encoding)
44
46
  @state[uri.to_s] = document
45
47
  end
46
48
 
@@ -4,9 +4,14 @@
4
4
  module RubyLsp
5
5
  # Used to indicate that a request shouldn't return a response
6
6
  VOID = T.let(Object.new.freeze, Object)
7
-
8
- # This freeze is not redundant since the interpolated string is mutable
9
- WORKSPACE_URI = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
7
+ BUNDLE_PATH = T.let(
8
+ begin
9
+ Bundler.bundle_path.to_s
10
+ rescue Bundler::GemfileNotFound
11
+ nil
12
+ end,
13
+ T.nilable(String),
14
+ )
10
15
 
11
16
  # A notification to be sent to the client
12
17
  class Message
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.12.2
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-31 00:00:00.000000000 Z
11
+ date: 2023-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -30,20 +30,20 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.15.1
33
+ version: 0.18.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
- version: '0.16'
36
+ version: '0.19'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.15.1
43
+ version: 0.18.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.16'
46
+ version: '0.19'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sorbet-runtime
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -78,12 +78,12 @@ files:
78
78
  - lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
79
79
  - lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
80
80
  - lib/ruby-lsp.rb
81
+ - lib/ruby_indexer/lib/ruby_indexer/collector.rb
81
82
  - lib/ruby_indexer/lib/ruby_indexer/configuration.rb
82
83
  - lib/ruby_indexer/lib/ruby_indexer/entry.rb
83
84
  - lib/ruby_indexer/lib/ruby_indexer/index.rb
84
85
  - lib/ruby_indexer/lib/ruby_indexer/indexable_path.rb
85
86
  - lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
86
- - lib/ruby_indexer/lib/ruby_indexer/visitor.rb
87
87
  - lib/ruby_indexer/ruby_indexer.rb
88
88
  - lib/ruby_indexer/test/classes_and_modules_test.rb
89
89
  - lib/ruby_indexer/test/configuration_test.rb
@@ -132,6 +132,7 @@ files:
132
132
  - lib/ruby_lsp/requests/support/source_uri.rb
133
133
  - lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb
134
134
  - lib/ruby_lsp/requests/workspace_symbol.rb
135
+ - lib/ruby_lsp/ruby_document.rb
135
136
  - lib/ruby_lsp/server.rb
136
137
  - lib/ruby_lsp/setup_bundler.rb
137
138
  - lib/ruby_lsp/store.rb