ruby-lsp 0.18.4 → 0.19.0
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/lib/core_ext/uri.rb +9 -4
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +66 -8
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +56 -32
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +3 -2
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +18 -4
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +262 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +11 -0
- data/lib/ruby_indexer/test/constant_test.rb +8 -0
- data/lib/ruby_indexer/test/enhancements_test.rb +2 -0
- data/lib/ruby_indexer/test/instance_variables_test.rb +12 -0
- data/lib/ruby_indexer/test/method_test.rb +10 -0
- data/lib/ruby_indexer/test/reference_finder_test.rb +86 -0
- data/lib/ruby_lsp/addon.rb +69 -7
- data/lib/ruby_lsp/erb_document.rb +3 -2
- data/lib/ruby_lsp/global_state.rb +8 -0
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/completion.rb +1 -1
- data/lib/ruby_lsp/listeners/hover.rb +19 -0
- data/lib/ruby_lsp/requests/completion_resolve.rb +29 -0
- data/lib/ruby_lsp/requests/rename.rb +189 -0
- data/lib/ruby_lsp/requests/support/source_uri.rb +8 -1
- data/lib/ruby_lsp/server.rb +35 -8
- data/lib/ruby_lsp/static_docs.rb +15 -0
- data/lib/ruby_lsp/store.rb +12 -0
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +6 -1
- data/static_docs/yield.md +81 -0
- metadata +18 -7
@@ -40,6 +40,8 @@ module RubyLsp
|
|
40
40
|
# For example, forgetting to return the `insertText` included in the original item will make the editor use the
|
41
41
|
# `label` for the text edit instead
|
42
42
|
label = @item[:label].dup
|
43
|
+
return keyword_resolve(@item) if @item.dig(:data, :keyword)
|
44
|
+
|
43
45
|
entries = @index[label] || []
|
44
46
|
|
45
47
|
owner_name = @item.dig(:data, :owner_name)
|
@@ -72,6 +74,33 @@ module RubyLsp
|
|
72
74
|
|
73
75
|
@item
|
74
76
|
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
sig { params(item: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
81
|
+
def keyword_resolve(item)
|
82
|
+
keyword = item[:label]
|
83
|
+
content = KEYWORD_DOCS[keyword]
|
84
|
+
|
85
|
+
if content
|
86
|
+
doc_path = File.join(STATIC_DOCS_PATH, "#{keyword}.md")
|
87
|
+
|
88
|
+
@item[:documentation] = Interface::MarkupContent.new(
|
89
|
+
kind: "markdown",
|
90
|
+
value: <<~MARKDOWN.chomp,
|
91
|
+
```ruby
|
92
|
+
#{keyword}
|
93
|
+
```
|
94
|
+
|
95
|
+
[Read more](#{doc_path})
|
96
|
+
|
97
|
+
#{content}
|
98
|
+
MARKDOWN
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
item
|
103
|
+
end
|
75
104
|
end
|
76
105
|
end
|
77
106
|
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
module Requests
|
6
|
+
# The
|
7
|
+
# [rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_rename)
|
8
|
+
# request renames all instances of a symbol in a document.
|
9
|
+
class Rename < Request
|
10
|
+
extend T::Sig
|
11
|
+
include Support::Common
|
12
|
+
|
13
|
+
class InvalidNameError < StandardError; end
|
14
|
+
|
15
|
+
sig do
|
16
|
+
params(
|
17
|
+
global_state: GlobalState,
|
18
|
+
store: Store,
|
19
|
+
document: T.any(RubyDocument, ERBDocument),
|
20
|
+
params: T::Hash[Symbol, T.untyped],
|
21
|
+
).void
|
22
|
+
end
|
23
|
+
def initialize(global_state, store, document, params)
|
24
|
+
super()
|
25
|
+
@global_state = global_state
|
26
|
+
@store = store
|
27
|
+
@document = document
|
28
|
+
@position = T.let(params[:position], T::Hash[Symbol, Integer])
|
29
|
+
@new_name = T.let(params[:newName], String)
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { override.returns(T.nilable(Interface::WorkspaceEdit)) }
|
33
|
+
def perform
|
34
|
+
char_position = @document.create_scanner.find_char_position(@position)
|
35
|
+
|
36
|
+
node_context = RubyDocument.locate(
|
37
|
+
@document.parse_result.value,
|
38
|
+
char_position,
|
39
|
+
node_types: [Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode],
|
40
|
+
)
|
41
|
+
target = node_context.node
|
42
|
+
parent = node_context.parent
|
43
|
+
return if !target || target.is_a?(Prism::ProgramNode)
|
44
|
+
|
45
|
+
if target.is_a?(Prism::ConstantReadNode) && parent.is_a?(Prism::ConstantPathNode)
|
46
|
+
target = determine_target(
|
47
|
+
target,
|
48
|
+
parent,
|
49
|
+
@position,
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
target = T.cast(
|
54
|
+
target,
|
55
|
+
T.any(Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::ConstantPathTargetNode),
|
56
|
+
)
|
57
|
+
|
58
|
+
name = constant_name(target)
|
59
|
+
return unless name
|
60
|
+
|
61
|
+
entries = @global_state.index.resolve(name, node_context.nesting)
|
62
|
+
return unless entries
|
63
|
+
|
64
|
+
if (conflict_entries = @global_state.index.resolve(@new_name, node_context.nesting))
|
65
|
+
raise InvalidNameError, "The new name is already in use by #{T.must(conflict_entries.first).name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
fully_qualified_name = T.must(entries.first).name
|
69
|
+
changes = collect_text_edits(fully_qualified_name, name)
|
70
|
+
|
71
|
+
# If the client doesn't support resource operations, such as renaming files, then we can only return the basic
|
72
|
+
# text changes
|
73
|
+
unless @global_state.supported_resource_operations.include?("rename")
|
74
|
+
return Interface::WorkspaceEdit.new(changes: changes)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Text edits must be applied before any resource operations, such as renaming files. Otherwise, the file is
|
78
|
+
# renamed and then the URI associated to the text edit no longer exists, causing it to be dropped
|
79
|
+
document_changes = changes.map do |uri, edits|
|
80
|
+
Interface::TextDocumentEdit.new(
|
81
|
+
text_document: Interface::VersionedTextDocumentIdentifier.new(uri: uri, version: nil),
|
82
|
+
edits: edits,
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
collect_file_renames(fully_qualified_name, document_changes)
|
87
|
+
Interface::WorkspaceEdit.new(document_changes: document_changes)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
sig do
|
93
|
+
params(
|
94
|
+
fully_qualified_name: String,
|
95
|
+
document_changes: T::Array[T.any(Interface::RenameFile, Interface::TextDocumentEdit)],
|
96
|
+
).void
|
97
|
+
end
|
98
|
+
def collect_file_renames(fully_qualified_name, document_changes)
|
99
|
+
# Check if the declarations of the symbol being renamed match the file name. In case they do, we automatically
|
100
|
+
# rename the files for the user.
|
101
|
+
#
|
102
|
+
# We also look for an associated test file and rename it too
|
103
|
+
short_name = T.must(fully_qualified_name.split("::").last)
|
104
|
+
|
105
|
+
T.must(@global_state.index[fully_qualified_name]).each do |entry|
|
106
|
+
# Do not rename files that are not part of the workspace
|
107
|
+
next unless entry.file_path.start_with?(@global_state.workspace_path)
|
108
|
+
|
109
|
+
case entry
|
110
|
+
when RubyIndexer::Entry::Class, RubyIndexer::Entry::Module, RubyIndexer::Entry::Constant,
|
111
|
+
RubyIndexer::Entry::ConstantAlias, RubyIndexer::Entry::UnresolvedConstantAlias
|
112
|
+
|
113
|
+
file_name = file_from_constant_name(short_name)
|
114
|
+
|
115
|
+
if "#{file_name}.rb" == entry.file_name
|
116
|
+
new_file_name = file_from_constant_name(T.must(@new_name.split("::").last))
|
117
|
+
|
118
|
+
old_uri = URI::Generic.from_path(path: entry.file_path).to_s
|
119
|
+
new_uri = URI::Generic.from_path(path: File.join(
|
120
|
+
File.dirname(entry.file_path),
|
121
|
+
"#{new_file_name}.rb",
|
122
|
+
)).to_s
|
123
|
+
|
124
|
+
document_changes << Interface::RenameFile.new(kind: "rename", old_uri: old_uri, new_uri: new_uri)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
sig { params(fully_qualified_name: String, name: String).returns(T::Hash[String, T::Array[Interface::TextEdit]]) }
|
131
|
+
def collect_text_edits(fully_qualified_name, name)
|
132
|
+
changes = {}
|
133
|
+
|
134
|
+
Dir.glob(File.join(@global_state.workspace_path, "**/*.rb")).each do |path|
|
135
|
+
uri = URI::Generic.from_path(path: path)
|
136
|
+
# If the document is being managed by the client, then we should use whatever is present in the store instead
|
137
|
+
# of reading from disk
|
138
|
+
next if @store.key?(uri)
|
139
|
+
|
140
|
+
parse_result = Prism.parse_file(path)
|
141
|
+
edits = collect_changes(fully_qualified_name, parse_result, name, uri)
|
142
|
+
changes[uri.to_s] = edits unless edits.empty?
|
143
|
+
end
|
144
|
+
|
145
|
+
@store.each do |uri, document|
|
146
|
+
edits = collect_changes(fully_qualified_name, document.parse_result, name, document.uri)
|
147
|
+
changes[uri] = edits unless edits.empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
changes
|
151
|
+
end
|
152
|
+
|
153
|
+
sig do
|
154
|
+
params(
|
155
|
+
fully_qualified_name: String,
|
156
|
+
parse_result: Prism::ParseResult,
|
157
|
+
name: String,
|
158
|
+
uri: URI::Generic,
|
159
|
+
).returns(T::Array[Interface::TextEdit])
|
160
|
+
end
|
161
|
+
def collect_changes(fully_qualified_name, parse_result, name, uri)
|
162
|
+
dispatcher = Prism::Dispatcher.new
|
163
|
+
finder = RubyIndexer::ReferenceFinder.new(fully_qualified_name, @global_state.index, dispatcher)
|
164
|
+
dispatcher.visit(parse_result.value)
|
165
|
+
|
166
|
+
finder.references.uniq(&:location).map do |reference|
|
167
|
+
adjust_reference_for_edit(name, reference)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
sig { params(name: String, reference: RubyIndexer::ReferenceFinder::Reference).returns(Interface::TextEdit) }
|
172
|
+
def adjust_reference_for_edit(name, reference)
|
173
|
+
# The reference may include a namespace in front. We need to check if the rename new name includes namespaces
|
174
|
+
# and then adjust both the text and the location to produce the correct edit
|
175
|
+
location = reference.location
|
176
|
+
new_text = reference.name.sub(name, @new_name)
|
177
|
+
|
178
|
+
Interface::TextEdit.new(range: range_from_location(location), new_text: new_text)
|
179
|
+
end
|
180
|
+
|
181
|
+
sig { params(constant_name: String).returns(String) }
|
182
|
+
def file_from_constant_name(constant_name)
|
183
|
+
constant_name
|
184
|
+
.gsub(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/, '\1\3_\2\4')
|
185
|
+
.downcase
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -19,6 +19,13 @@ module URI
|
|
19
19
|
T::Array[Symbol],
|
20
20
|
)
|
21
21
|
|
22
|
+
# `uri` for Ruby 3.4 switched the default parser from RFC2396 to RFC3986. The new parser emits a deprecation
|
23
|
+
# warning on a few methods and delegates them to RFC2396, namely `extract`/`make_regexp`/`escape`/`unescape`.
|
24
|
+
# On earlier versions of the uri gem, the RFC2396_PARSER constant doesn't exist, so it needs some special
|
25
|
+
# handling to select a parser that doesn't emit deprecations. While it was backported to Ruby 3.1, users may
|
26
|
+
# have the uri gem in their own bundle and thus not use a compatible version.
|
27
|
+
PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
|
28
|
+
|
22
29
|
T.unsafe(self).alias_method(:gem_name, :host)
|
23
30
|
T.unsafe(self).alias_method(:line_number, :fragment)
|
24
31
|
|
@@ -41,7 +48,7 @@ module URI
|
|
41
48
|
{
|
42
49
|
scheme: "source",
|
43
50
|
host: gem_name,
|
44
|
-
path:
|
51
|
+
path: PARSER.escape("/#{gem_version}/#{path}"),
|
45
52
|
fragment: line_number,
|
46
53
|
}
|
47
54
|
)
|
data/lib/ruby_lsp/server.rb
CHANGED
@@ -67,6 +67,8 @@ module RubyLsp
|
|
67
67
|
text_document_definition(message)
|
68
68
|
when "textDocument/prepareTypeHierarchy"
|
69
69
|
text_document_prepare_type_hierarchy(message)
|
70
|
+
when "textDocument/rename"
|
71
|
+
text_document_rename(message)
|
70
72
|
when "typeHierarchy/supertypes"
|
71
73
|
type_hierarchy_supertypes(message)
|
72
74
|
when "typeHierarchy/subtypes"
|
@@ -123,9 +125,9 @@ module RubyLsp
|
|
123
125
|
send_log_message("Error processing #{message[:method]}: #{e.full_message}", type: Constant::MessageType::ERROR)
|
124
126
|
end
|
125
127
|
|
126
|
-
sig { void }
|
127
|
-
def load_addons
|
128
|
-
errors = Addon.load_addons(@global_state, @outgoing_queue)
|
128
|
+
sig { params(include_project_addons: T::Boolean).void }
|
129
|
+
def load_addons(include_project_addons: true)
|
130
|
+
errors = Addon.load_addons(@global_state, @outgoing_queue, include_project_addons: include_project_addons)
|
129
131
|
|
130
132
|
if errors.any?
|
131
133
|
send_log_message(
|
@@ -227,6 +229,7 @@ module RubyLsp
|
|
227
229
|
workspace_symbol_provider: enabled_features["workspaceSymbol"] && !@global_state.has_type_checker,
|
228
230
|
signature_help_provider: signature_help_provider,
|
229
231
|
type_hierarchy_provider: type_hierarchy_provider,
|
232
|
+
rename_provider: !@global_state.has_type_checker,
|
230
233
|
experimental: {
|
231
234
|
addon_detection: true,
|
232
235
|
},
|
@@ -320,14 +323,18 @@ module RubyLsp
|
|
320
323
|
language_id: language_id,
|
321
324
|
)
|
322
325
|
|
323
|
-
if document.past_expensive_limit?
|
326
|
+
if document.past_expensive_limit? && text_document[:uri].scheme == "file"
|
327
|
+
log_message = <<~MESSAGE
|
328
|
+
The file #{text_document[:uri].path} is too long. For performance reasons, semantic highlighting and
|
329
|
+
diagnostics will be disabled.
|
330
|
+
MESSAGE
|
331
|
+
|
324
332
|
send_message(
|
325
333
|
Notification.new(
|
326
|
-
method: "window/
|
327
|
-
params: Interface::
|
334
|
+
method: "window/logMessage",
|
335
|
+
params: Interface::LogMessageParams.new(
|
328
336
|
type: Constant::MessageType::WARNING,
|
329
|
-
message:
|
330
|
-
"diagnostics will be disabled",
|
337
|
+
message: log_message,
|
331
338
|
),
|
332
339
|
),
|
333
340
|
)
|
@@ -609,6 +616,26 @@ module RubyLsp
|
|
609
616
|
)
|
610
617
|
end
|
611
618
|
|
619
|
+
sig { params(message: T::Hash[Symbol, T.untyped]).void }
|
620
|
+
def text_document_rename(message)
|
621
|
+
params = message[:params]
|
622
|
+
document = @store.get(params.dig(:textDocument, :uri))
|
623
|
+
|
624
|
+
unless document.is_a?(RubyDocument)
|
625
|
+
send_empty_response(message[:id])
|
626
|
+
return
|
627
|
+
end
|
628
|
+
|
629
|
+
send_message(
|
630
|
+
Result.new(
|
631
|
+
id: message[:id],
|
632
|
+
response: Requests::Rename.new(@global_state, @store, document, params).perform,
|
633
|
+
),
|
634
|
+
)
|
635
|
+
rescue Requests::Rename::InvalidNameError => e
|
636
|
+
send_message(Error.new(id: message[:id], code: Constant::ErrorCodes::REQUEST_FAILED, message: e.message))
|
637
|
+
end
|
638
|
+
|
612
639
|
sig { params(document: Document[T.untyped]).returns(RubyDocument::SorbetLevel) }
|
613
640
|
def sorbet_level(document)
|
614
641
|
return RubyDocument::SorbetLevel::Ignore unless @global_state.has_type_checker
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyLsp
|
5
|
+
# The path to the `static_docs` directory, where we keep long-form static documentation
|
6
|
+
STATIC_DOCS_PATH = T.let(File.join(File.dirname(File.dirname(T.must(__dir__))), "static_docs"), String)
|
7
|
+
|
8
|
+
# A map of keyword => short documentation to be displayed on hover or completion
|
9
|
+
KEYWORD_DOCS = T.let(
|
10
|
+
{
|
11
|
+
"yield" => "Invokes the passed block with the given arguments",
|
12
|
+
}.freeze,
|
13
|
+
T::Hash[String, String],
|
14
|
+
)
|
15
|
+
end
|
data/lib/ruby_lsp/store.rb
CHANGED
@@ -99,6 +99,18 @@ module RubyLsp
|
|
99
99
|
@state.delete(uri.to_s)
|
100
100
|
end
|
101
101
|
|
102
|
+
sig { params(uri: URI::Generic).returns(T::Boolean) }
|
103
|
+
def key?(uri)
|
104
|
+
@state.key?(uri.to_s)
|
105
|
+
end
|
106
|
+
|
107
|
+
sig { params(block: T.proc.params(uri: String, document: Document[T.untyped]).void).void }
|
108
|
+
def each(&block)
|
109
|
+
@state.each do |uri, document|
|
110
|
+
block.call(uri, document)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
102
114
|
sig do
|
103
115
|
type_parameters(:T)
|
104
116
|
.params(
|
data/lib/ruby_lsp/test_helper.rb
CHANGED
@@ -42,7 +42,7 @@ module RubyLsp
|
|
42
42
|
RubyIndexer::IndexablePath.new(nil, T.must(uri.to_standardized_path)),
|
43
43
|
source,
|
44
44
|
)
|
45
|
-
server.load_addons if load_addons
|
45
|
+
server.load_addons(include_project_addons: false) if load_addons
|
46
46
|
block.call(server, uri)
|
47
47
|
ensure
|
48
48
|
if load_addons
|
@@ -89,7 +89,12 @@ module RubyLsp
|
|
89
89
|
|
90
90
|
Type.new("#{parts.join("::")}::#{last}::<Class:#{last}>")
|
91
91
|
else
|
92
|
-
|
92
|
+
|
93
|
+
raw_receiver = if receiver.is_a?(Prism::CallNode)
|
94
|
+
receiver.message
|
95
|
+
else
|
96
|
+
receiver&.slice
|
97
|
+
end
|
93
98
|
|
94
99
|
if raw_receiver
|
95
100
|
guessed_name = raw_receiver
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Yield
|
2
|
+
|
3
|
+
In Ruby, every method implicitly accepts a block, even when not included in the parameters list.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
def foo
|
7
|
+
end
|
8
|
+
|
9
|
+
foo { 123 } # works!
|
10
|
+
```
|
11
|
+
|
12
|
+
The `yield` keyword is used to invoke the block that was passed with arguments.
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Consider this method call. The block being passed to the method `foo` accepts an argument called `a`.
|
16
|
+
# It then takes whatever argument was passed and multiplies it by 2
|
17
|
+
foo do |a|
|
18
|
+
a * 2
|
19
|
+
end
|
20
|
+
|
21
|
+
# In the `foo` method declaration, we can use `yield` to invoke the block that was passed and provide the block
|
22
|
+
# with the value for the `a` argument
|
23
|
+
def foo
|
24
|
+
# Invoke the block passed to `foo` with the number 10 as the argument `a`
|
25
|
+
result = yield(10)
|
26
|
+
puts result # Will print 20
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
If `yield` is used to invoke the block, but no block was passed, that will result in a local jump error.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# If we invoke `foo` without a block, trying to `yield` will fail
|
34
|
+
foo
|
35
|
+
|
36
|
+
# `foo': no block given (yield) (LocalJumpError)
|
37
|
+
```
|
38
|
+
|
39
|
+
We can decide to use `yield` conditionally by using Ruby's `block_given?` method, which will return `true` if a block
|
40
|
+
was passed to the method.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
def foo
|
44
|
+
# If a block is passed when invoking `foo`, call the block with argument 10 and print the result.
|
45
|
+
# Otherwise, just print that no block was passed
|
46
|
+
if block_given?
|
47
|
+
result = yield(10)
|
48
|
+
puts result
|
49
|
+
else
|
50
|
+
puts "No block passed!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
foo do |a|
|
55
|
+
a * 2
|
56
|
+
end
|
57
|
+
# => 20
|
58
|
+
|
59
|
+
foo
|
60
|
+
# => No block passed!
|
61
|
+
```
|
62
|
+
|
63
|
+
## Block parameter
|
64
|
+
|
65
|
+
In addition to implicit blocks, Ruby also allows developers to use explicit block parameters as part of the method's
|
66
|
+
signature. In this scenario, we can use the reference to the block directly instead of relying on the `yield` keyword.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# Block parameters are prefixed with & and a name
|
70
|
+
def foo(&my_block_param)
|
71
|
+
# If a block was passed to `foo`, `my_block_param` will be a `Proc` object. Otherwise, it will be `nil`. We can use
|
72
|
+
# that to check for its presence
|
73
|
+
if my_block_param
|
74
|
+
# Explicit block parameters are invoked using the method `call`, which is present in all `Proc` objects
|
75
|
+
result = my_block_param.call(10)
|
76
|
+
puts result
|
77
|
+
else
|
78
|
+
puts "No block passed!"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-lsp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: language_server-protocol
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: prism
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
- - "<"
|
32
35
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
36
|
+
version: '2.0'
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.1'
|
44
|
+
- - "<"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
46
|
+
version: '2.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: rbs
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,6 +105,7 @@ files:
|
|
99
105
|
- lib/ruby_indexer/lib/ruby_indexer/location.rb
|
100
106
|
- lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb
|
101
107
|
- lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
|
108
|
+
- lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb
|
102
109
|
- lib/ruby_indexer/ruby_indexer.rb
|
103
110
|
- lib/ruby_indexer/test/classes_and_modules_test.rb
|
104
111
|
- lib/ruby_indexer/test/configuration_test.rb
|
@@ -109,6 +116,7 @@ files:
|
|
109
116
|
- lib/ruby_indexer/test/method_test.rb
|
110
117
|
- lib/ruby_indexer/test/prefix_tree_test.rb
|
111
118
|
- lib/ruby_indexer/test/rbs_indexer_test.rb
|
119
|
+
- lib/ruby_indexer/test/reference_finder_test.rb
|
112
120
|
- lib/ruby_indexer/test/test_case.rb
|
113
121
|
- lib/ruby_lsp/addon.rb
|
114
122
|
- lib/ruby_lsp/base_server.rb
|
@@ -146,6 +154,7 @@ files:
|
|
146
154
|
- lib/ruby_lsp/requests/inlay_hints.rb
|
147
155
|
- lib/ruby_lsp/requests/on_type_formatting.rb
|
148
156
|
- lib/ruby_lsp/requests/prepare_type_hierarchy.rb
|
157
|
+
- lib/ruby_lsp/requests/rename.rb
|
149
158
|
- lib/ruby_lsp/requests/request.rb
|
150
159
|
- lib/ruby_lsp/requests/selection_ranges.rb
|
151
160
|
- lib/ruby_lsp/requests/semantic_highlighting.rb
|
@@ -173,10 +182,12 @@ files:
|
|
173
182
|
- lib/ruby_lsp/scope.rb
|
174
183
|
- lib/ruby_lsp/server.rb
|
175
184
|
- lib/ruby_lsp/setup_bundler.rb
|
185
|
+
- lib/ruby_lsp/static_docs.rb
|
176
186
|
- lib/ruby_lsp/store.rb
|
177
187
|
- lib/ruby_lsp/test_helper.rb
|
178
188
|
- lib/ruby_lsp/type_inferrer.rb
|
179
189
|
- lib/ruby_lsp/utils.rb
|
190
|
+
- static_docs/yield.md
|
180
191
|
homepage: https://github.com/Shopify/ruby-lsp
|
181
192
|
licenses:
|
182
193
|
- MIT
|
@@ -198,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
209
|
- !ruby/object:Gem::Version
|
199
210
|
version: '0'
|
200
211
|
requirements: []
|
201
|
-
rubygems_version: 3.5.
|
212
|
+
rubygems_version: 3.5.20
|
202
213
|
signing_key:
|
203
214
|
specification_version: 4
|
204
215
|
summary: An opinionated language server for Ruby
|