ruby-lsp 0.12.2 → 0.13.0

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