ruby-lsp 0.4.2 → 0.5.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.
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