ruby-lsp 0.20.0 → 0.21.0
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/README.md +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +24 -3
- data/exe/ruby-lsp-launcher +127 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +63 -12
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +56 -2
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +21 -6
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +15 -21
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +2 -2
- data/lib/ruby_indexer/test/enhancements_test.rb +51 -19
- data/lib/ruby_indexer/test/index_test.rb +91 -2
- data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +26 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +9 -2
- data/lib/ruby_lsp/base_server.rb +14 -5
- data/lib/ruby_lsp/client_capabilities.rb +60 -0
- data/lib/ruby_lsp/document.rb +1 -1
- data/lib/ruby_lsp/global_state.rb +20 -19
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/completion.rb +62 -0
- data/lib/ruby_lsp/listeners/definition.rb +48 -13
- data/lib/ruby_lsp/listeners/hover.rb +52 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/completion.rb +7 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/definition.rb +26 -11
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
- data/lib/ruby_lsp/requests/hover.rb +24 -6
- data/lib/ruby_lsp/requests/references.rb +2 -0
- data/lib/ruby_lsp/requests/rename.rb +3 -1
- data/lib/ruby_lsp/requests/request.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +11 -1
- data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
- data/lib/ruby_lsp/server.rb +54 -16
- data/lib/ruby_lsp/setup_bundler.rb +132 -24
- data/lib/ruby_lsp/utils.rb +8 -0
- metadata +8 -3
@@ -904,7 +904,7 @@ module RubyIndexer
|
|
904
904
|
assert_equal(14, entry.location.start_line)
|
905
905
|
end
|
906
906
|
|
907
|
-
def
|
907
|
+
def test_resolving_inherited_aliased_namespace
|
908
908
|
index(<<~RUBY)
|
909
909
|
module Bar
|
910
910
|
TARGET = 123
|
@@ -1490,7 +1490,7 @@ module RubyIndexer
|
|
1490
1490
|
assert_kind_of(Entry::UnresolvedMethodAlias, entry)
|
1491
1491
|
end
|
1492
1492
|
|
1493
|
-
def
|
1493
|
+
def test_unresolvable_method_aliases
|
1494
1494
|
index(<<~RUBY)
|
1495
1495
|
class Foo
|
1496
1496
|
alias bar baz
|
@@ -1934,5 +1934,94 @@ module RubyIndexer
|
|
1934
1934
|
real_namespace = @index.follow_aliased_namespace("Namespace::Second")
|
1935
1935
|
assert_equal("First::Second", real_namespace)
|
1936
1936
|
end
|
1937
|
+
|
1938
|
+
def test_resolving_alias_to_non_existing_namespace
|
1939
|
+
index(<<~RUBY)
|
1940
|
+
module Namespace
|
1941
|
+
class Foo
|
1942
|
+
module InnerNamespace
|
1943
|
+
Constants = Namespace::Foo::Constants
|
1944
|
+
end
|
1945
|
+
end
|
1946
|
+
end
|
1947
|
+
RUBY
|
1948
|
+
|
1949
|
+
entry = @index.resolve("Constants", ["Namespace", "Foo", "InnerNamespace"])&.first
|
1950
|
+
assert_instance_of(Entry::UnresolvedConstantAlias, entry)
|
1951
|
+
|
1952
|
+
entry = @index.resolve("Namespace::Foo::Constants", ["Namespace", "Foo", "InnerNamespace"])&.first
|
1953
|
+
assert_nil(entry)
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
def test_resolving_alias_to_existing_constant_from_inner_namespace
|
1957
|
+
index(<<~RUBY)
|
1958
|
+
module Parent
|
1959
|
+
CONST = 123
|
1960
|
+
end
|
1961
|
+
|
1962
|
+
module First
|
1963
|
+
module Namespace
|
1964
|
+
class Foo
|
1965
|
+
include Parent
|
1966
|
+
|
1967
|
+
module InnerNamespace
|
1968
|
+
Constants = Namespace::Foo::CONST
|
1969
|
+
end
|
1970
|
+
end
|
1971
|
+
end
|
1972
|
+
end
|
1973
|
+
RUBY
|
1974
|
+
|
1975
|
+
entry = @index.resolve("Namespace::Foo::CONST", ["First", "Namespace", "Foo", "InnerNamespace"])&.first
|
1976
|
+
assert_equal("Parent::CONST", entry.name)
|
1977
|
+
assert_instance_of(Entry::Constant, entry)
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
def test_build_non_redundant_name
|
1981
|
+
assert_equal(
|
1982
|
+
"Namespace::Foo::Constants",
|
1983
|
+
@index.send(
|
1984
|
+
:build_non_redundant_full_name,
|
1985
|
+
"Namespace::Foo::Constants",
|
1986
|
+
["Namespace", "Foo", "InnerNamespace"],
|
1987
|
+
),
|
1988
|
+
)
|
1989
|
+
|
1990
|
+
assert_equal(
|
1991
|
+
"Namespace::Foo::Constants",
|
1992
|
+
@index.send(
|
1993
|
+
:build_non_redundant_full_name,
|
1994
|
+
"Namespace::Foo::Constants",
|
1995
|
+
["Namespace", "Foo"],
|
1996
|
+
),
|
1997
|
+
)
|
1998
|
+
|
1999
|
+
assert_equal(
|
2000
|
+
"Namespace::Foo::Constants",
|
2001
|
+
@index.send(
|
2002
|
+
:build_non_redundant_full_name,
|
2003
|
+
"Foo::Constants",
|
2004
|
+
["Namespace", "Foo"],
|
2005
|
+
),
|
2006
|
+
)
|
2007
|
+
|
2008
|
+
assert_equal(
|
2009
|
+
"Bar::Namespace::Foo::Constants",
|
2010
|
+
@index.send(
|
2011
|
+
:build_non_redundant_full_name,
|
2012
|
+
"Namespace::Foo::Constants",
|
2013
|
+
["Bar"],
|
2014
|
+
),
|
2015
|
+
)
|
2016
|
+
|
2017
|
+
assert_equal(
|
2018
|
+
"First::Namespace::Foo::Constants",
|
2019
|
+
@index.send(
|
2020
|
+
:build_non_redundant_full_name,
|
2021
|
+
"Namespace::Foo::Constants",
|
2022
|
+
["First", "Namespace", "Foo", "InnerNamespace"],
|
2023
|
+
),
|
2024
|
+
)
|
2025
|
+
end
|
1937
2026
|
end
|
1938
2027
|
end
|
@@ -209,7 +209,7 @@ module RubyIndexer
|
|
209
209
|
end
|
210
210
|
RUBY
|
211
211
|
|
212
|
-
# If the surrounding method is
|
212
|
+
# If the surrounding method is being defined on any dynamic value that isn't `self`, then we attribute the
|
213
213
|
# instance variable to the wrong owner since there's no way to understand that statically
|
214
214
|
entry = T.must(@index["@a"]&.first)
|
215
215
|
owner = T.must(entry.owner)
|
@@ -123,6 +123,32 @@ module RubyIndexer
|
|
123
123
|
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
|
124
124
|
end
|
125
125
|
|
126
|
+
def test_visibility_tracking_with_module_function
|
127
|
+
index(<<~RUBY)
|
128
|
+
module Test
|
129
|
+
def foo; end
|
130
|
+
def bar; end
|
131
|
+
module_function :foo, "bar"
|
132
|
+
end
|
133
|
+
RUBY
|
134
|
+
|
135
|
+
["foo", "bar"].each do |keyword|
|
136
|
+
entries = T.must(@index[keyword])
|
137
|
+
# should receive two entries because module_function creates a singleton method
|
138
|
+
# for the Test module and a private method for classes include the Test module
|
139
|
+
assert_equal(entries.size, 2)
|
140
|
+
first_entry, second_entry = *entries
|
141
|
+
# The first entry points to the location of the module_function call
|
142
|
+
assert_equal("Test", first_entry.owner.name)
|
143
|
+
assert_instance_of(Entry::Module, first_entry.owner)
|
144
|
+
assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility)
|
145
|
+
# The second entry points to the public singleton method
|
146
|
+
assert_equal("Test::<Class:Test>", second_entry.owner.name)
|
147
|
+
assert_instance_of(Entry::SingletonClass, second_entry.owner)
|
148
|
+
assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
126
152
|
def test_method_with_parameters
|
127
153
|
index(<<~RUBY)
|
128
154
|
class Foo
|
@@ -100,7 +100,7 @@ module RubyIndexer
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def test_location_and_name_location_are_the_same
|
103
|
-
# NOTE: RBS does not store the name location for classes, modules or methods. This
|
103
|
+
# NOTE: RBS does not store the name location for classes, modules or methods. This behavior is not exactly what
|
104
104
|
# we would like, but for now we assign the same location to both
|
105
105
|
|
106
106
|
entries = @index["Array"]
|
data/lib/ruby_lsp/addon.rb
CHANGED
@@ -46,7 +46,7 @@ module RubyLsp
|
|
46
46
|
sig { returns(T::Array[T.class_of(Addon)]) }
|
47
47
|
attr_reader :addon_classes
|
48
48
|
|
49
|
-
# Automatically track and instantiate
|
49
|
+
# Automatically track and instantiate add-on classes
|
50
50
|
sig { params(child_class: T.class_of(Addon)).void }
|
51
51
|
def inherited(child_class)
|
52
52
|
addon_classes << child_class
|
@@ -82,7 +82,7 @@ module RubyLsp
|
|
82
82
|
e
|
83
83
|
end
|
84
84
|
|
85
|
-
# Instantiate all discovered
|
85
|
+
# Instantiate all discovered add-on classes
|
86
86
|
self.addons = addon_classes.map(&:new)
|
87
87
|
self.file_watcher_addons = addons.select { |addon| addon.respond_to?(:workspace_did_change_watched_files) }
|
88
88
|
|
@@ -194,6 +194,13 @@ module RubyLsp
|
|
194
194
|
sig { abstract.returns(String) }
|
195
195
|
def version; end
|
196
196
|
|
197
|
+
# Handle a response from a window/showMessageRequest request. Add-ons must include the addon_name as part of the
|
198
|
+
# original request so that the response is delegated to the correct add-on and must override this method to handle
|
199
|
+
# the response
|
200
|
+
# https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
|
201
|
+
sig { overridable.params(title: String).void }
|
202
|
+
def handle_window_show_message_response(title); end
|
203
|
+
|
197
204
|
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
|
198
205
|
sig do
|
199
206
|
overridable.params(
|
data/lib/ruby_lsp/base_server.rb
CHANGED
@@ -8,9 +8,11 @@ module RubyLsp
|
|
8
8
|
|
9
9
|
abstract!
|
10
10
|
|
11
|
-
sig { params(
|
12
|
-
def initialize(
|
13
|
-
@test_mode = T.let(test_mode, T::Boolean)
|
11
|
+
sig { params(options: T.untyped).void }
|
12
|
+
def initialize(**options)
|
13
|
+
@test_mode = T.let(options[:test_mode], T.nilable(T::Boolean))
|
14
|
+
@setup_error = T.let(options[:setup_error], T.nilable(StandardError))
|
15
|
+
@install_error = T.let(options[:install_error], T.nilable(StandardError))
|
14
16
|
@writer = T.let(Transport::Stdio::Writer.new, Transport::Stdio::Writer)
|
15
17
|
@reader = T.let(Transport::Stdio::Reader.new, Transport::Stdio::Reader)
|
16
18
|
@incoming_queue = T.let(Thread::Queue.new, Thread::Queue)
|
@@ -22,7 +24,7 @@ module RubyLsp
|
|
22
24
|
@store = T.let(Store.new, Store)
|
23
25
|
@outgoing_dispatcher = T.let(
|
24
26
|
Thread.new do
|
25
|
-
unless test_mode
|
27
|
+
unless @test_mode
|
26
28
|
while (message = @outgoing_queue.pop)
|
27
29
|
@mutex.synchronize { @writer.write(message.to_hash) }
|
28
30
|
end
|
@@ -33,6 +35,11 @@ module RubyLsp
|
|
33
35
|
|
34
36
|
@global_state = T.let(GlobalState.new, GlobalState)
|
35
37
|
Thread.main.priority = 1
|
38
|
+
|
39
|
+
# We read the initialize request in `exe/ruby-lsp` to be able to determine the workspace URI where Bundler should
|
40
|
+
# be set up
|
41
|
+
initialize_request = options[:initialize_request]
|
42
|
+
process_message(initialize_request) if initialize_request
|
36
43
|
end
|
37
44
|
|
38
45
|
sig { void }
|
@@ -59,7 +66,9 @@ module RubyLsp
|
|
59
66
|
# If the client supports request delegation and we're working with an ERB document and there was
|
60
67
|
# something to parse, then we have to maintain the client updated about the virtual state of the host
|
61
68
|
# language source
|
62
|
-
if document.parse! && @global_state.supports_request_delegation &&
|
69
|
+
if document.parse! && @global_state.client_capabilities.supports_request_delegation &&
|
70
|
+
document.is_a?(ERBDocument)
|
71
|
+
|
63
72
|
send_message(
|
64
73
|
Notification.new(
|
65
74
|
method: "delegate/textDocument/virtualState",
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# This class stores all client capabilities that the Ruby LSP and its add-ons depend on to ensure that we're
|
6
|
+
# not enabling functionality unsupported by the editor connecting to the server
|
7
|
+
class ClientCapabilities
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(T::Boolean) }
|
11
|
+
attr_reader :supports_watching_files,
|
12
|
+
:supports_request_delegation,
|
13
|
+
:window_show_message_supports_extra_properties
|
14
|
+
|
15
|
+
sig { void }
|
16
|
+
def initialize
|
17
|
+
# The editor supports watching files. This requires two capabilities: dynamic registration and relative pattern
|
18
|
+
# support
|
19
|
+
@supports_watching_files = T.let(false, T::Boolean)
|
20
|
+
|
21
|
+
# The editor supports request delegation. This is an experimental capability since request delegation has not been
|
22
|
+
# standardized into the LSP spec yet
|
23
|
+
@supports_request_delegation = T.let(false, T::Boolean)
|
24
|
+
|
25
|
+
# The editor supports extra arbitrary properties for `window/showMessageRequest`. Necessary for add-ons to show
|
26
|
+
# dialogs with user interactions
|
27
|
+
@window_show_message_supports_extra_properties = T.let(false, T::Boolean)
|
28
|
+
|
29
|
+
# Which resource operations the editor supports, like renaming files
|
30
|
+
@supported_resource_operations = T.let([], T::Array[String])
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { params(capabilities: T::Hash[Symbol, T.untyped]).void }
|
34
|
+
def apply_client_capabilities(capabilities)
|
35
|
+
workspace_capabilities = capabilities[:workspace] || {}
|
36
|
+
|
37
|
+
file_watching_caps = workspace_capabilities[:didChangeWatchedFiles]
|
38
|
+
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
39
|
+
@supports_watching_files = true
|
40
|
+
end
|
41
|
+
|
42
|
+
@supports_request_delegation = capabilities.dig(:experimental, :requestDelegation) || false
|
43
|
+
supported_resource_operations = workspace_capabilities.dig(:workspaceEdit, :resourceOperations)
|
44
|
+
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
45
|
+
|
46
|
+
supports_additional_properties = capabilities.dig(
|
47
|
+
:window,
|
48
|
+
:showMessage,
|
49
|
+
:messageActionItem,
|
50
|
+
:additionalPropertiesSupport,
|
51
|
+
)
|
52
|
+
@window_show_message_supports_extra_properties = supports_additional_properties || false
|
53
|
+
end
|
54
|
+
|
55
|
+
sig { returns(T::Boolean) }
|
56
|
+
def supports_rename?
|
57
|
+
@supported_resource_operations.include?("rename")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -63,7 +63,7 @@ module RubyLsp
|
|
63
63
|
sig { abstract.returns(LanguageId) }
|
64
64
|
def language_id; end
|
65
65
|
|
66
|
-
# TODO: remove this method once all
|
66
|
+
# TODO: remove this method once all non-positional requests have been migrated to the listener pattern
|
67
67
|
sig do
|
68
68
|
type_parameters(:T)
|
69
69
|
.params(
|
@@ -21,14 +21,14 @@ module RubyLsp
|
|
21
21
|
attr_reader :encoding
|
22
22
|
|
23
23
|
sig { returns(T::Boolean) }
|
24
|
-
attr_reader :
|
25
|
-
|
26
|
-
sig { returns(T::Array[String]) }
|
27
|
-
attr_reader :supported_resource_operations
|
24
|
+
attr_reader :experimental_features, :top_level_bundle
|
28
25
|
|
29
26
|
sig { returns(TypeInferrer) }
|
30
27
|
attr_reader :type_inferrer
|
31
28
|
|
29
|
+
sig { returns(ClientCapabilities) }
|
30
|
+
attr_reader :client_capabilities
|
31
|
+
|
32
32
|
sig { void }
|
33
33
|
def initialize
|
34
34
|
@workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
|
@@ -40,12 +40,19 @@ module RubyLsp
|
|
40
40
|
@has_type_checker = T.let(true, T::Boolean)
|
41
41
|
@index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
|
42
42
|
@supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
|
43
|
-
@supports_watching_files = T.let(false, T::Boolean)
|
44
43
|
@experimental_features = T.let(false, T::Boolean)
|
45
44
|
@type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
|
46
45
|
@addon_settings = T.let({}, T::Hash[String, T.untyped])
|
47
|
-
@
|
48
|
-
|
46
|
+
@top_level_bundle = T.let(
|
47
|
+
begin
|
48
|
+
Bundler.with_original_env { Bundler.default_gemfile }
|
49
|
+
true
|
50
|
+
rescue Bundler::GemfileNotFound, Bundler::GitError
|
51
|
+
false
|
52
|
+
end,
|
53
|
+
T::Boolean,
|
54
|
+
)
|
55
|
+
@client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
|
49
56
|
end
|
50
57
|
|
51
58
|
sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
|
@@ -123,12 +130,8 @@ module RubyLsp
|
|
123
130
|
end
|
124
131
|
@index.configuration.encoding = @encoding
|
125
132
|
|
126
|
-
file_watching_caps = options.dig(:capabilities, :workspace, :didChangeWatchedFiles)
|
127
|
-
if file_watching_caps&.dig(:dynamicRegistration) && file_watching_caps&.dig(:relativePatternSupport)
|
128
|
-
@supports_watching_files = true
|
129
|
-
end
|
130
|
-
|
131
133
|
@experimental_features = options.dig(:initializationOptions, :experimentalFeaturesEnabled) || false
|
134
|
+
@client_capabilities.apply_client_capabilities(options[:capabilities]) if options[:capabilities]
|
132
135
|
|
133
136
|
addon_settings = options.dig(:initializationOptions, :addonSettings)
|
134
137
|
if addon_settings
|
@@ -136,10 +139,6 @@ module RubyLsp
|
|
136
139
|
@addon_settings.merge!(addon_settings)
|
137
140
|
end
|
138
141
|
|
139
|
-
@supports_request_delegation = options.dig(:capabilities, :experimental, :requestDelegation) || false
|
140
|
-
supported_resource_operations = options.dig(:capabilities, :workspace, :workspaceEdit, :resourceOperations)
|
141
|
-
@supported_resource_operations = supported_resource_operations if supported_resource_operations
|
142
|
-
|
143
142
|
notifications
|
144
143
|
end
|
145
144
|
|
@@ -231,14 +230,16 @@ module RubyLsp
|
|
231
230
|
sig { returns(T::Array[String]) }
|
232
231
|
def gather_direct_dependencies
|
233
232
|
Bundler.with_original_env { Bundler.default_gemfile }
|
234
|
-
|
233
|
+
|
234
|
+
dependencies = Bundler.locked_gems&.dependencies&.keys || []
|
235
|
+
dependencies + gemspec_dependencies
|
235
236
|
rescue Bundler::GemfileNotFound
|
236
237
|
[]
|
237
238
|
end
|
238
239
|
|
239
240
|
sig { returns(T::Array[String]) }
|
240
241
|
def gemspec_dependencies
|
241
|
-
Bundler.locked_gems
|
242
|
+
(Bundler.locked_gems&.sources || [])
|
242
243
|
.grep(Bundler::Source::Gemspec)
|
243
244
|
.flat_map { _1.gemspec&.dependencies&.map(&:name) }
|
244
245
|
end
|
@@ -246,7 +247,7 @@ module RubyLsp
|
|
246
247
|
sig { returns(T::Array[String]) }
|
247
248
|
def gather_direct_and_indirect_dependencies
|
248
249
|
Bundler.with_original_env { Bundler.default_gemfile }
|
249
|
-
Bundler.locked_gems
|
250
|
+
Bundler.locked_gems&.specs&.map(&:name) || []
|
250
251
|
rescue Bundler::GemfileNotFound
|
251
252
|
[]
|
252
253
|
end
|
data/lib/ruby_lsp/internal.rb
CHANGED
@@ -12,6 +12,7 @@ require "sorbet-runtime"
|
|
12
12
|
require "bundler"
|
13
13
|
Bundler.ui.level = :silent
|
14
14
|
|
15
|
+
require "json"
|
15
16
|
require "uri"
|
16
17
|
require "cgi"
|
17
18
|
require "set"
|
@@ -28,6 +29,7 @@ require "core_ext/uri"
|
|
28
29
|
require "ruby_lsp/utils"
|
29
30
|
require "ruby_lsp/static_docs"
|
30
31
|
require "ruby_lsp/scope"
|
32
|
+
require "ruby_lsp/client_capabilities"
|
31
33
|
require "ruby_lsp/global_state"
|
32
34
|
require "ruby_lsp/server"
|
33
35
|
require "ruby_lsp/type_inferrer"
|
@@ -85,6 +85,12 @@ module RubyLsp
|
|
85
85
|
:on_constant_path_node_enter,
|
86
86
|
:on_constant_read_node_enter,
|
87
87
|
:on_call_node_enter,
|
88
|
+
:on_global_variable_and_write_node_enter,
|
89
|
+
:on_global_variable_operator_write_node_enter,
|
90
|
+
:on_global_variable_or_write_node_enter,
|
91
|
+
:on_global_variable_read_node_enter,
|
92
|
+
:on_global_variable_target_node_enter,
|
93
|
+
:on_global_variable_write_node_enter,
|
88
94
|
:on_instance_variable_read_node_enter,
|
89
95
|
:on_instance_variable_write_node_enter,
|
90
96
|
:on_instance_variable_and_write_node_enter,
|
@@ -180,6 +186,36 @@ module RubyLsp
|
|
180
186
|
end
|
181
187
|
end
|
182
188
|
|
189
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
190
|
+
def on_global_variable_and_write_node_enter(node)
|
191
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
192
|
+
end
|
193
|
+
|
194
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
195
|
+
def on_global_variable_operator_write_node_enter(node)
|
196
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
197
|
+
end
|
198
|
+
|
199
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
200
|
+
def on_global_variable_or_write_node_enter(node)
|
201
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
202
|
+
end
|
203
|
+
|
204
|
+
sig { params(node: Prism::GlobalVariableReadNode).void }
|
205
|
+
def on_global_variable_read_node_enter(node)
|
206
|
+
handle_global_variable_completion(node.name.to_s, node.location)
|
207
|
+
end
|
208
|
+
|
209
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
210
|
+
def on_global_variable_target_node_enter(node)
|
211
|
+
handle_global_variable_completion(node.name.to_s, node.location)
|
212
|
+
end
|
213
|
+
|
214
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
215
|
+
def on_global_variable_write_node_enter(node)
|
216
|
+
handle_global_variable_completion(node.name.to_s, node.name_loc)
|
217
|
+
end
|
218
|
+
|
183
219
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
184
220
|
def on_instance_variable_read_node_enter(node)
|
185
221
|
handle_instance_variable_completion(node.name.to_s, node.location)
|
@@ -267,6 +303,29 @@ module RubyLsp
|
|
267
303
|
end
|
268
304
|
end
|
269
305
|
|
306
|
+
sig { params(name: String, location: Prism::Location).void }
|
307
|
+
def handle_global_variable_completion(name, location)
|
308
|
+
candidates = @index.prefix_search(name)
|
309
|
+
|
310
|
+
return if candidates.none?
|
311
|
+
|
312
|
+
range = range_from_location(location)
|
313
|
+
|
314
|
+
candidates.flatten.uniq(&:name).each do |entry|
|
315
|
+
entry_name = entry.name
|
316
|
+
|
317
|
+
@response_builder << Interface::CompletionItem.new(
|
318
|
+
label: entry_name,
|
319
|
+
filter_text: entry_name,
|
320
|
+
label_details: Interface::CompletionItemLabelDetails.new(
|
321
|
+
description: entry.file_name,
|
322
|
+
),
|
323
|
+
text_edit: Interface::TextEdit.new(range: range, new_text: entry_name),
|
324
|
+
kind: Constant::CompletionItemKind::VARIABLE,
|
325
|
+
)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
270
329
|
sig { params(name: String, location: Prism::Location).void }
|
271
330
|
def handle_instance_variable_completion(name, location)
|
272
331
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -381,8 +440,11 @@ module RubyLsp
|
|
381
440
|
return unless range
|
382
441
|
|
383
442
|
guessed_type = type.is_a?(TypeInferrer::GuessedType) && type.name
|
443
|
+
external_references = @node_context.fully_qualified_name != type.name
|
384
444
|
|
385
445
|
@index.method_completion_candidates(method_name, type.name).each do |entry|
|
446
|
+
next if entry.visibility != RubyIndexer::Entry::Visibility::PUBLIC && external_references
|
447
|
+
|
386
448
|
entry_name = entry.name
|
387
449
|
owner_name = entry.owner&.name
|
388
450
|
|
@@ -39,7 +39,12 @@ module RubyLsp
|
|
39
39
|
:on_block_argument_node_enter,
|
40
40
|
:on_constant_read_node_enter,
|
41
41
|
:on_constant_path_node_enter,
|
42
|
+
:on_global_variable_and_write_node_enter,
|
43
|
+
:on_global_variable_operator_write_node_enter,
|
44
|
+
:on_global_variable_or_write_node_enter,
|
42
45
|
:on_global_variable_read_node_enter,
|
46
|
+
:on_global_variable_target_node_enter,
|
47
|
+
:on_global_variable_write_node_enter,
|
43
48
|
:on_instance_variable_read_node_enter,
|
44
49
|
:on_instance_variable_write_node_enter,
|
45
50
|
:on_instance_variable_and_write_node_enter,
|
@@ -121,23 +126,34 @@ module RubyLsp
|
|
121
126
|
find_in_index(name)
|
122
127
|
end
|
123
128
|
|
129
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
130
|
+
def on_global_variable_and_write_node_enter(node)
|
131
|
+
handle_global_variable_definition(node.name.to_s)
|
132
|
+
end
|
133
|
+
|
134
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
135
|
+
def on_global_variable_operator_write_node_enter(node)
|
136
|
+
handle_global_variable_definition(node.name.to_s)
|
137
|
+
end
|
138
|
+
|
139
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
140
|
+
def on_global_variable_or_write_node_enter(node)
|
141
|
+
handle_global_variable_definition(node.name.to_s)
|
142
|
+
end
|
143
|
+
|
124
144
|
sig { params(node: Prism::GlobalVariableReadNode).void }
|
125
145
|
def on_global_variable_read_node_enter(node)
|
126
|
-
|
127
|
-
|
128
|
-
return unless entries
|
146
|
+
handle_global_variable_definition(node.name.to_s)
|
147
|
+
end
|
129
148
|
|
130
|
-
|
131
|
-
|
149
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
150
|
+
def on_global_variable_target_node_enter(node)
|
151
|
+
handle_global_variable_definition(node.name.to_s)
|
152
|
+
end
|
132
153
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
137
|
-
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
138
|
-
),
|
139
|
-
)
|
140
|
-
end
|
154
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
155
|
+
def on_global_variable_write_node_enter(node)
|
156
|
+
handle_global_variable_definition(node.name.to_s)
|
141
157
|
end
|
142
158
|
|
143
159
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
@@ -197,6 +213,25 @@ module RubyLsp
|
|
197
213
|
)
|
198
214
|
end
|
199
215
|
|
216
|
+
sig { params(name: String).void }
|
217
|
+
def handle_global_variable_definition(name)
|
218
|
+
entries = @index[name]
|
219
|
+
|
220
|
+
return unless entries
|
221
|
+
|
222
|
+
entries.each do |entry|
|
223
|
+
location = entry.location
|
224
|
+
|
225
|
+
@response_builder << Interface::Location.new(
|
226
|
+
uri: URI::Generic.from_path(path: entry.file_path).to_s,
|
227
|
+
range: Interface::Range.new(
|
228
|
+
start: Interface::Position.new(line: location.start_line - 1, character: location.start_column),
|
229
|
+
end: Interface::Position.new(line: location.end_line - 1, character: location.end_column),
|
230
|
+
),
|
231
|
+
)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
200
235
|
sig { params(name: String).void }
|
201
236
|
def handle_instance_variable_definition(name)
|
202
237
|
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
@@ -13,6 +13,12 @@ module RubyLsp
|
|
13
13
|
Prism::ConstantReadNode,
|
14
14
|
Prism::ConstantWriteNode,
|
15
15
|
Prism::ConstantPathNode,
|
16
|
+
Prism::GlobalVariableAndWriteNode,
|
17
|
+
Prism::GlobalVariableOperatorWriteNode,
|
18
|
+
Prism::GlobalVariableOrWriteNode,
|
19
|
+
Prism::GlobalVariableReadNode,
|
20
|
+
Prism::GlobalVariableTargetNode,
|
21
|
+
Prism::GlobalVariableWriteNode,
|
16
22
|
Prism::InstanceVariableReadNode,
|
17
23
|
Prism::InstanceVariableAndWriteNode,
|
18
24
|
Prism::InstanceVariableOperatorWriteNode,
|
@@ -62,6 +68,12 @@ module RubyLsp
|
|
62
68
|
:on_constant_write_node_enter,
|
63
69
|
:on_constant_path_node_enter,
|
64
70
|
:on_call_node_enter,
|
71
|
+
:on_global_variable_and_write_node_enter,
|
72
|
+
:on_global_variable_operator_write_node_enter,
|
73
|
+
:on_global_variable_or_write_node_enter,
|
74
|
+
:on_global_variable_read_node_enter,
|
75
|
+
:on_global_variable_target_node_enter,
|
76
|
+
:on_global_variable_write_node_enter,
|
65
77
|
:on_instance_variable_read_node_enter,
|
66
78
|
:on_instance_variable_write_node_enter,
|
67
79
|
:on_instance_variable_and_write_node_enter,
|
@@ -128,6 +140,36 @@ module RubyLsp
|
|
128
140
|
handle_method_hover(message)
|
129
141
|
end
|
130
142
|
|
143
|
+
sig { params(node: Prism::GlobalVariableAndWriteNode).void }
|
144
|
+
def on_global_variable_and_write_node_enter(node)
|
145
|
+
handle_global_variable_hover(node.name.to_s)
|
146
|
+
end
|
147
|
+
|
148
|
+
sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
|
149
|
+
def on_global_variable_operator_write_node_enter(node)
|
150
|
+
handle_global_variable_hover(node.name.to_s)
|
151
|
+
end
|
152
|
+
|
153
|
+
sig { params(node: Prism::GlobalVariableOrWriteNode).void }
|
154
|
+
def on_global_variable_or_write_node_enter(node)
|
155
|
+
handle_global_variable_hover(node.name.to_s)
|
156
|
+
end
|
157
|
+
|
158
|
+
sig { params(node: Prism::GlobalVariableReadNode).void }
|
159
|
+
def on_global_variable_read_node_enter(node)
|
160
|
+
handle_global_variable_hover(node.name.to_s)
|
161
|
+
end
|
162
|
+
|
163
|
+
sig { params(node: Prism::GlobalVariableTargetNode).void }
|
164
|
+
def on_global_variable_target_node_enter(node)
|
165
|
+
handle_global_variable_hover(node.name.to_s)
|
166
|
+
end
|
167
|
+
|
168
|
+
sig { params(node: Prism::GlobalVariableWriteNode).void }
|
169
|
+
def on_global_variable_write_node_enter(node)
|
170
|
+
handle_global_variable_hover(node.name.to_s)
|
171
|
+
end
|
172
|
+
|
131
173
|
sig { params(node: Prism::InstanceVariableReadNode).void }
|
132
174
|
def on_instance_variable_read_node_enter(node)
|
133
175
|
handle_instance_variable_hover(node.name.to_s)
|
@@ -265,6 +307,16 @@ module RubyLsp
|
|
265
307
|
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
266
308
|
end
|
267
309
|
|
310
|
+
sig { params(name: String).void }
|
311
|
+
def handle_global_variable_hover(name)
|
312
|
+
entries = @index[name]
|
313
|
+
return unless entries
|
314
|
+
|
315
|
+
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
316
|
+
@response_builder.push(content, category: category)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
268
320
|
sig { params(name: String, location: Prism::Location).void }
|
269
321
|
def generate_hover(name, location)
|
270
322
|
entries = @index.resolve(name, @node_context.nesting)
|