ruby-lsp 0.27.0.beta1 → 0.27.0.beta2
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/lib/ruby_indexer/ruby_indexer.rb +0 -1
- data/lib/ruby_lsp/addon.rb +19 -19
- data/lib/ruby_lsp/global_state.rb +1 -1
- data/lib/ruby_lsp/internal.rb +1 -1
- data/lib/ruby_lsp/listeners/definition.rb +1 -1
- data/lib/ruby_lsp/listeners/document_link.rb +4 -0
- data/lib/ruby_lsp/listeners/hover.rb +223 -73
- data/lib/ruby_lsp/listeners/spec_style.rb +6 -1
- data/lib/ruby_lsp/listeners/test_discovery.rb +21 -14
- data/lib/ruby_lsp/listeners/test_style.rb +20 -8
- data/lib/ruby_lsp/node_context.rb +31 -8
- data/lib/ruby_lsp/requests/completion_resolve.rb +9 -13
- 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 +170 -70
- data/lib/ruby_lsp/requests/request.rb +3 -33
- data/lib/ruby_lsp/requests/support/common.rb +13 -0
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +82 -46
- data/lib/ruby_lsp/rubydex/declaration.rb +48 -0
- data/lib/ruby_lsp/rubydex/definition.rb +57 -0
- data/lib/ruby_lsp/rubydex/reference.rb +6 -1
- data/lib/ruby_lsp/server.rb +49 -9
- data/lib/ruby_lsp/type_inferrer.rb +87 -9
- metadata +2 -5
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +0 -335
- data/lib/ruby_lsp/static_docs.rb +0 -20
- data/static_docs/break.md +0 -103
- data/static_docs/yield.md +0 -81
|
@@ -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 &&
|
|
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
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Rubydex
|
|
5
|
+
class Declaration
|
|
6
|
+
# Detail text shown on a `TypeHierarchyItem` for this declaration. Hints at multiplicity
|
|
7
|
+
# when the declaration spans more than one re-open; otherwise falls back to the primary
|
|
8
|
+
# definition's file name so users can quickly see where the type comes from.
|
|
9
|
+
#
|
|
10
|
+
#: () -> String?
|
|
11
|
+
def lsp_type_hierarchy_detail
|
|
12
|
+
defs = definitions
|
|
13
|
+
count = defs.count
|
|
14
|
+
return "#{count} definitions" if count > 1
|
|
15
|
+
|
|
16
|
+
primary = defs.first
|
|
17
|
+
return unless primary
|
|
18
|
+
|
|
19
|
+
uri = URI(primary.location.uri)
|
|
20
|
+
path = uri.full_path
|
|
21
|
+
path ? File.basename(path) : uri.to_s
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Namespace
|
|
26
|
+
# Resolved, deduplicated direct supertypes across every re-open of this declaration.
|
|
27
|
+
# Aggregates each definition's own `superclass`/`include`/`prepend` references and drops
|
|
28
|
+
# unresolved ones. Order is stable (first-seen across definitions).
|
|
29
|
+
#: () -> Array[Rubydex::Namespace]
|
|
30
|
+
def direct_supertypes
|
|
31
|
+
seen = {} #: Hash[String, Rubydex::Namespace]
|
|
32
|
+
|
|
33
|
+
definitions.each do |definition|
|
|
34
|
+
definition.direct_supertype_references.each do |ref|
|
|
35
|
+
next unless ref.is_a?(ResolvedConstantReference)
|
|
36
|
+
|
|
37
|
+
target = ref.declaration
|
|
38
|
+
next unless target.is_a?(Namespace)
|
|
39
|
+
next if seen.key?(target.name)
|
|
40
|
+
|
|
41
|
+
seen[target.name] = target
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
seen.values
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
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
|
|
@@ -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
|
data/lib/ruby_lsp/server.rb
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
class Server < BaseServer
|
|
6
|
-
NON_REPORTABLE_SETUP_ERRORS = [
|
|
6
|
+
NON_REPORTABLE_SETUP_ERRORS = [
|
|
7
|
+
Bundler::GemNotFound,
|
|
8
|
+
Bundler::GitError,
|
|
9
|
+
Bundler::Dsl::DSLError,
|
|
10
|
+
].freeze #: Array[singleton(StandardError)]
|
|
7
11
|
|
|
8
12
|
# Only for testing
|
|
9
13
|
#: GlobalState
|
|
@@ -120,6 +124,16 @@ module RubyLsp
|
|
|
120
124
|
@global_state.synchronize { @cancelled_requests << message[:params][:id] }
|
|
121
125
|
when nil
|
|
122
126
|
process_response(message) if message[:result]
|
|
127
|
+
else
|
|
128
|
+
id = message[:id]
|
|
129
|
+
|
|
130
|
+
if id
|
|
131
|
+
send_message(Error.new(
|
|
132
|
+
id: id,
|
|
133
|
+
code: Constant::ErrorCodes::METHOD_NOT_FOUND,
|
|
134
|
+
message: "Method not found: #{message[:method]}",
|
|
135
|
+
))
|
|
136
|
+
end
|
|
123
137
|
end
|
|
124
138
|
rescue DelegateRequestError
|
|
125
139
|
send_message(Error.new(id: message[:id], code: DelegateRequestError::CODE, message: "DELEGATE_REQUEST"))
|
|
@@ -422,8 +436,11 @@ module RubyLsp
|
|
|
422
436
|
|
|
423
437
|
if [:ruby, :rbs].include?(language_id)
|
|
424
438
|
graph = @global_state.graph
|
|
425
|
-
|
|
426
|
-
|
|
439
|
+
|
|
440
|
+
benchmark("index_source") do
|
|
441
|
+
graph.index_source(text_document[:uri].to_s, document.source, language_id.to_s)
|
|
442
|
+
end
|
|
443
|
+
benchmark("incremental_resolve") { graph.resolve }
|
|
427
444
|
end
|
|
428
445
|
end
|
|
429
446
|
|
|
@@ -1056,8 +1073,8 @@ module RubyLsp
|
|
|
1056
1073
|
acc << path
|
|
1057
1074
|
end
|
|
1058
1075
|
end
|
|
1059
|
-
graph.index_all(additions_and_changes)
|
|
1060
|
-
graph.resolve
|
|
1076
|
+
benchmark("index_all") { graph.index_all(additions_and_changes) }
|
|
1077
|
+
benchmark("incremental_resolve") { graph.resolve }
|
|
1061
1078
|
|
|
1062
1079
|
index = @global_state.index
|
|
1063
1080
|
changes.each do |change|
|
|
@@ -1195,7 +1212,7 @@ module RubyLsp
|
|
|
1195
1212
|
|
|
1196
1213
|
response = Requests::PrepareTypeHierarchy.new(
|
|
1197
1214
|
document,
|
|
1198
|
-
@global_state
|
|
1215
|
+
@global_state,
|
|
1199
1216
|
params[:position],
|
|
1200
1217
|
).perform
|
|
1201
1218
|
|
|
@@ -1205,7 +1222,7 @@ module RubyLsp
|
|
|
1205
1222
|
#: (Hash[Symbol, untyped] message) -> void
|
|
1206
1223
|
def type_hierarchy_supertypes(message)
|
|
1207
1224
|
response = Requests::TypeHierarchySupertypes.new(
|
|
1208
|
-
@global_state
|
|
1225
|
+
@global_state,
|
|
1209
1226
|
message.dig(:params, :item),
|
|
1210
1227
|
).perform
|
|
1211
1228
|
send_message(Result.new(id: message[:id], response: response))
|
|
@@ -1255,10 +1272,10 @@ module RubyLsp
|
|
|
1255
1272
|
#: -> void
|
|
1256
1273
|
def perform_initial_indexing
|
|
1257
1274
|
progress("indexing-progress", message: "Indexing workspace...")
|
|
1258
|
-
@global_state.graph.index_workspace
|
|
1275
|
+
benchmark("index_workspace") { @global_state.graph.index_workspace }
|
|
1259
1276
|
|
|
1260
1277
|
progress("indexing-progress", message: "Resolving graph...")
|
|
1261
|
-
@global_state.graph.resolve
|
|
1278
|
+
benchmark("full_resolve") { @global_state.graph.resolve }
|
|
1262
1279
|
|
|
1263
1280
|
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
|
|
1264
1281
|
# stuck indexing files
|
|
@@ -1558,5 +1575,28 @@ module RubyLsp
|
|
|
1558
1575
|
response: code_lens,
|
|
1559
1576
|
))
|
|
1560
1577
|
end
|
|
1578
|
+
|
|
1579
|
+
#: [T] (String) { () -> T } -> T
|
|
1580
|
+
def benchmark(label, &block)
|
|
1581
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
|
1582
|
+
result = block.call
|
|
1583
|
+
duration = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start
|
|
1584
|
+
|
|
1585
|
+
send_message(Notification.telemetry({
|
|
1586
|
+
type: "data",
|
|
1587
|
+
eventName: "ruby_lsp.response_time",
|
|
1588
|
+
data: {
|
|
1589
|
+
type: "histogram",
|
|
1590
|
+
value: duration,
|
|
1591
|
+
attributes: {
|
|
1592
|
+
message: label,
|
|
1593
|
+
lspVersion: RubyLsp::VERSION,
|
|
1594
|
+
rubyVersion: RUBY_VERSION,
|
|
1595
|
+
},
|
|
1596
|
+
},
|
|
1597
|
+
}))
|
|
1598
|
+
|
|
1599
|
+
result
|
|
1600
|
+
end
|
|
1561
1601
|
end
|
|
1562
1602
|
end
|
|
@@ -19,7 +19,7 @@ module RubyLsp
|
|
|
19
19
|
infer_receiver_for_call_node(node, node_context)
|
|
20
20
|
when Prism::InstanceVariableReadNode, Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableWriteNode,
|
|
21
21
|
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableTargetNode,
|
|
22
|
-
Prism::SuperNode, Prism::ForwardingSuperNode
|
|
22
|
+
Prism::SuperNode, Prism::ForwardingSuperNode, Prism::DefNode
|
|
23
23
|
self_receiver_handling(node_context)
|
|
24
24
|
when Prism::ClassVariableAndWriteNode, Prism::ClassVariableWriteNode, Prism::ClassVariableOperatorWriteNode,
|
|
25
25
|
Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
|
@@ -129,21 +129,99 @@ module RubyLsp
|
|
|
129
129
|
GuessedType.new(declaration.name)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
#: (NodeContext node_context) -> Type
|
|
132
|
+
#: (NodeContext node_context) -> Type?
|
|
133
133
|
def self_receiver_handling(node_context)
|
|
134
134
|
nesting = node_context.nesting
|
|
135
135
|
# If we're at the top level, then the invocation is happening on `<main>`, which is a special singleton that
|
|
136
136
|
# inherits from Object
|
|
137
137
|
return Type.new("Object") if nesting.empty?
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
surrounding_method = node_context.surrounding_method
|
|
140
|
+
|
|
141
|
+
if surrounding_method
|
|
142
|
+
receiver_name = surrounding_method.receiver
|
|
143
|
+
|
|
144
|
+
case receiver_name
|
|
145
|
+
when "self"
|
|
146
|
+
# `def self.foo` — self is the singleton of the enclosing class/module
|
|
147
|
+
return resolve_singleton_type_from_nesting(nesting)
|
|
148
|
+
when "none"
|
|
149
|
+
# Instance method — self is an instance of the enclosing class/module
|
|
150
|
+
return resolve_type_from_nesting(nesting)
|
|
151
|
+
when nil
|
|
152
|
+
# Dynamic receiver that we cannot handle
|
|
153
|
+
return
|
|
154
|
+
else
|
|
155
|
+
# Explicit constant receiver (e.g. `def Bar.baz`) — self is that constant's singleton class
|
|
156
|
+
resolved = resolve_receiver_singleton_type(receiver_name, nesting)
|
|
157
|
+
return resolved if resolved
|
|
158
|
+
|
|
159
|
+
return resolve_type_from_nesting(nesting)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
139
162
|
|
|
140
163
|
# If we're not inside a method, then we're inside the body of a class or module, which is a singleton
|
|
141
|
-
# context.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
# context. Resolve through the graph to get the correct fully qualified name
|
|
165
|
+
resolve_singleton_type_from_nesting(nesting)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Resolves the fully qualified name of the innermost constant from the nesting and returns it as a type.
|
|
169
|
+
# For instance methods, the nesting won't have singleton markers, so the result is an instance type.
|
|
170
|
+
# For `def self.` methods, the nesting includes a singleton marker, which is preserved in the result.
|
|
171
|
+
#: (Array[String] nesting) -> Type
|
|
172
|
+
def resolve_type_from_nesting(nesting)
|
|
173
|
+
resolved_name = resolve_nesting_fully_qualified_name(nesting)
|
|
174
|
+
Type.new(resolved_name)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Resolves the nesting and returns a singleton type (appends `::<Last>`)
|
|
178
|
+
#: (Array[String] nesting) -> Type
|
|
179
|
+
def resolve_singleton_type_from_nesting(nesting)
|
|
180
|
+
resolved_name = resolve_nesting_fully_qualified_name(nesting)
|
|
181
|
+
last_part = resolved_name.split("::").last #: as !nil
|
|
182
|
+
Type.new("#{resolved_name}::<#{last_part}>")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Resolves the innermost constant in the nesting through the graph, handling compact-path definitions
|
|
186
|
+
# like `class Bar::Baz` inside a different module where the lexical nesting doesn't reflect the true
|
|
187
|
+
# constant hierarchy. Falls back to lexical joining if resolution fails.
|
|
188
|
+
#: (Array[String] nesting) -> String
|
|
189
|
+
def resolve_nesting_fully_qualified_name(nesting)
|
|
190
|
+
nesting_parts = nesting.dup
|
|
191
|
+
trailing_singletons = [] #: Array[String]
|
|
192
|
+
|
|
193
|
+
nesting_parts.reverse_each do |part|
|
|
194
|
+
break unless part.start_with?("<")
|
|
195
|
+
|
|
196
|
+
popped = nesting_parts.pop #: as !nil
|
|
197
|
+
trailing_singletons.unshift(popped)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if nesting_parts.any?
|
|
201
|
+
resolved = @graph.resolve_constant(
|
|
202
|
+
nesting_parts.last, #: as !nil
|
|
203
|
+
nesting_parts[0...-1], #: as !nil
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if resolved
|
|
207
|
+
parts = resolved.name.split("::") + trailing_singletons
|
|
208
|
+
return parts.join("::")
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Fallback to lexical joining if resolution fails
|
|
213
|
+
nesting.flat_map { |part| part.split("::") }.join("::")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
#: (String, Array[String]) -> Type?
|
|
217
|
+
def resolve_receiver_singleton_type(receiver_name, nesting)
|
|
218
|
+
receiver_declaration = @graph.resolve_constant(receiver_name, nesting)
|
|
219
|
+
return unless receiver_declaration.is_a?(Rubydex::Namespace)
|
|
220
|
+
|
|
221
|
+
singleton = receiver_declaration.singleton_class
|
|
222
|
+
return unless singleton
|
|
223
|
+
|
|
224
|
+
Type.new(singleton.name)
|
|
147
225
|
end
|
|
148
226
|
|
|
149
227
|
#: (NodeContext node_context) -> Type?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-lsp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.27.0.
|
|
4
|
+
version: 0.27.0.beta2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
@@ -106,7 +106,6 @@ files:
|
|
|
106
106
|
- lib/ruby_indexer/lib/ruby_indexer/location.rb
|
|
107
107
|
- lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
|
|
108
108
|
- lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
|
|
109
|
-
- lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
|
|
110
109
|
- lib/ruby_indexer/lib/ruby_indexer/uri.rb
|
|
111
110
|
- lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb
|
|
112
111
|
- lib/ruby_indexer/ruby_indexer.rb
|
|
@@ -182,6 +181,7 @@ files:
|
|
|
182
181
|
- lib/ruby_lsp/response_builders/signature_help.rb
|
|
183
182
|
- lib/ruby_lsp/response_builders/test_collection.rb
|
|
184
183
|
- lib/ruby_lsp/ruby_document.rb
|
|
184
|
+
- lib/ruby_lsp/rubydex/declaration.rb
|
|
185
185
|
- lib/ruby_lsp/rubydex/definition.rb
|
|
186
186
|
- lib/ruby_lsp/rubydex/reference.rb
|
|
187
187
|
- lib/ruby_lsp/scope.rb
|
|
@@ -189,7 +189,6 @@ files:
|
|
|
189
189
|
- lib/ruby_lsp/scripts/compose_bundle_windows.rb
|
|
190
190
|
- lib/ruby_lsp/server.rb
|
|
191
191
|
- lib/ruby_lsp/setup_bundler.rb
|
|
192
|
-
- lib/ruby_lsp/static_docs.rb
|
|
193
192
|
- lib/ruby_lsp/store.rb
|
|
194
193
|
- lib/ruby_lsp/test_helper.rb
|
|
195
194
|
- lib/ruby_lsp/test_reporters/lsp_reporter.rb
|
|
@@ -197,8 +196,6 @@ files:
|
|
|
197
196
|
- lib/ruby_lsp/test_reporters/test_unit_reporter.rb
|
|
198
197
|
- lib/ruby_lsp/type_inferrer.rb
|
|
199
198
|
- lib/ruby_lsp/utils.rb
|
|
200
|
-
- static_docs/break.md
|
|
201
|
-
- static_docs/yield.md
|
|
202
199
|
homepage: https://github.com/Shopify/ruby-lsp
|
|
203
200
|
licenses:
|
|
204
201
|
- MIT
|