ruby-lsp 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59e3f670a003f565c001d00333a727d8cf4080eb274d4ebdf248bc0ad76b9c16
4
- data.tar.gz: 5010473614d96f47ba10a267844448d6b5985b469cdfec752489be268ba95510
3
+ metadata.gz: 5f1ed447e51909ada66f6bebceca82d807686a4889468838a797e2ed8afff50e
4
+ data.tar.gz: 7b429773bbdebc171debbe446adb868136bd15b5f493542898df0a9c82f3514e
5
5
  SHA512:
6
- metadata.gz: '0853a29d497f7befc9bcbbd0387c3e456293728788b0067b91026f4c61c5013240284adc913471b5c015184b19752ecac5e958ab021f301e41c24ce55ed876e2'
7
- data.tar.gz: c2ee016b997013590cad9c575b1461d50f833d3fc06a7b22aa953aa73d6163feb5d821fe443c4ba20b0ac25cf686f32ed26c34348016408f6118f45261cce8a9
6
+ metadata.gz: 62e8a6ee36c43dec6ec0af16fd19830b6369446455a5fc16871c368c8bb4ccdb47c0d9c5678990a43604bf03c622c2ef13bb82c6a6e9f26fdcde72b3b18ec99e
7
+ data.tar.gz: e441a670c22bcdbfdc3afe99743fe6e1c4aa5f28edaedcf09f47255372322c52ead68988bc6d7ce09170e368e4059c94dac73b395a640c86f20b2969ae8e8095
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Ruby LSP
4
4
 
5
- This gem is an implementation of the language server protocol specification for Ruby, used to improve editor features.
5
+ This gem is an implementation of the [language server protocol specification](https://microsoft.github.io/language-server-protocol/) for Ruby, used to improve editor features.
6
6
 
7
7
  ## Usage
8
8
 
@@ -14,7 +14,7 @@ group :development do
14
14
  end
15
15
  ```
16
16
 
17
- If using VS Code, install the [Ruby LSP plugin](https://github.com/Shopify/vscode-ruby-lsp) to get the extra features in
17
+ If using VS Code, install the [Ruby LSP extension](https://github.com/Shopify/vscode-ruby-lsp) to get the extra features in
18
18
  the editor.
19
19
 
20
20
  See the [documentation](https://shopify.github.io/ruby-lsp) for
@@ -28,10 +28,25 @@ are expected to adhere to the
28
28
  [Contributor Covenant](https://github.com/Shopify/ruby-lsp/blob/main/CODE_OF_CONDUCT.md)
29
29
  code of conduct.
30
30
 
31
+ ### Running the test suite
32
+
33
+ Run the test suite with `bin/test`.
34
+
35
+ For more visibility into which tests are running, use the `SpecReporter`:
36
+
37
+ `SPEC_REPORTER=1 bin/test`
38
+
39
+ By default the tests run with warnings disabled to reduce noise. To enable warnings, pass `VERBOSE=1`.
40
+ Warnings are always shown when running in CI.
41
+
31
42
  ### Expectation testing
32
43
 
33
44
  To simplify the way we run tests over different pieces of Ruby code, we use a custom expectations test framework against a set of Ruby fixtures.
34
45
 
46
+ We define expectations as `.exp` files, of which there are two variants:
47
+ * `.exp.rb`, to indicate the resulting code after an operation.
48
+ * `.exp.json`, consisting of a `result`, and an optional set of input `params`.
49
+
35
50
  To add a new fixture to the expectations test suite:
36
51
 
37
52
  1. Add a new fixture `my_fixture.rb` file under `test/fixtures`
@@ -78,6 +93,18 @@ Possible values are:
78
93
  * `messages`: display requests and responses notifications
79
94
  * `verbose`: display each request and response as JSON
80
95
 
96
+ ### Debugging using VS Code
97
+
98
+ The `launch.json` contains a 'Minitest - current file' configuration for the debugger.
99
+
100
+ 1. Add a breakpoint using the VS Code UI.
101
+ 1. Open the relevant test file.
102
+ 1. Open the **Run and Debug** panel on the sidebar.
103
+ 1. Ensure `Minitest - current file` is selected in the top dropdown.
104
+ 1. Press `F5` OR click the green triangle next to the top dropdown. VS Code will then run the test file with debugger activated.
105
+ 1. When the breakpoint is triggered, the process will pause and VS Code will connect to the debugger and activate the debugger UI.
106
+ 1. Open the Debug Console view to use the debugger's REPL.
107
+
81
108
  ## License
82
109
 
83
110
  The gem is available as open source under the terms of the
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.6
1
+ 0.3.8
@@ -15,15 +15,11 @@ module RubyLsp
15
15
  sig { returns(String) }
16
16
  attr_reader :source
17
17
 
18
- sig { returns(T::Array[EditShape]) }
19
- attr_reader :syntax_error_edits
20
-
21
- sig { params(source: String).void }
22
- def initialize(source)
18
+ sig { params(source: String, encoding: String).void }
19
+ def initialize(source, encoding = "utf-8")
23
20
  @cache = T.let({}, T::Hash[Symbol, T.untyped])
24
- @syntax_error_edits = T.let([], T::Array[EditShape])
21
+ @encoding = T.let(encoding, String)
25
22
  @source = T.let(source, String)
26
- @parsable_source = T.let(source.dup, String)
27
23
  @unparsed_edits = T.let([], T::Array[EditShape])
28
24
  @tree = T.let(SyntaxTree.parse(@source), T.nilable(SyntaxTree::Node))
29
25
  rescue SyntaxTree::Parser::ParseError
@@ -53,8 +49,15 @@ module RubyLsp
53
49
 
54
50
  sig { params(edits: T::Array[EditShape]).void }
55
51
  def push_edits(edits)
56
- # Apply the edits on the real source
57
- edits.each { |edit| apply_edit(@source, edit[:range], edit[:text]) }
52
+ edits.each do |edit|
53
+ range = edit[:range]
54
+ scanner = create_scanner
55
+
56
+ start_position = scanner.find_char_position(range[:start])
57
+ end_position = scanner.find_char_position(range[:end])
58
+
59
+ @source[start_position...end_position] = edit[:text]
60
+ end
58
61
 
59
62
  @unparsed_edits.concat(edits)
60
63
  @cache.clear
@@ -65,17 +68,14 @@ module RubyLsp
65
68
  return if @unparsed_edits.empty?
66
69
 
67
70
  @tree = SyntaxTree.parse(@source)
68
- @syntax_error_edits.clear
69
71
  @unparsed_edits.clear
70
- @parsable_source = @source.dup
71
72
  rescue SyntaxTree::Parser::ParseError
72
- @syntax_error_edits = @unparsed_edits
73
- update_parsable_source(@unparsed_edits)
73
+ # Do nothing if we fail parse
74
74
  end
75
75
 
76
76
  sig { returns(T::Boolean) }
77
- def syntax_errors?
78
- @syntax_error_edits.any?
77
+ def syntax_error?
78
+ @unparsed_edits.any?
79
79
  end
80
80
 
81
81
  sig { returns(T::Boolean) }
@@ -83,52 +83,57 @@ module RubyLsp
83
83
  !@tree.nil?
84
84
  end
85
85
 
86
- private
87
-
88
- sig { params(edits: T::Array[EditShape]).void }
89
- def update_parsable_source(edits)
90
- # If the new edits caused a syntax error, make all edits blank spaces and line breaks to adjust the line and
91
- # column numbers. This is attempt to make the document parsable while partial edits are being applied
92
- edits.each do |edit|
93
- next if edit[:text].empty? # skip deletions, since they may have caused the syntax error
94
-
95
- apply_edit(@parsable_source, edit[:range], edit[:text].gsub(/[^\r\n]/, " "))
96
- end
97
-
98
- @tree = SyntaxTree.parse(@parsable_source)
99
- rescue StandardError
100
- # Trying to maintain a parsable source when there are syntax errors is a best effort. If we fail to apply edits or
101
- # parse, just ignore it
102
- end
103
-
104
- sig { params(source: String, range: RangeShape, text: String).void }
105
- def apply_edit(source, range, text)
106
- scanner = Scanner.new(source)
107
- start_position = scanner.find_position(range[:start])
108
- end_position = scanner.find_position(range[:end])
109
-
110
- source[start_position...end_position] = text
86
+ sig { returns(Scanner) }
87
+ def create_scanner
88
+ Scanner.new(@source, @encoding)
111
89
  end
112
90
 
113
91
  class Scanner
114
92
  extend T::Sig
115
93
 
116
- sig { params(source: String).void }
117
- def initialize(source)
94
+ LINE_BREAK = T.let(0x0A, Integer)
95
+ # After character 0xFFFF, UTF-16 considers characters to have length 2 and we have to account for that
96
+ SURROGATE_PAIR_START = T.let(0xFFFF, Integer)
97
+
98
+ sig { params(source: String, encoding: String).void }
99
+ def initialize(source, encoding)
118
100
  @current_line = T.let(0, Integer)
119
101
  @pos = T.let(0, Integer)
120
- @source = source
102
+ @source = T.let(source.codepoints, T::Array[Integer])
103
+ @encoding = encoding
121
104
  end
122
105
 
106
+ # Finds the character index inside the source string for a given line and column
123
107
  sig { params(position: PositionShape).returns(Integer) }
124
- def find_position(position)
108
+ def find_char_position(position)
109
+ # Find the character index for the beginning of the requested line
125
110
  until @current_line == position[:line]
126
- @pos += 1 until /\R/.match?(@source[@pos])
111
+ @pos += 1 until LINE_BREAK == @source[@pos]
127
112
  @pos += 1
128
113
  @current_line += 1
129
114
  end
130
115
 
131
- @pos + position[:character]
116
+ # The final position is the beginning of the line plus the requested column. If the encoding is UTF-16, we also
117
+ # need to adjust for surrogate pairs
118
+ requested_position = @pos + position[:character]
119
+ requested_position -= utf_16_character_position_correction(@pos, requested_position) if @encoding == "utf-16"
120
+ requested_position
121
+ end
122
+
123
+ # Subtract 1 for each character after 0xFFFF in the current line from the column position, so that we hit the
124
+ # right character in the UTF-8 representation
125
+ sig { params(current_position: Integer, requested_position: Integer).returns(Integer) }
126
+ def utf_16_character_position_correction(current_position, requested_position)
127
+ utf16_unicode_correction = 0
128
+
129
+ until current_position == requested_position
130
+ codepoint = @source[current_position]
131
+ utf16_unicode_correction += 1 if codepoint && codepoint > SURROGATE_PAIR_START
132
+
133
+ current_position += 1
134
+ end
135
+
136
+ utf16_unicode_correction
132
137
  end
133
138
  end
134
139
  end
@@ -59,7 +59,7 @@ module RubyLsp
59
59
 
60
60
  sig { void }
61
61
  def start
62
- $stderr.puts "Starting Ruby LSP..."
62
+ warn("Starting Ruby LSP...")
63
63
 
64
64
  @reader.read do |request|
65
65
  handler = @handlers[request[:method]]
@@ -94,7 +94,7 @@ module RubyLsp
94
94
 
95
95
  sig { void }
96
96
  def shutdown
97
- $stderr.puts "Shutting down Ruby LSP..."
97
+ warn("Shutting down Ruby LSP...")
98
98
  @queue.shutdown
99
99
  store.clear
100
100
  end
@@ -29,13 +29,17 @@ module RubyLsp
29
29
  loc = node.location
30
30
 
31
31
  LanguageServer::Protocol::Interface::Range.new(
32
- start: LanguageServer::Protocol::Interface::Position.new(line: loc.start_line - 1,
33
- character: loc.start_column),
32
+ start: LanguageServer::Protocol::Interface::Position.new(
33
+ line: loc.start_line - 1,
34
+ character: loc.start_column,
35
+ ),
34
36
  end: LanguageServer::Protocol::Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
35
37
  )
36
38
  end
37
39
 
38
- sig { params(node: SyntaxTree::ConstPathRef).returns(String) }
40
+ sig do
41
+ params(node: T.any(SyntaxTree::ConstPathRef, SyntaxTree::ConstRef, SyntaxTree::TopConstRef)).returns(String)
42
+ end
39
43
  def full_constant_name(node)
40
44
  name = +node.constant.value
41
45
  constant = T.let(node, SyntaxTree::Node)
@@ -56,28 +60,48 @@ module RubyLsp
56
60
 
57
61
  sig do
58
62
  params(
59
- parent: SyntaxTree::Node,
60
- target_nodes: T::Array[T.class_of(SyntaxTree::Node)],
63
+ node: SyntaxTree::Node,
61
64
  position: Integer,
62
- ).returns(T::Array[SyntaxTree::Node])
65
+ ).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
63
66
  end
64
- def locate_node_and_parent(parent, target_nodes, position)
65
- matched = parent.child_nodes.compact.bsearch do |child|
66
- if (child.location.start_char...child.location.end_char).cover?(position)
67
- 0
68
- else
69
- position <=> child.location.start_char
67
+ def locate(node, position)
68
+ queue = T.let(node.child_nodes.compact, T::Array[T.nilable(SyntaxTree::Node)])
69
+ closest = node
70
+
71
+ until queue.empty?
72
+ candidate = queue.shift
73
+
74
+ # Skip nil child nodes
75
+ next if candidate.nil?
76
+
77
+ # Add the next child_nodes to the queue to be processed
78
+ queue.concat(candidate.child_nodes)
79
+
80
+ # Skip if the current node doesn't cover the desired position
81
+ loc = candidate.location
82
+ next unless (loc.start_char...loc.end_char).cover?(position)
83
+
84
+ # If the node's start character is already past the position, then we should've found the closest node already
85
+ break if position < loc.start_char
86
+
87
+ # If the current node is narrower than or equal to the previous closest node, then it is more precise
88
+ closest_loc = closest.location
89
+ if loc.end_char - loc.start_char <= closest_loc.end_char - closest_loc.start_char
90
+ parent = T.let(closest, SyntaxTree::Node)
91
+ closest = candidate
70
92
  end
71
93
  end
72
94
 
73
- case matched
74
- when *target_nodes
75
- [matched, parent]
76
- when SyntaxTree::Node
77
- locate_node_and_parent(matched, target_nodes, position)
78
- else
79
- []
80
- end
95
+ [closest, parent]
96
+ end
97
+
98
+ sig { params(node: T.nilable(SyntaxTree::Node), range: T.nilable(T::Range[Integer])).returns(T::Boolean) }
99
+ def visible?(node, range)
100
+ return true if range.nil?
101
+ return false if node.nil?
102
+
103
+ loc = node.location
104
+ range.cover?(loc.start_line - 1) && range.cover?(loc.end_line - 1)
81
105
  end
82
106
  end
83
107
  end
@@ -6,7 +6,7 @@ module RubyLsp
6
6
  # ![Code actions demo](../../misc/code_actions.gif)
7
7
  #
8
8
  # The [code actions](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
9
- # request informs the editor of RuboCop quick fixes that can be applied. These are accesible by hovering over a
9
+ # request informs the editor of RuboCop quick fixes that can be applied. These are accessible by hovering over a
10
10
  # specific diagnostic.
11
11
  #
12
12
  # # Example
@@ -35,7 +35,7 @@ module RubyLsp
35
35
 
36
36
  sig { override.returns(T.all(T::Array[LanguageServer::Protocol::Interface::CodeAction], Object)) }
37
37
  def run
38
- diagnostics = Diagnostics.new(@uri, @document).run
38
+ diagnostics = @document.cache_fetch(:diagnostics) { Diagnostics.new(@uri, @document).run }
39
39
  corrections = diagnostics.select do |diagnostic|
40
40
  diagnostic.correctable? && T.cast(diagnostic, Support::RuboCopDiagnostic).in_range?(@range)
41
41
  end
@@ -37,18 +37,11 @@ module RubyLsp
37
37
  )
38
38
  end
39
39
  def run
40
- return syntax_error_diagnostics if @document.syntax_errors?
40
+ return [] if @document.syntax_error?
41
41
  return [] unless defined?(Support::RuboCopDiagnosticsRunner)
42
42
 
43
43
  Support::RuboCopDiagnosticsRunner.instance.run(@uri, @document)
44
44
  end
45
-
46
- private
47
-
48
- sig { returns(T::Array[Support::SyntaxErrorDiagnostic]) }
49
- def syntax_error_diagnostics
50
- @document.syntax_error_edits.map { |e| Support::SyntaxErrorDiagnostic.new(e) }
51
- end
52
45
  end
53
46
  end
54
47
  end
@@ -7,7 +7,7 @@ module RubyLsp
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
10
- # the cursor is on the `F` of the constant `FOO`, the editor should identify other occurences of `FOO`
10
+ # the cursor is on the `F` of the constant `FOO`, the editor should identify other occurrences of `FOO`
11
11
  # and highlight them.
12
12
  #
13
13
  # For writable elements like constants or variables, their read/write occurrences should be highlighted differently.
@@ -30,10 +30,9 @@ module RubyLsp
30
30
  super(document)
31
31
 
32
32
  @highlights = T.let([], T::Array[LanguageServer::Protocol::Interface::DocumentHighlight])
33
- position = Document::Scanner.new(document.source).find_position(position)
34
-
35
33
  return unless document.parsed?
36
34
 
35
+ position = document.create_scanner.find_char_position(position)
37
36
  @target = T.let(find(T.must(document.tree), position), T.nilable(Support::HighlightTarget))
38
37
  end
39
38
 
@@ -56,13 +55,16 @@ module RubyLsp
56
55
 
57
56
  private
58
57
 
59
- DIRECT_HIGHLIGHTS = T.let([
60
- SyntaxTree::GVar,
61
- SyntaxTree::IVar,
62
- SyntaxTree::Const,
63
- SyntaxTree::CVar,
64
- SyntaxTree::VarField,
65
- ], T::Array[T.class_of(SyntaxTree::Node)])
58
+ DIRECT_HIGHLIGHTS = T.let(
59
+ [
60
+ SyntaxTree::GVar,
61
+ SyntaxTree::IVar,
62
+ SyntaxTree::Const,
63
+ SyntaxTree::CVar,
64
+ SyntaxTree::VarField,
65
+ ],
66
+ T::Array[T.class_of(SyntaxTree::Node)],
67
+ )
66
68
 
67
69
  sig do
68
70
  params(
@@ -71,9 +73,10 @@ module RubyLsp
71
73
  ).returns(T.nilable(Support::HighlightTarget))
72
74
  end
73
75
  def find(node, position)
74
- matched, parent = locate_node_and_parent(node, DIRECT_HIGHLIGHTS + [SyntaxTree::Ident], position)
76
+ matched, parent = locate(node, position)
75
77
 
76
78
  return unless matched && parent
79
+ return unless matched.is_a?(SyntaxTree::Ident) || DIRECT_HIGHLIGHTS.include?(matched.class)
77
80
 
78
81
  case matched
79
82
  when *DIRECT_HIGHLIGHTS
@@ -33,35 +33,39 @@ module RubyLsp
33
33
 
34
34
  sig { returns(T::Hash[String, T::Hash[String, T::Hash[String, String]]]) }
35
35
  def gem_paths
36
- @gem_paths ||= T.let(begin
37
- lookup = {}
38
-
39
- Gem::Specification.stubs.each do |stub|
40
- spec = stub.to_spec
41
- lookup[spec.name] = {}
42
- lookup[spec.name][spec.version.to_s] = {}
43
-
44
- Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
45
- lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
36
+ @gem_paths ||= T.let(
37
+ begin
38
+ lookup = {}
39
+
40
+ Gem::Specification.stubs.each do |stub|
41
+ spec = stub.to_spec
42
+ lookup[spec.name] = {}
43
+ lookup[spec.name][spec.version.to_s] = {}
44
+
45
+ Dir.glob("**/*.rb", base: "#{spec.full_gem_path}/").each do |path|
46
+ lookup[spec.name][spec.version.to_s][path] = "#{spec.full_gem_path}/#{path}"
47
+ end
46
48
  end
47
- end
48
-
49
- Gem::Specification.default_stubs.each do |stub|
50
- spec = stub.to_spec
51
- lookup[spec.name] = {}
52
- lookup[spec.name][spec.version.to_s] = {}
53
- prefix_matchers = [//]
54
- prefix_matchers.concat(spec.require_paths.map { |rp| Regexp.new("^#{rp}/") })
55
- prefix_matcher = Regexp.union(prefix_matchers)
56
-
57
- spec.files.each do |file|
58
- path = file.sub(prefix_matcher, "")
59
- lookup[spec.name][spec.version.to_s][path] = "#{RbConfig::CONFIG["rubylibdir"]}/#{path}"
49
+
50
+ Gem::Specification.default_stubs.each do |stub|
51
+ spec = stub.to_spec
52
+ lookup[spec.name] = {}
53
+ lookup[spec.name][spec.version.to_s] = {}
54
+ prefix_matchers = Regexp.union(spec.require_paths.map do |rp|
55
+ Regexp.new("^#{rp}/")
56
+ end)
57
+ prefix_matcher = Regexp.union(prefix_matchers, //)
58
+
59
+ spec.files.each do |file|
60
+ path = file.sub(prefix_matcher, "")
61
+ lookup[spec.name][spec.version.to_s][path] = "#{RbConfig::CONFIG["rubylibdir"]}/#{path}"
62
+ end
60
63
  end
61
- end
62
64
 
63
- lookup
64
- end, T.nilable(T::Hash[String, T::Hash[String, T::Hash[String, String]]]))
65
+ lookup
66
+ end,
67
+ T.nilable(T::Hash[String, T::Hash[String, T::Hash[String, String]]]),
68
+ )
65
69
  end
66
70
  end
67
71
 
@@ -10,7 +10,7 @@ module RubyLsp
10
10
  # informs the editor of all the important symbols, such as classes, variables, and methods, defined in a file. With
11
11
  # this information, the editor can populate breadcrumbs, file outline and allow for fuzzy symbol searches.
12
12
  #
13
- # In VS Code, fuzzy symbol search can be accessed by opened the command palette and inserting an `@` symbol.
13
+ # In VS Code, fuzzy symbol search can be accessed by opening the command palette and inserting an `@` symbol.
14
14
  #
15
15
  # # Example
16
16
  #
@@ -29,34 +29,37 @@ module RubyLsp
29
29
  class DocumentSymbol < BaseRequest
30
30
  extend T::Sig
31
31
 
32
- SYMBOL_KIND = T.let({
33
- file: 1,
34
- module: 2,
35
- namespace: 3,
36
- package: 4,
37
- class: 5,
38
- method: 6,
39
- property: 7,
40
- field: 8,
41
- constructor: 9,
42
- enum: 10,
43
- interface: 11,
44
- function: 12,
45
- variable: 13,
46
- constant: 14,
47
- string: 15,
48
- number: 16,
49
- boolean: 17,
50
- array: 18,
51
- object: 19,
52
- key: 20,
53
- null: 21,
54
- enummember: 22,
55
- struct: 23,
56
- event: 24,
57
- operator: 25,
58
- typeparameter: 26,
59
- }.freeze, T::Hash[Symbol, Integer])
32
+ SYMBOL_KIND = T.let(
33
+ {
34
+ file: 1,
35
+ module: 2,
36
+ namespace: 3,
37
+ package: 4,
38
+ class: 5,
39
+ method: 6,
40
+ property: 7,
41
+ field: 8,
42
+ constructor: 9,
43
+ enum: 10,
44
+ interface: 11,
45
+ function: 12,
46
+ variable: 13,
47
+ constant: 14,
48
+ string: 15,
49
+ number: 16,
50
+ boolean: 17,
51
+ array: 18,
52
+ object: 19,
53
+ key: 20,
54
+ null: 21,
55
+ enummember: 22,
56
+ struct: 23,
57
+ event: 24,
58
+ operator: 25,
59
+ typeparameter: 26,
60
+ }.freeze,
61
+ T::Hash[Symbol, Integer],
62
+ )
60
63
 
61
64
  ATTR_ACCESSORS = T.let(["attr_reader", "attr_writer", "attr_accessor"].freeze, T::Array[String])
62
65
 
@@ -92,7 +95,7 @@ module RubyLsp
92
95
  sig { override.params(node: SyntaxTree::ClassDeclaration).void }
93
96
  def visit_class(node)
94
97
  symbol = create_document_symbol(
95
- name: fully_qualified_name(node),
98
+ name: full_constant_name(node.constant),
96
99
  kind: :class,
97
100
  range_node: node,
98
101
  selection_range_node: node.constant,
@@ -129,43 +132,19 @@ module RubyLsp
129
132
  )
130
133
  end
131
134
 
132
- sig { override.params(node: SyntaxTree::Def).void }
135
+ sig { override.params(node: SyntaxTree::DefNode).void }
133
136
  def visit_def(node)
134
- name = node.name.value
135
-
136
- symbol = create_document_symbol(
137
- name: name,
138
- kind: name == "initialize" ? :constructor : :method,
139
- range_node: node,
140
- selection_range_node: node.name,
141
- )
142
-
143
- @stack << symbol
144
- visit(node.bodystmt)
145
- @stack.pop
146
- end
147
-
148
- sig { override.params(node: SyntaxTree::DefEndless).void }
149
- def visit_def_endless(node)
150
- name = node.name.value
137
+ if node.target&.value&.value == "self"
138
+ name = "self.#{node.name.value}"
139
+ kind = :method
140
+ else
141
+ name = node.name.value
142
+ kind = name == "initialize" ? :constructor : :method
143
+ end
151
144
 
152
145
  symbol = create_document_symbol(
153
146
  name: name,
154
- kind: name == "initialize" ? :constructor : :method,
155
- range_node: node,
156
- selection_range_node: node.name,
157
- )
158
-
159
- @stack << symbol
160
- visit(node.statement)
161
- @stack.pop
162
- end
163
-
164
- sig { override.params(node: SyntaxTree::Defs).void }
165
- def visit_defs(node)
166
- symbol = create_document_symbol(
167
- name: "self.#{node.name.value}",
168
- kind: :method,
147
+ kind: kind,
169
148
  range_node: node,
170
149
  selection_range_node: node.name,
171
150
  )
@@ -178,7 +157,7 @@ module RubyLsp
178
157
  sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
179
158
  def visit_module(node)
180
159
  symbol = create_document_symbol(
181
- name: fully_qualified_name(node),
160
+ name: full_constant_name(node.constant),
182
161
  kind: :module,
183
162
  range_node: node,
184
163
  selection_range_node: node.constant,
@@ -241,25 +220,6 @@ module RubyLsp
241
220
 
242
221
  symbol
243
222
  end
244
-
245
- sig { params(node: T.any(SyntaxTree::ClassDeclaration, SyntaxTree::ModuleDeclaration)).returns(String) }
246
- def fully_qualified_name(node)
247
- constant = T.let(node.constant, SyntaxTree::Node)
248
- name = +node.constant.constant.value
249
-
250
- while constant.is_a?(SyntaxTree::ConstPathRef)
251
- constant = constant.parent
252
-
253
- case constant
254
- when SyntaxTree::ConstPathRef
255
- name.prepend("#{constant.constant.value}::")
256
- when SyntaxTree::VarRef
257
- name.prepend("#{constant.value.value}::")
258
- end
259
- end
260
-
261
- name
262
- end
263
223
  end
264
224
  end
265
225
  end