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
- # ![Code action resolve demo](../../misc/code_action_resolve.gif)
6
+ # ![Code action resolve demo](../../code_action_resolve.gif)
7
7
  #
8
8
  # The [code action resolve](https://microsoft.github.io/language-server-protocol/specification#codeAction_resolve)
9
9
  # request is used to to resolve the edit field for a given code action, if it is not already provided in the
@@ -54,7 +54,8 @@ module RubyLsp
54
54
  extracted_source = T.must(@document.source[start_index...end_index])
55
55
 
56
56
  # Find the closest statements node, so that we place the refactor in a valid position
57
- closest_statements = locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements]).first
57
+ closest_statements, parent_statements = @document
58
+ .locate(T.must(@document.tree), start_index, node_types: [SyntaxTree::Statements])
58
59
  return Error::InvalidTargetRange if closest_statements.nil?
59
60
 
60
61
  # Find the node with the end line closest to the requested position, so that we can place the refactor
@@ -64,25 +65,51 @@ module RubyLsp
64
65
  distance <= 0 ? Float::INFINITY : distance
65
66
  end
66
67
 
67
- # When trying to extract the first node inside of a statements block, then we can just select one line above it
68
- target_line = if closest_node == closest_statements.child_nodes.first
69
- closest_node.location.start_line - 1
68
+ # Find the parent expression of the closest node
69
+ parent_expression = parent_statements.child_nodes.compact.find do |node|
70
+ loc = node.location
71
+ loc.start_line - 1 <= source_range.dig(:start, :line) && loc.end_line - 1 >= source_range.dig(:end, :line)
72
+ end if parent_statements
73
+
74
+ closest_node_loc = closest_node.location
75
+ # If the parent expression is a single line block, then we have to extract it inside of the oneline block
76
+ if parent_expression.is_a?(SyntaxTree::MethodAddBlock) &&
77
+ parent_expression.location.start_line == parent_expression.location.end_line
78
+
79
+ variable_source = " #{NEW_VARIABLE_NAME} = #{extracted_source};"
80
+ character = source_range.dig(:start, :character) - 1
81
+ target_range = {
82
+ start: { line: closest_node_loc.end_line - 1, character: character },
83
+ end: { line: closest_node_loc.end_line - 1, character: character },
84
+ }
70
85
  else
71
- closest_node.location.end_line
72
- end
73
-
74
- lines = @document.source.lines
75
- indentation = T.must(T.must(lines[target_line - 1])[/\A */]).size
76
-
77
- target_range = {
78
- start: { line: target_line, character: indentation },
79
- end: { line: target_line, character: indentation },
80
- }
81
-
82
- variable_source = if T.must(lines[target_line]).strip.empty?
83
- "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
84
- else
85
- "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}"
86
+ # If the closest node covers the requested location, then we're extracting a statement nested inside of it. In
87
+ # that case, we want to place the extraction at the start of the closest node (one line above). Otherwise, we
88
+ # want to place the extract right below the closest node
89
+ if closest_node_loc.start_line - 1 <= source_range.dig(
90
+ :start,
91
+ :line,
92
+ ) && closest_node_loc.end_line - 1 >= source_range.dig(:end, :line)
93
+ indentation_line = closest_node_loc.start_line - 1
94
+ target_line = indentation_line
95
+ else
96
+ target_line = closest_node_loc.end_line
97
+ indentation_line = closest_node_loc.end_line - 1
98
+ end
99
+
100
+ lines = @document.source.lines
101
+ indentation = T.must(T.must(lines[indentation_line])[/\A */]).size
102
+
103
+ target_range = {
104
+ start: { line: target_line, character: indentation },
105
+ end: { line: target_line, character: indentation },
106
+ }
107
+
108
+ variable_source = if T.must(lines[target_line]).strip.empty?
109
+ "\n#{" " * indentation}#{NEW_VARIABLE_NAME} = #{extracted_source}"
110
+ else
111
+ "#{NEW_VARIABLE_NAME} = #{extracted_source}\n#{" " * indentation}"
112
+ end
86
113
  end
87
114
 
