ruby-lsp 0.27.0.beta1 → 0.27.0.beta3

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +0 -46
  4. data/exe/ruby-lsp-check +0 -15
  5. data/lib/ruby_lsp/addon.rb +19 -19
  6. data/lib/ruby_lsp/global_state.rb +1 -6
  7. data/lib/ruby_lsp/internal.rb +3 -2
  8. data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
  9. data/lib/ruby_lsp/listeners/completion.rb +246 -382
  10. data/lib/ruby_lsp/listeners/definition.rb +7 -10
  11. data/lib/ruby_lsp/listeners/document_link.rb +4 -0
  12. data/lib/ruby_lsp/listeners/hover.rb +234 -82
  13. data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
  14. data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
  15. data/lib/ruby_lsp/listeners/test_discovery.rb +38 -15
  16. data/lib/ruby_lsp/listeners/test_style.rb +21 -9
  17. data/lib/ruby_lsp/node_context.rb +31 -8
  18. data/lib/ruby_lsp/requests/completion_resolve.rb +55 -39
  19. data/lib/ruby_lsp/requests/discover_tests.rb +5 -41
  20. data/lib/ruby_lsp/requests/hover.rb +2 -5
  21. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +66 -22
  22. data/lib/ruby_lsp/requests/references.rb +180 -66
  23. data/lib/ruby_lsp/requests/rename.rb +1 -1
  24. data/lib/ruby_lsp/requests/request.rb +3 -33
  25. data/lib/ruby_lsp/requests/support/common.rb +82 -68
  26. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
  27. data/lib/ruby_lsp/ruby_document.rb +0 -73
  28. data/lib/ruby_lsp/rubydex/declaration.rb +174 -0
  29. data/lib/ruby_lsp/rubydex/definition.rb +73 -0
  30. data/lib/ruby_lsp/rubydex/reference.rb +6 -1
  31. data/lib/ruby_lsp/rubydex/signature.rb +107 -0
  32. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  33. data/lib/ruby_lsp/server.rb +56 -171
  34. data/lib/ruby_lsp/test_helper.rb +0 -1
  35. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
  36. data/lib/ruby_lsp/type_inferrer.rb +89 -11
  37. metadata +12 -18
  38. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
  39. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
  40. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
  41. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
  42. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
  43. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
  44. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
  45. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
  46. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
  47. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
  48. data/lib/ruby_indexer/ruby_indexer.rb +0 -20
  49. data/lib/ruby_lsp/static_docs.rb +0 -20
  50. data/static_docs/break.md +0 -103
  51. data/static_docs/yield.md +0 -81
  52. /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
@@ -9,126 +9,240 @@ module RubyLsp
9
9
  class References < Request
10
10
  include Support::Common
11
11
 
12
+ MAX_NUMBER_OF_METHOD_CANDIDATES_WITHOUT_RECEIVER = 30
13
+
12
14
  #: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void
13
15
  def initialize(global_state, store, document, params)
14
16
  super()
15
17
  @global_state = global_state
18
+ @type_inferrer = global_state.type_inferrer #: TypeInferrer
19
+ @graph = global_state.graph #: Rubydex::Graph
16
20
  @store = store
17
21
  @document = document
18
22
  @params = params
19
23
  @locations = [] #: Array[Interface::Location]
24
+ @char_position = 0 #: Integer
20
25
  end
21
26
 
22
27
  # @override
23
28
  #: -> Array[Interface::Location]
24
29
  def perform
25
- position = @params[:position]
26
- char_position, _ = @document.find_index_by_position(position)
30
+ include_declarations = @params.dig(:context, :includeDeclaration) || false
31
+ @char_position, _ = @document.find_index_by_position(@params[:position])
27
32
 
