ruby-lsp 0.3.6 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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