ruby-lsp 0.17.3 → 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +241 -91
  4. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -102
  5. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +81 -19
  6. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +50 -2
  7. data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
  8. data/lib/ruby_indexer/test/index_test.rb +326 -27
  9. data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
  10. data/lib/ruby_indexer/test/method_test.rb +54 -24
  11. data/lib/ruby_indexer/test/rbs_indexer_test.rb +27 -2
  12. data/lib/ruby_lsp/document.rb +37 -8
  13. data/lib/ruby_lsp/global_state.rb +7 -3
  14. data/lib/ruby_lsp/internal.rb +1 -0
  15. data/lib/ruby_lsp/listeners/completion.rb +53 -14
  16. data/lib/ruby_lsp/listeners/definition.rb +11 -7
  17. data/lib/ruby_lsp/listeners/hover.rb +14 -7
  18. data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
  19. data/lib/ruby_lsp/node_context.rb +6 -1
  20. data/lib/ruby_lsp/requests/completion.rb +5 -4
  21. data/lib/ruby_lsp/requests/completion_resolve.rb +8 -0
  22. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
  23. data/lib/ruby_lsp/requests/support/common.rb +19 -1
  24. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
  25. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +91 -0
  26. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
  27. data/lib/ruby_lsp/requests.rb +2 -0
  28. data/lib/ruby_lsp/server.rb +54 -15
  29. data/lib/ruby_lsp/test_helper.rb +1 -1
  30. data/lib/ruby_lsp/type_inferrer.rb +86 -0
  31. metadata +5 -2
@@ -36,7 +36,7 @@ module RubyLsp
36
36
  def provider
37
37
  Interface::CompletionOptions.new(
38
38
  resolve_provider: true,
39
- trigger_characters: ["/", "\"", "'", ":", "@"],
39
+ trigger_characters: ["/", "\"", "'", ":", "@", "."],
40
40
  completion_item: {
41
41
  labelDetailsSupport: true,
42
42
  },
@@ -48,18 +48,18 @@ module RubyLsp
48
48
  params(
49
49
  document: Document,
50
50
  global_state: GlobalState,
51
- position: T::Hash[Symbol, T.untyped],
51
+ params: T::Hash[Symbol, T.untyped],
52
52
  typechecker_enabled: T::Boolean,
53
53
  dispatcher: Prism::Dispatcher,
54
54
  ).void
55
55
  end
56
- def initialize(document, global_state, position, typechecker_enabled, dispatcher)
56
+ def initialize(document, global_state, params, typechecker_enabled, dispatcher)
57
57
  super()
58
58
  @target = T.let(nil, T.nilable(Prism::Node))
59
59
  @dispatcher = dispatcher
60
60
  # Completion always receives the position immediately after the character that was just typed. Here we adjust it
61
61
  # back by 1, so that we find the right node
62
- char_position = document.create_scanner.find_char_position(position) - 1
62
+ char_position = document.create_scanner.find_char_position(params[:position]) - 1
63
63
  node_context = document.locate(
64
64
  document.tree,
65
65
  char_position,
@@ -87,6 +87,7 @@ module RubyLsp
87
87
  typechecker_enabled,
88
88
  dispatcher,
89
89
  document.uri,
90
+ params.dig(:context, :triggerCharacter),
90
91
  )
91
92
 
92
93
  Addon.addons.each do |addon|
@@ -56,8 +56,16 @@ module RubyLsp
56
56
  end
57
57
  end
58
58
 
59
+ first_entry = T.must(entries.first)
60
+
61
+ if first_entry.is_a?(RubyIndexer::Entry::Member)
62
+ detail = first_entry.decorated_parameters
63
+ label = "#{label}#{first_entry.decorated_parameters}"
64
+ end
65
+
59
66
  @item[:labelDetails] = Interface::CompletionItemLabelDetails.new(
60
67
  description: entries.take(MAX_DOCUMENTATION_ENTRIES).map(&:file_name).join(","),
68
+ detail: detail,
61
69
  )
62
70
 
63
71
  @item[:documentation] = Interface::MarkupContent.new(
@@ -0,0 +1,88 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Prepare type hierarchy demo](../../prepare_type_hierarchy.gif)
7
+ #
8
+ # The [prepare type hierarchy
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareTypeHierarchy)
10
+ # displays the list of ancestors (supertypes) and descendants (subtypes) for the selected type.
11
+ #
12
+ # Currently only supports supertypes due to a limitation of the index.
13
+ #
14
+ # # Example
15
+ #
16
+ # ```ruby
17
+ # class Foo; end
18
+ # class Bar < Foo; end
19
+ #
20
+ # puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
21
+ # ```
22
+ class PrepareTypeHierarchy < Request
23
+ extend T::Sig
24
+
25
+ include Support::Common
26
+
27
+ class << self
28
+ extend T::Sig
29
+
30
+ sig { returns(Interface::TypeHierarchyOptions) }
31
+ def provider
32
+ Interface::TypeHierarchyOptions.new
33
+ end
34
+ end
35
+
36
+ sig do
37
+ params(
38
+ document: Document,
39
+ index: RubyIndexer::Index,
40
+ position: T::Hash[Symbol, T.untyped],
41
+ ).void
42
+ end
43
+ def initialize(document, index, position)
44
+ super()
45
+
46
+ @document = document
47
+ @index = index
48
+ @position = position
49
+ end
50
+
51
+ sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
52
+ def perform
53
+ context = @document.locate_node(
54
+ @position,
55
+ node_types: [
56
+ Prism::ConstantReadNode,
57
+ Prism::ConstantWriteNode,
58
+ Prism::ConstantPathNode,
59
+ ],
60
+ )
61
+
62
+ node = context.node
63
+ parent = context.parent
64
+ return unless node && parent
65
+
66
+ target = determine_target(node, parent, @position)
67
+ entries = @index.resolve(target.slice, context.nesting)
68
+ return unless entries
69
+
70
+ # While the spec allows for multiple entries, VSCode seems to only support one
71
+ # We'll just return the first one for now
72
+ first_entry = T.must(entries.first)
73
+
74
+ range = range_from_location(first_entry.location)
75
+
76
+ [
77
+ Interface::TypeHierarchyItem.new(
78
+ name: first_entry.name,
79
+ kind: kind_for_entry(first_entry),
80
+ uri: URI::Generic.from_path(path: first_entry.file_path).to_s,
81
+ range: range,
82
+ selection_range: range,
83
+ ),
84
+ ]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -26,7 +26,7 @@ module RubyLsp
26
26
  )
