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 +4 -4
- data/README.md +29 -2
- data/VERSION +1 -1
- data/lib/ruby_lsp/document.rb +51 -46
- data/lib/ruby_lsp/handler.rb +2 -2
- data/lib/ruby_lsp/requests/base_request.rb +44 -20
- data/lib/ruby_lsp/requests/code_actions.rb +2 -2
- data/lib/ruby_lsp/requests/diagnostics.rb +1 -8
- data/lib/ruby_lsp/requests/document_highlight.rb +14 -11
- data/lib/ruby_lsp/requests/document_link.rb +30 -26
- data/lib/ruby_lsp/requests/document_symbol.rb +43 -83
- data/lib/ruby_lsp/requests/folding_ranges.rb +58 -38
- data/lib/ruby_lsp/requests/formatting.rb +2 -0
- data/lib/ruby_lsp/requests/hover.rb +16 -7
- data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +19 -22
- data/lib/ruby_lsp/requests/selection_ranges.rb +40 -38
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +96 -73
- data/lib/ruby_lsp/requests/support/highlight_target.rb +8 -9
- data/lib/ruby_lsp/requests/support/rails_document_client.rb +13 -6
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +15 -10
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -6
- data/lib/ruby_lsp/requests/support/source_uri.rb +10 -7
- data/lib/ruby_lsp/server.rb +19 -7
- data/lib/ruby_lsp/store.rb +5 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f1ed447e51909ada66f6bebceca82d807686a4889468838a797e2ed8afff50e
|
4
|
+
data.tar.gz: 7b429773bbdebc171debbe446adb868136bd15b5f493542898df0a9c82f3514e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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.
|
1
|
+
0.3.8
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -15,15 +15,11 @@ module RubyLsp
|
|
15
15
|
sig { returns(String) }
|
16
16
|
attr_reader :source
|
17
17
|
|
18
|
-
sig {
|
19
|
-
|
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
|
-
@
|
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
|
-
|
57
|
-
|
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
|
-
|
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
|
78
|
-
@
|
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
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
117
|
-
|
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
|
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
|
111
|
+
@pos += 1 until LINE_BREAK == @source[@pos]
|
127
112
|
@pos += 1
|
128
113
|
@current_line += 1
|
129
114
|
end
|
130
115
|
|
131
|
-
|
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
|
data/lib/ruby_lsp/handler.rb
CHANGED
@@ -59,7 +59,7 @@ module RubyLsp
|
|
59
59
|
|
60
60
|
sig { void }
|
61
61
|
def start
|
62
|
-
|
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
|
-
|
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(
|
33
|
-
|
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
|
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
|
-
|
60
|
-
target_nodes: T::Array[T.class_of(SyntaxTree::Node)],
|
63
|
+
node: SyntaxTree::Node,
|
61
64
|
position: Integer,
|
62
|
-
).returns(T::
|
65
|
+
).returns([T.nilable(SyntaxTree::Node), T.nilable(SyntaxTree::Node)])
|
63
66
|
end
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
# 
|
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
|
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
|
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
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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 =
|
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(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
64
|
-
|
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
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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:
|
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::
|
135
|
+
sig { override.params(node: SyntaxTree::DefNode).void }
|
133
136
|
def visit_def(node)
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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:
|
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:
|
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
|