@@ -6,45 +6,13 @@ module Listeners
66 class Hover
77 include Requests ::Support ::Common
88
9- ALLOWED_TARGETS = [
10- Prism ::BreakNode ,
11- Prism ::CallNode ,
12- Prism ::ConstantReadNode ,
13- Prism ::ConstantWriteNode ,
14- Prism ::ConstantPathNode ,
15- Prism ::GlobalVariableAndWriteNode ,
16- Prism ::GlobalVariableOperatorWriteNode ,
17- Prism ::GlobalVariableOrWriteNode ,
18- Prism ::GlobalVariableReadNode ,
19- Prism ::GlobalVariableTargetNode ,
20- Prism ::GlobalVariableWriteNode ,
21- Prism ::InstanceVariableReadNode ,
22- Prism ::InstanceVariableAndWriteNode ,
23- Prism ::InstanceVariableOperatorWriteNode ,
24- Prism ::InstanceVariableOrWriteNode ,
25- Prism ::InstanceVariableTargetNode ,
26- Prism ::InstanceVariableWriteNode ,
27- Prism ::SymbolNode ,
28- Prism ::StringNode ,
29- Prism ::InterpolatedStringNode ,
30- Prism ::SuperNode ,
31- Prism ::ForwardingSuperNode ,
32- Prism ::YieldNode ,
33- Prism ::ClassVariableAndWriteNode ,
34- Prism ::ClassVariableOperatorWriteNode ,
35- Prism ::ClassVariableOrWriteNode ,
36- Prism ::ClassVariableReadNode ,
37- Prism ::ClassVariableTargetNode ,
38- Prism ::ClassVariableWriteNode ,
39- ] #: Array[singleton(Prism::Node)]
40-
419 ALLOWED_REMOTE_PROVIDERS = [
4210 "https://github.com" ,
4311 "https://gitlab.com" ,
4412 ] . freeze #: Array[String]
4513
46- #: (ResponseBuilders::Hover response_builder, GlobalState global_state, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
47- def initialize ( response_builder , global_state , uri , node_context , dispatcher , sorbet_level ) # rubocop:disable Metrics/ParameterLists
14+ #: (ResponseBuilders::Hover response_builder, GlobalState global_state, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level, Hash[Symbol, untyped] position ) -> void
15+ def initialize ( response_builder , global_state , uri , node_context , dispatcher , sorbet_level , position ) # rubocop:disable Metrics/ParameterLists
4816 @response_builder = response_builder
4917 @global_state = global_state
5018 @index = global_state . index #: RubyIndexer::Index
@@ -53,45 +21,80 @@ def initialize(response_builder, global_state, uri, node_context, dispatcher, so
5321 @path = uri . to_standardized_path #: String?
5422 @node_context = node_context
5523 @sorbet_level = sorbet_level
24+ @position = position
5625
5726 dispatcher . register (
5827 self ,
28+ :on_alias_global_variable_node_enter ,
29+ :on_alias_method_node_enter ,
30+ :on_and_node_enter ,
31+ :on_begin_node_enter ,
32+ :on_block_node_enter ,
5933 :on_break_node_enter ,
34+ :on_call_node_enter ,
35+ :on_case_match_node_enter ,
36+ :on_case_node_enter ,
37+ :on_class_node_enter ,
38+ :on_singleton_class_node_enter ,
39+ :on_lambda_node_enter ,
40+ :on_class_variable_and_write_node_enter ,
41+ :on_class_variable_operator_write_node_enter ,
42+ :on_class_variable_or_write_node_enter ,
43+ :on_class_variable_read_node_enter ,
44+ :on_class_variable_target_node_enter ,
45+ :on_class_variable_write_node_enter ,
46+ :on_constant_path_node_enter ,
6047 :on_constant_read_node_enter ,
6148 :on_constant_write_node_enter ,
62- :on_constant_path_node_enter ,
63- :on_call_node_enter ,
49+ :on_def_node_enter ,
50+ :on_defined_node_enter ,
51+ :on_else_node_enter ,
52+ :on_ensure_node_enter ,
53+ :on_false_node_enter ,
54+ :on_for_node_enter ,
55+ :on_forwarding_super_node_enter ,
6456 :on_global_variable_and_write_node_enter ,
6557 :on_global_variable_operator_write_node_enter ,
6658 :on_global_variable_or_write_node_enter ,
6759 :on_global_variable_read_node_enter ,
6860 :on_global_variable_target_node_enter ,
6961 :on_global_variable_write_node_enter ,
70- :on_instance_variable_read_node_enter ,
71- :on_instance_variable_write_node_enter ,
62+ :on_if_node_enter ,
63+ :on_in_node_enter ,
7264 :on_instance_variable_and_write_node_enter ,
7365 :on_instance_variable_operator_write_node_enter ,
7466 :on_instance_variable_or_write_node_enter ,
67+ :on_instance_variable_read_node_enter ,
7568 :on_instance_variable_target_node_enter ,
76- :on_super_node_enter ,
77- :on_forwarding_super_node_enter ,
78- :on_string_node_enter ,
69+ :on_instance_variable_write_node_enter ,
7970 :on_interpolated_string_node_enter ,
71+ :on_module_node_enter ,
72+ :on_next_node_enter ,
73+ :on_nil_node_enter ,
74+ :on_or_node_enter ,
75+ :on_post_execution_node_enter ,
76+ :on_pre_execution_node_enter ,
77+ :on_redo_node_enter ,
78+ :on_rescue_modifier_node_enter ,
79+ :on_rescue_node_enter ,
80+ :on_retry_node_enter ,
81+ :on_return_node_enter ,
82+ :on_self_node_enter ,
83+ :on_source_encoding_node_enter ,
84+ :on_source_file_node_enter ,
85+ :on_source_line_node_enter ,
86+ :on_string_node_enter ,
87+ :on_super_node_enter ,
88+ :on_true_node_enter ,
89+ :on_undef_node_enter ,
90+ :on_unless_node_enter ,
91+ :on_until_node_enter ,
92+ :on_when_node_enter ,
93+ :on_while_node_enter ,
8094 :on_yield_node_enter ,
81- :on_class_variable_and_write_node_enter ,
82- :on_class_variable_operator_write_node_enter ,
83- :on_class_variable_or_write_node_enter ,
84- :on_class_variable_read_node_enter ,
85- :on_class_variable_target_node_enter ,
86- :on_class_variable_write_node_enter ,
8795 )
8896 end
8997
90- #: (Prism::BreakNode node) -> void
91- def on_break_node_enter ( node )
92- handle_keyword_documentation ( node . keyword )
93- end
94-
9598 #: (Prism::StringNode node) -> void
9699 def on_string_node_enter ( node )
97100 if @path && File . basename ( @path ) == GEMFILE_NAME
@@ -144,6 +147,12 @@ def on_call_node_enter(node)
144147 message = node . message
145148 return unless message
146149
150+ # `not x` is parsed as a call to `!` whose message_loc slices to "not"
151+ if node . name == :! && message == "not"
152+ handle_keyword_documentation ( "not" )
153+ return
154+ end
155+
147156 handle_method_hover ( message )
148157 end
149158
@@ -209,19 +218,150 @@ def on_instance_variable_target_node_enter(node)
209218
210219 #: (Prism::SuperNode node) -> void
211220 def on_super_node_enter ( node )
212- handle_super_node_hover
221+ handle_super_node_hover ( node . keyword_loc )
213222 end
214223
215224 #: (Prism::ForwardingSuperNode node) -> void
216225 def on_forwarding_super_node_enter ( node )
217- handle_super_node_hover
226+ handle_super_node_hover ( node . location )
227+ end
228+
229+ #: (Prism::AliasGlobalVariableNode) -> void
230+ def on_alias_global_variable_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
231+
232+ #: (Prism::AliasMethodNode) -> void
233+ def on_alias_method_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
234+
235+ #: (Prism::AndNode) -> void
236+ def on_and_node_enter ( node ) = handle_keyword_at_location ( node . operator_loc )
237+
238+ #: (Prism::BeginNode) -> void
239+ def on_begin_node_enter ( node ) = handle_keyword_at_location ( node . begin_keyword_loc , node . end_keyword_loc )
240+
241+ #: (Prism::BlockNode) -> void
242+ def on_block_node_enter ( node ) = handle_keyword_at_location ( node . opening_loc , node . closing_loc )
243+
244+ #: (Prism::BreakNode) -> void
245+ def on_break_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
246+
247+ #: (Prism::CaseMatchNode) -> void
248+ def on_case_match_node_enter ( node ) = handle_keyword_at_location ( node . case_keyword_loc , node . end_keyword_loc )
249+
250+ #: (Prism::CaseNode) -> void
251+ def on_case_node_enter ( node ) = handle_keyword_at_location ( node . case_keyword_loc , node . end_keyword_loc )
252+
253+ #: (Prism::ClassNode) -> void
254+ def on_class_node_enter ( node ) = handle_keyword_at_location ( node . class_keyword_loc , node . end_keyword_loc )
255+
256+ #: (Prism::SingletonClassNode) -> void
257+ def on_singleton_class_node_enter ( node )
258+ handle_keyword_at_location ( node . class_keyword_loc , node . end_keyword_loc )
259+ end
260+
261+ #: (Prism::LambdaNode) -> void
262+ def on_lambda_node_enter ( node ) = handle_keyword_at_location ( node . opening_loc , node . closing_loc )
263+
264+ #: (Prism::DefNode) -> void
265+ def on_def_node_enter ( node ) = handle_keyword_at_location ( node . def_keyword_loc , node . end_keyword_loc )
266+
267+ #: (Prism::DefinedNode) -> void
268+ def on_defined_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
269+
270+ #: (Prism::ElseNode) -> void
271+ def on_else_node_enter ( node ) = handle_keyword_at_location ( node . else_keyword_loc , node . end_keyword_loc )
272+
273+ #: (Prism::EnsureNode) -> void
274+ def on_ensure_node_enter ( node ) = handle_keyword_at_location ( node . ensure_keyword_loc , node . end_keyword_loc )
275+
276+ #: (Prism::FalseNode) -> void
277+ def on_false_node_enter ( node ) = handle_keyword_at_location ( node . location )
278+
279+ #: (Prism::ForNode) -> void
280+ def on_for_node_enter ( node )
281+ handle_keyword_at_location (
282+ node . for_keyword_loc ,
283+ node . in_keyword_loc ,
284+ node . do_keyword_loc ,
285+ node . end_keyword_loc ,
286+ )
287+ end
288+
289+ #: (Prism::IfNode) -> void
290+ def on_if_node_enter ( node )
291+ handle_keyword_at_location ( node . if_keyword_loc , node . then_keyword_loc , node . end_keyword_loc )
218292 end
219293
220- #: (Prism::YieldNode node) -> void
221- def on_yield_node_enter ( node )
222- handle_keyword_documentation ( node . keyword )
294+ #: (Prism::InNode) -> void
295+ def on_in_node_enter ( node ) = handle_keyword_at_location ( node . in_loc , node . then_loc )
296+
297+ #: (Prism::ModuleNode) -> void
298+ def on_module_node_enter ( node ) = handle_keyword_at_location ( node . module_keyword_loc , node . end_keyword_loc )
299+
300+ #: (Prism::NextNode) -> void
301+ def on_next_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
302+
303+ #: (Prism::NilNode) -> void
304+ def on_nil_node_enter ( node ) = handle_keyword_at_location ( node . location )
305+
306+ #: (Prism::OrNode) -> void
307+ def on_or_node_enter ( node ) = handle_keyword_at_location ( node . operator_loc )
308+
309+ #: (Prism::PostExecutionNode) -> void
310+ def on_post_execution_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
311+
312+ #: (Prism::PreExecutionNode) -> void
313+ def on_pre_execution_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
314+
315+ #: (Prism::RedoNode) -> void
316+ def on_redo_node_enter ( node ) = handle_keyword_at_location ( node . location )
317+
318+ #: (Prism::RescueModifierNode) -> void
319+ def on_rescue_modifier_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
320+
321+ #: (Prism::RescueNode) -> void
322+ def on_rescue_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc , node . then_keyword_loc )
323+
324+ #: (Prism::RetryNode) -> void
325+ def on_retry_node_enter ( node ) = handle_keyword_at_location ( node . location )
326+
327+ #: (Prism::ReturnNode) -> void
328+ def on_return_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
329+
330+ #: (Prism::SelfNode) -> void
331+ def on_self_node_enter ( node ) = handle_keyword_at_location ( node . location )
332+
333+ #: (Prism::SourceEncodingNode) -> void
334+ def on_source_encoding_node_enter ( node ) = handle_keyword_at_location ( node . location )
335+
336+ #: (Prism::SourceFileNode) -> void
337+ def on_source_file_node_enter ( node ) = handle_keyword_at_location ( node . location )
338+
339+ #: (Prism::SourceLineNode) -> void
340+ def on_source_line_node_enter ( node ) = handle_keyword_at_location ( node . location )
341+
342+ #: (Prism::TrueNode) -> void
343+ def on_true_node_enter ( node ) = handle_keyword_at_location ( node . location )
344+
345+ #: (Prism::UndefNode) -> void
346+ def on_undef_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
347+
348+ #: (Prism::UnlessNode) -> void
349+ def on_unless_node_enter ( node )
350+ handle_keyword_at_location ( node . keyword_loc , node . then_keyword_loc , node . end_keyword_loc )
223351 end
224352
353+ #: (Prism::UntilNode) -> void
354+ def on_until_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc , node . do_keyword_loc , node . closing_loc )
355+
356+ #: (Prism::WhenNode) -> void
357+ def on_when_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc , node . then_keyword_loc )
358+
359+ #: (Prism::WhileNode) -> void
360+ def on_while_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc , node . do_keyword_loc , node . closing_loc )
361+
362+ #: (Prism::YieldNode) -> void
363+ def on_yield_node_enter ( node ) = handle_keyword_at_location ( node . keyword_loc )
364+
225365 #: (Prism::ClassVariableAndWriteNode node) -> void
226366 def on_class_variable_and_write_node_enter ( node )
227367 handle_variable_hover ( node . name . to_s )
@@ -279,27 +419,37 @@ def generate_heredoc_hover(node)
279419 end
280420 end
281421
282- #: (String keyword ) -> void
283- def handle_keyword_documentation ( keyword )
284- content = KEYWORD_DOCS [ keyword ]
285- return unless content
422+ #: (String) -> void
423+ def handle_keyword_documentation ( name )
424+ keyword = @graph . keyword ( name )
425+ return unless keyword
286426
287- doc_uri = URI ::Generic . from_path ( path : File . join ( STATIC_DOCS_PATH , "#{ keyword } .md" ) )
288-
289- @response_builder . push ( "```ruby\n #{ keyword } \n ```" , category : :title )
290- @response_builder . push ( "[Read more](#{ doc_uri } )" , category : :links )
291- @response_builder . push ( content , category : :documentation )
427+ @response_builder . push ( "```ruby\n #{ keyword . name } \n ```" , category : :title )
428+ @response_builder . push ( keyword . documentation , category : :documentation )
292429 end
293430
294- #: -> void
295- def handle_super_node_hover
296- # Sorbet can handle super hover on typed true or higher
297- return if @sorbet_level . true_or_higher?
431+ # Push keyword documentation when the cursor is on one of the provided locations. The keyword name is taken from
432+ # the covering location's slice so that operator forms (`&&`, `||`, `{`, `}`, ternary `? :`) yield no hover —
433+ # their slice is not a keyword in the Rubydex graph.
434+ #
435+ #: (*Prism::Location?) -> void
436+ def handle_keyword_at_location ( *locations )
437+ loc = locations . find { |l | l && covers_position? ( l , @position ) }
438+ return unless loc
298439
299- surrounding_method = @node_context . surrounding_method
300- return unless surrounding_method
440+ handle_keyword_documentation ( loc . slice )
441+ end
442+
443+ #: (Prism::Location keyword_location) -> void
444+ def handle_super_node_hover ( keyword_location )
445+ # Sorbet can handle the inherited-method hover on typed true or higher, but it does not surface keyword docs, so
446+ # we still push those
447+ unless @sorbet_level . true_or_higher?
448+ surrounding_method = @node_context . surrounding_method
449+ handle_method_hover ( surrounding_method , inherited_only : true ) if surrounding_method
450+ end
301451
302- handle_method_hover ( surrounding_method , inherited_only : true )
452+ handle_keyword_at_location ( keyword_location )
303453 end
304454
305455 #: (String message, ?inherited_only: bool) -> void
0 commit comments