ruby-lsp 0.17.3 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
# 
|
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
|
+
# 
|
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:
|