ruby-lsp 0.23.11 → 0.26.4
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 +2 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +45 -22
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +140 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +107 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +188 -285
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +25 -57
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +49 -9
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +225 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
- data/lib/ruby_indexer/test/method_test.rb +166 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +70 -75
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +88 -86
- data/lib/ruby_lsp/base_server.rb +79 -65
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +205 -104
- data/lib/ruby_lsp/erb_document.rb +45 -47
- data/lib/ruby_lsp/global_state.rb +73 -57
- data/lib/ruby_lsp/internal.rb +8 -3
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +81 -76
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +149 -151
- data/lib/ruby_lsp/listeners/document_link.rb +94 -82
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +107 -115
- data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- data/lib/ruby_lsp/listeners/spec_style.rb +231 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +107 -0
- data/lib/ruby_lsp/listeners/test_style.rb +207 -95
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +10 -11
- data/lib/ruby_lsp/requests/code_action_resolve.rb +65 -61
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +31 -21
- data/lib/ruby_lsp/requests/completion.rb +8 -21
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +139 -0
- data/lib/ruby_lsp/requests/hover.rb +12 -25
- data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +17 -57
- data/lib/ruby_lsp/requests/rename.rb +27 -51
- data/lib/ruby_lsp/requests/request.rb +13 -25
- data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- data/lib/ruby_lsp/requests/signature_help.rb +9 -27
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +23 -61
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +24 -16
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +6 -9
- data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
- data/lib/ruby_lsp/response_builders/hover.rb +12 -18
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
- data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
- data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
- data/lib/ruby_lsp/ruby_document.rb +32 -98
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +305 -198
- data/lib/ruby_lsp/setup_bundler.rb +131 -82
- data/lib/ruby_lsp/static_docs.rb +12 -7
- data/lib/ruby_lsp/store.rb +21 -49
- data/lib/ruby_lsp/test_helper.rb +3 -16
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +241 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +138 -93
- data/static_docs/break.md +103 -0
- metadata +15 -20
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
|
@@ -6,12 +6,8 @@ module RubyLsp
|
|
|
6
6
|
# The [on type formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting)
|
|
7
7
|
# request formats code as the user is typing. For example, automatically adding `end` to class definitions.
|
|
8
8
|
class OnTypeFormatting < Request
|
|
9
|
-
extend T::Sig
|
|
10
|
-
|
|
11
9
|
class << self
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
sig { returns(Interface::DocumentOnTypeFormattingRegistrationOptions) }
|
|
10
|
+
#: -> Interface::DocumentOnTypeFormattingRegistrationOptions
|
|
15
11
|
def provider
|
|
16
12
|
Interface::DocumentOnTypeFormattingRegistrationOptions.new(
|
|
17
13
|
document_selector: nil,
|
|
@@ -21,38 +17,29 @@ module RubyLsp
|
|
|
21
17
|
end
|
|
22
18
|
end
|
|
23
19
|
|
|
24
|
-
END_REGEXES =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
sig do
|
|
34
|
-
params(
|
|
35
|
-
document: RubyDocument,
|
|
36
|
-
position: T::Hash[Symbol, T.untyped],
|
|
37
|
-
trigger_character: String,
|
|
38
|
-
client_name: String,
|
|
39
|
-
).void
|
|
40
|
-
end
|
|
20
|
+
END_REGEXES = [
|
|
21
|
+
/\b(if|unless|for|while|until)\b($|\s|\()/,
|
|
22
|
+
/\b(class|module|def|case)\b($|\s)/,
|
|
23
|
+
/.*\s\bdo\b($|\s)/,
|
|
24
|
+
] #: Array[Regexp]
|
|
25
|
+
|
|
26
|
+
#: (RubyDocument document, Hash[Symbol, untyped] position, String trigger_character, String client_name) -> void
|
|
41
27
|
def initialize(document, position, trigger_character, client_name)
|
|
42
28
|
super()
|
|
43
29
|
@document = document
|
|
44
|
-
@lines =
|
|
30
|
+
@lines = @document.source.lines #: Array[String]
|
|
45
31
|
line = @lines[[position[:line] - 1, 0].max]
|
|
46
32
|
|
|
47
|
-
@indentation =
|
|
48
|
-
@previous_line =
|
|
33
|
+
@indentation = line ? find_indentation(line) : 0 #: Integer
|
|
34
|
+
@previous_line = line ? line.strip.chomp : "" #: String
|
|
49
35
|
@position = position
|
|
50
|
-
@edits =
|
|
36
|
+
@edits = [] #: Array[Interface::TextEdit]
|
|
51
37
|
@trigger_character = trigger_character
|
|
52
38
|
@client_name = client_name
|
|
53
39
|
end
|
|
54
40
|
|
|
55
|
-
|
|
41
|
+
# @override
|
|
42
|
+
#: -> (Array[Interface::TextEdit] & Object)
|
|
56
43
|
def perform
|
|
57
44
|
case @trigger_character
|
|
58
45
|
when "{"
|
|
@@ -60,8 +47,13 @@ module RubyLsp
|
|
|
60
47
|
when "|"
|
|
61
48
|
handle_pipe if @document.syntax_error?
|
|
62
49
|
when "\n"
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
# If the previous line is a simple comment, we'll add a comment continuation
|
|
51
|
+
# But if it's a RBS signature starting with `#:`, we'll ignore it
|
|
52
|
+
# so users can immediately continue typing the method definition
|
|
53
|
+
if (comment_match = @previous_line.match(/^#(?!:)(\s*)/))
|
|
54
|
+
handle_comment_line(
|
|
55
|
+
comment_match[1], #: as !nil
|
|
56
|
+
)
|
|
65
57
|
elsif @document.syntax_error?
|
|
66
58
|
match = /(<<((-|~)?))(?<quote>['"`]?)(?<delimiter>\w+)\k<quote>/.match(@previous_line)
|
|
67
59
|
heredoc_delimiter = match && match.named_captures["delimiter"]
|
|
@@ -81,12 +73,12 @@ module RubyLsp
|
|
|
81
73
|
|
|
82
74
|
private
|
|
83
75
|
|
|
84
|
-
|
|
76
|
+
#: -> void
|
|
85
77
|
def handle_pipe
|
|
86
78
|
current_line = @lines[@position[:line]]
|
|
87
79
|
return unless /((?<=do)|(?<={))\s+\|/.match?(current_line)
|
|
88
80
|
|
|
89
|
-
line =
|
|
81
|
+
line = current_line #: as !nil
|
|
90
82
|
|
|
91
83
|
# If the user inserts the closing pipe manually to the end of the block argument, we need to avoid adding
|
|
92
84
|
# an additional one and remove the previous one. This also helps to remove the user who accidentally
|
|
@@ -112,7 +104,7 @@ module RubyLsp
|
|
|
112
104
|
move_cursor_to(@position[:line], @position[:character])
|
|
113
105
|
end
|
|
114
106
|
|
|
115
|
-
|
|
107
|
+
#: -> void
|
|
116
108
|
def handle_curly_brace
|
|
117
109
|
return unless /".*#\{/.match?(@previous_line)
|
|
118
110
|
|
|
@@ -120,7 +112,7 @@ module RubyLsp
|
|
|
120
112
|
move_cursor_to(@position[:line], @position[:character])
|
|
121
113
|
end
|
|
122
114
|
|
|
123
|
-
|
|
115
|
+
#: -> void
|
|
124
116
|
def handle_statement_end
|
|
125
117
|
# If a keyword occurs in a line which appears be a comment or a string, we will not try to format it, since
|
|
126
118
|
# it could be a coincidental match. This approach is not perfect, but it should cover most cases.
|
|
@@ -142,7 +134,7 @@ module RubyLsp
|
|
|
142
134
|
end
|
|
143
135
|
end
|
|
144
136
|
|
|
145
|
-
|
|
137
|
+
#: (String delimiter) -> void
|
|
146
138
|
def handle_heredoc_end(delimiter)
|
|
147
139
|
indents = " " * @indentation
|
|
148
140
|
add_edit_with_text("\n")
|
|
@@ -150,12 +142,12 @@ module RubyLsp
|
|
|
150
142
|
move_cursor_to(@position[:line], @indentation + 2)
|
|
151
143
|
end
|
|
152
144
|
|
|
153
|
-
|
|
145
|
+
#: (String spaces) -> void
|
|
154
146
|
def handle_comment_line(spaces)
|
|
155
147
|
add_edit_with_text("##{spaces}")
|
|
156
148
|
end
|
|
157
149
|
|
|
158
|
-
|
|
150
|
+
#: (String text, ?Hash[Symbol, untyped] position) -> void
|
|
159
151
|
def add_edit_with_text(text, position = @position)
|
|
160
152
|
pos = Interface::Position.new(
|
|
161
153
|
line: position[:line],
|
|
@@ -168,9 +160,9 @@ module RubyLsp
|
|
|
168
160
|
)
|
|
169
161
|
end
|
|
170
162
|
|
|
171
|
-
|
|
163
|
+
#: (Integer line, Integer character) -> void
|
|
172
164
|
def move_cursor_to(line, character)
|
|
173
|
-
return unless /Visual Studio Code|Cursor/.match?(@client_name)
|
|
165
|
+
return unless /Visual Studio Code|Cursor|VSCodium|Windsurf/.match?(@client_name)
|
|
174
166
|
|
|
175
167
|
position = Interface::Position.new(
|
|
176
168
|
line: line,
|
|
@@ -189,7 +181,7 @@ module RubyLsp
|
|
|
189
181
|
)
|
|
190
182
|
end
|
|
191
183
|
|
|
192
|
-
|
|
184
|
+
#: (String line) -> Integer
|
|
193
185
|
def find_indentation(line)
|
|
194
186
|
count = 0
|
|
195
187
|
|
|
@@ -202,7 +194,7 @@ module RubyLsp
|
|
|
202
194
|
count
|
|
203
195
|
end
|
|
204
196
|
|
|
205
|
-
|
|
197
|
+
#: -> void
|
|
206
198
|
def auto_indent_after_end_keyword
|
|
207
199
|
current_line = @lines[@position[:line]]
|
|
208
200
|
return unless current_line && current_line.strip == "end"
|
|
@@ -7,27 +7,22 @@ module RubyLsp
|
|
|
7
7
|
# [prepare_rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename)
|
|
8
8
|
# # request checks the validity of a rename operation at a given location.
|
|
9
9
|
class PrepareRename < Request
|
|
10
|
-
extend T::Sig
|
|
11
10
|
include Support::Common
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
params(
|
|
15
|
-
document: RubyDocument,
|
|
16
|
-
position: T::Hash[Symbol, T.untyped],
|
|
17
|
-
).void
|
|
18
|
-
end
|
|
12
|
+
#: (RubyDocument document, Hash[Symbol, untyped] position) -> void
|
|
19
13
|
def initialize(document, position)
|
|
20
14
|
super()
|
|
21
15
|
@document = document
|
|
22
|
-
@position =
|
|
16
|
+
@position = position #: Hash[Symbol, Integer]
|
|
23
17
|
end
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
# @override
|
|
20
|
+
#: -> Interface::Range?
|
|
26
21
|
def perform
|
|
27
22
|
char_position, _ = @document.find_index_by_position(@position)
|
|
28
23
|
|
|
29
24
|
node_context = RubyDocument.locate(
|
|
30
|
-
@document.
|
|
25
|
+
@document.ast,
|
|
31
26
|
char_position,
|
|
32
27
|
node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
|
|
33
28
|
code_units_cache: @document.code_units_cache,
|
|
@@ -9,26 +9,16 @@ module RubyLsp
|
|
|
9
9
|
#
|
|
10
10
|
# Currently only supports supertypes due to a limitation of the index.
|
|
11
11
|
class PrepareTypeHierarchy < Request
|
|
12
|
-
extend T::Sig
|
|
13
|
-
|
|
14
12
|
include Support::Common
|
|
15
13
|
|
|
16
14
|
class << self
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
sig { returns(Interface::TypeHierarchyOptions) }
|
|
15
|
+
#: -> Interface::TypeHierarchyOptions
|
|
20
16
|
def provider
|
|
21
17
|
Interface::TypeHierarchyOptions.new
|
|
22
18
|
end
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
params(
|
|
27
|
-
document: T.any(RubyDocument, ERBDocument),
|
|
28
|
-
index: RubyIndexer::Index,
|
|
29
|
-
position: T::Hash[Symbol, T.untyped],
|
|
30
|
-
).void
|
|
31
|
-
end
|
|
21
|
+
#: ((RubyDocument | ERBDocument) document, RubyIndexer::Index index, Hash[Symbol, untyped] position) -> void
|
|
32
22
|
def initialize(document, index, position)
|
|
33
23
|
super()
|
|
34
24
|
|
|
@@ -37,7 +27,8 @@ module RubyLsp
|
|
|
37
27
|
@position = position
|
|
38
28
|
end
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
# @override
|
|
31
|
+
#: -> Array[Interface::TypeHierarchyItem]?
|
|
41
32
|
def perform
|
|
42
33
|
context = @document.locate_node(
|
|
43
34
|
@position,
|
|
@@ -58,8 +49,7 @@ module RubyLsp
|
|
|
58
49
|
|
|
59
50
|
# While the spec allows for multiple entries, VSCode seems to only support one
|
|
60
51
|
# We'll just return the first one for now
|
|
61
|
-
first_entry =
|
|
62
|
-
|
|
52
|
+
first_entry = entries.first #: as !nil
|
|
63
53
|
range = range_from_location(first_entry.location)
|
|
64
54
|
|
|
65
55
|
[
|
|
@@ -6,18 +6,17 @@ module RubyLsp
|
|
|
6
6
|
# The [range formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting)
|
|
7
7
|
# is used to format a selection or to format on paste.
|
|
8
8
|
class RangeFormatting < Request
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
sig { params(global_state: GlobalState, document: RubyDocument, params: T::Hash[Symbol, T.untyped]).void }
|
|
9
|
+
#: (GlobalState global_state, RubyDocument document, Hash[Symbol, untyped] params) -> void
|
|
12
10
|
def initialize(global_state, document, params)
|
|
13
11
|
super()
|
|
14
12
|
@document = document
|
|
15
|
-
@uri =
|
|
13
|
+
@uri = document.uri #: URI::Generic
|
|
16
14
|
@params = params
|
|
17
|
-
@active_formatter =
|
|
15
|
+
@active_formatter = global_state.active_formatter #: Support::Formatter?
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
# @override
|
|
19
|
+
#: -> Array[Interface::TextEdit]?
|
|
21
20
|
def perform
|
|
22
21
|
return unless @active_formatter
|
|
23
22
|
return if @document.syntax_error?
|
|
@@ -7,33 +7,26 @@ module RubyLsp
|
|
|
7
7
|
# [references](https://microsoft.github.io/language-server-protocol/specification#textDocument_references)
|
|
8
8
|
# request finds all references for the selected symbol.
|
|
9
9
|
class References < Request
|
|
10
|
-
extend T::Sig
|
|
11
10
|
include Support::Common
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
params(
|
|
15
|
-
global_state: GlobalState,
|
|
16
|
-
store: Store,
|
|
17
|
-
document: T.any(RubyDocument, ERBDocument),
|
|
18
|
-
params: T::Hash[Symbol, T.untyped],
|
|
19
|
-
).void
|
|
20
|
-
end
|
|
12
|
+
#: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void
|
|
21
13
|
def initialize(global_state, store, document, params)
|
|
22
14
|
super()
|
|
23
15
|
@global_state = global_state
|
|
24
16
|
@store = store
|
|
25
17
|
@document = document
|
|
26
18
|
@params = params
|
|
27
|
-
@locations =
|
|
19
|
+
@locations = [] #: Array[Interface::Location]
|
|
28
20
|
end
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
# @override
|
|
23
|
+
#: -> Array[Interface::Location]
|
|
31
24
|
def perform
|
|
32
25
|
position = @params[:position]
|
|
33
26
|
char_position, _ = @document.find_index_by_position(position)
|
|
34
27
|
|
|
35
28
|
node_context = RubyDocument.locate(
|
|
36
|
-
@document.
|
|
29
|
+
@document.ast,
|
|
37
30
|
char_position,
|
|
38
31
|
node_types: [
|
|
39
32
|
Prism::ConstantReadNode,
|
|
@@ -62,22 +55,7 @@ module RubyLsp
|
|
|
62
55
|
)
|
|
63
56
|
end
|
|
64
57
|
|
|
65
|
-
target =
|
|
66
|
-
target,
|
|
67
|
-
T.any(
|
|
68
|
-
Prism::ConstantReadNode,
|
|
69
|
-
Prism::ConstantPathNode,
|
|
70
|
-
Prism::ConstantPathTargetNode,
|
|
71
|
-
Prism::InstanceVariableAndWriteNode,
|
|
72
|
-
Prism::InstanceVariableOperatorWriteNode,
|
|
73
|
-
Prism::InstanceVariableOrWriteNode,
|
|
74
|
-
Prism::InstanceVariableReadNode,
|
|
75
|
-
Prism::InstanceVariableTargetNode,
|
|
76
|
-
Prism::InstanceVariableWriteNode,
|
|
77
|
-
Prism::CallNode,
|
|
78
|
-
Prism::DefNode,
|
|
79
|
-
),
|
|
80
|
-
)
|
|
58
|
+
target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode,
|
|
81
59
|
|
|
82
60
|
reference_target = create_reference_target(target, node_context)
|
|
83
61
|
return @locations unless reference_target
|
|
@@ -88,7 +66,7 @@ module RubyLsp
|
|
|
88
66
|
# of reading from disk
|
|
89
67
|
next if @store.key?(uri)
|
|
90
68
|
|
|
91
|
-
parse_result = Prism.
|
|
69
|
+
parse_result = Prism.parse_lex_file(path)
|
|
92
70
|
collect_references(reference_target, parse_result, uri)
|
|
93
71
|
rescue Errno::EISDIR, Errno::ENOENT
|
|
94
72
|
# If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
|
|
@@ -103,24 +81,7 @@ module RubyLsp
|
|
|
103
81
|
|
|
104
82
|
private
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
params(
|
|
108
|
-
target_node: T.any(
|
|
109
|
-
Prism::ConstantReadNode,
|
|
110
|
-
Prism::ConstantPathNode,
|
|
111
|
-
Prism::ConstantPathTargetNode,
|
|
112
|
-
Prism::InstanceVariableAndWriteNode,
|
|
113
|
-
Prism::InstanceVariableOperatorWriteNode,
|
|
114
|
-
Prism::InstanceVariableOrWriteNode,
|
|
115
|
-
Prism::InstanceVariableReadNode,
|
|
116
|
-
Prism::InstanceVariableTargetNode,
|
|
117
|
-
Prism::InstanceVariableWriteNode,
|
|
118
|
-
Prism::CallNode,
|
|
119
|
-
Prism::DefNode,
|
|
120
|
-
),
|
|
121
|
-
node_context: NodeContext,
|
|
122
|
-
).returns(T.nilable(RubyIndexer::ReferenceFinder::Target))
|
|
123
|
-
end
|
|
84
|
+
#: ((Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode | Prism::InstanceVariableAndWriteNode | Prism::InstanceVariableOperatorWriteNode | Prism::InstanceVariableOrWriteNode | Prism::InstanceVariableReadNode | Prism::InstanceVariableTargetNode | Prism::InstanceVariableWriteNode | Prism::CallNode | Prism::DefNode) target_node, NodeContext node_context) -> RubyIndexer::ReferenceFinder::Target?
|
|
124
85
|
def create_reference_target(target_node, node_context)
|
|
125
86
|
case target_node
|
|
126
87
|
when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode
|
|
@@ -130,7 +91,8 @@ module RubyLsp
|
|
|
130
91
|
entries = @global_state.index.resolve(name, node_context.nesting)
|
|
131
92
|
return unless entries
|
|
132
93
|
|
|
133
|
-
fully_qualified_name =
|
|
94
|
+
fully_qualified_name = entries.first #: as !nil
|
|
95
|
+
.name
|
|
134
96
|
RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
|
|
135
97
|
when
|
|
136
98
|
Prism::InstanceVariableAndWriteNode,
|
|
@@ -139,19 +101,17 @@ module RubyLsp
|
|
|
139
101
|
Prism::InstanceVariableReadNode,
|
|
140
102
|
Prism::InstanceVariableTargetNode,
|
|
141
103
|
Prism::InstanceVariableWriteNode
|
|
142
|
-
|
|
104
|
+
receiver_type = @global_state.type_inferrer.infer_receiver_type(node_context)
|
|
105
|
+
return unless receiver_type
|
|
106
|
+
|
|
107
|
+
ancestors = @global_state.index.linearized_ancestors_of(receiver_type.name)
|
|
108
|
+
RubyIndexer::ReferenceFinder::InstanceVariableTarget.new(target_node.name.to_s, ancestors)
|
|
143
109
|
when Prism::CallNode, Prism::DefNode
|
|
144
110
|
RubyIndexer::ReferenceFinder::MethodTarget.new(target_node.name.to_s)
|
|
145
111
|
end
|
|
146
112
|
end
|
|
147
113
|
|
|
148
|
-
|
|
149
|
-
params(
|
|
150
|
-
target: RubyIndexer::ReferenceFinder::Target,
|
|
151
|
-
parse_result: Prism::ParseResult,
|
|
152
|
-
uri: URI::Generic,
|
|
153
|
-
).void
|
|
154
|
-
end
|
|
114
|
+
#: (RubyIndexer::ReferenceFinder::Target target, Prism::LexResult parse_result, URI::Generic uri) -> void
|
|
155
115
|
def collect_references(target, parse_result, uri)
|
|
156
116
|
dispatcher = Prism::Dispatcher.new
|
|
157
117
|
finder = RubyIndexer::ReferenceFinder.new(
|
|
@@ -161,7 +121,7 @@ module RubyLsp
|
|
|
161
121
|
uri,
|
|
162
122
|
include_declarations: @params.dig(:context, :includeDeclaration) || true,
|
|
163
123
|
)
|
|
164
|
-
dispatcher.visit(parse_result.value)
|
|
124
|
+
dispatcher.visit(parse_result.value.first)
|
|
165
125
|
|
|
166
126
|
finder.references.each do |reference|
|
|
167
127
|
@locations << Interface::Location.new(
|
|
@@ -7,43 +7,34 @@ module RubyLsp
|
|
|
7
7
|
# [rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_rename)
|
|
8
8
|
# request renames all instances of a symbol in a document.
|
|
9
9
|
class Rename < Request
|
|
10
|
-
extend T::Sig
|
|
11
10
|
include Support::Common
|
|
12
11
|
|
|
13
12
|
class InvalidNameError < StandardError; end
|
|
14
13
|
|
|
15
14
|
class << self
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
sig { returns(Interface::RenameOptions) }
|
|
15
|
+
#: -> Interface::RenameOptions
|
|
19
16
|
def provider
|
|
20
17
|
Interface::RenameOptions.new(prepare_provider: true)
|
|
21
18
|
end
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
params(
|
|
26
|
-
global_state: GlobalState,
|
|
27
|
-
store: Store,
|
|
28
|
-
document: T.any(RubyDocument, ERBDocument),
|
|
29
|
-
params: T::Hash[Symbol, T.untyped],
|
|
30
|
-
).void
|
|
31
|
-
end
|
|
21
|
+
#: (GlobalState global_state, Store store, (RubyDocument | ERBDocument) document, Hash[Symbol, untyped] params) -> void
|
|
32
22
|
def initialize(global_state, store, document, params)
|
|
33
23
|
super()
|
|
34
24
|
@global_state = global_state
|
|
35
25
|
@store = store
|
|
36
26
|
@document = document
|
|
37
|
-
@position =
|
|
38
|
-
@new_name =
|
|
27
|
+
@position = params[:position] #: Hash[Symbol, Integer]
|
|
28
|
+
@new_name = params[:newName] #: String
|
|
39
29
|
end
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
# @override
|
|
32
|
+
#: -> Interface::WorkspaceEdit?
|
|
42
33
|
def perform
|
|
43
34
|
char_position, _ = @document.find_index_by_position(@position)
|
|
44
35
|
|
|
45
36
|
node_context = RubyDocument.locate(
|
|
46
|
-
@document.
|
|
37
|
+
@document.ast,
|
|
47
38
|
char_position,
|
|
48
39
|
node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
|
|
49
40
|
code_units_cache: @document.code_units_cache,
|
|
@@ -60,10 +51,7 @@ module RubyLsp
|
|
|
60
51
|
)
|
|
61
52
|
end
|
|
62
53
|
|
|
63
|
-
target =
|
|
64
|
-
target,
|
|
65
|
-
T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode),
|
|
66
|
-
)
|
|
54
|
+
target = target #: as Prism::ConstantReadNode | Prism::ConstantPathNode | Prism::ConstantPathTargetNode
|
|
67
55
|
|
|
68
56
|
name = RubyIndexer::Index.constant_name(target)
|
|
69
57
|
return unless name
|
|
@@ -72,10 +60,11 @@ module RubyLsp
|
|
|
72
60
|
return unless entries
|
|
73
61
|
|
|
74
62
|
if (conflict_entries = @global_state.index.resolve(@new_name, node_context.nesting))
|
|
75
|
-
raise InvalidNameError, "The new name is already in use by #{
|
|
63
|
+
raise InvalidNameError, "The new name is already in use by #{conflict_entries.first&.name}"
|
|
76
64
|
end
|
|
77
65
|
|
|
78
|
-
fully_qualified_name =
|
|
66
|
+
fully_qualified_name = entries.first #: as !nil
|
|
67
|
+
.name
|
|
79
68
|
reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
|
|
80
69
|
changes = collect_text_edits(reference_target, name)
|
|
81
70
|
|
|
@@ -100,20 +89,15 @@ module RubyLsp
|
|
|
100
89
|
|
|
101
90
|
private
|
|
102
91
|
|
|
103
|
-
|
|
104
|
-
params(
|
|
105
|
-
fully_qualified_name: String,
|
|
106
|
-
document_changes: T::Array[T.any(Interface::RenameFile, Interface::TextDocumentEdit)],
|
|
107
|
-
).void
|
|
108
|
-
end
|
|
92
|
+
#: (String fully_qualified_name, Array[(Interface::RenameFile | Interface::TextDocumentEdit)] document_changes) -> void
|
|
109
93
|
def collect_file_renames(fully_qualified_name, document_changes)
|
|
110
94
|
# Check if the declarations of the symbol being renamed match the file name. In case they do, we automatically
|
|
111
95
|
# rename the files for the user.
|
|
112
96
|
#
|
|
113
97
|
# We also look for an associated test file and rename it too
|
|
114
|
-
short_name =
|
|
98
|
+
short_name = fully_qualified_name.split("::").last #: as !nil
|
|
115
99
|
|
|
116
|
-
|
|
100
|
+
@global_state.index[fully_qualified_name]&.each do |entry|
|
|
117
101
|
# Do not rename files that are not part of the workspace
|
|
118
102
|
uri = entry.uri
|
|
119
103
|
file_path = uri.full_path
|
|
@@ -126,7 +110,9 @@ module RubyLsp
|
|
|
126
110
|
file_name = file_from_constant_name(short_name)
|
|
127
111
|
|
|
128
112
|
if "#{file_name}.rb" == entry.file_name
|
|
129
|
-
new_file_name = file_from_constant_name(
|
|
113
|
+
new_file_name = file_from_constant_name(
|
|
114
|
+
@new_name.split("::").last, #: as !nil
|
|
115
|
+
)
|
|
130
116
|
|
|
131
117
|
new_uri = URI::Generic.from_path(path: File.join(
|
|
132
118
|
File.dirname(file_path),
|
|
@@ -139,12 +125,7 @@ module RubyLsp
|
|
|
139
125
|
end
|
|
140
126
|
end
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
params(
|
|
144
|
-
target: RubyIndexer::ReferenceFinder::Target,
|
|
145
|
-
name: String,
|
|
146
|
-
).returns(T::Hash[String, T::Array[Interface::TextEdit]])
|
|
147
|
-
end
|
|
128
|
+
#: (RubyIndexer::ReferenceFinder::Target target, String name) -> Hash[String, Array[Interface::TextEdit]]
|
|
148
129
|
def collect_text_edits(target, name)
|
|
149
130
|
changes = {}
|
|
150
131
|
|
|
@@ -155,39 +136,34 @@ module RubyLsp
|
|
|
155
136
|
next if @store.key?(uri)
|
|
156
137
|
|
|
157
138
|
parse_result = Prism.parse_file(path)
|
|
158
|
-
edits = collect_changes(target, parse_result, name, uri)
|
|
139
|
+
edits = collect_changes(target, parse_result.value, name, uri)
|
|
159
140
|
changes[uri.to_s] = edits unless edits.empty?
|
|
160
141
|
rescue Errno::EISDIR, Errno::ENOENT
|
|
161
142
|
# If `path` is a directory, just ignore it and continue. If the file doesn't exist, then we also ignore it.
|
|
162
143
|
end
|
|
163
144
|
|
|
164
145
|
@store.each do |uri, document|
|
|
165
|
-
|
|
146
|
+
next unless document.is_a?(RubyDocument) || document.is_a?(ERBDocument)
|
|
147
|
+
|
|
148
|
+
edits = collect_changes(target, document.ast, name, document.uri)
|
|
166
149
|
changes[uri] = edits unless edits.empty?
|
|
167
150
|
end
|
|
168
151
|
|
|
169
152
|
changes
|
|
170
153
|
end
|
|
171
154
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
target: RubyIndexer::ReferenceFinder::Target,
|
|
175
|
-
parse_result: Prism::ParseResult,
|
|
176
|
-
name: String,
|
|
177
|
-
uri: URI::Generic,
|
|
178
|
-
).returns(T::Array[Interface::TextEdit])
|
|
179
|
-
end
|
|
180
|
-
def collect_changes(target, parse_result, name, uri)
|
|
155
|
+
#: (RubyIndexer::ReferenceFinder::Target target, Prism::Node ast, String name, URI::Generic uri) -> Array[Interface::TextEdit]
|
|
156
|
+
def collect_changes(target, ast, name, uri)
|
|
181
157
|
dispatcher = Prism::Dispatcher.new
|
|
182
158
|
finder = RubyIndexer::ReferenceFinder.new(target, @global_state.index, dispatcher, uri)
|
|
183
|
-
dispatcher.visit(
|
|
159
|
+
dispatcher.visit(ast)
|
|
184
160
|
|
|
185
161
|
finder.references.map do |reference|
|
|
186
162
|
adjust_reference_for_edit(name, reference)
|
|
187
163
|
end
|
|
188
164
|
end
|
|
189
165
|
|
|
190
|
-
|
|
166
|
+
#: (String name, RubyIndexer::ReferenceFinder::Reference reference) -> Interface::TextEdit
|
|
191
167
|
def adjust_reference_for_edit(name, reference)
|
|
192
168
|
# The reference may include a namespace in front. We need to check if the rename new name includes namespaces
|
|
193
169
|
# and then adjust both the text and the location to produce the correct edit
|
|
@@ -197,7 +173,7 @@ module RubyLsp
|
|
|
197
173
|
Interface::TextEdit.new(range: range_from_location(location), new_text: new_text)
|
|
198
174
|
end
|
|
199
175
|
|
|
200
|
-
|
|
176
|
+
#: (String constant_name) -> String
|
|
201
177
|
def file_from_constant_name(constant_name)
|
|
202
178
|
constant_name
|
|
203
179
|
.gsub(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/, '\1\3_\2\4')
|
|
@@ -3,28 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
module RubyLsp
|
|
5
5
|
module Requests
|
|
6
|
+
# @abstract
|
|
6
7
|
class Request
|
|
7
|
-
extend T::Sig
|
|
8
|
-
extend T::Generic
|
|
9
|
-
|
|
10
8
|
class InvalidFormatter < StandardError; end
|
|
11
9
|
|
|
12
|
-
abstract
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
# @abstract
|
|
11
|
+
#: -> untyped
|
|
12
|
+
def perform
|
|
13
|
+
raise AbstractMethodInvokedError
|
|
14
|
+
end
|
|
16
15
|
|
|
17
16
|
private
|
|
18
17
|
|
|
19
18
|
# Signals to the client that the request should be delegated to the language server server for the host language
|
|
20
19
|
# in ERB files
|
|
21
|
-
|
|
22
|
-
params(
|
|
23
|
-
global_state: GlobalState,
|
|
24
|
-
document: Document[T.untyped],
|
|
25
|
-
char_position: Integer,
|
|
26
|
-
).void
|
|
27
|
-
end
|
|
20
|
+
#: (GlobalState global_state, Document[untyped] document, Integer char_position) -> void
|
|
28
21
|
def delegate_request_if_needed!(global_state, document, char_position)
|
|
29
22
|
if global_state.client_capabilities.supports_request_delegation &&
|
|
30
23
|
document.is_a?(ERBDocument) &&
|
|
@@ -34,7 +27,7 @@ module RubyLsp
|
|
|
34
27
|
end
|
|
35
28
|
|
|
36
29
|
# Checks if a location covers a position
|
|
37
|
-
|
|
30
|
+
#: (Prism::Location location, untyped position) -> bool
|
|
38
31
|
def cover?(location, position)
|
|
39
32
|
start_covered =
|
|
40
33
|
location.start_line - 1 < position[:line] ||
|
|
@@ -61,18 +54,13 @@ module RubyLsp
|
|
|
61
54
|
# # ^ Going to definition here should go to Foo::Bar
|
|
62
55
|
# #^ Going to definition here should go to Foo
|
|
63
56
|
# ```
|
|
64
|
-
|
|
65
|
-
params(
|
|
66
|
-
target: Prism::Node,
|
|
67
|
-
parent: Prism::Node,
|
|
68
|
-
position: T::Hash[Symbol, Integer],
|
|
69
|
-
).returns(Prism::Node)
|
|
70
|
-
end
|
|
57
|
+
#: (Prism::Node target, Prism::Node parent, Hash[Symbol, Integer] position) -> Prism::Node
|
|
71
58
|
def determine_target(target, parent, position)
|
|
72
59
|
return target unless parent.is_a?(Prism::ConstantPathNode)
|
|
73
60
|
|
|
74
|
-
target =
|
|
75
|
-
parent =
|
|
61
|
+
target = parent #: Prism::Node
|
|
62
|
+
parent = target #: as Prism::ConstantPathNode
|
|
63
|
+
.parent #: Prism::Node?
|
|
76
64
|
|
|
77
65
|
while parent && cover?(parent.location, position)
|
|
78
66
|
target = parent
|
|
@@ -83,7 +71,7 @@ module RubyLsp
|
|
|
83
71
|
end
|
|
84
72
|
|
|
85
73
|
# Checks if a given location covers the position requested
|
|
86
|
-
|
|
74
|
+
#: (Prism::Location? location, Hash[Symbol, untyped] position) -> bool
|
|
87
75
|
def covers_position?(location, position)
|
|
88
76
|
return false unless location
|
|
89
77
|
|