27
27
  end
28
28
 
29
- sig { params(location: Prism::Location).returns(Interface::Range) }
29
+ sig { params(location: T.any(Prism::Location, RubyIndexer::Location)).returns(Interface::Range) }
30
30
  def range_from_location(location)
31
31
  Interface::Range.new(
32
32
  start: Interface::Position.new(
@@ -186,6 +186,24 @@ module RubyLsp
186
186
  current = current.parent
187
187
  end
188
188
  end
189
+
190
+ sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
191
+ def kind_for_entry(entry)
192
+ case entry
193
+ when RubyIndexer::Entry::Class
194
+ Constant::SymbolKind::CLASS
195
+ when RubyIndexer::Entry::Module
196
+ Constant::SymbolKind::NAMESPACE
197
+ when RubyIndexer::Entry::Constant
198
+ Constant::SymbolKind::CONSTANT
199
+ when RubyIndexer::Entry::Method
200
+ entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
201
+ when RubyIndexer::Entry::Accessor
202
+ Constant::SymbolKind::PROPERTY
203
+ when RubyIndexer::Entry::InstanceVariable
204
+ Constant::SymbolKind::FIELD
205
+ end
206
+ end
189
207
  end
190
208
  end
191
209
  end
@@ -42,7 +42,7 @@ module RubyLsp
42
42
  def to_lsp_code_actions
43
43
  code_actions = []
44
44
 
45
- code_actions << autocorrect_action if @offense.correctable?
45
+ code_actions << autocorrect_action if correctable?
46
46
  code_actions << disable_line_action
47
47
 
48
48
  code_actions
@@ -70,7 +70,7 @@ module RubyLsp
70
70
  ),
71
71
  ),
72
72
  data: {
73
- correctable: @offense.correctable?,
73
+ correctable: correctable?,
74
74
  code_actions: to_lsp_code_actions,
75
75
  },
76
76
  )
@@ -81,7 +81,7 @@ module RubyLsp
81
81
  sig { returns(String) }
82
82
  def message
83
83
  message = @offense.message
84
- message += "\n\nThis offense is not auto-correctable.\n" unless @offense.correctable?
84
+ message += "\n\nThis offense is not auto-correctable.\n" unless correctable?
85
85
  message
