ruby-lsp 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -116
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp +10 -1
  5. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +62 -0
  6. data/lib/ruby_lsp/check_docs.rb +112 -0
  7. data/lib/ruby_lsp/document.rb +87 -8
  8. data/lib/ruby_lsp/event_emitter.rb +120 -0
  9. data/lib/ruby_lsp/executor.rb +191 -44
  10. data/lib/ruby_lsp/extension.rb +104 -0
  11. data/lib/ruby_lsp/internal.rb +4 -0
  12. data/lib/ruby_lsp/listener.rb +42 -0
  13. data/lib/ruby_lsp/requests/base_request.rb +2 -90
  14. data/lib/ruby_lsp/requests/code_action_resolve.rb +47 -20
  15. data/lib/ruby_lsp/requests/code_actions.rb +6 -5
  16. data/lib/ruby_lsp/requests/code_lens.rb +151 -0
  17. data/lib/ruby_lsp/requests/diagnostics.rb +5 -5
  18. data/lib/ruby_lsp/requests/document_highlight.rb +8 -10
  19. data/lib/ruby_lsp/requests/document_link.rb +17 -15
  20. data/lib/ruby_lsp/requests/document_symbol.rb +63 -40
  21. data/lib/ruby_lsp/requests/folding_ranges.rb +14 -10
  22. data/lib/ruby_lsp/requests/formatting.rb +15 -15
  23. data/lib/ruby_lsp/requests/hover.rb +45 -34
  24. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -5
  25. data/lib/ruby_lsp/requests/on_type_formatting.rb +5 -1
  26. data/lib/ruby_lsp/requests/path_completion.rb +21 -51
  27. data/lib/ruby_lsp/requests/selection_ranges.rb +4 -4
  28. data/lib/ruby_lsp/requests/semantic_highlighting.rb +30 -16
  29. data/lib/ruby_lsp/requests/support/common.rb +91 -0
  30. data/lib/ruby_lsp/requests/support/highlight_target.rb +5 -4
  31. data/lib/ruby_lsp/requests/support/rails_document_client.rb +7 -6
  32. data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
  33. data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
  34. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -1
  35. data/lib/ruby_lsp/requests/support/semantic_token_encoder.rb +2 -2
  36. data/lib/ruby_lsp/requests/support/sorbet.rb +5 -15
  37. data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +42 -0
  38. data/lib/ruby_lsp/requests.rb +17 -14
  39. data/lib/ruby_lsp/server.rb +45 -9
  40. data/lib/ruby_lsp/store.rb +11 -11
  41. data/lib/ruby_lsp/utils.rb +9 -8
  42. metadata +13 -5
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Folding ranges demo](../../misc/folding_ranges.gif)
6
+ # ![Folding ranges demo](../../folding_ranges.gif)
7
7
  #
8
8
  # The [folding ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
9
9
  # request informs the editor of the ranges where and how code can be folded.
@@ -63,11 +63,11 @@ module RubyLsp
63
63
  def initialize(document)
64
64
  super
65
65
 
66
- @ranges = T.let([], T::Array[LanguageServer::Protocol::Interface::FoldingRange])
66
+ @ranges = T.let([], T::Array[Interface::FoldingRange])
67
67
  @partial_range = T.let(nil, T.nilable(PartialRange))
68
68
  end
69
69
 
70
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::FoldingRange], Object)) }
70
+ sig { override.returns(T.all(T::Array[Interface::FoldingRange], Object)) }
71
71
  def run
72
72
  if @document.parsed?
73
73
  visit(@document.tree)
@@ -161,9 +161,9 @@ module RubyLsp
161
161
  node.is_a?(SyntaxTree::Comment) && @end_line + 1 != node.location.start_line - 1
162
162
  end
163
163
 
164
- sig { returns(LanguageServer::Protocol::Interface::FoldingRange) }
164
+ sig { returns(Interface::FoldingRange) }
165
165
  def to_range
