-
Notifications
You must be signed in to change notification settings - Fork 193
/
Copy pathhover.rb
104 lines (86 loc) · 3.32 KB
/
hover.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# typed: strict
# frozen_string_literal: true
require "ruby_lsp/listeners/hover"
module RubyLsp
module Requests
# The [hover request](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
# displays the documentation for the symbol currently under the cursor.
class Hover < Request
extend T::Generic
class << self
#: -> Interface::HoverOptions
def provider
Interface::HoverOptions.new
end
end
ResponseType = type_member { { fixed: T.nilable(Interface::Hover) } }
#: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, RubyDocument::SorbetLevel sorbet_level) -> void
def initialize(document, global_state, position, dispatcher, sorbet_level)
super()
char_position, _ = document.find_index_by_position(position)
delegate_request_if_needed!(global_state, document, char_position)
node_context = RubyDocument.locate(
document.parse_result.value,
char_position,
node_types: Listeners::Hover::ALLOWED_TARGETS,
code_units_cache: document.code_units_cache,
)
target = node_context.node
parent = node_context.parent
if should_refine_target?(parent, target)
target = determine_target(
T.must(target),
T.must(parent),
position,
)
elsif position_outside_target?(position, target)
target = nil
end
# Don't need to instantiate any listeners if there's no target
return unless target
@target = target #: Prism::Node?
uri = document.uri
@response_builder = ResponseBuilders::Hover.new #: ResponseBuilders::Hover
Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level)
Addon.addons.each do |addon|
Addon.notify(addon, :create_hover_listener, @response_builder, node_context, dispatcher)
end
@dispatcher = dispatcher
end
# @override
#: -> ResponseType
def perform
return unless @target
@dispatcher.dispatch_once(@target)
return if @response_builder.empty?
Interface::Hover.new(
contents: Interface::MarkupContent.new(
kind: "markdown",
value: @response_builder.response,
),
)
end
private
#: (Prism::Node? parent, Prism::Node? target) -> bool
def should_refine_target?(parent, target)
(Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
!Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
(parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
end
#: (Hash[Symbol, untyped] position, Prism::Node? target) -> bool
def position_outside_target?(position, target)
case target
when Prism::GlobalVariableAndWriteNode,
Prism::GlobalVariableOperatorWriteNode,
Prism::GlobalVariableOrWriteNode,
Prism::GlobalVariableWriteNode
!covers_position?(target.name_loc, position)
when Prism::CallNode
!covers_position?(target.message_loc, position)
else
false
end
end
end
end
end