86
86
  end
87
87
 
@@ -115,7 +115,7 @@ module RubyLsp
115
115
  uri: @uri.to_s,
116
116
  version: nil,
117
117
  ),
118
- edits: @offense.correctable? ? offense_replacements : [],
118
+ edits: correctable? ? offense_replacements : [],
119
119
  ),
120
120
  ],
121
121
  ),
@@ -193,6 +193,14 @@ module RubyLsp
193
193
  line.length
194
194
  end
195
195
  end
196
+
197
+ # When `RuboCop::LSP.enable` is called, contextual autocorrect will not offer itself
198
+ # as `correctable?` to prevent annoying changes while typing. Instead check if
199
+ # a corrector is present. If it is, then that means some code transformation can be applied.
200
+ sig { returns(T::Boolean) }
201
+ def correctable?
202
+ !@offense.corrector.nil?
203
+ end
196
204
  end
197
205
  end
198
206
  end
@@ -0,0 +1,91 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Type hierarchy supertypes demo](../../type_hierarchy_supertypes.gif)
7
+ #
8
+ # The [type hierarchy supertypes
9
+ # request](https://microsoft.github.io/language-server-protocol/specification#typeHierarchy_supertypes)
10
+ # displays the list of ancestors (supertypes) for the selected type.
11
+ #
12
+ # # Example
13
+ #
14
+ # ```ruby
15
+ # class Foo; end
16
+ # class Bar < Foo; end
17
+ #
18
+ # puts Bar # <-- right click on `Bar` and select "Show Type Hierarchy"
19
+ # ```
20
+ class TypeHierarchySupertypes < Request
21
+ extend T::Sig
22
+
23
+ include Support::Common
24
+
25
+ sig { params(index: RubyIndexer::Index, item: T::Hash[Symbol, T.untyped]).void }
26
+ def initialize(index, item)
27
+ super()
28
+
29
+ @index = index
30
+ @item = item
31
+ end
32
+
33
+ sig { override.returns(T.nilable(T::Array[Interface::TypeHierarchyItem])) }
34
+ def perform
35
+ name = @item[:name]
36
+ entries = @index[name]
37
+
38
+ parents = T.let(Set.new, T::Set[RubyIndexer::Entry::Namespace])
39
+ return unless entries&.any?
40
+
41
+ entries.each do |entry|
42
+ next unless entry.is_a?(RubyIndexer::Entry::Namespace)
43
+
44
+ if entry.is_a?(RubyIndexer::Entry::Class)
45
+ parent_class_name = entry.parent_class
46
+ if parent_class_name
47
+ resolved_parent_entries = @index.resolve(parent_class_name, entry.nesting)
48
+ resolved_parent_entries&.each do |entry|
49
+ next unless entry.is_a?(RubyIndexer::Entry::Class)
50
+
51
+ parents << entry
52
+ end
53
+ end
54
+ end
55
+
56
+ entry.mixin_operations.each do |mixin_operation|
57
+ next if mixin_operation.is_a?(RubyIndexer::Entry::Extend)
58
+
59
+ mixin_name = mixin_operation.module_name
60
+ resolved_mixin_entries = @index.resolve(mixin_name, entry.nesting)
61
+ next unless resolved_mixin_entries
62
+
63
+ resolved_mixin_entries.each do |mixin_entry|
64
+ next unless mixin_entry.is_a?(RubyIndexer::Entry::Module)
65
+
66
+ parents << mixin_entry
67
+ end
68
+ end
69
+ end
70
+
71
+ parents.map { |entry| hierarchy_item(entry) }
72
+ end
73
+
74
+ private
75
+
76
+ sig { params(entry: RubyIndexer::Entry).returns(Interface::TypeHierarchyItem) }
77
+ def hierarchy_item(entry)
78
+ range = range_from_location(entry.location)
79
+
80
+ Interface::TypeHierarchyItem.new(
81
+ name: entry.name,
82
+ kind: kind_for_entry(entry),
83
+ uri: URI::Generic.from_path(path: entry.file_path).to_s,
84
+ range: range,
85
+ selection_range: range,
86
+ detail: entry.file_name,
87
+ )
88
+ end
89
+ end
90
+ end
91
+ end
@@ -52,7 +52,7 @@ module RubyLsp
52
52
 