166
- LanguageServer::Protocol::Interface::FoldingRange.new(
166
+ Interface::FoldingRange.new(
167
167
  start_line: @start_line,
168
168
  end_line: @end_line,
169
169
  kind: @kind,
@@ -220,7 +220,8 @@ module RubyLsp
220
220
 
221
221
  sig { params(node: T.any(SyntaxTree::CallNode, SyntaxTree::CommandCall)).void }
222
222
  def add_call_range(node)
223
- receiver = T.let(node.receiver, SyntaxTree::Node)
223
+ receiver = T.let(node.receiver, T.nilable(SyntaxTree::Node))
224
+
224
225
  loop do
225
226
  case receiver
226
227
  when SyntaxTree::CallNode
@@ -255,9 +256,10 @@ module RubyLsp
255
256
  def add_def_range(node)
256
257
  # For an endless method with no arguments, `node.params` returns `nil` for Ruby 3.0, but a `Syntax::Params`
257
258
  # for Ruby 3.1
258
- return unless node.params
259
+ params = node.params
260
+ return unless params
259
261
 
260
- params_location = node.params.location
262
+ params_location = params.location
261
263
 
262
264
  if params_location.start_line < params_location.end_line
263
265
  add_lines_range(params_location.end_line, node.location.end_line - 1)
@@ -276,7 +278,9 @@ module RubyLsp
276
278
 
277
279
  sig { params(node: SyntaxTree::Node, statements: SyntaxTree::Statements).void }
278
280
  def add_statements_range(node, statements)
279
- add_lines_range(node.location.start_line, statements.body.last.location.end_line) unless statements.empty?
281
+ return if statements.empty?
282
+
283
+ add_lines_range(node.location.start_line, T.must(statements.body.last).location.end_line)
280
284
  end
281
285
 
282
286
  sig { params(node: SyntaxTree::StringConcat).void }
@@ -291,7 +295,7 @@ module RubyLsp
291
295
  def add_lines_range(start_line, end_line)
292
296
  return if start_line >= end_line
293
297
 
294
- @ranges << LanguageServer::Protocol::Interface::FoldingRange.new(
298
+ @ranges << Interface::FoldingRange.new(
295
299
  start_line: start_line - 1,
296
300
  end_line: end_line - 1,
297
301
  kind: "region",
@@ -2,16 +2,21 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "ruby_lsp/requests/support/rubocop_formatting_runner"
5
+ require "ruby_lsp/requests/support/syntax_tree_formatting_runner"
5
6
 
6
7
  module RubyLsp
7
8
  module Requests
8
- # ![Formatting symbol demo](../../misc/formatting.gif)
9
+ # ![Formatting symbol demo](../../formatting.gif)
9
10
  #
10
11
  # The [formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
11
12
  # request uses RuboCop to fix auto-correctable offenses in the document. This requires enabling format on save and
12
13
  # registering the ruby-lsp as the Ruby formatter.
13
14
  #
14
- # If RuboCop is not available, the request will fall back to SyntaxTree.
15
+ # The `rubyLsp.formatter` setting specifies which formatter to use.
16
+ # If set to `auto`` then it behaves as follows:
17
+ # * It will use RuboCop if it is part of the bundle.
18
+ # * If RuboCop is not available, and `syntax_tree` is a direct dependency, it will use that.
19
+ # * Otherwise, no formatting will be applied.
15
20
  #
16
21
  # # Example
17
22
  #
@@ -26,19 +31,12 @@ module RubyLsp
26
31
 
27
32
  extend T::Sig
28
33
 
29
- sig { params(uri: String, document: Document, formatter: String).void }
30
- def initialize(uri, document, formatter: "auto")
34
+ sig { params(document: Document, formatter: String).void }
35
+ def initialize(document, formatter: "auto")
31
36
  super(document)
32
37
 
33
- @uri = uri
34
- @formatter = T.let(
35
- if formatter == "auto"
36
- defined?(Support::RuboCopFormattingRunner) ? "rubocop" : "syntax_tree"
37
- else
38
- formatter
39
- end,
40
- String,
41
- )
38
+ @uri = T.let(document.uri, String)
39
+ @formatter = formatter
42
40
  end
43
41
 
44
42
  sig { override.returns(T.nilable(T.all(T::Array[Interface::TextEdit], Object))) }
@@ -71,9 +69,11 @@ module RubyLsp
71
69
  def formatted_file
72
70
  case @formatter
73
71
  when "rubocop"
74
- Support::RuboCopFormattingRunner.instance.run(@uri, @document)
72
+ if defined?(Support::RuboCopFormattingRunner)
73
+ Support::RuboCopFormattingRunner.instance.run(@uri, @document)
74
+ end
75
75
  when "syntax_tree"
76
- SyntaxTree.format(@document.source)
76
+ Support::SyntaxTreeFormattingRunner.instance.run(@uri, @document)
77
77
  else
78
78
  raise InvalidFormatter, "Unknown formatter: #{@formatter}"
79
79
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Hover demo](../../misc/rails_document_link_hover.gif)
6
+ # ![Hover demo](../../rails_document_link_hover.gif)
7
7
  #
8
8
  # The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
9
9
  # renders a clickable link to the code's official documentation.
@@ -17,8 +17,11 @@ module RubyLsp
17
17
  # before_save :do_something # when hovering on before_save, the link will be rendered
18
18
  # end
19
19
  # ```
20
- class Hover < BaseRequest
20
+ class Hover < Listener
21
21
  extend T::Sig
22
+ extend T::Generic
23
+
24
+ ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }
22
25
 
23
26
  ALLOWED_TARGETS = T.let(
24
27
  [
@@ -29,52 +32,60 @@ module RubyLsp
29
32
  T::Array[T.class_of(SyntaxTree::Node)],
30
33
  )
31
34
 
32
- sig { params(document: Document, position: Document::PositionShape).void }
33
- def initialize(document, position)
34
- super(document)
35
+ sig { override.returns(ResponseType) }
36
+ attr_reader :response
37
+
38
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
39
+ def initialize(emitter, message_queue)
40
+ super
35
41
 
36
- @position = T.let(document.create_scanner.find_char_position(position), Integer)
42
+ @response = T.let(nil, ResponseType)
43
+ emitter.register(self, :on_command, :on_const_path_ref, :on_call)
37
44
  end
38
45
 
39
- sig { override.returns(T.nilable(LanguageServer::Protocol::Interface::Hover)) }
40
- def run
41
- return unless @document.parsed?
46
+ # Merges responses from other hover listeners
47
+ sig { params(other: Listener[ResponseType]).returns(T.self_type) }
48
+ def merge_response!(other)
49
+ other_response = other.response
50
+ return self unless other_response
42
51
 
43
- target, parent = locate(T.must(@document.tree), @position)
44
- target = parent if !ALLOWED_TARGETS.include?(target.class) && ALLOWED_TARGETS.include?(parent.class)
52
+ if @response.nil?
53
+ @response = other.response
54
+ else
55
+ @response.contents.value << other_response.contents.value << "\n\n"
56
+ end
45
57
 
46
- case target
47
- when SyntaxTree::Command
48
- message = target.message
49
- generate_rails_document_link_hover(message.value, message)
50
- when SyntaxTree::CallNode
51
- return if target.message == :call
58
+ self
59
+ end
52
60
 
53
- generate_rails_document_link_hover(target.message.value, target.message)
54
- when SyntaxTree::ConstPathRef
55
- constant_name = full_constant_name(target)
56
- generate_rails_document_link_hover(constant_name, target)
57
- end
61
+ sig { params(node: SyntaxTree::Command).void }
62
+ def on_command(node)
63
+ message = node.message
64
+ @response = generate_rails_document_link_hover(message.value, message)
58
65
  end
59
66
 
60
- private
67
+ sig { params(node: SyntaxTree::ConstPathRef).void }
68
+ def on_const_path_ref(node)
69
+ @response = generate_rails_document_link_hover(full_constant_name(node), node)
70
+ end
71
+
72
+ sig { params(node: SyntaxTree::CallNode).void }
73
+ def on_call(node)
74
+ message = node.message
75
+ return if message.is_a?(Symbol)
61
76
 
62
- sig do
63
- params(name: String, node: SyntaxTree::Node).returns(T.nilable(LanguageServer::Protocol::Interface::Hover))
77
+ @response = generate_rails_document_link_hover(message.value, message)
64
78
  end
79
+
80
+ private
81
+
82
+ sig { params(name: String, node: SyntaxTree::Node).returns(T.nilable(Interface::Hover)) }
65
83
  def generate_rails_document_link_hover(name, node)
66
84
  urls = Support::RailsDocumentClient.generate_rails_document_urls(name)
67
-
68
85
  return if urls.empty?
69
86
 
70
- contents = LanguageServer::Protocol::Interface::MarkupContent.new(
71
- kind: "markdown",
72
- value: urls.join("\n\n"),
73
- )
74
- LanguageServer::Protocol::Interface::Hover.new(
75
- range: range_from_syntax_tree_node(node),
76
- contents: contents,
77
- )
87
+ contents = Interface::MarkupContent.new(kind: "markdown", value: urls.join("\n\n"))
88
+ Interface::Hover.new(range: range_from_syntax_tree_node(node), contents: contents)
78
89
  end
79
90
  end
80
91
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Inlay hint demo](../../misc/inlay_hint.gif)
6
+ # ![Inlay hint demo](../../inlay_hint.gif)
7
7
  #
8
8
  # [Inlay hints](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
9
9
  # are labels added directly in the code that explicitly show the user something that might
@@ -25,11 +25,11 @@ module RubyLsp
25
25
  def initialize(document, range)
26
26
  super(document)
27
27
 
28
- @hints = T.let([], T::Array[LanguageServer::Protocol::Interface::InlayHint])
28
+ @hints = T.let([], T::Array[Interface::InlayHint])
29
29
  @range = range
30
30
  end
31
31
 
32
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::InlayHint], Object)) }
32
+ sig { override.returns(T.all(T::Array[Interface::InlayHint], Object)) }
33
33
  def run