28
33
  node_context = RubyDocument.locate(
29
34
  @document.ast,
30
- char_position,
35
+ @char_position,
31
36
  node_types: [
32
37
  Prism::ConstantReadNode,
33
38
  Prism::ConstantPathNode,
34
39
  Prism::ConstantPathTargetNode,
40
+ Prism::ConstantAndWriteNode,
41
+ Prism::ConstantOperatorWriteNode,
42
+ Prism::ConstantOrWriteNode,
43
+ Prism::ConstantTargetNode,
44
+ Prism::ConstantWriteNode,
35
45
  Prism::InstanceVariableAndWriteNode,
36
46
  Prism::InstanceVariableOperatorWriteNode,
37
47
  Prism::InstanceVariableOrWriteNode,
38
48
  Prism::InstanceVariableReadNode,
39
49
  Prism::InstanceVariableTargetNode,
40
50
  Prism::InstanceVariableWriteNode,
51
+ Prism::ClassVariableAndWriteNode,
52
+ Prism::ClassVariableOperatorWriteNode,
53
+ Prism::ClassVariableOrWriteNode,
54
+ Prism::ClassVariableReadNode,
55
+ Prism::ClassVariableTargetNode,
56
+ Prism::ClassVariableWriteNode,
57
+ Prism::GlobalVariableAndWriteNode,
58
+ Prism::GlobalVariableOperatorWriteNode,
59
+ Prism::GlobalVariableOrWriteNode,
60
+ Prism::GlobalVariableReadNode,
61
+ Prism::GlobalVariableTargetNode,
62
+ Prism::GlobalVariableWriteNode,
41
63
  Prism::CallNode,
64
+ Prism::CallAndWriteNode,
65
+ Prism::CallOperatorWriteNode,
66
+ Prism::CallOrWriteNode,
42
67
  Prism::DefNode,
43
68
  ],
44
69
  code_units_cache: @document.code_units_cache,
45
70
  )
46
71
  target = node_context.node
47
- parent = node_context.parent
48
72
  return @locations if !target || target.is_a?(Prism::ProgramNode)
49
73
 
50
- if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
51
- target = determine_target(
52
- target,
53
- parent,
54
- position,
55
- )
74
+ case target
75
+ when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
76
+ name = constant_name(target)
77
+ handle_constant_references(name, node_context, include_declarations) if name
78
+ when Prism::ConstantTargetNode
79
+ handle_constant_references(target.name.to_s, node_context, include_declarations)
80
+ when Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantOrWriteNode,
81
+ Prism::ConstantWriteNode
82
+ if cursor_on_name?(target.name_loc)
83
+ handle_constant_references(target.name.to_s, node_context, include_declarations)
84
+ end
85
+ when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode,
86
+ Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
87
+ handle_variable_references(target.name.to_s, node_context, include_declarations)
88
+ when Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode,
89
+ Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableWriteNode,
90
+ Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode,
91
+ Prism::ClassVariableOrWriteNode, Prism::ClassVariableWriteNode
92
+ if cursor_on_name?(target.name_loc)
93
+ handle_variable_references(target.name.to_s, node_context, include_declarations)
94
+ end
95
+ when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
96
+ handle_global_variable_references(target.name.to_s, include_declarations)
97
+ when Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode,
98
+ Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableWriteNode
99
+ if cursor_on_name?(target.name_loc)
100
+ handle_global_variable_references(target.name.to_s, include_declarations)
101
+ end
102
+ when Prism::CallNode
103
+ message_loc = target.message_loc
104
+ message = target.message
105
+ if message && message_loc && cursor_on_name?(message_loc)
106
+ resolve_method_references(message, node_context, include_declarations)
107
+ end
108
+ when Prism::CallAndWriteNode, Prism::CallOperatorWriteNode, Prism::CallOrWriteNode
109
+ message_loc = target.message_loc
110
+ if message_loc && cursor_on_name?(message_loc)
111
+ resolve_method_references(target.read_name.to_s, node_context, include_declarations)
112
+ end
113
+ when Prism::DefNode
114
+ handle_def_node_references(target, node_context, include_declarations) if cursor_on_name?(target.name_loc)
56
115
  end
57
116
 
58
- target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode,
117
+ @locations
118
+ end
59
119
 
60
- reference_target = create_reference_target(target, node_context)
61
- return @locations unless reference_target
120
+ private
62
121
 
63
- Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
64
- uri = URI::Generic.from_path(path: path)
65
- # If the document is being managed by the client, then we should use whatever is present in the store instead
66
- # of reading from disk
67
- next if @store.key?(uri)
122
+ #: (String name, NodeContext node_context, bool include_declarations) -> void
123
+ def handle_constant_references(name, node_context, include_declarations)
124
+ declaration = @graph.resolve_constant(name, node_context.nesting)
125
+ return unless declaration
68
126
 
69
- parse_result = Prism.parse_lex_file(path)
70
- collect_references(reference_target, parse_result, uri)
71
- rescue Errno::EISDIR, Errno::ENOENT
72
- # If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
127
+ collect_references(declaration.references, [declaration], include_declarations)
128
+ end
129
+
130
+ #: (String message, NodeContext node_context, bool include_declarations) -> void
131
+ def resolve_method_references(message, node_context, include_declarations)
132
+ receiver_type = @type_inferrer.infer_receiver_type(node_context)
133
+
134
+ declaration = if receiver_type
135
+ owner = @graph[receiver_type.name]
136
+ owner.find_member("#{message}()") if owner.is_a?(Rubydex::Namespace)
73
137
  end
74
138
 
75
- @store.each do |_uri, document|
76
- collect_references(reference_target, document.parse_result, document.uri)
139
+ declarations = if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && declaration.nil?)
140
+ @graph.search("##{message}()").take(MAX_NUMBER_OF_METHOD_CANDIDATES_WITHOUT_RECEIVER)
141
+ elsif declaration
142
+ [declaration]
143
+ else
144
+ []
77
145
  end
146
+ return if declarations.empty?
78
147
 
79
- @locations
148
+ collect_references(method_references_for(message, declarations), declarations, include_declarations)
80
149
  end
81
150
 
82
- private
151
+ # Handles instance and class variable references. Resolves the receiver type from the node context to locate
152
+ # the owning namespace, then looks up the member through the ancestor chain via `find_member`.
153
+ #: (String name, NodeContext node_context, bool include_declarations) -> void
154
+ def handle_variable_references(name, node_context, include_declarations)
155
+ type = @type_inferrer.infer_receiver_type(node_context)
156
+ return unless type
83
157
 
84
- #: ((Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode) target_node, NodeContext node_context) -> RubyIndexer::ReferenceFinder::Target?
85
- def create_reference_target(target_node, node_context)
86
- case target_node
87
- when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
88
- name = RubyIndexer::Index.constant_name(target_node)
89
- return unless name
158
+ owner = @graph[type.name]
159
+ return unless owner.is_a?(Rubydex::Namespace)
160
+
161
+ declaration = owner.find_member(name)
162
+ return unless declaration
163
+
164
+ collect_references(declaration.references, [declaration], include_declarations)
165
+ end
166
+
167
+ # Handles global variable references. Globals are keyed by their full name (including `$`) in the graph, so we
168
+ # can look them up directly without needing to resolve a receiver type.
169
+ #: (String name, bool include_declarations) -> void
170
+ def handle_global_variable_references(name, include_declarations)
171
+ declaration = @graph[name]
172
+ return unless declaration
173
+
174
+ collect_references(declaration.references, [declaration], include_declarations)
175
+ end
176
+
177
+ #: (Prism::DefNode target, NodeContext node_context, bool include_declarations) -> void
178
+ def handle_def_node_references(target, node_context, include_declarations)
179
+ method_name = target.name.to_s
90
180
 
91
- entries = @global_state.index.resolve(name, node_context.nesting)
92
- return unless entries
181
+ owner_type = @type_inferrer.infer_receiver_type(node_context)
182
+ return unless owner_type
93
183
 
94
- fully_qualified_name = entries.first #: as !nil
184
+ owner = @graph[owner_type.name]
185
+ return unless owner.is_a?(Rubydex::Namespace)
186
+
187
+ declaration = owner.find_member("#{method_name}()")
188
+ return unless declaration
189
+
190
+ collect_references(method_references_for(method_name, [declaration]), [declaration], include_declarations)
191
+ end
192
+
193
+ #: (String, Array[Rubydex::Declaration]) -> Array[Rubydex::MethodReference]
194
+ def method_references_for(method_name, declarations)
195
+ target_owner_names = declarations.map do |d|
196
+ d.owner #: as Rubydex::Namespace
95
197
  .name
96
- RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
97
- when
98
- Prism::InstanceVariableAndWriteNode,
99
- Prism::InstanceVariableOperatorWriteNode,
100
- Prism::InstanceVariableOrWriteNode,
101
- Prism::InstanceVariableReadNode,
102
- Prism::InstanceVariableTargetNode,
103
- Prism::InstanceVariableWriteNode
104
- receiver_type = @global_state.type_inferrer.infer_receiver_type(node_context)
105
- return unless receiver_type
106
-
107
- ancestors = @global_state.index.linearized_ancestors_of(receiver_type.name)
108
- RubyIndexer::ReferenceFinder::InstanceVariableTarget.new(target_node.name.to_s, ancestors)
109
- when Prism::CallNode, Prism::DefNode
110
- RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
198
+ end
199
+
200
+ @graph.method_references.select do |reference|
201
+ next false unless reference.name == method_name
202
+
203
+ receiver = reference.receiver
204
+ next true if receiver.nil? || target_owner_names.empty?
205
+
206
+ if receiver.is_a?(Rubydex::Namespace)
207
+ receiver.ancestors.any? { |ancestor| target_owner_names.include?(ancestor.name) }
208
+ else
209
+ target_owner_names.include?(receiver.name)
210
+ end
111
211
  end
112
212
  end
113
213
 
114
- #: (RubyIndexer::ReferenceFinder::Target target, Prism::LexResult parse_result, URI::Generic uri) -> void
115
- def collect_references(target, parse_result, uri)
116
- dispatcher = Prism::Dispatcher.new
117
- finder = RubyIndexer::ReferenceFinder.new(
118
- target,
119
- @global_state.index,
120
- dispatcher,
121
- uri,
122
- include_declarations: @params.dig(:context, :includeDeclaration) || true,
123
- )
124
- dispatcher.visit(parse_result.value.first)
214
+ #: (Enumerable[Rubydex::Reference] references, Array[Rubydex::Declaration] declarations, bool include_declarations) -> void
215
+ def collect_references(references, declarations, include_declarations)
216
+ references.each do |reference|
217
+ next if rubydex_internal_uri?(reference.location.uri)
125
218
 
126
- finder.references.each do |reference|
127
- @locations << Interface::Location.new(
128
- uri: uri.to_s,
129
- range: range_from_location(reference.location),
130
- )
219
+ @locations << reference.to_lsp_location
131
220
  end
221
+
222
+ return unless include_declarations
223
+
224
+ declarations.each do |declaration|
225
+ declaration.definitions.each do |definition|
226
+ next if rubydex_internal_uri?(definition.location.uri)
227
+
228
+ @locations << definition.to_lsp_selection_location
229
+ end
230
+ end
231
+ end
232
+
233
+ #: (String uri) -> bool
234
+ def rubydex_internal_uri?(uri)
235
+ URI(uri).scheme == "rubydex"
236
+ end
237
+
238
+ # Write, operator-write, and call-with-message nodes cover more than just the identifier —
239
+ # they span the whole assignment or call expression. We only resolve references when the
240
+ # cursor is positioned directly on the name itself, not on operators, values, or arguments.
241
+ #: (Prism::Location name_loc) -> bool
242
+ def cursor_on_name?(name_loc)
243
+ start = name_loc.cached_start_code_units_offset(@document.code_units_cache)
244
+ finish = name_loc.cached_end_code_units_offset(@document.code_units_cache)
245
+ (start...finish).cover?(@char_position)
132
246
  end
133
247
  end
134
248
  end
@@ -54,7 +54,7 @@ module RubyLsp
54
54
 
55
55
  target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode
56
56
 
57
- name = RubyIndexer::Index.constant_name(target)
57
+ name = constant_name(target)
58
58
  return unless name
59
59
 