53
53
  Interface::WorkspaceSymbol.new(
54
54
  name: entry.name,
55
- container_name: T.must(container).join("::"),
55
+ container_name: container.join("::"),
56
56
  kind: kind,
57
57
  location: Interface::Location.new(
58
58
  uri: URI::Generic.from_path(path: file_path).to_s,
@@ -64,26 +64,6 @@ module RubyLsp
64
64
  )
65
65
  end
66
66
  end
67
-
68
- private
69
-
70
- sig { params(entry: RubyIndexer::Entry).returns(T.nilable(Integer)) }
71
- def kind_for_entry(entry)
72
- case entry
73
- when RubyIndexer::Entry::Class
74
- Constant::SymbolKind::CLASS
75
- when RubyIndexer::Entry::Module
76
- Constant::SymbolKind::NAMESPACE
77
- when RubyIndexer::Entry::Constant
78
- Constant::SymbolKind::CONSTANT
79
- when RubyIndexer::Entry::Method
80
- entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
81
- when RubyIndexer::Entry::Accessor
82
- Constant::SymbolKind::PROPERTY
83
- when RubyIndexer::Entry::InstanceVariable
84
- Constant::SymbolKind::FIELD
85
- end
86
- end
87
67
  end
88
68
  end
89
69
  end
@@ -47,6 +47,8 @@ module RubyLsp
47
47
  autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
48
48
  autoload :WorkspaceSymbol, "ruby_lsp/requests/workspace_symbol"
49
49
  autoload :SignatureHelp, "ruby_lsp/requests/signature_help"
50
+ autoload :PrepareTypeHierarchy, "ruby_lsp/requests/prepare_type_hierarchy"
51
+ autoload :TypeHierarchySupertypes, "ruby_lsp/requests/type_hierarchy_supertypes"
50
52
 
51
53
  # :nodoc:
52
54
  module Support
@@ -68,6 +68,12 @@ module RubyLsp
68
68
  text_document_signature_help(message)
69
69
  when "textDocument/definition"
70
70
  text_document_definition(message)
71
+ when "textDocument/prepareTypeHierarchy"
72
+ text_document_prepare_type_hierarchy(message)
73
+ when "typeHierarchy/supertypes"
74
+ type_hierarchy_supertypes(message)
75
+ when "typeHierarchy/subtypes"
76
+ type_hierarchy_subtypes(message)
71
77
  when "workspace/didChangeWatchedFiles"
72
78
  workspace_did_change_watched_files(message)
73
79
  when "workspace/symbol"
@@ -76,6 +82,16 @@ module RubyLsp
76
82
  text_document_show_syntax_tree(message)
77
83
  when "rubyLsp/workspace/dependencies"
78
84
  workspace_dependencies(message)
85
+ when "rubyLsp/workspace/addons"
86
+ send_message(
87
+ Result.new(
88
+ id: message[:id],
89
+ response:
90
+ Addon.addons.map do |addon|
91
+ { name: addon.name, errored: addon.error? }
92
+ end,
93
+ ),
94
+ )
79
95
  when "$/cancelRequest"
80
96
  @mutex.synchronize { @cancelled_requests << message[:params][:id] }
81
97
  end
@@ -104,7 +120,7 @@ module RubyLsp
104
120
  ),
105
121
  )
106
122
 
107
- $stderr.puts(errored_addons.map(&:errors_details).join("\n\n"))
123
+ $stderr.puts(errored_addons.map(&:errors_details).join("\n\n")) unless @test_mode
108
124
  end
109
125
  end
110
126
 
@@ -152,6 +168,7 @@ module RubyLsp
152
168
  inlay_hint_provider = Requests::InlayHints.provider if enabled_features["inlayHint"]
153
169
  completion_provider = Requests::Completion.provider if enabled_features["completion"]
154
170
  signature_help_provider = Requests::SignatureHelp.provider if enabled_features["signatureHelp"]
171
+ type_hierarchy_provider = Requests::PrepareTypeHierarchy.provider if enabled_features["typeHierarchy"]
155
172
 