34
34
  visit(@document.tree) if @document.parsed?
35
35
  @hints
@@ -37,12 +37,13 @@ module RubyLsp
37
37
 
38
38
  sig { override.params(node: SyntaxTree::Rescue).void }
39
39
  def visit_rescue(node)
40
- return unless node.exception.nil?
40
+ exception = node.exception
41
+ return unless exception.nil? || exception.exceptions.nil?
41
42
 
42
43
  loc = node.location
43
44
  return unless visible?(node, @range)
44
45
 
45
- @hints << LanguageServer::Protocol::Interface::InlayHint.new(
46
+ @hints << Interface::InlayHint.new(
46
47
  position: { line: loc.start_line - 1, character: loc.start_column + RESCUE_STRING_LENGTH },
47
48
  label: "StandardError",
48
49
  padding_left: true,
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![On type formatting demo](../../misc/on_type_formatting.gif)
6
+ # ![On type formatting demo](../../on_type_formatting.gif)
7
7
  #
8
8
  # The [on type formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting)
9
9
  # request formats code as the user is typing. For example, automatically adding `end` to class definitions.
@@ -80,6 +80,10 @@ module RubyLsp
80
80
 
81
81
  sig { void }
82
82
  def handle_statement_end
83
+ # If a keyword occurs in a line which appears be a comment or a string, we will not try to format it, since
84
+ # it could be a coincidental match. This approach is not perfect, but it should cover most cases.
85
+ return if @previous_line.start_with?(/["'#]/)
86
+
83
87
  return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
84
88
 
85
89
  indents = " " * @indentation
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Path completion demo](../../misc/path_completion.gif)
6
+ # ![Path completion demo](../../path_completion.gif)
7
7
  #
8
8
  # The [completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
9
9
  # request looks up Ruby files in the $LOAD_PATH to suggest path completion inside `require` statements.
@@ -13,30 +13,28 @@ module RubyLsp
13
13
  # ```ruby
14
14
  # require "ruby_lsp/requests" # --> completion: suggests `base_request`, `code_actions`, ...
15
15
  # ```
16
- class PathCompletion < BaseRequest
16
+ class PathCompletion < Listener
17
17
  extend T::Sig
18
+ extend T::Generic
18
19
 
19
- sig { params(document: Document, position: Document::PositionShape).void }
20
- def initialize(document, position)
21
- super(document)
20
+ ResponseType = type_member { { fixed: T::Array[Interface::CompletionItem] } }
22
21
 
23
- @tree = T.let(Support::PrefixTree.new(collect_load_path_files), Support::PrefixTree)
24
- @position = position
25
- end
22
+ sig { override.returns(ResponseType) }
23
+ attr_reader :response
26
24
 
27
- sig { override.returns(T.all(T::Array[Interface::CompletionItem], Object)) }
28
- def run
29
- # We can't verify if we're inside a require when there are syntax errors
30
- return [] if @document.syntax_error?
25
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
26
+ def initialize(emitter, message_queue)
27
+ super
28
+ @response = T.let([], ResponseType)
29
+ @tree = T.let(Support::PrefixTree.new(collect_load_path_files), Support::PrefixTree)
31
30
 
32
- char_position = @document.create_scanner.find_char_position(@position)
33
- target = T.let(find(char_position), T.nilable(SyntaxTree::TStringContent))
34
- # no target means the we are not inside a `require` call
35
- return [] unless target
31
+ emitter.register(self, :on_tstring_content)
32
+ end
36
33
 
37
- text = target.value
38
- @tree.search(text).sort.map! do |path|
39
- build_completion(path, path.delete_prefix(text))
34
+ sig { params(node: SyntaxTree::TStringContent).void }
35
+ def on_tstring_content(node)
36
+ @tree.search(node.value).sort.each do |path|
37
+ @response << build_completion(path, node)
40
38
  end
41
39
  end
42
40
 
@@ -51,41 +49,13 @@ module RubyLsp
51
49
  end
52
50
  end
53
51
 
54
- sig { params(position: Integer).returns(T.nilable(SyntaxTree::TStringContent)) }
55
- def find(position)
56
- matched, parent = locate(
57
- T.must(@document.tree),
58
- position,
59
- node_types: [SyntaxTree::Command, SyntaxTree::CommandCall, SyntaxTree::CallNode],
60
- )
61
-
62
- return unless matched && parent
63
-
64
- case matched
65
- when SyntaxTree::Command, SyntaxTree::CallNode, SyntaxTree::CommandCall
66
- return unless matched.message.value == "require"
67
-
68
- args = matched.arguments
69
- args = args.arguments if args.is_a?(SyntaxTree::ArgParen)
70
-
71
- path_node = args.parts.first.parts.first
72
- return unless path_node
73
- return unless (path_node.location.start_char..path_node.location.end_char).cover?(position)
74
-
75
- path_node
76
- end
77
- end
78
-
79
- sig { params(label: String, insert_text: String).returns(Interface::CompletionItem) }
80
- def build_completion(label, insert_text)
52
+ sig { params(label: String, node: SyntaxTree::TStringContent).returns(Interface::CompletionItem) }
53
+ def build_completion(label, node)
81
54
  Interface::CompletionItem.new(
82
55
  label: label,
83
56
  text_edit: Interface::TextEdit.new(
84
- range: Interface::Range.new(
85
- start: @position,
86
- end: @position,
87
- ),
88
- new_text: insert_text,
57
+ range: range_from_syntax_tree_node(node),
58
+ new_text: label,
89
59
  ),
90
60
  kind: Constant::CompletionItemKind::REFERENCE,
91
61
  )
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Selection ranges demo](../../misc/selection_ranges.gif)
6
+ # ![Selection ranges demo](../../selection_ranges.gif)
7
7
  #
8
8
  # The [selection ranges](https://microsoft.github.io/language-server-protocol/specification#textDocument_selectionRange)
9
9
  # request informs the editor of ranges that the user may want to select based on the location(s)
@@ -100,12 +100,12 @@ module RubyLsp
100
100
  end
101
101
  def create_selection_range(location, parent = nil)
102
102
  RubyLsp::Requests::Support::SelectionRange.new(
103
- range: LanguageServer::Protocol::Interface::Range.new(
104
- start: LanguageServer::Protocol::Interface::Position.new(
103
+ range: Interface::Range.new(
104
+ start: Interface::Position.new(
105
105
  line: location.start_line - 1,
106
106
  character: location.start_column,
107
107
  ),
108
- end: LanguageServer::Protocol::Interface::Position.new(
108
+ end: Interface::Position.new(
109
109
  line: location.end_line - 1,
110
110
  character: location.end_column,
111
111
  ),
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Semantic highlighting demo](../../misc/semantic_highlighting.gif)
6
+ # ![Semantic highlighting demo](../../semantic_highlighting.gif)
7
7
  #
8
8
  # The [semantic
9
9
  # highlighting](https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens)
@@ -122,7 +122,7 @@ module RubyLsp
122
122
  sig do
123
123
  override.returns(
124
124
  T.any(
125
- LanguageServer::Protocol::Interface::SemanticTokens,
125
+ Interface::SemanticTokens,
126
126
  T.all(T::Array[SemanticToken], Object),
127
127
  ),
128
128
  )
@@ -143,7 +143,7 @@ module RubyLsp
143
143
  visit(node.receiver)
144
144
 
145
145
  message = node.message
146
- if message != :call && !special_method?(message.value)
146
+ if !message.is_a?(Symbol) && !special_method?(message.value)
147
147
  type = Support::Sorbet.annotation?(node) ? :type : :method
148
148
 
149
149
  add_token(message.location, type)
@@ -168,7 +168,8 @@ module RubyLsp
168
168
  return super unless visible?(node, @range)
169
169
 
170
170
  visit(node.receiver)
171
- add_token(node.message.location, :method)
171
+ message = node.message
172
+ add_token(message.location, :method) unless message.is_a?(Symbol)
172
173
  visit(node.arguments)
173
174
  visit(node.block)
174
175
  end
@@ -205,7 +206,7 @@ module RubyLsp
205
206
  def visit_params(node)
206
207
  return super unless visible?(node, @range)
207
208
 
208
- node.keywords.each do |keyword,|
209
+ node.keywords.each do |keyword, *|
209
210
  location = keyword.location
210
211
  add_token(location_without_colon(location), :parameter)
211
212
  end
@@ -215,7 +216,7 @@ module RubyLsp
215
216
  end
216
217
 
217
218
  rest = node.keyword_rest
218
- if rest && !rest.is_a?(SyntaxTree::ArgsForward)
219
+ if rest && !rest.is_a?(SyntaxTree::ArgsForward) && !rest.is_a?(Symbol)
219
220
  name = rest.name
220
221
  add_token(name.location, :parameter) if name
221
222
  end
@@ -238,9 +239,14 @@ module RubyLsp
238
239
 
239
240
  value = node.value
240
241
 
241
- if value.is_a?(SyntaxTree::Ident)
242
+ case value
243
+ when SyntaxTree::Ident
242
244
  type = type_for_local(value)
243
245
  add_token(value.location, type)
246
+ when Symbol
247
+ # do nothing
248
+ else
249
+ visit(value)
244
250
  end
245
251
 
246
252
  super
@@ -256,6 +262,8 @@ module RubyLsp
256
262
  when SyntaxTree::Ident
257
263
  type = type_for_local(value)
258
264
  add_token(value.location, type)
265
+ when Symbol
266
+ # do nothing
259
267
  else
260
268
  visit(value)
261
269
  end
@@ -305,18 +313,21 @@ module RubyLsp
305
313
  return unless node.operator == :=~
306
314
 
307
315
  left = node.left
316
+ # The regexp needs to be on the left hand side of the =~ for local variable capture
317
+ return unless left.is_a?(SyntaxTree::RegexpLiteral)
318
+
308
319
  parts = left.parts
320
+ return unless parts.one?
309
321
 
310
- if left.is_a?(SyntaxTree::RegexpLiteral) && parts.one? && parts.first.is_a?(SyntaxTree::TStringContent)
311
- content = parts.first
322
+ content = parts.first
323
+ return unless content.is_a?(SyntaxTree::TStringContent)
312
324
 
313
- # For each capture name we find in the regexp, look for a local in the current_scope
314
- Regexp.new(content.value, Regexp::FIXEDENCODING).names.each do |name|
315
- local = current_scope.find_local(name)
316
- next unless local
325
+ # For each capture name we find in the regexp, look for a local in the current_scope
326
+ Regexp.new(content.value, Regexp::FIXEDENCODING).names.each do |name|
327
+ local = current_scope.find_local(name)
328
+ next unless local
317
329
 
318
- local.definitions.each { |definition| add_token(definition, :variable) }
319
- end
330
+ local.definitions.each { |definition| add_token(definition, :variable) }
320
331
  end
321
332
  end
322
333
 
@@ -325,7 +336,10 @@ module RubyLsp
325
336
  return super unless visible?(node, @range)
326
337
 
327
338
  add_token(node.constant.location, :class, [:declaration])
328
- add_token(node.superclass.location, :class) if node.superclass
339
+
340
+ superclass = node.superclass
341
+ add_token(superclass.location, :class) if superclass
342
+
329
343
  visit(node.bodystmt)
330
344
  end
331
345
 
@@ -0,0 +1,91 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ module Support
7
+ module Common
8
+ extend T::Sig
9
+
10
+ sig { params(node: SyntaxTree::Node).returns(Interface::Range) }
11
+ def range_from_syntax_tree_node(node)
12
+ loc = node.location
13
+
14
+ Interface::Range.new(
15
+ start: Interface::Position.new(
16
+ line: loc.start_line - 1,
17
+ character: loc.start_column,
18
+ ),
19
+ end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
20
+ )
21
+ end
22
+
23
+ sig do
24
+ params(node: T.any(SyntaxTree::ConstPathRef, SyntaxTree::ConstRef, SyntaxTree::TopConstRef)).returns(String)
25
+ end
26
+ def full_constant_name(node)
27
+ name = +node.constant.value
28
+ constant = T.let(node, SyntaxTree::Node)
29
+
30
+ while constant.is_a?(SyntaxTree::ConstPathRef)
31
+ constant = constant.parent
32
+
33
+ case constant
34
+ when SyntaxTree::ConstPathRef
35
+ name.prepend("#{constant.constant.value}::")
36
+ when SyntaxTree::VarRef
37
+ name.prepend("#{constant.value.value}::")
38
+ end
39
+ end
40
+
41
+ name
42
+ end
43
+
44
+ sig { params(node: T.nilable(SyntaxTree::Node), range: T.nilable(T::Range[Integer])).returns(T::Boolean) }
45
+ def visible?(node, range)
46
+ return true if range.nil?
47
+ return false if node.nil?
48
+
49
+ loc = node.location
50
+ range.cover?(loc.start_line - 1) && range.cover?(loc.end_line - 1)
51
+ end
52
+
53
+ sig do
54
+ params(
55
+ node: SyntaxTree::Node,
56
+ title: String,
57
+ command_name: String,
58
+ path: String,
59
+ name: String,
60
+ test_command: String,
61
+ type: String,
62
+ ).returns(Interface::CodeLens)
63
+ end
64
+ def create_code_lens(node, title:, command_name:, path:, name:, test_command:, type:)
65
+ range = range_from_syntax_tree_node(node)
66
+ arguments = [
67
+ path,
68
+ name,
69
+ test_command,
70
+ {
71
+ start_line: node.location.start_line - 1,
72
+ start_column: node.location.start_column,
73
+ end_line: node.location.end_line - 1,
74
+ end_column: node.location.end_column,
75
+ },
76
+ ]
77
+
78
+ Interface::CodeLens.new(
79
+ range: range,
80
+ command: Interface::Command.new(
81
+ title: title,
82
+ command: command_name,
83
+ arguments: arguments,
84
+ ),
85
+ data: { type: type },
86
+ )
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -7,8 +7,8 @@ module RubyLsp
7
7
  class HighlightTarget
8
8
  extend T::Sig
9
9
 
10
- READ = LanguageServer::Protocol::Constant::DocumentHighlightKind::READ
11
- WRITE = LanguageServer::Protocol::Constant::DocumentHighlightKind::WRITE
10
+ READ = Constant::DocumentHighlightKind::READ
11
+ WRITE = Constant::DocumentHighlightKind::WRITE
12
12
 
13
13
  class HighlightMatch
14
14
  extend T::Sig
@@ -84,10 +84,11 @@ module RubyLsp
84
84
  SyntaxTree::KwRestParam, SyntaxTree::BlockArg
85
85
  node.name&.value
86
86
  when SyntaxTree::VarField, SyntaxTree::VarRef, SyntaxTree::VCall
87
- node.value&.value
87
+ value = node.value
88
+ value.value unless value.nil? || value.is_a?(Symbol)
88
89
  when SyntaxTree::CallNode, SyntaxTree::Command, SyntaxTree::CommandCall
89
90
  message = node.message
90
- message != :call ? message.value : nil
91
+ message.value unless message.is_a?(Symbol)
91
92
  when SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration
92
93
  node.constant.constant.value
93
94
  end