ruby-lsp 0.17.3 → 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +241 -91
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +74 -102
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +81 -19
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +50 -2
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
- data/lib/ruby_indexer/test/index_test.rb +326 -27
- data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
- data/lib/ruby_indexer/test/method_test.rb +54 -24
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +27 -2
- data/lib/ruby_lsp/document.rb +37 -8
- data/lib/ruby_lsp/global_state.rb +7 -3
- data/lib/ruby_lsp/internal.rb +1 -0
- data/lib/ruby_lsp/listeners/completion.rb +53 -14
- data/lib/ruby_lsp/listeners/definition.rb +11 -7
- data/lib/ruby_lsp/listeners/hover.rb +14 -7
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
- data/lib/ruby_lsp/node_context.rb +6 -1
- data/lib/ruby_lsp/requests/completion.rb +5 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
- data/lib/ruby_lsp/requests/support/common.rb +19 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +91 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +54 -15
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +86 -0
- 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
|
-
|
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,
|
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
|
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:
|
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
|
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:
|
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:
|
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
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -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
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -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.
|
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.
|
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
|
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
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -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(:
|
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.
|
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
|
+
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:
|