156
173
  response = {
157
174
  capabilities: Interface::ServerCapabilities.new(
@@ -175,8 +192,12 @@ module RubyLsp
175
192
  completion_provider: completion_provider,
176
193
  code_lens_provider: code_lens_provider,
177
194
  definition_provider: enabled_features["definition"],
178
- workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.typechecker,
195
+ workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.has_type_checker,
179
196
  signature_help_provider: signature_help_provider,
197
+ type_hierarchy_provider: type_hierarchy_provider,
198
+ experimental: {
199
+ addon_detection: true,
200
+ },
180
201
  ),
181
202
  serverInfo: {
182
203
  name: "Ruby LSP",
@@ -449,7 +470,7 @@ module RubyLsp
449
470
 
450
471
  sig { params(document: Document).returns(T::Boolean) }
451
472
  def typechecker_enabled?(document)
452
- @global_state.typechecker && document.sorbet_sigil_is_true_or_higher
473
+ @global_state.has_type_checker && document.sorbet_sigil_is_true_or_higher
453
474
  end
454
475
 
455
476
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
@@ -551,7 +572,7 @@ module RubyLsp
551
572
  response: Requests::Completion.new(
552
573
  document,
553
574
  @global_state,
554
- params[:position],
575
+ params,
555
576
  typechecker_enabled?(document),
556
577
  dispatcher,
557
578
  ).perform,
@@ -660,6 +681,33 @@ module RubyLsp
660
681
  send_message(Result.new(id: message[:id], response: response))
661
682
  end
662
683
 
684
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
685
+ def text_document_prepare_type_hierarchy(message)
686
+ params = message[:params]
687
+ response = Requests::PrepareTypeHierarchy.new(
688
+ @store.get(params.dig(:textDocument, :uri)),
689
+ @global_state.index,
690
+ params[:position],
691
+ ).perform
692
+ send_message(Result.new(id: message[:id], response: response))
693
+ end
694
+
695
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
696
+ def type_hierarchy_supertypes(message)
697
+ response = Requests::TypeHierarchySupertypes.new(
698
+ @global_state.index,
699
+ message.dig(:params, :item),
700
+ ).perform
701
+ send_message(Result.new(id: message[:id], response: response))
702
+ end
703
+
704
+ sig { params(message: T::Hash[Symbol, T.untyped]).void }
705
+ def type_hierarchy_subtypes(message)
706
+ # TODO: implement subtypes
707
+ # The current index representation doesn't allow us to find the children of an entry.
708
+ send_message(Result.new(id: message[:id], response: nil))
709
+ end
710
+
663
711
  sig { params(message: T::Hash[Symbol, T.untyped]).void }
664
712
  def workspace_dependencies(message)
665
713
  response = begin
@@ -690,23 +738,14 @@ module RubyLsp
690
738
 
691
739
  sig { params(config_hash: T::Hash[String, T.untyped]).void }
692
740
  def perform_initial_indexing(config_hash)
693
- index_ruby_core
694
- index_ruby_code(config_hash)
695
- end
696
-
697
- sig { void }
698
- def index_ruby_core
699
- RubyIndexer::RBSIndexer.new(@global_state.index).index_ruby_core
700
- end
701
-
702
- sig { params(config_hash: T::Hash[String, T.untyped]).void }
703
- def index_ruby_code(config_hash)
704
741
  # The begin progress invocation happens during `initialize`, so that the notification is sent before we are
705
742
  # stuck indexing files
706
743
  RubyIndexer.configuration.apply_config(config_hash)
707
744
 
708
745
  Thread.new do
709
746
  begin
747
+ RubyIndexer::RBSIndexer.new(@global_state.index).index_ruby_core
748
+
710
749
  @global_state.index.index_all do |percentage|
711
750
  progress("indexing-progress", percentage)
712
751
  true
@@ -20,7 +20,7 @@ module RubyLsp
20
20
  def with_server(source = nil, uri = Kernel.URI("file:///fake.rb"), stub_no_typechecker: false, load_addons: true,
21
21
  &block)
22
22
  server = RubyLsp::Server.new(test_mode: true)
23
- server.global_state.stubs(:typechecker).returns(false) if stub_no_typechecker
23
+ server.global_state.stubs(:has_type_checker).returns(false) if stub_no_typechecker
24
24
  server.global_state.apply_options({})
25
25
 
26
26
  if source
@@ -0,0 +1,86 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ # A minimalistic type checker to try to resolve types that can be inferred without requiring a type system or
6
+ # annotations
7
+ class TypeInferrer
8
+ extend T::Sig
9
+
10
+ sig { params(index: RubyIndexer::Index).void }
11
+ def initialize(index)
12
+ @index = index
13
+ end
14
+
15
+ sig { params(node_context: NodeContext).returns(T.nilable(String)) }
16
+ def infer_receiver_type(node_context)
17
+ node = node_context.node
18
+
19
+ case node
20
+ when Prism::CallNode
21
+ infer_receiver_for_call_node(node, node_context)
22
+ when Prism::InstanceVariableReadNode, Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableWriteNode,
23
+ Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode
24
+ nesting = node_context.nesting
25
+ # If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
26
+ # inherits from Object
27
+ return "Object" if nesting.empty?
28
+
29
+ fully_qualified_name = node_context.fully_qualified_name
30
+ return fully_qualified_name if node_context.surrounding_method
31
+
32
+ "#{fully_qualified_name}::<Class:#{nesting.last}>"
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ sig { params(node: Prism::CallNode, node_context: NodeContext).returns(T.nilable(String)) }
39
+ def infer_receiver_for_call_node(node, node_context)
40
+ receiver = node.receiver
41
+
42
+ case receiver
43
+ when Prism::SelfNode, nil
44
+ nesting = node_context.nesting
45
+ # If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
46
+ # inherits from Object
47
+ return "Object" if nesting.empty?
48
+ return node_context.fully_qualified_name if node_context.surrounding_method
49
+
50
+ # If we're not inside a method, then we're inside the body of a class or module, which is a singleton
51
+ # context
52
+ "#{nesting.join("::")}::<Class:#{nesting.last}>"
53
+ when Prism::ConstantPathNode, Prism::ConstantReadNode
54
+ # When the receiver is a constant reference, we have to try to resolve it to figure out the right
55
+ # receiver. But since the invocation is directly on the constant, that's the singleton context of that
56
+ # class/module
57
+ receiver_name = constant_name(receiver)
58
+ return unless receiver_name
59
+
60
+ resolved_receiver = @index.resolve(receiver_name, node_context.nesting)
61
+ name = resolved_receiver&.first&.name
62
+ return unless name
63
+
64
+ *parts, last = name.split("::")
65
+ return "#{last}::<Class:#{last}>" if parts.empty?
66
+
67
+ "#{parts.join("::")}::#{last}::<Class:#{last}>"
68
+ end
69
+ end
70
+
71
+ sig do
72
+ params(
73
+ node: T.any(
74
+ Prism::ConstantPathNode,
75
+ Prism::ConstantReadNode,
76
+ ),
77
+ ).returns(T.nilable(String))
78
+ end
79
+ def constant_name(node)
80
+ node.full_name
81
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
82
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
83
+ nil
84
+ end
85
+ end
86
+ end
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.17.3
4
+ version: 0.17.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-11 00:00:00.000000000 Z
11
+ date: 2024-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: language_server-protocol
@@ -152,6 +152,7 @@ files:
152
152
  - lib/ruby_lsp/requests/hover.rb
153
153
  - lib/ruby_lsp/requests/inlay_hints.rb
154
154
  - lib/ruby_lsp/requests/on_type_formatting.rb
155
+ - lib/ruby_lsp/requests/prepare_type_hierarchy.rb
155
156
  - lib/ruby_lsp/requests/request.rb
156
157
  - lib/ruby_lsp/requests/selection_ranges.rb
157
158
  - lib/ruby_lsp/requests/semantic_highlighting.rb
@@ -167,6 +168,7 @@ files:
167
168
  - lib/ruby_lsp/requests/support/sorbet.rb
168
169
  - lib/ruby_lsp/requests/support/source_uri.rb
169
170
  - lib/ruby_lsp/requests/support/syntax_tree_formatter.rb
171
+ - lib/ruby_lsp/requests/type_hierarchy_supertypes.rb
170
172
  - lib/ruby_lsp/requests/workspace_symbol.rb
171
173
  - lib/ruby_lsp/response_builders.rb
172
174
  - lib/ruby_lsp/response_builders/collection_response_builder.rb
@@ -180,6 +182,7 @@ files:
180
182
  - lib/ruby_lsp/setup_bundler.rb
181
183
  - lib/ruby_lsp/store.rb
182
184
  - lib/ruby_lsp/test_helper.rb
185
+ - lib/ruby_lsp/type_inferrer.rb
183
186
  - lib/ruby_lsp/utils.rb
184
187
  homepage: https://github.com/Shopify/ruby-lsp
185
188
  licenses: