ruby-lsp 0.14.5 → 0.15.0
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/exe/ruby-lsp +1 -16
- data/exe/ruby-lsp-check +13 -22
- data/lib/ruby_indexer/lib/ruby_indexer/collector.rb +21 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +12 -24
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +16 -0
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +3 -2
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +46 -0
- data/lib/ruby_indexer/test/configuration_test.rb +2 -11
- data/lib/ruby_indexer/test/index_test.rb +5 -0
- data/lib/ruby_lsp/addon.rb +18 -5
- data/lib/ruby_lsp/base_server.rb +147 -0
- data/lib/ruby_lsp/document.rb +0 -5
- data/lib/ruby_lsp/{requests/support/dependency_detector.rb → global_state.rb} +30 -9
- data/lib/ruby_lsp/internal.rb +2 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +66 -18
- data/lib/ruby_lsp/listeners/completion.rb +13 -14
- data/lib/ruby_lsp/listeners/definition.rb +4 -3
- data/lib/ruby_lsp/listeners/document_symbol.rb +91 -3
- data/lib/ruby_lsp/listeners/hover.rb +6 -5
- data/lib/ruby_lsp/listeners/signature_help.rb +7 -4
- data/lib/ruby_lsp/load_sorbet.rb +62 -0
- data/lib/ruby_lsp/requests/code_lens.rb +4 -3
- data/lib/ruby_lsp/requests/completion.rb +15 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +56 -0
- data/lib/ruby_lsp/requests/definition.rb +18 -5
- data/lib/ruby_lsp/requests/document_symbol.rb +3 -3
- data/lib/ruby_lsp/requests/hover.rb +9 -5
- data/lib/ruby_lsp/requests/request.rb +51 -0
- data/lib/ruby_lsp/requests/signature_help.rb +4 -3
- data/lib/ruby_lsp/requests/support/common.rb +25 -5
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +5 -4
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +756 -142
- data/lib/ruby_lsp/store.rb +0 -8
- data/lib/ruby_lsp/test_helper.rb +45 -0
- data/lib/ruby_lsp/utils.rb +68 -33
- metadata +8 -5
- data/lib/ruby_lsp/executor.rb +0 -612
@@ -31,13 +31,13 @@ module RubyLsp
|
|
31
31
|
sig do
|
32
32
|
params(
|
33
33
|
document: Document,
|
34
|
-
|
34
|
+
global_state: GlobalState,
|
35
35
|
position: T::Hash[Symbol, T.untyped],
|
36
36
|
dispatcher: Prism::Dispatcher,
|
37
37
|
typechecker_enabled: T::Boolean,
|
38
38
|
).void
|
39
39
|
end
|
40
|
-
def initialize(document,
|
40
|
+
def initialize(document, global_state, position, dispatcher, typechecker_enabled)
|
41
41
|
super()
|
42
42
|
@response_builder = T.let(
|
43
43
|
ResponseBuilders::CollectionResponseBuilder[Interface::Location].new,
|
@@ -49,12 +49,25 @@ module RubyLsp
|
|
49
49
|
node_types: [Prism::CallNode, Prism::ConstantReadNode, Prism::ConstantPathNode],
|
50
50
|
)
|
51
51
|
|
52
|
-
|
52
|
+
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
53
|
+
target = determine_target(
|
54
|
+
target,
|
55
|
+
parent,
|
56
|
+
position,
|
57
|
+
)
|
58
|
+
end
|
53
59
|
|
54
|
-
Listeners::Definition.new(
|
60
|
+
Listeners::Definition.new(
|
61
|
+
@response_builder,
|
62
|
+
global_state,
|
63
|
+
document.uri,
|
64
|
+
nesting,
|
65
|
+
dispatcher,
|
66
|
+
typechecker_enabled,
|
67
|
+
)
|
55
68
|
|
56
69
|
Addon.addons.each do |addon|
|
57
|
-
addon.create_definition_listener(@response_builder, document.uri, nesting,
|
70
|
+
addon.create_definition_listener(@response_builder, global_state, document.uri, nesting, dispatcher)
|
58
71
|
end
|
59
72
|
|
60
73
|
@target = T.let(target, T.nilable(Prism::Node))
|
@@ -45,11 +45,11 @@ module RubyLsp
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
sig { params(dispatcher: Prism::Dispatcher).void }
|
49
|
-
def initialize(dispatcher)
|
48
|
+
sig { params(uri: URI::Generic, dispatcher: Prism::Dispatcher).void }
|
49
|
+
def initialize(uri, dispatcher)
|
50
50
|
super()
|
51
51
|
@response_builder = T.let(ResponseBuilders::DocumentSymbol.new, ResponseBuilders::DocumentSymbol)
|
52
|
-
Listeners::DocumentSymbol.new(@response_builder, dispatcher)
|
52
|
+
Listeners::DocumentSymbol.new(@response_builder, uri, dispatcher)
|
53
53
|
|
54
54
|
Addon.addons.each do |addon|
|
55
55
|
addon.create_document_symbol_listener(@response_builder, dispatcher)
|
@@ -33,13 +33,13 @@ module RubyLsp
|
|
33
33
|
sig do
|
34
34
|
params(
|
35
35
|
document: Document,
|
36
|
-
|
36
|
+
global_state: GlobalState,
|
37
37
|
position: T::Hash[Symbol, T.untyped],
|
38
38
|
dispatcher: Prism::Dispatcher,
|
39
39
|
typechecker_enabled: T::Boolean,
|
40
40
|
).void
|
41
41
|
end
|
42
|
-
def initialize(document,
|
42
|
+
def initialize(document, global_state, position, dispatcher, typechecker_enabled)
|
43
43
|
super()
|
44
44
|
@target = T.let(nil, T.nilable(Prism::Node))
|
45
45
|
@target, parent, nesting = document.locate_node(
|
@@ -50,7 +50,11 @@ module RubyLsp
|
|
50
50
|
if (Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
|
51
51
|
!Listeners::Hover::ALLOWED_TARGETS.include?(@target.class)) ||
|
52
52
|
(parent.is_a?(Prism::ConstantPathNode) && @target.is_a?(Prism::ConstantReadNode))
|
53
|
-
@target =
|
53
|
+
@target = determine_target(
|
54
|
+
T.must(@target),
|
55
|
+
T.must(parent),
|
56
|
+
position,
|
57
|
+
)
|
54
58
|
end
|
55
59
|
|
56
60
|
# Don't need to instantiate any listeners if there's no target
|
@@ -58,9 +62,9 @@ module RubyLsp
|
|
58
62
|
|
59
63
|
uri = document.uri
|
60
64
|
@response_builder = T.let(ResponseBuilders::Hover.new, ResponseBuilders::Hover)
|
61
|
-
Listeners::Hover.new(@response_builder, uri, nesting,
|
65
|
+
Listeners::Hover.new(@response_builder, global_state, uri, nesting, dispatcher, typechecker_enabled)
|
62
66
|
Addon.addons.each do |addon|
|
63
|
-
addon.create_hover_listener(@response_builder,
|
67
|
+
addon.create_hover_listener(@response_builder, global_state, nesting, dispatcher)
|
64
68
|
end
|
65
69
|
|
66
70
|
@dispatcher = dispatcher
|
@@ -12,6 +12,57 @@ module RubyLsp
|
|
12
12
|
|
13
13
|
sig { abstract.returns(T.anything) }
|
14
14
|
def perform; end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Checks if a location covers a position
|
19
|
+
sig { params(location: Prism::Location, position: T.untyped).returns(T::Boolean) }
|
20
|
+
def cover?(location, position)
|
21
|
+
start_covered =
|
22
|
+
location.start_line - 1 < position[:line] ||
|
23
|
+
(
|
24
|
+
location.start_line - 1 == position[:line] &&
|
25
|
+
location.start_column <= position[:character]
|
26
|
+
)
|
27
|
+
end_covered =
|
28
|
+
location.end_line - 1 > position[:line] ||
|
29
|
+
(
|
30
|
+
location.end_line - 1 == position[:line] &&
|
31
|
+
location.end_column >= position[:character]
|
32
|
+
)
|
33
|
+
start_covered && end_covered
|
34
|
+
end
|
35
|
+
|
36
|
+
# Based on a constant node target, a constant path node parent and a position, this method will find the exact
|
37
|
+
# portion of the constant path that matches the requested position, for higher precision in hover and
|
38
|
+
# definition. For example:
|
39
|
+
#
|
40
|
+
# ```ruby
|
41
|
+
# Foo::Bar::Baz
|
42
|
+
# # ^ Going to definition here should go to Foo::Bar::Baz
|
43
|
+
# # ^ Going to definition here should go to Foo::Bar
|
44
|
+
# #^ Going to definition here should go to Foo
|
45
|
+
# ```
|
46
|
+
sig do
|
47
|
+
params(
|
48
|
+
target: Prism::Node,
|
49
|
+
parent: Prism::Node,
|
50
|
+
position: T::Hash[Symbol, Integer],
|
51
|
+
).returns(Prism::Node)
|
52
|
+
end
|
53
|
+
def determine_target(target, parent, position)
|
54
|
+
return target unless parent.is_a?(Prism::ConstantPathNode)
|
55
|
+
|
56
|
+
target = T.let(parent, Prism::Node)
|
57
|
+
parent = T.let(T.cast(target, Prism::ConstantPathNode).parent, T.nilable(Prism::Node))
|
58
|
+
|
59
|
+
while parent && cover?(parent.location, position)
|
60
|
+
target = parent
|
61
|
+
parent = target.is_a?(Prism::ConstantPathNode) ? target.parent : nil
|
62
|
+
end
|
63
|
+
|
64
|
+
target
|
65
|
+
end
|
15
66
|
end
|
16
67
|
end
|
17
68
|
end
|
@@ -42,13 +42,14 @@ module RubyLsp
|
|
42
42
|
sig do
|
43
43
|
params(
|
44
44
|
document: Document,
|
45
|
-
|
45
|
+
global_state: GlobalState,
|
46
46
|
position: T::Hash[Symbol, T.untyped],
|
47
47
|
context: T.nilable(T::Hash[Symbol, T.untyped]),
|
48
48
|
dispatcher: Prism::Dispatcher,
|
49
|
+
typechecker_enabled: T::Boolean,
|
49
50
|
).void
|
50
51
|
end
|
51
|
-
def initialize(document,
|
52
|
+
def initialize(document, global_state, position, context, dispatcher, typechecker_enabled) # rubocop:disable Metrics/ParameterLists
|
52
53
|
super()
|
53
54
|
target, parent, nesting = document.locate_node(
|
54
55
|
{ line: position[:line], character: position[:character] },
|
@@ -60,7 +61,7 @@ module RubyLsp
|
|
60
61
|
@target = T.let(target, T.nilable(Prism::Node))
|
61
62
|
@dispatcher = dispatcher
|
62
63
|
@response_builder = T.let(ResponseBuilders::SignatureHelp.new, ResponseBuilders::SignatureHelp)
|
63
|
-
Listeners::SignatureHelp.new(@response_builder, nesting,
|
64
|
+
Listeners::SignatureHelp.new(@response_builder, global_state, nesting, dispatcher, typechecker_enabled)
|
64
65
|
end
|
65
66
|
|
66
67
|
sig { override.returns(T.nilable(Interface::SignatureHelp)) }
|
@@ -86,13 +86,16 @@ module RubyLsp
|
|
86
86
|
params(
|
87
87
|
title: String,
|
88
88
|
entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
|
89
|
+
max_entries: T.nilable(Integer),
|
89
90
|
).returns(T::Hash[Symbol, String])
|
90
91
|
end
|
91
|
-
def categorized_markdown_from_index_entries(title, entries)
|
92
|
+
def categorized_markdown_from_index_entries(title, entries, max_entries = nil)
|
92
93
|
markdown_title = "```ruby\n#{title}\n```"
|
93
94
|
definitions = []
|
94
95
|
content = +""
|
95
|
-
Array(entries)
|
96
|
+
entries = Array(entries)
|
97
|
+
entries_to_format = max_entries ? entries.take(max_entries) : entries
|
98
|
+
entries_to_format.each do |entry|
|
96
99
|
loc = entry.location
|
97
100
|
|
98
101
|
# We always handle locations as zero based. However, for file links in Markdown we need them to be one
|
@@ -108,9 +111,16 @@ module RubyLsp
|
|
108
111
|
content << "\n\n#{entry.comments.join("\n")}" unless entry.comments.empty?
|
109
112
|
end
|
110
113
|
|
114
|
+
additional_entries_text = if max_entries && entries.length > max_entries
|
115
|
+
additional = entries.length - max_entries
|
116
|
+
" | #{additional} other#{additional > 1 ? "s" : ""}"
|
117
|
+
else
|
118
|
+
""
|
119
|
+
end
|
120
|
+
|
111
121
|
{
|
112
122
|
title: markdown_title,
|
113
|
-
links: "**Definitions**: #{definitions.join(" | ")}",
|
123
|
+
links: "**Definitions**: #{definitions.join(" | ")}#{additional_entries_text}",
|
114
124
|
documentation: content,
|
115
125
|
}
|
116
126
|
end
|
@@ -119,10 +129,11 @@ module RubyLsp
|
|
119
129
|
params(
|
120
130
|
title: String,
|
121
131
|
entries: T.any(T::Array[RubyIndexer::Entry], RubyIndexer::Entry),
|
132
|
+
max_entries: T.nilable(Integer),
|
122
133
|
).returns(String)
|
123
134
|
end
|
124
|
-
def markdown_from_index_entries(title, entries)
|
125
|
-
categorized_markdown = categorized_markdown_from_index_entries(title, entries)
|
135
|
+
def markdown_from_index_entries(title, entries, max_entries = nil)
|
136
|
+
categorized_markdown = categorized_markdown_from_index_entries(title, entries, max_entries)
|
126
137
|
|
127
138
|
<<~MARKDOWN.chomp
|
128
139
|
#{categorized_markdown[:title]}
|
@@ -147,6 +158,15 @@ module RubyLsp
|
|
147
158
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError
|
148
159
|
nil
|
149
160
|
end
|
161
|
+
|
162
|
+
sig { params(node: T.any(Prism::ModuleNode, Prism::ClassNode)).returns(T.nilable(String)) }
|
163
|
+
def namespace_constant_name(node)
|
164
|
+
path = node.constant_path
|
165
|
+
case path
|
166
|
+
when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode
|
167
|
+
constant_name(path)
|
168
|
+
end
|
169
|
+
end
|
150
170
|
end
|
151
171
|
end
|
152
172
|
end
|
@@ -92,6 +92,10 @@ module RubyLsp
|
|
92
92
|
@options[:stdin] = contents
|
93
93
|
|
94
94
|
super([path])
|
95
|
+
|
96
|
+
# RuboCop rescues interrupts and then sets the `@aborting` variable to true. We don't want them to be rescued,
|
97
|
+
# so here we re-raise in case RuboCop received an interrupt.
|
98
|
+
raise Interrupt if aborting?
|
95
99
|
rescue RuboCop::Runner::InfiniteCorrectionLoop => error
|
96
100
|
raise Formatting::Error, error.message
|
97
101
|
rescue RuboCop::ValidationError => error
|
@@ -22,11 +22,12 @@ module RubyLsp
|
|
22
22
|
extend T::Sig
|
23
23
|
include Support::Common
|
24
24
|
|
25
|
-
sig { params(query: T.nilable(String)
|
26
|
-
def initialize(
|
25
|
+
sig { params(global_state: GlobalState, query: T.nilable(String)).void }
|
26
|
+
def initialize(global_state, query)
|
27
27
|
super()
|
28
|
+
@global_state = global_state
|
28
29
|
@query = query
|
29
|
-
@index = index
|
30
|
+
@index = T.let(global_state.index, RubyIndexer::Index)
|
30
31
|
end
|
31
32
|
|
32
33
|
sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
|
@@ -35,7 +36,7 @@ module RubyLsp
|
|
35
36
|
# If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
|
36
37
|
# we still return entries defined in gems to allow developers to jump directly to the source
|
37
38
|
file_path = entry.file_path
|
38
|
-
next if
|
39
|
+
next if @global_state.typechecker && not_in_dependencies?(file_path)
|
39
40
|
|
40
41
|
# We should never show private symbols when searching the entire workspace
|
41
42
|
next if entry.visibility == :private
|
data/lib/ruby_lsp/requests.rb
CHANGED
@@ -18,6 +18,7 @@ module RubyLsp
|
|
18
18
|
# - [DocumentHighlight](rdoc-ref:RubyLsp::Requests::DocumentHighlight)
|
19
19
|
# - [InlayHint](rdoc-ref:RubyLsp::Requests::InlayHints)
|
20
20
|
# - [Completion](rdoc-ref:RubyLsp::Requests::Completion)
|
21
|
+
# - [CompletionResolve](rdoc-ref:RubyLsp::Requests::CompletionResolve)
|
21
22
|
# - [CodeLens](rdoc-ref:RubyLsp::Requests::CodeLens)
|
22
23
|
# - [Definition](rdoc-ref:RubyLsp::Requests::Definition)
|
23
24
|
# - [ShowSyntaxTree](rdoc-ref:RubyLsp::Requests::ShowSyntaxTree)
|
@@ -40,6 +41,7 @@ module RubyLsp
|
|
40
41
|
autoload :DocumentHighlight, "ruby_lsp/requests/document_highlight"
|
41
42
|
autoload :InlayHints, "ruby_lsp/requests/inlay_hints"
|
42
43
|
autoload :Completion, "ruby_lsp/requests/completion"
|
44
|
+
autoload :CompletionResolve, "ruby_lsp/requests/completion_resolve"
|
43
45
|
autoload :CodeLens, "ruby_lsp/requests/code_lens"
|
44
46
|
autoload :Definition, "ruby_lsp/requests/definition"
|
45
47
|
autoload :ShowSyntaxTree, "ruby_lsp/requests/show_syntax_tree"
|