Skip to content

Commit ab52713

Browse files
committed
Add 'ensure' keyword
1 parent bf83dcd commit ab52713

File tree

4 files changed

+101
-0
lines changed

4 files changed

+101
-0
lines changed

lib/ruby_lsp/listeners/hover.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Hover
1717
Prism::DefNode,
1818
Prism::DefinedNode,
1919
Prism::ElseNode,
20+
Prism::EnsureNode,
2021
Prism::GlobalVariableAndWriteNode,
2122
Prism::GlobalVariableOperatorWriteNode,
2223
Prism::GlobalVariableOrWriteNode,
@@ -70,6 +71,7 @@ def initialize(response_builder, global_state, uri, node_context, dispatcher, so
7071
:on_def_node_enter,
7172
:on_defined_node_enter,
7273
:on_else_node_enter,
74+
:on_ensure_node_enter,
7375
:on_global_variable_and_write_node_enter,
7476
:on_global_variable_operator_write_node_enter,
7577
:on_global_variable_or_write_node_enter,
@@ -139,6 +141,11 @@ def on_else_node_enter(node)
139141
handle_keyword_documentation(node.else_keyword)
140142
end
141143

144+
#: (Prism::EnsureNode node) -> void
145+
def on_ensure_node_enter(node)
146+
handle_keyword_documentation(node.ensure_keyword)
147+
end
148+
142149
#: (Prism::InterpolatedStringNode node) -> void
143150
def on_interpolated_string_node_enter(node)
144151
generate_heredoc_hover(node)

lib/ruby_lsp/static_docs.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module RubyLsp
2020
"def" => "Defines a method",
2121
"defined" => "Checks if a constant or method is defined",
2222
"else" => "Executes the code in the else block if the condition is false",
23+
"ensure" => "Executes the code in the ensure block regardless of whether an exception is raised or not",
2324
"yield" => "Invokes the passed block with the given arguments",
2425
}.freeze #: Hash[String, String]
2526
end

static_docs/ensure.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Ensure
2+
3+
In Ruby, the `ensure` keyword is used to define a block of code that will always execute, regardless of whether an exception was raised or not. It's commonly used for cleanup operations like closing files or network connections.
4+
5+
```ruby
6+
# Basic ensure usage
7+
file = File.open("example.txt")
8+
begin
9+
content = file.read
10+
rescue StandardError => e
11+
puts "Error reading file: #{e.message}"
12+
ensure
13+
file.close # Always executes
14+
end
15+
```
16+
17+
The `ensure` clause can be used with or without `rescue` blocks, and it will execute even if there's a return statement in the main block.
18+
19+
```ruby
20+
def process_data
21+
connection = Database.connect
22+
begin
23+
return connection.query("SELECT * FROM users")
24+
ensure
25+
connection.close # Executes even with the return statement
26+
end
27+
end
28+
29+
# Without rescue clause
30+
def write_log(message)
31+
file = File.open("log.txt", "a")
32+
begin
33+
file.puts(message)
34+
ensure
35+
file.close
36+
end
37+
end
38+
```
39+
40+
## Multiple Rescue Clauses
41+
42+
When using multiple `rescue` clauses, the `ensure` block always comes last and executes regardless of which `rescue` clause is triggered.
43+
44+
```ruby
45+
def perform_operation
46+
begin
47+
# Main operation
48+
result = dangerous_operation
49+
rescue ArgumentError => e
50+
puts "Invalid arguments: #{e.message}"
51+
rescue StandardError => e
52+
puts "Other error: #{e.message}"
53+
ensure
54+
# Cleanup code always runs
55+
cleanup_resources
56+
end
57+
end
58+
```
59+
60+
## Implicit Begin Blocks
61+
62+
In methods and class definitions, you can use `ensure` without an explicit `begin` block.
63+
64+
```ruby
65+
def process_file(path)
66+
file = File.open(path)
67+
file.read # If this raises an error, ensure still executes
68+
ensure
69+
file&.close # Using safe navigation operator in case file is nil
70+
end
71+
72+
class DataProcessor
73+
def initialize
74+
@connection = Database.connect
75+
rescue StandardError => e
76+
puts "Failed to connect: #{e.message}"
77+
ensure
78+
puts "Initialization complete"
79+
end
80+
end
81+
```
82+
83+
The `ensure` keyword is essential for writing robust Ruby code that properly manages resources and handles cleanup operations, regardless of whether exceptions occur.

test/requests/hover_expectations_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,16 @@ def my_method
962962
RUBY
963963
position: { line: 2, character: 2 },
964964
},
965+
"ensure" => {
966+
source: <<~RUBY,
967+
begin
968+
true
969+
ensure
970+
false
971+
end
972+
RUBY
973+
position: { line: 2, character: 2 },
974+
},
965975
}
966976

967977
test_cases.each do |keyword, config|

0 commit comments

Comments
 (0)