88
115
  Interface::CodeAction.new(
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Code actions demo](../../misc/code_actions.gif)
6
+ # ![Code actions demo](../../code_actions.gif)
7
7
  #
8
8
  # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
9
9
  # request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
@@ -21,16 +21,15 @@ module RubyLsp
21
21
 
22
22
  sig do
23
23
  params(
24
- uri: String,
25
24
  document: Document,
26
25
  range: Document::RangeShape,
27
26
  context: T::Hash[Symbol, T.untyped],
28
27
  ).void
29
28
  end
30
- def initialize(uri, document, range, context)
29
+ def initialize(document, range, context)
31
30
  super(document)
32
31
 
33
- @uri = uri
32
+ @uri = T.let(document.uri, String)
34
33
  @range = range
35
34
  @context = context
36
35
  end
@@ -49,7 +48,9 @@ module RubyLsp
49
48
  code_action if diagnostic.dig(:data, :correctable) && cover?(range)
50
49
  end
51
50
 
52
- code_actions << refactor_code_action(@range, @uri)
51
+ # Only add refactor actions if there's a non empty selection in the editor
52
+ code_actions << refactor_code_action(@range, @uri) unless @range.dig(:start) == @range.dig(:end)
53
+ code_actions
53
54
  end
54
55
 
55
56
  private
@@ -0,0 +1,151 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module RubyLsp
5
+ module Requests
6
+ # ![Code lens demo](../../code_lens.gif)
7
+ #
8
+ # This feature is currently experimental. Clients will need to pass `experimentalFeaturesEnabled`
9
+ # in the initialization options to enable it.
10
+ #
11
+ # The
12
+ # [code lens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
13
+ # request informs the editor of runnable commands such as tests
14
+ #
15
+ # # Example
16
+ #
17
+ # ```ruby
18
+ # # Run
19
+ # class Test < Minitest::Test
20
+ # end
21
+ # ```
22
+ class CodeLens < Listener
23
+ extend T::Sig
24
+ extend T::Generic
25
+
26
+ ResponseType = type_member { { fixed: T::Array[Interface::CodeLens] } }
27
+
28
+ BASE_COMMAND = T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
29
+ ACCESS_MODIFIERS = T.let(["public", "private", "protected"], T::Array[String])
30
+
31
+ sig { override.returns(ResponseType) }
32
+ attr_reader :response
33
+
34
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
35
+ def initialize(uri, emitter, message_queue)
36
+ super(emitter, message_queue)
37
+
38
+ @response = T.let([], ResponseType)
39
+ @path = T.let(T.must(URI(uri).path), String)
40
+ @visibility = T.let("public", String)
41
+ @prev_visibility = T.let("public", String)
42
+
43
+ emitter.register(self, :on_class, :on_def, :on_command, :after_command, :on_call, :after_call, :on_vcall)
44
+ end
45
+
46
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
47
+ def on_class(node)
48
+ class_name = node.constant.constant.value
49
+ if class_name.end_with?("Test")
50
+ add_code_lens(node, name: class_name, command: BASE_COMMAND + @path)
51
+ end
52
+ end
53
+
54
+ sig { params(node: SyntaxTree::DefNode).void }
55
+ def on_def(node)
56
+ if @visibility == "public"
57
+ method_name = node.name.value
58
+ if method_name.start_with?("test_")
59
+ add_code_lens(
60
+ node,
61
+ name: method_name,
62
+ command: BASE_COMMAND + @path + " --name " + method_name,
63
+ )
64
+ end
65
+ end
66
+ end
67
+
68
+ sig { params(node: SyntaxTree::Command).void }
69
+ def on_command(node)
70
+ if ACCESS_MODIFIERS.include?(node.message.value) && node.arguments.parts.any?
71
+ @prev_visibility = @visibility
72
+ @visibility = node.message.value
73
+ end
74
+ end
75
+
76
+ sig { params(node: SyntaxTree::Command).void }
77
+ def after_command(node)
78
+ @visibility = @prev_visibility
79
+ end
80
+
81
+ sig { params(node: SyntaxTree::CallNode).void }
82
+ def on_call(node)
83
+ ident = node.message if node.message.is_a?(SyntaxTree::Ident)
84
+
85
+ if ident
86
+ ident_value = T.cast(ident, SyntaxTree::Ident).value
87
+ if ACCESS_MODIFIERS.include?(ident_value)
88
+ @prev_visibility = @visibility
89
+ @visibility = ident_value
90
+ end
91
+ end
92
+ end
93
+
94
+ sig { params(node: SyntaxTree::CallNode).void }
95
+ def after_call(node)
96
+ @visibility = @prev_visibility
97
+ end
98
+
99
+ sig { params(node: SyntaxTree::VCall).void }
100
+ def on_vcall(node)
101
+ vcall_value = node.value.value
102
+
103
+ if ACCESS_MODIFIERS.include?(vcall_value)
104
+ @prev_visibility = vcall_value
105
+ @visibility = vcall_value
106
+ end
107
+ end
108
+
109
+ sig { params(other: Listener[ResponseType]).returns(T.self_type) }
110
+ def merge_response!(other)
111
+ @response.concat(other.response)
112
+ self
113
+ end
114
+
115
+ private
116
+
117
+ sig { params(node: SyntaxTree::Node, name: String, command: String).void }
118
+ def add_code_lens(node, name:, command:)
119
+ @response << create_code_lens(
120
+ node,
121
+ title: "Run",
122
+ command_name: "rubyLsp.runTest",
123
+ path: @path,
124
+ name: name,
125
+ test_command: command,
126
+ type: "test",
127
+ )
128
+
129
+ @response << create_code_lens(
130
+ node,
131
+ title: "Run In Terminal",
132
+ command_name: "rubyLsp.runTestInTerminal",
133
+ path: @path,
134
+ name: name,
135
+ test_command: command,
136
+ type: "test_in_terminal",
137
+ )
138
+
139
+ @response << create_code_lens(
140
+ node,
141
+ title: "Debug",
142
+ command_name: "rubyLsp.debugTest",
143
+ path: @path,
144
+ name: name,
145
+ test_command: command,
146
+ type: "debug",
147
+ )
148
+ end
149
+ end
150
+ end
151
+ end
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/rubocop_diagnostics_runner"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Diagnostics demo](../../misc/diagnostics.gif)
8
+ # ![Diagnostics demo](../../diagnostics.gif)
9
9
  #
10
10
  # The
11
11
  # [diagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
@@ -21,11 +21,11 @@ module RubyLsp
21
21
  class Diagnostics < BaseRequest
22
22
  extend T::Sig
23
23
 
24
- sig { params(uri: String, document: Document).void }
25
- def initialize(uri, document)
24
+ sig { params(document: Document).void }
25
+ def initialize(document)
26
26
  super(document)
27
27
 
28
- @uri = uri
28
+ @uri = T.let(document.uri, String)
29
29
  end
30
30
 
31
31
  sig { override.returns(T.nilable(T.all(T::Array[Support::RuboCopDiagnostic], Object))) }
@@ -34,7 +34,7 @@ module RubyLsp
34
34
  return unless defined?(Support::RuboCopDiagnosticsRunner)
35
35
 
36
36
  # Don't try to run RuboCop diagnostics for files outside the current working directory
37
- return unless @uri.start_with?(WORKSPACE_URI)
37
+ return unless URI(@uri).path&.start_with?(T.must(WORKSPACE_URI.path))
38
38
 
39
39
  Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
40
40
  end
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Document highlight demo](../../misc/document_highlight.gif)
6
+ # ![Document highlight demo](../../document_highlight.gif)
7
7
  #
8
8
  # The [document highlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
9
9
  # informs the editor all relevant elements of the currently pointed item for highlighting. For example, when
@@ -29,14 +29,13 @@ module RubyLsp
29
29
  def initialize(document, position)
30
30
  super(document)
31
31
 
32
- @highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
32
+ @highlights = T.let([], T::Array[Interface::DocumentHighlight])
33
33
  return unless document.parsed?
34
34
 
35
- position = document.create_scanner.find_char_position(position)
36
- @target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
35
+ @target = T.let(find(position), T.nilable(Support::HighlightTarget))
37
36
  end
38
37
 
39
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentHighlight], Object)) }
38
+ sig { override.returns(T.all(T::Array[Interface::DocumentHighlight], Object)) }
40
39
  def run
41
40
  # no @target means the target is not highlightable
42
41
  visit(@document.tree) if @document.parsed? && @target
@@ -68,12 +67,11 @@ module RubyLsp
68
67
 
69
68
  sig do
70
69
  params(
71
- node: SyntaxTree::Node,
72
- position: Integer,
70
+ position: Document::PositionShape,
73
71
  ).returns(T.nilable(Support::HighlightTarget))
74
72
  end
75
- def find(node, position)
76
- matched, parent = locate(node, position)
73
+ def find(position)
74
+ matched, parent = @document.locate_node(position)
77
75
 
78
76
  return unless matched && parent
79
77
  return unless matched.is_a?(SyntaxTree::Ident) || DIRECT_HIGHLIGHTS.include?(matched.class)
@@ -90,7 +88,7 @@ module RubyLsp
90
88
  sig { params(match: Support::HighlightTarget::HighlightMatch).void }
91
89
  def add_highlight(match)
92
90
  range = range_from_syntax_tree_node(match.node)
93
- @highlights << LanguageServer::Protocol::Interface::DocumentHighlight.new(range: range, kind: match.type)
91
+ @highlights << Interface::DocumentHighlight.new(range: range, kind: match.type)
94
92
  end
95
93
  end
96
94
  end
@@ -5,7 +5,7 @@ require "ruby_lsp/requests/support/source_uri"
5
5
 
6
6
  module RubyLsp
7
7
  module Requests
8
- # ![Document link demo](../../misc/document_link.gif)
8
+ # ![Document link demo](../../document_link.gif)
9
9
  #
10
10
  # The [document link](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentLink)
11
11
  # makes `# source://PATH_TO_FILE#line` comments in a Ruby/RBI file clickable if the file exists.
@@ -18,8 +18,11 @@ module RubyLsp
18
18
  # def format(source, maxwidth = T.unsafe(nil))
19
19
  # end
20
20
  # ```
21
- class DocumentLink < BaseRequest
21
+ class DocumentLink < Listener
22
22
  extend T::Sig
23
+ extend T::Generic
24
+
25
+ ResponseType = type_member { { fixed: T::Array[Interface::DocumentLink] } }
23
26
 
24
27
  GEM_TO_VERSION_MAP = T.let(
25
28
  [*::Gem::Specification.default_stubs, *::Gem::Specification.stubs].map! do |s|
@@ -69,34 +72,33 @@ module RubyLsp
69
72
  end
70
73
  end
71
74
 
72
- sig { params(uri: String, document: Document).void }
73
- def initialize(uri, document)
74
- super(document)
75
+ sig { override.returns(ResponseType) }
76
+ attr_reader :response
77
+
78
+ sig { params(uri: String, emitter: EventEmitter, message_queue: Thread::Queue).void }
79
+ def initialize(uri, emitter, message_queue)
80
+ super(emitter, message_queue)
75
81
 
76
82
  # Match the version based on the version in the RBI file name. Notice that the `@` symbol is sanitized to `%40`
77
83
  # in the URI
78
84
  version_match = /(?<=%40)[\d.]+(?=\.rbi$)/.match(uri)
79
85
  @gem_version = T.let(version_match && version_match[0], T.nilable(String))
80
- @links = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentLink])
81
- end
86
+ @response = T.let([], T::Array[Interface::DocumentLink])
82
87
 
83
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentLink], Object)) }
84
- def run
85
- visit(@document.tree) if @document.parsed?
86
- @links
88
+ emitter.register(self, :on_comment)
87
89
  end
88
90
 
89
- sig { override.params(node: SyntaxTree::Comment).void }
90
- def visit_comment(node)
91
+ sig { params(node: SyntaxTree::Comment).void }
92
+ def on_comment(node)
91
93
  match = node.value.match(%r{source://.*#\d+$})
92
94
  return unless match
93
95
 
94
- uri = T.cast(URI(match[0]), URI::Source)
96
+ uri = T.cast(URI(T.must(match[0])), URI::Source)
95
97
  gem_version = T.must(resolve_version(uri))
96
98
  file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, uri.path)
97
99
  return if file_path.nil?
98
100
 
99
- @links << LanguageServer::Protocol::Interface::DocumentLink.new(
101
+ @response << Interface::DocumentLink.new(
100
102
  range: range_from_syntax_tree_node(node),
101
103
  target: "file://#{file_path}##{uri.line_number}",
102
104
  tooltip: "Jump to #{file_path}##{uri.line_number}",
@@ -3,7 +3,7 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Requests
6
- # ![Document symbol demo](../../misc/document_symbol.gif)
6
+ # ![Document symbol demo](../../document_symbol.gif)
7
7
  #
8
8
  # The [document
9
9
  # symbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol) request
@@ -26,8 +26,11 @@ module RubyLsp
26
26
  # end
27
27
  # end
28
28
  # ```
29
- class DocumentSymbol < BaseRequest
29
+ class DocumentSymbol < Listener
30
30
  extend T::Sig
31
+ extend T::Generic
32
+
33
+ ResponseType = type_member { { fixed: T::Array[Interface::DocumentSymbol] } }
31
34
 
32
35
  SYMBOL_KIND = T.let(
33
36
  {
@@ -66,49 +69,62 @@ module RubyLsp
66
69
  class SymbolHierarchyRoot
67
70
  extend T::Sig
68
71
 
69
- sig { returns(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol]) }
72
+ sig { returns(T::Array[Interface::DocumentSymbol]) }
70
73
  attr_reader :children
71
74
 
72
75
  sig { void }
73
76
  def initialize
74
- @children = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentSymbol])
77
+ @children = T.let([], T::Array[Interface::DocumentSymbol])
75
78
  end
76
79
  end
77
80
 
78
- sig { params(document: Document).void }
79
- def initialize(document)
81
+ sig { override.returns(T::Array[Interface::DocumentSymbol]) }
82
+ attr_reader :response
83
+
84
+ sig { params(emitter: EventEmitter, message_queue: Thread::Queue).void }
85
+ def initialize(emitter, message_queue)
80
86
  super
81
87
 
82
88
  @root = T.let(SymbolHierarchyRoot.new, SymbolHierarchyRoot)
89
+ @response = T.let(@root.children, T::Array[Interface::DocumentSymbol])
83
90
  @stack = T.let(
84
91
  [@root],
85
- T::Array[T.any(SymbolHierarchyRoot, LanguageServer::Protocol::Interface::DocumentSymbol)],
92
+ T::Array[T.any(SymbolHierarchyRoot, Interface::DocumentSymbol)],
86
93
  )
87
- end
88
94
 
89
- sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::DocumentSymbol], Object)) }
90
- def run
91
- visit(@document.tree) if @document.parsed?
92
- @root.children
95
+ emitter.register(
96
+ self,
97
+ :on_class,
98
+ :after_class,
99
+ :on_command,
100
+ :on_const_path_field,
101
+ :on_def,
102
+ :after_def,
103
+ :on_module,
104
+ :after_module,
105
+ :on_top_const_field,
106
+ :on_var_field,
107
+ )
93
108
  end
94
109
 
95
- sig { override.params(node: SyntaxTree::ClassDeclaration).void }
96
- def visit_class(node)
97
- symbol = create_document_symbol(
110
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
111
+ def on_class(node)
112
+ @stack << create_document_symbol(
98
113
  name: full_constant_name(node.constant),
99
114
  kind: :class,
100
115
  range_node: node,
101
116
  selection_range_node: node.constant,
102
117
  )
118
+ end
103
119
 
104
- @stack << symbol
105
- visit(node.bodystmt)
120
+ sig { params(node: SyntaxTree::ClassDeclaration).void }
121
+ def after_class(node)
106
122
  @stack.pop
107
123
  end
108
124
 
109
- sig { override.params(node: SyntaxTree::Command).void }
110
- def visit_command(node)
111
- return visit(node.arguments) unless ATTR_ACCESSORS.include?(node.message.value)
125
+ sig { params(node: SyntaxTree::Command).void }
126
+ def on_command(node)
127
+ return unless ATTR_ACCESSORS.include?(node.message.value)
112
128
 
113
129
  node.arguments.parts.each do |argument|
114
130
  next unless argument.is_a?(SyntaxTree::SymbolLiteral)
@@ -122,8 +138,8 @@ module RubyLsp
122
138
  end
123
139
  end
124
140
 
125
- sig { override.params(node: SyntaxTree::ConstPathField).void }
126
- def visit_const_path_field(node)
141
+ sig { params(node: SyntaxTree::ConstPathField).void }
142
+ def on_const_path_field(node)
127
143
  create_document_symbol(
128
144
  name: node.constant.value,
129
145
  kind: :constant,
@@ -132,9 +148,11 @@ module RubyLsp
132
148
  )
133
149
  end
134
150
 
135
- sig { override.params(node: SyntaxTree::DefNode).void }
136
- def visit_def(node)
137
- if node.target&.value&.value == "self"
151
+ sig { params(node: SyntaxTree::DefNode).void }
152
+ def on_def(node)
153
+ target = node.target
154
+
155
+ if target.is_a?(SyntaxTree::VarRef) && target.value.is_a?(SyntaxTree::Kw) && target.value.value == "self"
138
156
  name = "self.#{node.name.value}"
139
157
  kind = :method
140
158
  else
@@ -150,26 +168,30 @@ module RubyLsp
150
168
  )
151
169
 
152
170
  @stack << symbol
153
- visit(node.bodystmt)
171
+ end
172
+
173
+ sig { params(node: SyntaxTree::DefNode).void }
174
+ def after_def(node)
154
175
  @stack.pop
155
176
  end
156
177
 
157
- sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
158
- def visit_module(node)
159
- symbol = create_document_symbol(
178
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
179
+ def on_module(node)
180
+ @stack << create_document_symbol(
160
181
  name: full_constant_name(node.constant),
161
182
  kind: :module,
162
183
  range_node: node,
163
184
  selection_range_node: node.constant,
164
185
  )
186
+ end
165
187
 
166
- @stack << symbol
167
- visit(node.bodystmt)
188
+ sig { params(node: SyntaxTree::ModuleDeclaration).void }
189
+ def after_module(node)
168
190
  @stack.pop
169
191
  end
170
192
 
171
- sig { override.params(node: SyntaxTree::TopConstField).void }
172
- def visit_top_const_field(node)
193
+ sig { params(node: SyntaxTree::TopConstField).void }
194
+ def on_top_const_field(node)
173
195
  create_document_symbol(
174
196
  name: node.constant.value,
175
197
  kind: :constant,
@@ -178,9 +200,10 @@ module RubyLsp
178
200
  )
179
201
  end
180
202
 
181
- sig { override.params(node: SyntaxTree::VarField).void }
182
- def visit_var_field(node)
183
- kind = case node.value
203
+ sig { params(node: SyntaxTree::VarField).void }
204
+ def on_var_field(node)
205
+ value = node.value
206
+ kind = case value
184
207
  when SyntaxTree::Const
185
208
  :constant
186
209
  when SyntaxTree::CVar, SyntaxTree::IVar
@@ -190,10 +213,10 @@ module RubyLsp
190
213
  end
191
214
 
192
215
  create_document_symbol(
193
- name: node.value.value,
216
+ name: value.value,
194
217
  kind: kind,
195
218
  range_node: node,
196
- selection_range_node: node.value,
219
+ selection_range_node: value,
197
220
  )
198
221
  end
199
222
 
@@ -205,10 +228,10 @@ module RubyLsp
205
228
  kind: Symbol,
206
229
  range_node: SyntaxTree::Node,
207
230
  selection_range_node: SyntaxTree::Node,
208
- ).returns(LanguageServer::Protocol::Interface::DocumentSymbol)
231
+ ).returns(Interface::DocumentSymbol)
209
232
  end
210
233
  def create_document_symbol(name:, kind:, range_node:, selection_range_node:)
211
- symbol = LanguageServer::Protocol::Interface::DocumentSymbol.new(
234
+ symbol = Interface::DocumentSymbol.new(
212
235
  name: name,
213
236
  kind: SYMBOL_KIND[kind],
214
237
  range: range_from_syntax_tree_node(range_node),