60
60
  declaration = @graph.resolve_constant(name, node_context.nesting)
@@ -5,6 +5,8 @@ module RubyLsp
5
5
  module Requests
6
6
  # @abstract
7
7
  class Request
8
+ include Support::Common
9
+
8
10
  class InvalidFormatter < StandardError; end
9
11
 
10
12
  # @abstract
@@ -26,24 +28,6 @@ module RubyLsp
26
28
  end
27
29
  end
28
30
 
29
- # Checks if a location covers a position
30
- #: (Prism::Location location, untyped position) -> bool
31
- def cover?(location, position)
32
- start_covered =
33
- location.start_line - 1 < position[:line] ||
34
- (
35
- location.start_line - 1 == position[:line] &&
36
- location.start_column <= position[:character]
37
- )
38
- end_covered =
39
- location.end_line - 1 > position[:line] ||
40
- (
41
- location.end_line - 1 == position[:line] &&
42
- location.end_column >= position[:character]
43
- )
44
- start_covered && end_covered
45
- end
46
-
47
31
  # Based on a constant node target, a constant path node parent and a position, this method will find the exact
48
32
  # portion of the constant path that matches the requested position, for higher precision in hover and
49
33
  # definition. For example:
@@ -62,27 +46,13 @@ module RubyLsp
62
46
  parent = target #: as Prism::ConstantPathNode
63
47
  .parent #: Prism::Node?
64
48
 
65
- while parent && cover?(parent.location, position)
49
+ while parent && covers_position?(parent.location, position)
66
50
  target = parent
67
51
  parent = target.is_a?(Prism::ConstantPathNode) ? target.parent : nil
68
52
  end
69
53
 
70
54
  target
71
55
  end
72
-
73
- # Checks if a given location covers the position requested
74
- #: (Prism::Location? location, Hash[Symbol, untyped] position) -> bool
75
- def covers_position?(location, position)
76
- return false unless location
77
-
78
- start_line = location.start_line - 1
79
- end_line = location.end_line - 1
80
- line = position[:line]
81
- character = position[:character]
82
-
83
- (start_line < line || (start_line == line && location.start_column <= character)) &&
84
- (end_line > line || (end_line == line && location.end_column >= character))
85
- end
86
56
  end
87
57
  end
88
58
  end
@@ -23,7 +23,7 @@ module RubyLsp
23
23
  )
24
24
  end
25
25
 
26
- #: ((Prism::Location | RubyIndexer::Location) location) -> Interface::Range
26
+ #: ((Prism::Location | Rubydex::Location) location) -> Interface::Range
27
27
  def range_from_location(location)
28
28
  Interface::Range.new(
29
29
  start: Interface::Position.new(
@@ -34,6 +34,19 @@ module RubyLsp
34
34
  )
35
35
  end
36
36
 
37
+ #: (Prism::Location? location, Hash[Symbol, untyped] position) -> bool
38
+ def covers_position?(location, position)
39
+ return false unless location
40
+
41
+ start_line = location.start_line - 1
42
+ end_line = location.end_line - 1
43
+ line = position[:line]
44
+ character = position[:character]
45
+
46
+ (start_line < line || (start_line == line && location.start_column <= character)) &&
47
+ (end_line > line || (end_line == line && location.end_column >= character))
48
+ end
49
+
37
50
  #: (Prism::Node node, title: String, command_name: String, arguments: Array[untyped]?, data: Hash[untyped, untyped]?) -> Interface::CodeLens
38
51
  def create_code_lens(node, title:, command_name:, arguments:, data:)
39
52
  range = range_from_node(node)
@@ -64,6 +77,44 @@ module RubyLsp
64
77
  receiver.nil? || receiver.is_a?(Prism::SelfNode)
65
78
  end
66
79
 
80
+ # Returns true when a constant declaration is reachable from the call site. Private constants are only
81
+ # reachable from within the namespace where they are defined.
82
+ #
83
+ #: (Rubydex::Declaration declaration, String value, NodeContext node_context) -> bool
84
+ def constant_reachable_from_call_site?(declaration, value, node_context)
85
+ return true unless declaration.is_a?(Rubydex::Visibility) && declaration.private?
86
+
87
+ declaration.name == "#{node_context.fully_qualified_name}::#{value}"
88
+ end
89
+
90
+ # Returns true when a method is reachable from the call site, considering visibility and receiver type.
91
+ # A method is reachable when:
92
+ # - there's no concrete receiver type to compare against
93
+ # - the call site is inside the receiver's own namespace (implicit/self call)
94
+ # - it is public
95
+ # - it is protected and the call site's class is in the same hierarchy as the method's defining class
96
+ #
97
+ # The `method_decl` is duck-typed to support `Rubydex::Method`, `RubyIndexer::Entry::Member` and
98
+ # `RubyIndexer::Entry::MethodAlias`. All respond to `public?`, `private?` and `owner` (an object with a
99
+ # `name` attribute).
100
+ #
101
+ #: (Rubydex::Method method_decl, TypeInferrer::Type? receiver_type, Rubydex::Graph graph, NodeContext node_context) -> bool
102
+ def method_reachable_from_call_site?(method_decl, receiver_type, graph, node_context)
103
+ return true unless receiver_type
104
+
105
+ caller_namespace = node_context.fully_qualified_name
106
+ return true if caller_namespace == receiver_type.name
107
+
108
+ return true if method_decl.public?
109
+ return false if method_decl.private?
110
+
111
+ caller_declaration = graph[caller_namespace]
112
+ return false unless caller_declaration.is_a?(Rubydex::Namespace)
113
+
114
+ owner_name = method_decl.owner.name
115
+ caller_declaration.ancestors.any? { |ancestor| ancestor.name == owner_name }
116
+ end
117
+
67
118
  #: (String, Enumerable[Rubydex::Definition], ?Integer?) -> Hash[Symbol, String]
68
119
  def categorized_markdown_from_definitions(title, definitions, max_entries = nil)
69
120
  markdown_title = "```ruby\n#{title}\n```"
@@ -74,17 +125,22 @@ module RubyLsp
74
125
  # For Markdown links, we need 1 based display locations
75
126
  loc = definition.location.to_display
76
127
  uri = URI(loc.uri)
77
- file_name = if uri.scheme == "untitled"
128
+
129
+ file_name = case uri.scheme
130
+ when "file"
131
+ full_path = uri.full_path #: as !nil
132
+ File.basename(full_path)
133
+ when "untitled"
78
134
  uri.opaque #: as !nil
79
- else
80
- File.basename(
81
- uri.full_path, #: as !nil
82
- )
83
135
  end
84
136
 
85
- # The format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
86
- string_uri = "#{loc.uri}#L#{loc.start_line},#{loc.start_column}-#{loc.end_line},#{loc.end_column}"
87
- file_links << "[#{file_name}](#{string_uri})"
137
+ # Omit the link for magic schemes like rubydex:built-in
138
+ if file_name
139
+ # The format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
140
+ string_uri = "#{loc.uri}#L#{loc.start_line},#{loc.start_column}-#{loc.end_line},#{loc.end_column}"
141
+ file_links << "[#{file_name}](#{string_uri})"
142
+ end
143
+
88
144
  content << "\n\n#{definition.comments.map { |comment| comment.string.delete_prefix("# ") }.join("\n")}" unless definition.comments.empty?
89
145
  end
90
146
 
@@ -104,42 +160,9 @@ module RubyLsp
104
160
  }
105
161
  end
106
162
 
