ruby-lsp 0.17.1 → 0.17.2
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_lsp/document.rb +24 -1
- data/lib/ruby_lsp/listeners/definition.rb +20 -18
- data/lib/ruby_lsp/listeners/hover.rb +2 -0
- data/lib/ruby_lsp/node_context.rb +15 -4
- data/lib/ruby_lsp/requests/definition.rb +5 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +23 -6
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +5 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +4 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -3
- data/lib/ruby_lsp/server.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 595a062d747f467d33b204523cfc6173b450f1b570616888b292109d0390a980
|
4
|
+
data.tar.gz: 97d1ed5a7a69521c427207a971684054eb7d35ddb6cd9e315e5fdb470c01d4d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 445852ced0242742544611c8ee39574eb09ca33f1a2b9b27ce53c8c33514256716313db4723ce7ce0a8e2ad6bd29ee47c75f68afc65150c243cbf1a0254dad2e
|
7
|
+
data.tar.gz: cb462f49c28c52ff92fa392790e030f3bdac3efeacfab865bf5892cb0b62494f7ee7c46c61a8e667867ed06f9abcd037786054e49567f982c974cd777da82551
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.17.
|
1
|
+
0.17.2
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -128,6 +128,7 @@ module RubyLsp
|
|
128
128
|
closest = node
|
129
129
|
parent = T.let(nil, T.nilable(Prism::Node))
|
130
130
|
nesting = T.let([], T::Array[T.any(Prism::ClassNode, Prism::ModuleNode)])
|
131
|
+
call_node = T.let(nil, T.nilable(Prism::CallNode))
|
131
132
|
|
132
133
|
until queue.empty?
|
133
134
|
candidate = queue.shift
|
@@ -159,6 +160,15 @@ module RubyLsp
|
|
159
160
|
nesting << candidate
|
160
161
|
end
|
161
162
|
|
163
|
+
if candidate.is_a?(Prism::CallNode)
|
164
|
+
arg_loc = candidate.arguments&.location
|
165
|
+
blk_loc = candidate.block&.location
|
166
|
+
if (arg_loc && (arg_loc.start_offset...arg_loc.end_offset).cover?(char_position)) ||
|
167
|
+
(blk_loc && (blk_loc.start_offset...blk_loc.end_offset).cover?(char_position))
|
168
|
+
call_node = candidate
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
162
172
|
# If there are node types to filter by, and the current node is not one of those types, then skip it
|
163
173
|
next if node_types.any? && node_types.none? { |type| candidate.class == type }
|
164
174
|
|
@@ -170,7 +180,20 @@ module RubyLsp
|
|
170
180
|
end
|
171
181
|
end
|
172
182
|
|
173
|
-
|
183
|
+
# When targeting the constant part of a class/module definition, we do not want the nesting to be duplicated. That
|
184
|
+
# is, when targeting Bar in the following example:
|
185
|
+
#
|
186
|
+
# ```ruby
|
187
|
+
# class Foo::Bar; end
|
188
|
+
# ```
|
189
|
+
# The correct target is `Foo::Bar` with an empty nesting. `Foo::Bar` should not appear in the nesting stack, even
|
190
|
+
# though the class/module node does indeed enclose the target, because it would lead to incorrect behavior
|
191
|
+
if closest.is_a?(Prism::ConstantReadNode) || closest.is_a?(Prism::ConstantPathNode)
|
192
|
+
last_level = nesting.last
|
193
|
+
nesting.pop if last_level && last_level.constant_path == closest
|
194
|
+
end
|
195
|
+
|
196
|
+
NodeContext.new(closest, parent, nesting.map { |n| n.constant_path.location.slice }, call_node)
|
174
197
|
end
|
175
198
|
|
176
199
|
sig { returns(T::Boolean) }
|
@@ -39,18 +39,27 @@ module RubyLsp
|
|
39
39
|
:on_instance_variable_operator_write_node_enter,
|
40
40
|
:on_instance_variable_or_write_node_enter,
|
41
41
|
:on_instance_variable_target_node_enter,
|
42
|
+
:on_string_node_enter,
|
42
43
|
)
|
43
44
|
end
|
44
45
|
|
45
46
|
sig { params(node: Prism::CallNode).void }
|
46
47
|
def on_call_node_enter(node)
|
47
|
-
message = node.
|
48
|
+
message = node.message
|
49
|
+
return unless message
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
handle_method_definition(message, self_receiver?(node))
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { params(node: Prism::StringNode).void }
|
55
|
+
def on_string_node_enter(node)
|
56
|
+
enclosing_call = @node_context.call_node
|
57
|
+
return unless enclosing_call
|
58
|
+
|
59
|
+
name = enclosing_call.name
|
60
|
+
return unless name == :require || name == :require_relative
|
61
|
+
|
62
|
+
handle_require_definition(node, name)
|
54
63
|
end
|
55
64
|
|
56
65
|
sig { params(node: Prism::BlockArgumentNode).void }
|
@@ -159,19 +168,12 @@ module RubyLsp
|
|
159
168
|
end
|
160
169
|
end
|
161
170
|
|
162
|
-
sig { params(node: Prism::
|
163
|
-
def handle_require_definition(node)
|
164
|
-
message = node.name
|
165
|
-
arguments = node.arguments
|
166
|
-
return unless arguments
|
167
|
-
|
168
|
-
argument = arguments.arguments.first
|
169
|
-
return unless argument.is_a?(Prism::StringNode)
|
170
|
-
|
171
|
+
sig { params(node: Prism::StringNode, message: Symbol).void }
|
172
|
+
def handle_require_definition(node, message)
|
171
173
|
case message
|
172
174
|
when :require
|
173
|
-
entry = @index.search_require_paths(
|
174
|
-
indexable_path.require_path ==
|
175
|
+
entry = @index.search_require_paths(node.content).find do |indexable_path|
|
176
|
+
indexable_path.require_path == node.content
|
175
177
|
end
|
176
178
|
|
177
179
|
if entry
|
@@ -186,7 +188,7 @@ module RubyLsp
|
|
186
188
|
)
|
187
189
|
end
|
188
190
|
when :require_relative
|
189
|
-
required_file = "#{
|
191
|
+
required_file = "#{node.content}.rb"
|
190
192
|
path = @uri.to_standardized_path
|
191
193
|
current_folder = path ? Pathname.new(CGI.unescape(path)).dirname : Dir.pwd
|
192
194
|
candidate = File.expand_path(File.join(current_folder, required_file))
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
module RubyLsp
|
5
|
-
# This class allows listeners to access contextual information about a node in the AST, such as its parent
|
6
|
-
#
|
5
|
+
# This class allows listeners to access contextual information about a node in the AST, such as its parent,
|
6
|
+
# its namespace nesting, and the surrounding CallNode (e.g. a method call).
|
7
7
|
class NodeContext
|
8
8
|
extend T::Sig
|
9
9
|
|
@@ -13,11 +13,22 @@ module RubyLsp
|
|
13
13
|
sig { returns(T::Array[String]) }
|
14
14
|
attr_reader :nesting
|
15
15
|
|
16
|
-
sig {
|
17
|
-
|
16
|
+
sig { returns(T.nilable(Prism::CallNode)) }
|
17
|
+
attr_reader :call_node
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
node: T.nilable(Prism::Node),
|
22
|
+
parent: T.nilable(Prism::Node),
|
23
|
+
nesting: T::Array[String],
|
24
|
+
call_node: T.nilable(Prism::CallNode),
|
25
|
+
).void
|
26
|
+
end
|
27
|
+
def initialize(node, parent, nesting, call_node)
|
18
28
|
@node = node
|
19
29
|
@parent = parent
|
20
30
|
@nesting = nesting
|
31
|
+
@call_node = call_node
|
21
32
|
end
|
22
33
|
|
23
34
|
sig { returns(String) }
|
@@ -60,6 +60,8 @@ module RubyLsp
|
|
60
60
|
Prism::InstanceVariableOrWriteNode,
|
61
61
|
Prism::InstanceVariableTargetNode,
|
62
62
|
Prism::InstanceVariableWriteNode,
|
63
|
+
Prism::SymbolNode,
|
64
|
+
Prism::StringNode,
|
63
65
|
],
|
64
66
|
)
|
65
67
|
|
@@ -79,6 +81,9 @@ module RubyLsp
|
|
79
81
|
# If the target is a method call, we need to ensure that the requested position is exactly on top of the
|
80
82
|
# method identifier. Otherwise, we risk showing definitions for unrelated things
|
81
83
|
target = nil
|
84
|
+
# For methods with block arguments using symbol-to-proc
|
85
|
+
elsif target.is_a?(Prism::SymbolNode) && parent.is_a?(Prism::BlockArgumentNode)
|
86
|
+
target = parent
|
82
87
|
end
|
83
88
|
|
84
89
|
if target
|
@@ -19,6 +19,16 @@ module RubyLsp
|
|
19
19
|
T::Hash[Symbol, Integer],
|
20
20
|
)
|
21
21
|
|
22
|
+
ENHANCED_DOC_URL = T.let(
|
23
|
+
begin
|
24
|
+
gem("rubocop", ">= 1.64.0")
|
25
|
+
true
|
26
|
+
rescue LoadError
|
27
|
+
false
|
28
|
+
end,
|
29
|
+
T::Boolean,
|
30
|
+
)
|
31
|
+
|
22
32
|
# TODO: avoid passing document once we have alternative ways to get at
|
23
33
|
# encoding and file source
|
24
34
|
sig { params(document: Document, offense: RuboCop::Cop::Offense, uri: URI::Generic).void }
|
@@ -38,8 +48,8 @@ module RubyLsp
|
|
38
48
|
code_actions
|
39
49
|
end
|
40
50
|
|
41
|
-
sig { returns(Interface::Diagnostic) }
|
42
|
-
def to_lsp_diagnostic
|
51
|
+
sig { params(config: RuboCop::Config).returns(Interface::Diagnostic) }
|
52
|
+
def to_lsp_diagnostic(config)
|
43
53
|
# highlighted_area contains the begin and end position of the first line
|
44
54
|
# This ensures that multiline offenses don't clutter the editor
|
45
55
|
highlighted = @offense.highlighted_area
|
@@ -47,7 +57,7 @@ module RubyLsp
|
|
47
57
|
message: message,
|
48
58
|
source: "RuboCop",
|
49
59
|
code: @offense.cop_name,
|
50
|
-
code_description: code_description,
|
60
|
+
code_description: code_description(config),
|
51
61
|
severity: severity,
|
52
62
|
range: Interface::Range.new(
|
53
63
|
start: Interface::Position.new(
|
@@ -80,9 +90,16 @@ module RubyLsp
|
|
80
90
|
RUBOCOP_TO_LSP_SEVERITY[@offense.severity.name]
|
81
91
|
end
|
82
92
|
|
83
|
-
sig { returns(T.nilable(Interface::CodeDescription)) }
|
84
|
-
def code_description
|
85
|
-
|
93
|
+
sig { params(config: RuboCop::Config).returns(T.nilable(Interface::CodeDescription)) }
|
94
|
+
def code_description(config)
|
95
|
+
cop = RuboCopRunner.find_cop_by_name(@offense.cop_name)
|
96
|
+
return unless cop
|
97
|
+
|
98
|
+
doc_url = if ENHANCED_DOC_URL
|
99
|
+
cop.documentation_url(config)
|
100
|
+
else
|
101
|
+
cop.documentation_url
|
102
|
+
end
|
86
103
|
Interface::CodeDescription.new(href: doc_url) if doc_url
|
87
104
|
end
|
88
105
|
|
@@ -38,7 +38,11 @@ module RubyLsp
|
|
38
38
|
@diagnostic_runner.run(filename, document.source)
|
39
39
|
|
40
40
|
@diagnostic_runner.offenses.map do |offense|
|
41
|
-
Support::RuboCopDiagnostic.new(
|
41
|
+
Support::RuboCopDiagnostic.new(
|
42
|
+
document,
|
43
|
+
offense,
|
44
|
+
uri,
|
45
|
+
).to_lsp_diagnostic(@diagnostic_runner.config_for_working_directory)
|
42
46
|
end
|
43
47
|
end
|
44
48
|
end
|
@@ -50,6 +50,9 @@ module RubyLsp
|
|
50
50
|
sig { returns(T::Array[RuboCop::Cop::Offense]) }
|
51
51
|
attr_reader :offenses
|
52
52
|
|
53
|
+
sig { returns(::RuboCop::Config) }
|
54
|
+
attr_reader :config_for_working_directory
|
55
|
+
|
53
56
|
DEFAULT_ARGS = T.let(
|
54
57
|
[
|
55
58
|
"--stderr", # Print any output to stderr so that our stdout does not get polluted
|
@@ -78,6 +81,7 @@ module RubyLsp
|
|
78
81
|
args += DEFAULT_ARGS
|
79
82
|
rubocop_options = ::RuboCop::Options.new.parse(args).first
|
80
83
|
config_store = ::RuboCop::ConfigStore.new
|
84
|
+
@config_for_working_directory = T.let(config_store.for_pwd, ::RuboCop::Config)
|
81
85
|
|
82
86
|
super(rubocop_options, config_store)
|
83
87
|
end
|
@@ -33,10 +33,11 @@ module RubyLsp
|
|
33
33
|
sig { override.returns(T::Array[Interface::WorkspaceSymbol]) }
|
34
34
|
def perform
|
35
35
|
@index.fuzzy_search(@query).filter_map do |entry|
|
36
|
-
# If the project is using Sorbet, we let Sorbet handle symbols defined inside the project itself and RBIs, but
|
37
|
-
# we still return entries defined in gems to allow developers to jump directly to the source
|
38
36
|
file_path = entry.file_path
|
39
|
-
|
37
|
+
|
38
|
+
# We only show symbols declared in the workspace
|
39
|
+
in_dependencies = !not_in_dependencies?(file_path)
|
40
|
+
next if in_dependencies
|
40
41
|
|
41
42
|
# We should never show private symbols when searching the entire workspace
|
42
43
|
next if entry.private?
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -175,7 +175,7 @@ module RubyLsp
|
|
175
175
|
completion_provider: completion_provider,
|
176
176
|
code_lens_provider: code_lens_provider,
|
177
177
|
definition_provider: enabled_features["definition"],
|
178
|
-
workspace_symbol_provider: enabled_features["workspaceSymbol"],
|
178
|
+
workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.typechecker,
|
179
179
|
signature_help_provider: signature_help_provider,
|
180
180
|
),
|
181
181
|
serverInfo: {
|
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05
|
11
|
+
date: 2024-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -180,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '0'
|
182
182
|
requirements: []
|
183
|
-
rubygems_version: 3.5.
|
183
|
+
rubygems_version: 3.5.11
|
184
184
|
signing_key:
|
185
185
|
specification_version: 4
|
186
186
|
summary: An opinionated language server for Ruby
|