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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/exe/ruby-lsp +0 -46
- data/exe/ruby-lsp-check +0 -15
- data/lib/ruby_lsp/addon.rb +19 -19
- data/lib/ruby_lsp/global_state.rb +1 -6
- data/lib/ruby_lsp/internal.rb +3 -2
- data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
- data/lib/ruby_lsp/listeners/completion.rb +246 -382
- data/lib/ruby_lsp/listeners/definition.rb +7 -10
- data/lib/ruby_lsp/listeners/document_link.rb +4 -0
- data/lib/ruby_lsp/listeners/hover.rb +234 -82
- data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
- data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
- data/lib/ruby_lsp/listeners/test_discovery.rb +38 -15
- data/lib/ruby_lsp/listeners/test_style.rb +21 -9
- data/lib/ruby_lsp/node_context.rb +31 -8
- data/lib/ruby_lsp/requests/completion_resolve.rb +55 -39
- data/lib/ruby_lsp/requests/discover_tests.rb +5 -41
- data/lib/ruby_lsp/requests/hover.rb +2 -5
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +66 -22
- data/lib/ruby_lsp/requests/references.rb +180 -66
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +3 -33
- data/lib/ruby_lsp/requests/support/common.rb +82 -68
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
- data/lib/ruby_lsp/ruby_document.rb +0 -73
- data/lib/ruby_lsp/rubydex/declaration.rb +174 -0
- data/lib/ruby_lsp/rubydex/definition.rb +73 -0
- data/lib/ruby_lsp/rubydex/reference.rb +6 -1
- data/lib/ruby_lsp/rubydex/signature.rb +107 -0
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +56 -171
- data/lib/ruby_lsp/test_helper.rb +0 -1
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +89 -11
- metadata +12 -18
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
- data/lib/ruby_indexer/ruby_indexer.rb +0 -20
- data/lib/ruby_lsp/static_docs.rb +0 -20
- data/static_docs/break.md +0 -103
- data/static_docs/yield.md +0 -81
- /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
|
@@ -9,65 +9,101 @@ module RubyLsp
|
|
|
9
9
|
class TypeHierarchySupertypes < Request
|
|
10
10
|
include Support::Common
|
|
11
11
|
|
|
12
|
-
#: (
|
|
13
|
-
def initialize(
|
|
12
|
+
#: (GlobalState, Hash[Symbol, untyped]) -> void
|
|
13
|
+
def initialize(global_state, item)
|
|
14
14
|
super()
|
|
15
15
|
|
|
16
|
-
@
|
|
16
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
17
17
|
@item = item
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# @override
|
|
21
21
|
#: -> Array[Interface::TypeHierarchyItem]?
|
|
22
22
|
def perform
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return unless
|
|
28
|
-
|
|
29
|
-
entries.each do |entry|
|
|
30
|
-
next unless entry.is_a?(RubyIndexer::Entry::Namespace)
|
|
31
|
-
|
|
32
|
-
if entry.is_a?(RubyIndexer::Entry::Class)
|
|
33
|
-
parent_class_name = entry.parent_class
|
|
34
|
-
if parent_class_name
|
|
35
|
-
resolved_parent_entries = @index.resolve(parent_class_name, entry.nesting)
|
|
36
|
-
resolved_parent_entries&.each do |entry|
|
|
37
|
-
next unless entry.is_a?(RubyIndexer::Entry::Class)
|
|
38
|
-
|
|
39
|
-
parents << entry
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
entry.mixin_operations.each do |mixin_operation|
|
|
45
|
-
mixin_name = mixin_operation.module_name
|
|
46
|
-
resolved_mixin_entries = @index.resolve(mixin_name, entry.nesting)
|
|
47
|
-
next unless resolved_mixin_entries
|
|
48
|
-
|
|
49
|
-
resolved_mixin_entries.each do |mixin_entry|
|
|
50
|
-
next unless mixin_entry.is_a?(RubyIndexer::Entry::Module)
|
|
51
|
-
|
|
52
|
-
parents << mixin_entry
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
23
|
+
fully_qualified_name = @item.dig(:data, :fully_qualified_name) || @item[:name] #: String?
|
|
24
|
+
return unless fully_qualified_name
|
|
25
|
+
|
|
26
|
+
declaration = @graph[fully_qualified_name]
|
|
27
|
+
return unless declaration.is_a?(Rubydex::Namespace)
|
|
56
28
|
|
|
57
|
-
|
|
29
|
+
compute_supertypes(declaration).filter_map { |name, backing| hierarchy_item(name, backing) }
|
|
58
30
|
end
|
|
59
31
|
|
|
60
32
|
private
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
34
|
+
# Returns an array of `[display_name, backing_declaration]` pairs. `display_name` is the name shown in the type
|
|
35
|
+
# hierarchy item (which may be a synthesized singleton class name like `Object::<Object>`). `backing_declaration`
|
|
36
|
+
# is the namespace whose primary definition provides the location for the hierarchy item — it may differ from the
|
|
37
|
+
# display name when the singleton class is implicit and has no definitions of its own, in which case we fall back
|
|
38
|
+
# to the attached object's definition so the user still lands somewhere useful.
|
|
39
|
+
#
|
|
40
|
+
#: (Rubydex::Namespace) -> Array[[String, Rubydex::Namespace]]
|
|
41
|
+
def compute_supertypes(declaration)
|
|
42
|
+
case declaration
|
|
43
|
+
when Rubydex::SingletonClass
|
|
44
|
+
singleton_supertypes(declaration)
|
|
45
|
+
when Rubydex::Class
|
|
46
|
+
class_supertypes(declaration)
|
|
47
|
+
else
|
|
48
|
+
explicit_supertypes(declaration)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#: (Rubydex::Class) -> Array[[String, Rubydex::Namespace]]
|
|
53
|
+
def class_supertypes(declaration)
|
|
54
|
+
# `BasicObject` is the root of the Ruby class hierarchy
|
|
55
|
+
supertypes = explicit_supertypes(declaration)
|
|
56
|
+
return supertypes if declaration.name == "BasicObject"
|
|
57
|
+
|
|
58
|
+
# If the class has any superclass reference (resolved or unresolved), don't re-add the implicit `Object`.
|
|
59
|
+
has_superclass = declaration.definitions.any? do |d|
|
|
60
|
+
d.is_a?(Rubydex::ClassDefinition) && !d.superclass.nil?
|
|
61
|
+
end
|
|
62
|
+
return supertypes if has_superclass
|
|
63
|
+
|
|
64
|
+
object = @graph["Object"] #: as Rubydex::Namespace
|
|
65
|
+
supertypes << ["Object", object]
|
|
66
|
+
supertypes
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: (Rubydex::Namespace) -> Array[[String, Rubydex::Namespace]]
|
|
70
|
+
def explicit_supertypes(declaration)
|
|
71
|
+
declaration.direct_supertypes.map { |s| [s.name, s] }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Singleton classes don't have their own superclass references. Their direct supertype is the singleton class of
|
|
75
|
+
# the attached object's superclass, computed recursively so that nested singleton classes (e.g.
|
|
76
|
+
# `Foo::<Foo>::<<Foo>>`) still resolve to the matching depth on the parent chain. When the synthesized singleton
|
|
77
|
+
# class name has no backing declaration with definitions (implicit singleton), we fall back to the attached
|
|
78
|
+
# supertype's backing so the user is still navigated to a meaningful location.
|
|
79
|
+
#
|
|
80
|
+
#: (Rubydex::SingletonClass) -> Array[[String, Rubydex::Namespace]]
|
|
81
|
+
def singleton_supertypes(declaration)
|
|
82
|
+
attached = declaration.owner
|
|
83
|
+
return [] unless attached.is_a?(Rubydex::Namespace)
|
|
84
|
+
|
|
85
|
+
compute_supertypes(attached).map do |parent_name, parent_backing|
|
|
86
|
+
singleton_name = singleton_name_of(parent_name)
|
|
87
|
+
found = @graph[singleton_name]
|
|
88
|
+
backing = found.is_a?(Rubydex::Namespace) && found.definitions.any? ? found : parent_backing
|
|
89
|
+
[singleton_name, backing]
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#: (String) -> String
|
|
94
|
+
def singleton_name_of(name)
|
|
95
|
+
unqualified = name.split("::").last || name
|
|
96
|
+
"#{name}::<#{unqualified}>"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#: (String, Rubydex::Namespace) -> Interface::TypeHierarchyItem?
|
|
100
|
+
def hierarchy_item(name, declaration)
|
|
101
|
+
primary = declaration.definitions.first #: Rubydex::Definition?
|
|
102
|
+
return unless primary
|
|
103
|
+
|
|
104
|
+
primary.to_lsp_type_hierarchy_item(
|
|
105
|
+
name,
|
|
106
|
+
detail: declaration.lsp_type_hierarchy_detail,
|
|
71
107
|
)
|
|
72
108
|
end
|
|
73
109
|
end
|
|
@@ -4,22 +4,6 @@
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
#: [ParseResultType = Prism::ParseLexResult]
|
|
6
6
|
class RubyDocument < Document
|
|
7
|
-
METHODS_THAT_CHANGE_DECLARATIONS = [
|
|
8
|
-
:private_constant,
|
|
9
|
-
:attr_reader,
|
|
10
|
-
:attr_writer,
|
|
11
|
-
:attr_accessor,
|
|
12
|
-
:alias_method,
|
|
13
|
-
:include,
|
|
14
|
-
:prepend,
|
|
15
|
-
:extend,
|
|
16
|
-
:public,
|
|
17
|
-
:protected,
|
|
18
|
-
:private,
|
|
19
|
-
:module_function,
|
|
20
|
-
:private_class_method,
|
|
21
|
-
].freeze
|
|
22
|
-
|
|
23
7
|
class << self
|
|
24
8
|
#: (Prism::Node node, Integer char_position, code_units_cache: (^(Integer arg0) -> Integer | Prism::CodeUnitsCache), ?node_types: Array[singleton(Prism::Node)]) -> NodeContext
|
|
25
9
|
def locate(node, char_position, code_units_cache:, node_types: [])
|
|
@@ -190,62 +174,5 @@ module RubyLsp
|
|
|
190
174
|
node_types: node_types,
|
|
191
175
|
)
|
|
192
176
|
end
|
|
193
|
-
|
|
194
|
-
#: -> bool
|
|
195
|
-
def should_index?
|
|
196
|
-
# This method controls when we should index documents. If there's no recent edit and the document has just been
|
|
197
|
-
# opened, we need to index it
|
|
198
|
-
return true unless @last_edit
|
|
199
|
-
|
|
200
|
-
last_edit_may_change_declarations?
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
private
|
|
204
|
-
|
|
205
|
-
#: -> bool
|
|
206
|
-
def last_edit_may_change_declarations?
|
|
207
|
-
case @last_edit
|
|
208
|
-
when Delete
|
|
209
|
-
# Not optimized yet. It's not trivial to identify that a declaration has been removed since the source is no
|
|
210
|
-
# longer there and we don't remember the deleted text
|
|
211
|
-
true
|
|
212
|
-
when Insert, Replace
|
|
213
|
-
position_may_impact_declarations?(@last_edit.range[:start])
|
|
214
|
-
else
|
|
215
|
-
false
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
#: (Hash[Symbol, Integer] position) -> bool
|
|
220
|
-
def position_may_impact_declarations?(position)
|
|
221
|
-
node_context = locate_node(position)
|
|
222
|
-
node_at_edit = node_context.node
|
|
223
|
-
|
|
224
|
-
# Adjust to the parent when editing the constant of a class/module declaration
|
|
225
|
-
if node_at_edit.is_a?(Prism::ConstantReadNode) || node_at_edit.is_a?(Prism::ConstantPathNode)
|
|
226
|
-
node_at_edit = node_context.parent
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
case node_at_edit
|
|
230
|
-
when Prism::ClassNode, Prism::ModuleNode, Prism::SingletonClassNode, Prism::DefNode,
|
|
231
|
-
Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
|
|
232
|
-
Prism::ConstantPathAndWriteNode, Prism::ConstantOrWriteNode, Prism::ConstantWriteNode,
|
|
233
|
-
Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
234
|
-
Prism::GlobalVariableOperatorWriteNode, Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode,
|
|
235
|
-
Prism::GlobalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::InstanceVariableAndWriteNode,
|
|
236
|
-
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode,
|
|
237
|
-
Prism::InstanceVariableTargetNode, Prism::AliasMethodNode
|
|
238
|
-
true
|
|
239
|
-
when Prism::MultiWriteNode
|
|
240
|
-
[*node_at_edit.lefts, *node_at_edit.rest, *node_at_edit.rights].any? do |node|
|
|
241
|
-
node.is_a?(Prism::ConstantTargetNode) || node.is_a?(Prism::ConstantPathTargetNode)
|
|
242
|
-
end
|
|
243
|
-
when Prism::CallNode
|
|
244
|
-
receiver = node_at_edit.receiver
|
|
245
|
-
(!receiver || receiver.is_a?(Prism::SelfNode)) && METHODS_THAT_CHANGE_DECLARATIONS.include?(node_at_edit.name)
|
|
246
|
-
else
|
|
247
|
-
false
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
177
|
end
|
|
251
178
|
end
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Rubydex
|
|
5
|
+
# @abstract
|
|
6
|
+
class Declaration
|
|
7
|
+
# Detail text shown on a `TypeHierarchyItem` for this declaration. Hints at multiplicity
|
|
8
|
+
# when the declaration spans more than one re-open; otherwise falls back to the primary
|
|
9
|
+
# definition's file name so users can quickly see where the type comes from.
|
|
10
|
+
#
|
|
11
|
+
#: -> String?
|
|
12
|
+
def lsp_type_hierarchy_detail
|
|
13
|
+
defs = definitions
|
|
14
|
+
count = defs.count
|
|
15
|
+
return "#{count} definitions" if count > 1
|
|
16
|
+
|
|
17
|
+
primary = defs.first
|
|
18
|
+
return unless primary
|
|
19
|
+
|
|
20
|
+
uri = URI(primary.location.uri)
|
|
21
|
+
path = uri.full_path
|
|
22
|
+
path ? File.basename(path) : uri.to_s
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @abstract
|
|
26
|
+
#: -> Integer
|
|
27
|
+
def to_lsp_completion_kind
|
|
28
|
+
raise RubyLsp::AbstractMethodInvokedError
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @abstract
|
|
33
|
+
class Namespace
|
|
34
|
+
# Resolved, deduplicated direct supertypes across every re-open of this declaration.
|
|
35
|
+
# Aggregates each definition's own `superclass`/`include`/`prepend` references and drops
|
|
36
|
+
# unresolved ones. Order is stable (first-seen across definitions).
|
|
37
|
+
#: -> Array[Rubydex::Namespace]
|
|
38
|
+
def direct_supertypes
|
|
39
|
+
seen = {} #: Hash[String, Rubydex::Namespace]
|
|
40
|
+
|
|
41
|
+
definitions.each do |definition|
|
|
42
|
+
definition.direct_supertype_references.each do |ref|
|
|
43
|
+
next unless ref.is_a?(ResolvedConstantReference)
|
|
44
|
+
|
|
45
|
+
target = ref.declaration
|
|
46
|
+
next unless target.is_a?(Namespace)
|
|
47
|
+
next if seen.key?(target.name)
|
|
48
|
+
|
|
49
|
+
seen[target.name] = target
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
seen.values
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Class
|
|
58
|
+
# @override
|
|
59
|
+
#: -> Integer
|
|
60
|
+
def to_lsp_completion_kind
|
|
61
|
+
RubyLsp::Constant::CompletionItemKind::CLASS
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Module
|
|
66
|
+
# @override
|
|
67
|
+
#: -> Integer
|
|
68
|
+
def to_lsp_completion_kind
|
|
69
|
+
RubyLsp::Constant::CompletionItemKind::MODULE
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class SingletonClass
|
|
74
|
+
# @override
|
|
75
|
+
#: -> Integer
|
|
76
|
+
def to_lsp_completion_kind
|
|
77
|
+
RubyLsp::Constant::CompletionItemKind::CLASS
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Todo
|
|
82
|
+
# @override
|
|
83
|
+
#: -> Integer
|
|
84
|
+
def to_lsp_completion_kind
|
|
85
|
+
RubyLsp::Constant::CompletionItemKind::CLASS
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Constant
|
|
90
|
+
# @override
|
|
91
|
+
#: -> Integer
|
|
92
|
+
def to_lsp_completion_kind
|
|
93
|
+
RubyLsp::Constant::CompletionItemKind::CONSTANT
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class ConstantAlias
|
|
98
|
+
# @override
|
|
99
|
+
#: -> Integer
|
|
100
|
+
def to_lsp_completion_kind
|
|
101
|
+
RubyLsp::Constant::CompletionItemKind::CONSTANT
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class Method
|
|
106
|
+
# @override
|
|
107
|
+
#: -> Integer
|
|
108
|
+
def to_lsp_completion_kind
|
|
109
|
+
RubyLsp::Constant::CompletionItemKind::METHOD
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# All signatures collected across every definition (re-opens, RBS overloads, alias targets) of this method.
|
|
113
|
+
#: () -> Array[Rubydex::Signature]
|
|
114
|
+
def signatures
|
|
115
|
+
definitions.flat_map do |defn|
|
|
116
|
+
case defn
|
|
117
|
+
when Rubydex::MethodDefinition, Rubydex::MethodAliasDefinition
|
|
118
|
+
defn.signatures
|
|
119
|
+
else
|
|
120
|
+
[]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Decorated parameter list of the first signature, e.g. `(a, b = <default>, &block)`. Returns `()` when there are
|
|
126
|
+
# no signatures (e.g. an unresolved alias).
|
|
127
|
+
#: () -> String
|
|
128
|
+
def decorated_parameters
|
|
129
|
+
first = signatures.first
|
|
130
|
+
return "()" unless first
|
|
131
|
+
|
|
132
|
+
"(#{first.format})"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Suffix line that hints at additional overloads beyond the first signature, matching the legacy index entry
|
|
136
|
+
# rendering used in hover.
|
|
137
|
+
#: () -> String
|
|
138
|
+
def formatted_signatures
|
|
139
|
+
count = signatures.size
|
|
140
|
+
case count
|
|
141
|
+
when 0, 1
|
|
142
|
+
""
|
|
143
|
+
when 2
|
|
144
|
+
"\n(+1 overload)"
|
|
145
|
+
else
|
|
146
|
+
"\n(+#{count - 1} overloads)"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class InstanceVariable
|
|
152
|
+
# @override
|
|
153
|
+
#: -> Integer
|
|
154
|
+
def to_lsp_completion_kind
|
|
155
|
+
RubyLsp::Constant::CompletionItemKind::FIELD
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
class ClassVariable
|
|
160
|
+
# @override
|
|
161
|
+
#: -> Integer
|
|
162
|
+
def to_lsp_completion_kind
|
|
163
|
+
RubyLsp::Constant::CompletionItemKind::FIELD
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class GlobalVariable
|
|
168
|
+
# @override
|
|
169
|
+
#: -> Integer
|
|
170
|
+
def to_lsp_completion_kind
|
|
171
|
+
RubyLsp::Constant::CompletionItemKind::VARIABLE
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -21,6 +21,29 @@ module Rubydex
|
|
|
21
21
|
raise RubyLsp::AbstractMethodInvokedError
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# Direct ancestor references contributed by this definition (superclass, includes, prepends).
|
|
25
|
+
# Extends are intentionally excluded here because they extend the singleton class, not the
|
|
26
|
+
# instance-side ancestor chain. Definition subclasses that can't contribute ancestors return [].
|
|
27
|
+
#: () -> Array[Rubydex::ConstantReference]
|
|
28
|
+
def direct_supertype_references
|
|
29
|
+
[]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#: (String name, ?detail: String?) -> RubyLsp::Interface::TypeHierarchyItem
|
|
33
|
+
def to_lsp_type_hierarchy_item(name, detail: nil)
|
|
34
|
+
range = to_lsp_selection_range
|
|
35
|
+
|
|
36
|
+
RubyLsp::Interface::TypeHierarchyItem.new(
|
|
37
|
+
name: name,
|
|
38
|
+
kind: to_lsp_kind,
|
|
39
|
+
uri: location.uri,
|
|
40
|
+
range: range,
|
|
41
|
+
selection_range: to_lsp_name_range || range,
|
|
42
|
+
detail: detail,
|
|
43
|
+
data: { fully_qualified_name: name },
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
24
47
|
#: (String name) -> RubyLsp::Interface::WorkspaceSymbol
|
|
25
48
|
def to_lsp_workspace_symbol(name)
|
|
26
49
|
# We use the namespace as the container name, but we also use the full name as the regular name. The reason we do
|
|
@@ -86,15 +109,47 @@ module Rubydex
|
|
|
86
109
|
end
|
|
87
110
|
end
|
|
88
111
|
|
|
112
|
+
# Shared supertype aggregation for Rubydex definition types that carry namespace mixins
|
|
113
|
+
# (`ClassDefinition`, `ModuleDefinition`, `SingletonClassDefinition`). The including class is
|
|
114
|
+
# expected to provide `#mixins`, which every Rubydex namespace definition already does.
|
|
115
|
+
# @abstract
|
|
116
|
+
module NamespaceDefinition
|
|
117
|
+
# @abstract
|
|
118
|
+
#: () -> Array[Rubydex::Mixin]
|
|
119
|
+
def mixins
|
|
120
|
+
raise RubyLsp::AbstractMethodInvokedError
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#: () -> Array[Rubydex::ConstantReference]
|
|
124
|
+
def direct_supertype_references
|
|
125
|
+
mixins.filter_map do |mixin|
|
|
126
|
+
mixin.constant_reference if mixin.is_a?(Include) || mixin.is_a?(Prepend)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
89
131
|
class ClassDefinition
|
|
132
|
+
include NamespaceDefinition
|
|
133
|
+
|
|
90
134
|
# @override
|
|
91
135
|
#: () -> Integer
|
|
92
136
|
def to_lsp_kind
|
|
93
137
|
RubyLsp::Constant::SymbolKind::CLASS
|
|
94
138
|
end
|
|
139
|
+
|
|
140
|
+
# @override
|
|
141
|
+
#: () -> Array[Rubydex::ConstantReference]
|
|
142
|
+
def direct_supertype_references
|
|
143
|
+
refs = super
|
|
144
|
+
superclass_ref = superclass
|
|
145
|
+
refs << superclass_ref if superclass_ref
|
|
146
|
+
refs
|
|
147
|
+
end
|
|
95
148
|
end
|
|
96
149
|
|
|
97
150
|
class ModuleDefinition
|
|
151
|
+
include NamespaceDefinition
|
|
152
|
+
|
|
98
153
|
# @override
|
|
99
154
|
#: () -> Integer
|
|
100
155
|
def to_lsp_kind
|
|
@@ -103,6 +158,8 @@ module Rubydex
|
|
|
103
158
|
end
|
|
104
159
|
|
|
105
160
|
class SingletonClassDefinition
|
|
161
|
+
include NamespaceDefinition
|
|
162
|
+
|
|
106
163
|
# @override
|
|
107
164
|
#: () -> Integer
|
|
108
165
|
def to_lsp_kind
|
|
@@ -126,6 +183,14 @@ module Rubydex
|
|
|
126
183
|
end
|
|
127
184
|
end
|
|
128
185
|
|
|
186
|
+
class ConstantVisibilityDefinition
|
|
187
|
+
# @override
|
|
188
|
+
#: () -> Integer
|
|
189
|
+
def to_lsp_kind
|
|
190
|
+
RubyLsp::Constant::SymbolKind::CONSTANT
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
129
194
|
class MethodDefinition
|
|
130
195
|
# @override
|
|
131
196
|
#: () -> Integer
|
|
@@ -142,6 +207,14 @@ module Rubydex
|
|
|
142
207
|
end
|
|
143
208
|
end
|
|
144
209
|
|
|
210
|
+
class MethodVisibilityDefinition
|
|
211
|
+
# @override
|
|
212
|
+
#: () -> Integer
|
|
213
|
+
def to_lsp_kind
|
|
214
|
+
RubyLsp::Constant::SymbolKind::METHOD
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
145
218
|
class AttrReaderDefinition
|
|
146
219
|
# @override
|
|
147
220
|
#: () -> Integer
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Rubydex
|
|
5
|
-
class
|
|
5
|
+
class Reference
|
|
6
6
|
#: () -> RubyLsp::Interface::Range
|
|
7
7
|
def to_lsp_range
|
|
8
8
|
loc = location
|
|
@@ -12,5 +12,10 @@ module Rubydex
|
|
|
12
12
|
end: RubyLsp::Interface::Position.new(line: loc.end_line, character: loc.end_column),
|
|
13
13
|
)
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
#: () -> RubyLsp::Interface::Location
|
|
17
|
+
def to_lsp_location
|
|
18
|
+
RubyLsp::Interface::Location.new(uri: location.uri, range: to_lsp_range)
|
|
19
|
+
end
|
|
15
20
|
end
|
|
16
21
|
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Rubydex
|
|
5
|
+
class Signature
|
|
6
|
+
# Returns a string with the decorated names of the parameters of this signature, e.g.
|
|
7
|
+
# `(a, b = <default>, *c, d, e:, f: <default>, **g, &h)`.
|
|
8
|
+
#: () -> String
|
|
9
|
+
def format
|
|
10
|
+
parameters.map { |param| decorated_name(param) }.join(", ")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns `true` if the given call node arguments array matches this signature. The matching is intentionally lenient
|
|
14
|
+
# because this method is used to detect which overload should be displayed in signature help while the user is still
|
|
15
|
+
# typing the call. We prefer returning `true` for situations that cannot be analyzed statically (e.g. presence of
|
|
16
|
+
# splats, keyword splats, forwarding) and accept missing arguments since the user may not be done typing yet.
|
|
17
|
+
#: (Array[Prism::Node] arguments) -> bool
|
|
18
|
+
def matches?(arguments)
|
|
19
|
+
min_pos = 0
|
|
20
|
+
max_pos = 0 #: (Integer | Float)
|
|
21
|
+
names = []
|
|
22
|
+
has_forward = false #: bool
|
|
23
|
+
has_keyword_rest = false #: bool
|
|
24
|
+
|
|
25
|
+
parameters.each do |param|
|
|
26
|
+
case param
|
|
27
|
+
when PositionalParameter, PostParameter
|
|
28
|
+
min_pos += 1
|
|
29
|
+
max_pos += 1
|
|
30
|
+
when OptionalPositionalParameter
|
|
31
|
+
max_pos += 1
|
|
32
|
+
when RestPositionalParameter
|
|
33
|
+
max_pos = Float::INFINITY
|
|
34
|
+
when ForwardParameter
|
|
35
|
+
max_pos = Float::INFINITY
|
|
36
|
+
has_forward = true
|
|
37
|
+
when KeywordParameter, OptionalKeywordParameter
|
|
38
|
+
names << param.name
|
|
39
|
+
when RestKeywordParameter
|
|
40
|
+
has_keyword_rest = true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
keyword_hash_nodes, positional_args = arguments.partition { |arg| arg.is_a?(Prism::KeywordHashNode) }
|
|
45
|
+
keyword_args = keyword_hash_nodes.first #: as Prism::KeywordHashNode?
|
|
46
|
+
&.elements
|
|
47
|
+
forwarding_arguments, positionals = positional_args.partition do |arg|
|
|
48
|
+
arg.is_a?(Prism::ForwardingArgumentsNode)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return true if has_forward && min_pos == 0
|
|
52
|
+
|
|
53
|
+
# If the only argument passed is a forwarding argument, then anything will match
|
|
54
|
+
(positionals.empty? && forwarding_arguments.any?) ||
|
|
55
|
+
(
|
|
56
|
+
positional_arguments_match?(positionals, forwarding_arguments, keyword_args, min_pos, max_pos) &&
|
|
57
|
+
(has_forward || has_keyword_rest || keyword_arguments_match?(keyword_args, names))
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
#: (Parameter) -> String
|
|
64
|
+
def decorated_name(param)
|
|
65
|
+
case param
|
|
66
|
+
when OptionalPositionalParameter
|
|
67
|
+
"#{param.name} = <default>"
|
|
68
|
+
when RestPositionalParameter
|
|
69
|
+
"*#{param.name}"
|
|
70
|
+
when KeywordParameter
|
|
71
|
+
"#{param.name}:"
|
|
72
|
+
when OptionalKeywordParameter
|
|
73
|
+
"#{param.name}: <default>"
|
|
74
|
+
when RestKeywordParameter
|
|
75
|
+
"**#{param.name}"
|
|
76
|
+
when BlockParameter
|
|
77
|
+
"&#{param.name}"
|
|
78
|
+
else
|
|
79
|
+
param.name.to_s
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#: (Array[Prism::Node] positional_args, Array[Prism::Node] forwarding_arguments, Array[Prism::Node]? keyword_args, Integer min_pos, (Integer | Float) max_pos) -> bool
|
|
84
|
+
def positional_arguments_match?(positional_args, forwarding_arguments, keyword_args, min_pos, max_pos)
|
|
85
|
+
(min_pos > 0 && positional_args.any? { |arg| arg.is_a?(Prism::SplatNode) }) ||
|
|
86
|
+
(min_pos - positional_args.length > 0 && keyword_args&.any? { |arg| arg.is_a?(Prism::AssocSplatNode) }) ||
|
|
87
|
+
(min_pos - positional_args.length > 0 && forwarding_arguments.any?) ||
|
|
88
|
+
(min_pos > 0 && positional_args.length <= max_pos) ||
|
|
89
|
+
(min_pos == 0 && positional_args.empty?)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#: (Array[Prism::Node]? args, Array[Symbol] names) -> bool
|
|
93
|
+
def keyword_arguments_match?(args, names)
|
|
94
|
+
return true unless args
|
|
95
|
+
return true if args.any? { |arg| arg.is_a?(Prism::AssocSplatNode) }
|
|
96
|
+
|
|
97
|
+
arg_names = args.filter_map do |arg|
|
|
98
|
+
next unless arg.is_a?(Prism::AssocNode)
|
|
99
|
+
|
|
100
|
+
key = arg.key
|
|
101
|
+
key.value&.to_sym if key.is_a?(Prism::SymbolNode)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
(arg_names - names).empty?
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -5,7 +5,7 @@ def compose(raw_initialize, **options)
|
|
|
5
5
|
require_relative "../setup_bundler"
|
|
6
6
|
require "json"
|
|
7
7
|
require "uri"
|
|
8
|
-
require_relative "
|
|
8
|
+
require_relative "../uri"
|
|
9
9
|
|
|
10
10
|
initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
|
|
11
11
|
workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
|