ruby-lsp 0.17.1 → 0.17.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61194ff211ddd14d6598752e0a60409c16c2c5460c4d007ac1e1bc7e56b50116
4
- data.tar.gz: 29ed28f4392929f09a4ba4ea6a19612f9dfc3e2c0b2641c551627ac69a32b3c0
3
+ metadata.gz: 595a062d747f467d33b204523cfc6173b450f1b570616888b292109d0390a980
4
+ data.tar.gz: 97d1ed5a7a69521c427207a971684054eb7d35ddb6cd9e315e5fdb470c01d4d8
5
5
  SHA512:
6
- metadata.gz: 56420391d81fe9e9c06a7fda2a04b1f99ac6fbb10211f064bfd65f36dd9148658d47743be437eb40240d5522786ec0566d0cea763b964630fb5ea67fe552687b
7
- data.tar.gz: f760fad5d7abf0d0071badf6dd04f82e2266a64b0122d3a08cd5c3c799be15e377c94da93a4e8164e1f6ae04350e5ca05b16afbb7a8e341a8e80590053728ed7
6
+ metadata.gz: 445852ced0242742544611c8ee39574eb09ca33f1a2b9b27ce53c8c33514256716313db4723ce7ce0a8e2ad6bd29ee47c75f68afc65150c243cbf1a0254dad2e
7
+ data.tar.gz: cb462f49c28c52ff92fa392790e030f3bdac3efeacfab865bf5892cb0b62494f7ee7c46c61a8e667867ed06f9abcd037786054e49567f982c974cd777da82551
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.17.1
1
+ 0.17.2
@@ -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
- NodeContext.new(closest, parent, nesting.map { |n| n.constant_path.location.slice })
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.name
48
+ message = node.message
49
+ return unless message
48
50
 
49
- if message == :require || message == :require_relative
50
- handle_require_definition(node)
51
- else
52
- handle_method_definition(message.to_s, self_receiver?(node))
53
- end
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::CallNode).void }
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(argument.content).find do |indexable_path|
174
- indexable_path.require_path == argument.content
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 = "#{argument.content}.rb"
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))
@@ -19,6 +19,8 @@ module RubyLsp
19
19
  Prism::InstanceVariableOrWriteNode,
20
20
  Prism::InstanceVariableTargetNode,
21
21
  Prism::InstanceVariableWriteNode,
22
+ Prism::SymbolNode,
23
+ Prism::StringNode,
22
24
  ],
23
25
  T::Array[T.class_of(Prism::Node)],
24
26
  )
@@ -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
- # and its namespace nesting.
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 { params(node: T.nilable(Prism::Node), parent: T.nilable(Prism::Node), nesting: T::Array[String]).void }
17
- def initialize(node, parent, nesting)
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
- doc_url = RuboCopRunner.find_cop_by_name(@offense.cop_name)&.documentation_url
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(document, offense, uri).to_lsp_diagnostic
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
- next if @global_state.typechecker && not_in_dependencies?(file_path)
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?
@@ -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.1
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-31 00:00:00.000000000 Z
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.10
183
+ rubygems_version: 3.5.11
184
184
  signing_key:
185
185
  specification_version: 4
186
186
  summary: An opinionated language server for Ruby