ruby-lsp 0.17.3 → 0.17.4

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 (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: