ruby-lsp 0.26.8 → 0.27.0.beta1
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/VERSION +1 -1
- data/exe/ruby-lsp +0 -8
- data/exe/ruby-lsp-launcher +1 -4
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +2 -2
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +8 -8
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +2 -2
- data/lib/ruby_lsp/global_state.rb +10 -1
- data/lib/ruby_lsp/internal.rb +5 -0
- data/lib/ruby_lsp/listeners/definition.rb +64 -98
- data/lib/ruby_lsp/listeners/document_link.rb +13 -1
- data/lib/ruby_lsp/listeners/hover.rb +35 -50
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/listeners/test_style.rb +1 -1
- data/lib/ruby_lsp/node_context.rb +2 -2
- data/lib/ruby_lsp/requests/on_type_formatting.rb +4 -0
- data/lib/ruby_lsp/requests/rename.rb +64 -72
- data/lib/ruby_lsp/requests/support/common.rb +40 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +15 -37
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +3 -3
- data/lib/ruby_lsp/rubydex/definition.rb +200 -0
- data/lib/ruby_lsp/rubydex/reference.rb +16 -0
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +0 -2
- data/lib/ruby_lsp/server.rb +43 -7
- data/lib/ruby_lsp/setup_bundler.rb +1 -4
- data/lib/ruby_lsp/store.rb +0 -6
- data/lib/ruby_lsp/test_helper.rb +3 -0
- data/lib/ruby_lsp/type_inferrer.rb +25 -23
- metadata +17 -1
|
@@ -48,6 +48,7 @@ module RubyLsp
|
|
|
48
48
|
@response_builder = response_builder
|
|
49
49
|
@global_state = global_state
|
|
50
50
|
@index = global_state.index #: RubyIndexer::Index
|
|
51
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
51
52
|
@type_inferrer = global_state.type_inferrer #: TypeInferrer
|
|
52
53
|
@path = uri.to_standardized_path #: String?
|
|
53
54
|
@node_context = node_context
|
|
@@ -178,32 +179,32 @@ module RubyLsp
|
|
|
178
179
|
|
|
179
180
|
#: (Prism::InstanceVariableReadNode node) -> void
|
|
180
181
|
def on_instance_variable_read_node_enter(node)
|
|
181
|
-
|
|
182
|
+
handle_variable_hover(node.name.to_s)
|
|
182
183
|
end
|
|
183
184
|
|
|
184
185
|
#: (Prism::InstanceVariableWriteNode node) -> void
|
|
185
186
|
def on_instance_variable_write_node_enter(node)
|
|
186
|
-
|
|
187
|
+
handle_variable_hover(node.name.to_s)
|
|
187
188
|
end
|
|
188
189
|
|
|
189
190
|
#: (Prism::InstanceVariableAndWriteNode node) -> void
|
|
190
191
|
def on_instance_variable_and_write_node_enter(node)
|
|
191
|
-
|
|
192
|
+
handle_variable_hover(node.name.to_s)
|
|
192
193
|
end
|
|
193
194
|
|
|
194
195
|
#: (Prism::InstanceVariableOperatorWriteNode node) -> void
|
|
195
196
|
def on_instance_variable_operator_write_node_enter(node)
|
|
196
|
-
|
|
197
|
+
handle_variable_hover(node.name.to_s)
|
|
197
198
|
end
|
|
198
199
|
|
|
199
200
|
#: (Prism::InstanceVariableOrWriteNode node) -> void
|
|
200
201
|
def on_instance_variable_or_write_node_enter(node)
|
|
201
|
-
|
|
202
|
+
handle_variable_hover(node.name.to_s)
|
|
202
203
|
end
|
|
203
204
|
|
|
204
205
|
#: (Prism::InstanceVariableTargetNode node) -> void
|
|
205
206
|
def on_instance_variable_target_node_enter(node)
|
|
206
|
-
|
|
207
|
+
handle_variable_hover(node.name.to_s)
|
|
207
208
|
end
|
|
208
209
|
|
|
209
210
|
#: (Prism::SuperNode node) -> void
|
|
@@ -223,32 +224,32 @@ module RubyLsp
|
|
|
223
224
|
|
|
224
225
|
#: (Prism::ClassVariableAndWriteNode node) -> void
|
|
225
226
|
def on_class_variable_and_write_node_enter(node)
|
|
226
|
-
|
|
227
|
+
handle_variable_hover(node.name.to_s)
|
|
227
228
|
end
|
|
228
229
|
|
|
229
230
|
#: (Prism::ClassVariableOperatorWriteNode node) -> void
|
|
230
231
|
def on_class_variable_operator_write_node_enter(node)
|
|
231
|
-
|
|
232
|
+
handle_variable_hover(node.name.to_s)
|
|
232
233
|
end
|
|
233
234
|
|
|
234
235
|
#: (Prism::ClassVariableOrWriteNode node) -> void
|
|
235
236
|
def on_class_variable_or_write_node_enter(node)
|
|
236
|
-
|
|
237
|
+
handle_variable_hover(node.name.to_s)
|
|
237
238
|
end
|
|
238
239
|
|
|
239
240
|
#: (Prism::ClassVariableTargetNode node) -> void
|
|
240
241
|
def on_class_variable_target_node_enter(node)
|
|
241
|
-
|
|
242
|
+
handle_variable_hover(node.name.to_s)
|
|
242
243
|
end
|
|
243
244
|
|
|
244
245
|
#: (Prism::ClassVariableReadNode node) -> void
|
|
245
246
|
def on_class_variable_read_node_enter(node)
|
|
246
|
-
|
|
247
|
+
handle_variable_hover(node.name.to_s)
|
|
247
248
|
end
|
|
248
249
|
|
|
249
250
|
#: (Prism::ClassVariableWriteNode node) -> void
|
|
250
251
|
def on_class_variable_write_node_enter(node)
|
|
251
|
-
|
|
252
|
+
handle_variable_hover(node.name.to_s)
|
|
252
253
|
end
|
|
253
254
|
|
|
254
255
|
private
|
|
@@ -324,62 +325,46 @@ module RubyLsp
|
|
|
324
325
|
end
|
|
325
326
|
end
|
|
326
327
|
|
|
327
|
-
#: (String name) -> void
|
|
328
|
-
def handle_instance_variable_hover(name)
|
|
329
|
-
# Sorbet enforces that all instance variables be declared on typed strict or higher, which means it will be able
|
|
330
|
-
# to provide all features for them
|
|
331
|
-
return if @sorbet_level.strict?
|
|
332
|
-
|
|
333
|
-
type = @type_inferrer.infer_receiver_type(@node_context)
|
|
334
|
-
return unless type
|
|
335
|
-
|
|
336
|
-
entries = @index.resolve_instance_variable(name, type.name)
|
|
337
|
-
return unless entries
|
|
338
|
-
|
|
339
|
-
categorized_markdown_from_index_entries(name, entries).each do |category, content|
|
|
340
|
-
@response_builder.push(content, category: category)
|
|
341
|
-
end
|
|
342
|
-
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
343
|
-
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
|
344
|
-
end
|
|
345
|
-
|
|
346
328
|
#: (String name) -> void
|
|
347
329
|
def handle_global_variable_hover(name)
|
|
348
|
-
|
|
349
|
-
return unless
|
|
330
|
+
declaration = @graph[name]
|
|
331
|
+
return unless declaration
|
|
350
332
|
|
|
351
|
-
|
|
333
|
+
categorized_markdown_from_definitions(name, declaration.definitions).each do |category, content|
|
|
352
334
|
@response_builder.push(content, category: category)
|
|
353
335
|
end
|
|
354
336
|
end
|
|
355
337
|
|
|
338
|
+
# Handle class or instance variables. We collect all definitions across the ancestors of the type
|
|
339
|
+
#
|
|
356
340
|
#: (String name) -> void
|
|
357
|
-
def
|
|
341
|
+
def handle_variable_hover(name)
|
|
342
|
+
# Sorbet enforces that all variables be declared on typed strict or higher, which means it will be able to
|
|
343
|
+
# provide all features for them
|
|
344
|
+
return if @sorbet_level.strict?
|
|
345
|
+
|
|
358
346
|
type = @type_inferrer.infer_receiver_type(@node_context)
|
|
359
347
|
return unless type
|
|
360
348
|
|
|
361
|
-
|
|
362
|
-
return unless
|
|
349
|
+
owner = @graph[type.name]
|
|
350
|
+
return unless owner.is_a?(Rubydex::Namespace)
|
|
363
351
|
|
|
364
|
-
|
|
365
|
-
|
|
352
|
+
owner.ancestors.each do |ancestor|
|
|
353
|
+
member = ancestor.member(name)
|
|
354
|
+
next unless member
|
|
355
|
+
|
|
356
|
+
categorized_markdown_from_definitions(member.name, member.definitions).each do |category, content|
|
|
357
|
+
@response_builder.push(content, category: category)
|
|
358
|
+
end
|
|
366
359
|
end
|
|
367
|
-
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
368
|
-
# If by any chance we haven't indexed the owner, then there's no way to find the right declaration
|
|
369
360
|
end
|
|
370
361
|
|
|
371
362
|
#: (String name, Prism::Location location) -> void
|
|
372
363
|
def generate_hover(name, location)
|
|
373
|
-
|
|
374
|
-
return unless
|
|
375
|
-
|
|
376
|
-
# We should only show hover for private constants if the constant is defined in the same namespace as the
|
|
377
|
-
# reference
|
|
378
|
-
first_entry = entries.first #: as !nil
|
|
379
|
-
full_name = first_entry.name
|
|
380
|
-
return if first_entry.private? && full_name != "#{@node_context.fully_qualified_name}::#{name}"
|
|
364
|
+
declaration = @graph.resolve_constant(name, @node_context.nesting)
|
|
365
|
+
return unless declaration
|
|
381
366
|
|
|
382
|
-
|
|
367
|
+
categorized_markdown_from_definitions(declaration.name, declaration.definitions).each do |category, content|
|
|
383
368
|
@response_builder.push(content, category: category)
|
|
384
369
|
end
|
|
385
370
|
end
|
|
@@ -162,7 +162,7 @@ module RubyLsp
|
|
|
162
162
|
|
|
163
163
|
#: (Prism::SelfNode node) -> void
|
|
164
164
|
def on_self_node_enter(node)
|
|
165
|
-
@response_builder.add_token(node.location, :variable, [:
|
|
165
|
+
@response_builder.add_token(node.location, :variable, [:defaultLibrary])
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
#: (Prism::LocalVariableWriteNode node) -> void
|
|
@@ -266,7 +266,7 @@ module RubyLsp
|
|
|
266
266
|
# We only support regular Minitest tests. The declarative syntax provided by ActiveSupport is handled by the
|
|
267
267
|
# Rails add-on
|
|
268
268
|
name_parts = fully_qualified_name.split("::")
|
|
269
|
-
singleton_name = "#{name_parts.join("::")}
|
|
269
|
+
singleton_name = "#{name_parts.join("::")}::<#{name_parts.last}>"
|
|
270
270
|
!@index.linearized_ancestors_of(singleton_name).include?("ActiveSupport::Testing::Declarative")
|
|
271
271
|
rescue RubyIndexer::Index::NonExistingNamespaceError
|
|
272
272
|
true
|
|
@@ -62,12 +62,12 @@ module RubyLsp
|
|
|
62
62
|
when Prism::ClassNode, Prism::ModuleNode
|
|
63
63
|
nesting << node.constant_path.slice
|
|
64
64
|
when Prism::SingletonClassNode
|
|
65
|
-
nesting << "
|
|
65
|
+
nesting << "<#{nesting.flat_map { |n| n.split("::") }.last}>"
|
|
66
66
|
when Prism::DefNode
|
|
67
67
|
surrounding_method = node.name.to_s
|
|
68
68
|
next unless node.receiver.is_a?(Prism::SelfNode)
|
|
69
69
|
|
|
70
|
-
nesting << "
|
|
70
|
+
nesting << "<#{nesting.flat_map { |n| n.split("::") }.last}>"
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -120,6 +120,10 @@ module RubyLsp
|
|
|
120
120
|
|
|
121
121
|
return unless END_REGEXES.any? { |regex| regex.match?(@previous_line) }
|
|
122
122
|
|
|
123
|
+
# Endless method definitions (e.g., `def foo = 42`) are complete statements
|
|
124
|
+
# and should not have `end` added
|
|
125
|
+
return if @previous_line.match?(/\bdef\s+[\w.]+[!?=]?(\([^)]*\))?\s*=[^=~>]/)
|
|
126
|
+
|
|
123
127
|
indents = " " * @indentation
|
|
124
128
|
current_line = @lines[@position[:line]]
|
|
125
129
|
next_line = @lines[@position[:line] + 1]
|
|
@@ -22,6 +22,7 @@ module RubyLsp
|
|
|
22
22
|
def initialize(global_state, store, document, params)
|
|
23
23
|
super()
|
|
24
24
|
@global_state = global_state
|
|
25
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
25
26
|
@store = store
|
|
26
27
|
@document = document
|
|
27
28
|
@position = params[:position] #: Hash[Symbol, Integer]
|
|
@@ -56,17 +57,14 @@ module RubyLsp
|
|
|
56
57
|
name = RubyIndexer::Index.constant_name(target)
|
|
57
58
|
return unless name
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
return unless
|
|
60
|
+
declaration = @graph.resolve_constant(name, node_context.nesting)
|
|
61
|
+
return unless declaration
|
|
61
62
|
|
|
62
|
-
if (
|
|
63
|
-
raise InvalidNameError, "The new name is already in use by #{
|
|
63
|
+
if (conflict = @graph.resolve_constant(@new_name, node_context.nesting))
|
|
64
|
+
raise InvalidNameError, "The new name is already in use by #{conflict.name}"
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
.name
|
|
68
|
-
reference_target = RubyIndexer::ReferenceFinder::ConstTarget.new(fully_qualified_name)
|
|
69
|
-
changes = collect_text_edits(reference_target, name)
|
|
67
|
+
changes = collect_text_edits(declaration, name)
|
|
70
68
|
|
|
71
69
|
# If the client doesn't support resource operations, such as renaming files, then we can only return the basic
|
|
72
70
|
# text changes
|
|
@@ -78,99 +76,93 @@ module RubyLsp
|
|
|
78
76
|
# renamed and then the URI associated to the text edit no longer exists, causing it to be dropped
|
|
79
77
|
document_changes = changes.map do |uri, edits|
|
|
80
78
|
Interface::TextDocumentEdit.new(
|
|
81
|
-
text_document: Interface::
|
|
79
|
+
text_document: Interface::OptionalVersionedTextDocumentIdentifier.new(uri: uri, version: nil),
|
|
82
80
|
edits: edits,
|
|
83
81
|
)
|
|
84
82
|
end
|
|
85
83
|
|
|
86
|
-
collect_file_renames(
|
|
84
|
+
collect_file_renames(declaration, document_changes)
|
|
87
85
|
Interface::WorkspaceEdit.new(document_changes: document_changes)
|
|
88
86
|
end
|
|
89
87
|
|
|
90
88
|
private
|
|
91
89
|
|
|
92
|
-
#: (
|
|
93
|
-
def collect_file_renames(
|
|
90
|
+
#: (Rubydex::Declaration, Array[(Interface::RenameFile | Interface::TextDocumentEdit)]) -> void
|
|
91
|
+
def collect_file_renames(declaration, document_changes)
|
|
94
92
|
# Check if the declarations of the symbol being renamed match the file name. In case they do, we automatically
|
|
95
93
|
# rename the files for the user.
|
|
96
94
|
#
|
|
97
95
|
# We also look for an associated test file and rename it too
|
|
98
|
-
short_name = fully_qualified_name.split("::").last #: as !nil
|
|
99
96
|
|
|
100
|
-
|
|
97
|
+
unless [
|
|
98
|
+
Rubydex::Class,
|
|
99
|
+
Rubydex::Module,
|
|
100
|
+
Rubydex::Constant,
|
|
101
|
+
Rubydex::ConstantAlias,
|
|
102
|
+
].any? { |type| declaration.is_a?(type) }
|
|
103
|
+
return
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
short_name = declaration.unqualified_name
|
|
107
|
+
|
|
108
|
+
declaration.definitions.each do |definition|
|
|
101
109
|
# Do not rename files that are not part of the workspace
|
|
102
|
-
uri =
|
|
110
|
+
uri = URI(definition.location.uri)
|
|
103
111
|
file_path = uri.full_path
|
|
104
112
|
next unless file_path&.start_with?(@global_state.workspace_path)
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
RubyIndexer::Entry::ConstantAlias, RubyIndexer::Entry::UnresolvedConstantAlias
|
|
109
|
-
|
|
110
|
-
file_name = file_from_constant_name(short_name)
|
|
114
|
+
file_name = file_from_constant_name(short_name)
|
|
115
|
+
next unless "#{file_name}.rb" == File.basename(file_path)
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
117
|
+
new_file_name = file_from_constant_name(
|
|
118
|
+
@new_name.split("::").last, #: as !nil
|
|
119
|
+
)
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
new_uri = URI::Generic.from_path(path: File.join(
|
|
122
|
+
File.dirname(file_path),
|
|
123
|
+
"#{new_file_name}.rb",
|
|
124
|
+
)).to_s
|
|
121
125
|
|
|
122
|
-
|
|
123
|
-
end
|
|
124
|
-
end
|
|
126
|
+
document_changes << Interface::RenameFile.new(kind: "rename", old_uri: uri.to_s, new_uri: new_uri)
|
|
125
127
|
end
|
|
126
128
|
end
|
|
127
129
|
|
|
128
|
-
#: (
|
|
129
|
-
def collect_text_edits(
|
|
130
|
-
changes = {}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
changes[
|
|
141
|
-
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
#: (Rubydex::Declaration declaration, String name) -> Hash[String, Array[Interface::TextEdit]]
|
|
131
|
+
def collect_text_edits(declaration, name)
|
|
132
|
+
changes = {} #: Hash[String, Array[Interface::TextEdit]]
|
|
133
|
+
short_name = name.split("::").last #: as !nil
|
|
134
|
+
new_short_name = @new_name.split("::").last #: as !nil
|
|
135
|
+
|
|
136
|
+
# Collect edits for definition sites (where the constant is declared)
|
|
137
|
+
declaration.definitions.each do |definition|
|
|
138
|
+
name_loc = definition.name_location
|
|
139
|
+
next unless name_loc
|
|
140
|
+
|
|
141
|
+
uri_string = name_loc.uri
|
|
142
|
+
edits = (changes[uri_string] ||= [])
|
|
143
|
+
|
|
144
|
+
# The name_location spans the constant name as written in the definition.
|
|
145
|
+
# We only replace the unqualified name portion (the last segment).
|
|
146
|
+
range = Interface::Range.new(
|
|
147
|
+
start: Interface::Position.new(
|
|
148
|
+
line: name_loc.end_line,
|
|
149
|
+
character: name_loc.end_column - short_name.length,
|
|
150
|
+
),
|
|
151
|
+
end: Interface::Position.new(line: name_loc.end_line, character: name_loc.end_column),
|
|
152
|
+
)
|
|
147
153
|
|
|
148
|
-
edits
|
|
149
|
-
changes[uri] = edits unless edits.empty?
|
|
154
|
+
edits << Interface::TextEdit.new(range: range, new_text: new_short_name)
|
|
150
155
|
end
|
|
151
156
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
finder = RubyIndexer::ReferenceFinder.new(target, @global_state.index, dispatcher, uri)
|
|
159
|
-
dispatcher.visit(ast)
|
|
160
|
-
|
|
161
|
-
finder.references.map do |reference|
|
|
162
|
-
adjust_reference_for_edit(name, reference)
|
|
157
|
+
# Collect edits for reference sites (where the constant is used)
|
|
158
|
+
declaration.references.each do |reference|
|
|
159
|
+
ref = reference #: as Rubydex::ConstantReference
|
|
160
|
+
uri_string = ref.location.uri
|
|
161
|
+
edits = (changes[uri_string] ||= [])
|
|
162
|
+
edits << Interface::TextEdit.new(range: ref.to_lsp_range, new_text: new_short_name)
|
|
163
163
|
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
#: (String name, RubyIndexer::ReferenceFinder::Reference reference) -> Interface::TextEdit
|
|
167
|
-
def adjust_reference_for_edit(name, reference)
|
|
168
|
-
# The reference may include a namespace in front. We need to check if the rename new name includes namespaces
|
|
169
|
-
# and then adjust both the text and the location to produce the correct edit
|
|
170
|
-
location = reference.location
|
|
171
|
-
new_text = reference.name.sub(name, @new_name)
|
|
172
164
|
|
|
173
|
-
|
|
165
|
+
changes
|
|
174
166
|
end
|
|
175
167
|
|
|
176
168
|
#: (String constant_name) -> String
|
|
@@ -64,6 +64,46 @@ module RubyLsp
|
|
|
64
64
|
receiver.nil? || receiver.is_a?(Prism::SelfNode)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
#: (String, Enumerable[Rubydex::Definition], ?Integer?) -> Hash[Symbol, String]
|
|
68
|
+
def categorized_markdown_from_definitions(title, definitions, max_entries = nil)
|
|
69
|
+
markdown_title = "```ruby\n#{title}\n```"
|
|
70
|
+
file_links = []
|
|
71
|
+
content = +""
|
|
72
|
+
defs = max_entries ? definitions.take(max_entries) : definitions
|
|
73
|
+
defs.each do |definition|
|
|
74
|
+
# For Markdown links, we need 1 based display locations
|
|
75
|
+
loc = definition.location.to_display
|
|
76
|
+
uri = URI(loc.uri)
|
|
77
|
+
file_name = if uri.scheme == "untitled"
|
|
78
|
+
uri.opaque #: as !nil
|
|
79
|
+
else
|
|
80
|
+
File.basename(
|
|
81
|
+
uri.full_path, #: as !nil
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# The format for VS Code file URIs is `file:///path/to/file.rb#Lstart_line,start_column-end_line,end_column`
|
|
86
|
+
string_uri = "#{loc.uri}#L#{loc.start_line},#{loc.start_column}-#{loc.end_line},#{loc.end_column}"
|
|
87
|
+
file_links << "[#{file_name}](#{string_uri})"
|
|
88
|
+
content << "\n\n#{definition.comments.map { |comment| comment.string.delete_prefix("# ") }.join("\n")}" unless definition.comments.empty?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
total_definitions = definitions.count
|
|
92
|
+
|
|
93
|
+
additional_entries_text = if max_entries && total_definitions > max_entries
|
|
94
|
+
additional = total_definitions - max_entries
|
|
95
|
+
" | #{additional} other#{additional > 1 ? "s" : ""}"
|
|
96
|
+
else
|
|
97
|
+
""
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
{
|
|
101
|
+
title: markdown_title,
|
|
102
|
+
links: "**Definitions**: #{file_links.join(" | ")}#{additional_entries_text}",
|
|
103
|
+
documentation: content,
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
67
107
|
#: (String title, (Array[RubyIndexer::Entry] | RubyIndexer::Entry) entries, ?Integer? max_entries) -> Hash[Symbol, String]
|
|
68
108
|
def categorized_markdown_from_index_entries(title, entries, max_entries = nil)
|
|
69
109
|
markdown_title = "```ruby\n#{title}\n```"
|
|
@@ -12,54 +12,32 @@ module RubyLsp
|
|
|
12
12
|
#: (GlobalState global_state, String? query) -> void
|
|
13
13
|
def initialize(global_state, query)
|
|
14
14
|
super()
|
|
15
|
-
@global_state = global_state
|
|
16
15
|
@query = query
|
|
17
|
-
@
|
|
16
|
+
@graph = global_state.graph #: Rubydex::Graph
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
# @override
|
|
21
20
|
#: -> Array[Interface::WorkspaceSymbol]
|
|
22
21
|
def perform
|
|
23
|
-
|
|
24
|
-
kind = kind_for_entry(entry)
|
|
25
|
-
loc = entry.location
|
|
22
|
+
response = []
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# short name `Bar`, then searching for `Foo::Bar` would not return any results
|
|
30
|
-
*container, _short_name = entry.name.split("::")
|
|
24
|
+
@graph.fuzzy_search(@query || "").each do |declaration|
|
|
25
|
+
name = declaration.name
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
location: Interface::Location.new(
|
|
37
|
-
uri: entry.uri.to_s,
|
|
38
|
-
range: Interface::Range.new(
|
|
39
|
-
start: Interface::Position.new(line: loc.start_line - 1, character: loc.start_column),
|
|
40
|
-
end: Interface::Position.new(line: loc.end_line - 1, character: loc.end_column),
|
|
41
|
-
),
|
|
42
|
-
),
|
|
43
|
-
)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
27
|
+
declaration.definitions.each do |definition|
|
|
28
|
+
location = definition.location
|
|
29
|
+
uri = URI(location.uri)
|
|
30
|
+
file_path = uri.full_path
|
|
48
31
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
file_path = entry.uri.full_path
|
|
32
|
+
# We only show symbols declared in the workspace
|
|
33
|
+
in_dependencies = file_path && !not_in_dependencies?(file_path)
|
|
34
|
+
next if in_dependencies
|
|
53
35
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
next if in_dependencies
|
|
57
|
-
|
|
58
|
-
# We should never show private symbols when searching the entire workspace
|
|
59
|
-
next if entry.private?
|
|
60
|
-
|
|
61
|
-
true
|
|
36
|
+
response << definition.to_lsp_workspace_symbol(name)
|
|
37
|
+
end
|
|
62
38
|
end
|
|
39
|
+
|
|
40
|
+
response
|
|
63
41
|
end
|
|
64
42
|
end
|
|
65
43
|
end
|
|
@@ -43,7 +43,7 @@ module RubyLsp
|
|
|
43
43
|
async: 6,
|
|
44
44
|
modification: 7,
|
|
45
45
|
documentation: 8,
|
|
46
|
-
|
|
46
|
+
defaultLibrary: 9,
|
|
47
47
|
}.freeze #: Hash[Symbol, Integer]
|
|
48
48
|
|
|
49
49
|
#: ((^(Integer arg0) -> Integer | Prism::CodeUnitsCache) code_units_cache) -> void
|
|
@@ -184,8 +184,8 @@ module RubyLsp
|
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
# Encode an array of modifiers to positions onto a bit flag
|
|
187
|
-
# For example, [:
|
|
188
|
-
# 0b1000000000, as :
|
|
187
|
+
# For example, [:defaultLibrary] will be encoded as
|
|
188
|
+
# 0b1000000000, as :defaultLibrary is the 10th bit according
|
|
189
189
|
# to the token modifiers index map.
|
|
190
190
|
#: (Array[Integer] modifiers) -> Integer
|
|
191
191
|
def encode_modifiers(modifiers)
|