107
- #: (String title, (Array[RubyIndexer::Entry] | RubyIndexer::Entry) entries, ?Integer? max_entries) -> Hash[Symbol, String]
108
- def categorized_markdown_from_index_entries(title, entries, max_entries = nil)
109
- markdown_title = "```ruby\n#{title}\n```"
110
- definitions = []
111
- content = +""
112
- entries = Array(entries)
113
- entries_to_format = max_entries ? entries.take(max_entries) : entries
114
- entries_to_format.each do |entry|
115
- loc = entry.location
116
-
117
- # We always handle locations as zero based. However, for file links in Markdown we need them to be one
118
- # based, which is why instead of the usual subtraction of 1 to line numbers, we are actually adding 1 to
119
- # columns. The format for VS Code file URIs is
120
- # `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
121
- uri = "#{entry.uri}#L#{loc.start_line},#{loc.start_column + 1}-#{loc.end_line},#{loc.end_column + 1}"
122
- definitions << "[#{entry.file_name}](#{uri})"
123
- content << "\n\n#{entry.comments}" unless entry.comments.empty?
124
- end
125
-
126
- additional_entries_text = if max_entries && entries.length > max_entries
127
- additional = entries.length - max_entries
128
- " | #{additional} other#{additional > 1 ? "s" : ""}"
129
- else
130
- ""
131
- end
132
-
133
- {
134
- title: markdown_title,
135
- links: "**Definitions**: #{definitions.join(" | ")}#{additional_entries_text}",
136
- documentation: content,
137
- }
138
- end
139
-
140
- #: (String title, (Array[RubyIndexer::Entry] | RubyIndexer::Entry) entries, ?Integer? max_entries, ?extra_links: String?) -> String
141
- def markdown_from_index_entries(title, entries, max_entries = nil, extra_links: nil)
142
- categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
163
+ #: (String title, Enumerable[Rubydex::Definition] definitions, ?Integer? max_entries, ?extra_links: String?) -> String
164
+ def markdown_from_definitions(title, definitions, max_entries = nil, extra_links: nil)
165
+ categorized_markdown = categorized_markdown_from_definitions(title, definitions, max_entries)
143
166
 
144
167
  markdown = +(categorized_markdown[:title] || "")
145
168
  markdown << "\n\n#{extra_links}" if extra_links
@@ -155,7 +178,20 @@ module RubyLsp
155
178
 
156
179
  #: ((Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) node) -> String?
157
180
  def constant_name(node)
158
- RubyIndexer::Index.constant_name(node)
181
+ Common.constant_name(node)
182
+ end
183
+
184
+ class << self
185
+ #: ((Prism::ConstantPathNode | Prism::ConstantReadNode | Prism::ConstantPathTargetNode | Prism::CallNode | Prism::MissingNode) node) -> String?
186
+ def constant_name(node)
187
+ case node
188
+ when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode
189
+ node.full_name
190
+ end
191
+ rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
192
+ Prism::ConstantPathNode::MissingNodesInConstantPathError
193
+ nil
194
+ end
159
195
  end
160
196
 
161
197
  #: ((Prism::ModuleNode | Prism::ClassNode) node) -> String?
@@ -179,28 +215,6 @@ module RubyLsp
179
215
  current = current.parent
180
216
  end
181
217
  end
182
-
183
- #: (RubyIndexer::Entry entry) -> Integer
184
- def kind_for_entry(entry)
185
- case entry
186
- when RubyIndexer::Entry::Class
187
- Constant::SymbolKind::CLASS
188
- when RubyIndexer::Entry::Module
189
- Constant::SymbolKind::NAMESPACE
190
- when RubyIndexer::Entry::Constant, RubyIndexer::Entry::UnresolvedConstantAlias, RubyIndexer::Entry::ConstantAlias
191
- Constant::SymbolKind::CONSTANT
192
- when RubyIndexer::Entry::Method, RubyIndexer::Entry::UnresolvedMethodAlias, RubyIndexer::Entry::MethodAlias
193
- entry.name == "initialize" ? Constant::SymbolKind::CONSTRUCTOR : Constant::SymbolKind::METHOD
194
- when RubyIndexer::Entry::Accessor
195
- Constant::SymbolKind::PROPERTY
196
- when RubyIndexer::Entry::InstanceVariable, RubyIndexer::Entry::ClassVariable
197
- Constant::SymbolKind::FIELD
198
- when RubyIndexer::Entry::GlobalVariable
199
- Constant::SymbolKind::VARIABLE
200
- else
201
- Constant::SymbolKind::NULL
202
- end
203
- end
204
218
  end
205
219
  end
206
220
  end