Skip to content

Commit 415a8ff

Browse files
committed
Add 'defined?' keyword
1 parent 481292d commit 415a8ff

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

lib/ruby_lsp/listeners/hover.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Hover
1515
Prism::ConstantWriteNode,
1616
Prism::ConstantPathNode,
1717
Prism::DefNode,
18+
Prism::DefinedNode,
1819
Prism::GlobalVariableAndWriteNode,
1920
Prism::GlobalVariableOperatorWriteNode,
2021
Prism::GlobalVariableOrWriteNode,
@@ -66,6 +67,7 @@ def initialize(response_builder, global_state, uri, node_context, dispatcher, so
6667
:on_constant_path_node_enter,
6768
:on_call_node_enter,
6869
:on_def_node_enter,
70+
:on_defined_node_enter,
6971
:on_global_variable_and_write_node_enter,
7072
:on_global_variable_operator_write_node_enter,
7173
:on_global_variable_or_write_node_enter,
@@ -125,6 +127,11 @@ def on_def_node_enter(node)
125127
handle_keyword_documentation(node.def_keyword)
126128
end
127129

130+
#: (Prism::DefinedNode node) -> void
131+
def on_defined_node_enter(node)
132+
handle_keyword_documentation(node.keyword)
133+
end
134+
128135
#: (Prism::InterpolatedStringNode node) -> void
129136
def on_interpolated_string_node_enter(node)
130137
generate_heredoc_hover(node)

lib/ruby_lsp/static_docs.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module RubyLsp
1818
"case" => "Starts a case expression for pattern matching or multiple condition checking",
1919
"class" => "Defines a class and its methods",
2020
"def" => "Defines a method",
21+
"defined?" => "Checks if a constant or method is defined",
2122
"yield" => "Invokes the passed block with the given arguments",
2223
}.freeze #: Hash[String, String]
2324
end

static_docs/defined?.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Defined?
2+
3+
In Ruby, the `defined?` keyword is a special operator that checks whether a given expression is defined and returns a description of that expression, or `nil` if the expression is not defined.
4+
5+
```ruby
6+
# Basic defined? usage
7+
x = 42
8+
puts defined?(x) # Output: local-variable
9+
puts defined?(y) # Output: nil
10+
puts defined?(puts) # Output: method
11+
```
12+
13+
The `defined?` operator can check various types of expressions and returns different description strings based on the type.
14+
15+
```ruby
16+
# Checking different types
17+
class Example
18+
CONSTANT = "Hello"
19+
@@class_var = "World"
20+
21+
def check_definitions
22+
@instance_var = "!"
23+
24+
puts defined?(CONSTANT) # Output: constant
25+
puts defined?(@@class_var) # Output: class variable
26+
puts defined?(@instance_var) # Output: instance-variable
27+
puts defined?(yield) # Output: yield (if block given)
28+
puts defined?(super) # Output: super (if method has super)
29+
end
30+
end
31+
32+
example = Example.new
33+
puts defined?(Example) # Output: constant
34+
puts defined?(String) # Output: constant
35+
puts defined?("string") # Output: expression
36+
```
37+
38+
## Common Use Cases
39+
40+
The `defined?` operator is often used for safe navigation and checking existence before execution.
41+
42+
```ruby
43+
def safe_operation(value)
44+
return "No block given" unless defined?(yield)
45+
46+
if defined?(value.length)
47+
"Length is #{value.length}"
48+
else
49+
"Cannot determine length"
50+
end
51+
end
52+
53+
puts safe_operation([1, 2, 3]) { |x| x * 2 } # Output: Length is 3
54+
puts safe_operation(42) { |x| x * 2 } # Output: Cannot determine length
55+
puts safe_operation([1, 2, 3]) # Output: No block given
56+
```
57+
58+
## Method and Block Checking
59+
60+
`defined?` is particularly useful for checking method existence and block presence.
61+
62+
```ruby
63+
class SafeCaller
64+
def execute
65+
if defined?(before_execute)
66+
before_execute
67+
end
68+
69+
puts "Executing main logic"
70+
71+
if defined?(after_execute)
72+
after_execute
73+
end
74+
end
75+
76+
def after_execute
77+
puts "After execution"
78+
end
79+
end
80+
81+
caller = SafeCaller.new
82+
caller.execute
83+
# Output:
84+
# Executing main logic
85+
# After execution
86+
87+
# Block checking
88+
def process_with_block
89+
if defined?(yield)
90+
"Block given: #{yield}"
91+
else
92+
"No block given"
93+
end
94+
end
95+
96+
puts process_with_block { "Hello!" } # Output: Block given: Hello!
97+
puts process_with_block # Output: No block given
98+
```
99+
100+
The `defined?` operator is a powerful tool for writing defensive code and handling optional features or dependencies in Ruby programs.

0 commit comments

Comments
 (0)