-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathhover.jl
More file actions
116 lines (105 loc) · 4.03 KB
/
hover.jl
File metadata and controls
116 lines (105 loc) · 4.03 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
const HOVER_REGISTRATION_ID = "jetls-hover"
const HOVER_REGISTRATION_METHOD = "textDocument/hover"
function hover_options()
return HoverOptions()
end
function hover_registration()
return Registration(;
id = HOVER_REGISTRATION_ID,
method = HOVER_REGISTRATION_METHOD,
registerOptions = HoverRegistrationOptions(;
documentSelector = DEFAULT_DOCUMENT_SELECTOR,
)
)
end
function handle_HoverRequest(server::Server, msg::HoverRequest)
pos = msg.params.position
uri = msg.params.textDocument.uri
fi = get_file_info(server.state, uri)
if fi === nothing
return send(server,
HoverResponse(;
id = msg.id,
result = nothing,
error = file_cache_error(uri)))
end
st0_top = build_tree!(JL.SyntaxTree, fi)
offset = xy_to_offset(fi, pos)
target_binding_definitions = select_target_binding_definitions(st0_top, offset)
if !isnothing(target_binding_definitions)
# TODO Ideally we would want to show the type of this local binding,
# but for now we'll just show the location of the local binding
target_binding, definitions = target_binding_definitions
io = IOBuffer()
n = length(definitions)
for (i, definition) in enumerate(definitions)
println(io, "```julia")
JL.showprov(io, definition; include_location=false)
println(io)
println(io, "```")
line, character = JS.source_location(definition)
showtext = "`@ " * simple_loc_text(uri; line) * "`"
println(io, create_source_location_link(uri, showtext; line, character))
if i ≠ n
println(io, "\n---\n") # separator
else
println(io)
end
end
value = String(take!(io))
contents = MarkupContent(;
kind = MarkupKind.Markdown,
value)
return send(server, HoverResponse(;
id = msg.id,
result = Hover(;
contents,
range = get_source_range(target_binding))))
end
node = select_target_node(st0_top, offset)
if node === nothing
return send(server, HoverResponse(; id = msg.id, result = null))
end
(; mod, analyzer, postprocessor) = get_context_info(server.state, uri, pos)
parentmod = mod
identifier_node = node
# TODO replace this AST hack with a proper abstract interpretation to resolve binding information
if JS.kind(node) === JS.K"." && JS.numchildren(node) ≥ 2
dotprefix = node[1]
dotprefixtyp = resolve_type(analyzer, mod, dotprefix)
if dotprefixtyp isa Core.Const
dotprefixval = dotprefixtyp.val
if dotprefixval isa Module
parentmod = dotprefixval
identifier_node = node[2]
end
end
end
if !JS.is_identifier(identifier_node)
return send(server, HoverResponse(; id = msg.id, result = null))
end
identifier = Expr(identifier_node)
if !(identifier isa Symbol)
return send(server, HoverResponse(; id = msg.id, result = null))
end
documentation = Base.Docs.doc(DocsBinding(parentmod, identifier))
value = postprocessor(documentation)
contents = MarkupContent(;
kind = MarkupKind.Markdown,
value)
range = get_source_range(node)
return send(server, HoverResponse(;
id = msg.id,
result = Hover(; contents, range)))
end
@eval function DocsBinding(parentmod::Module, identifier::Symbol)
if invokelatest(isdefinedglobal, parentmod, identifier)
x = invokelatest(getglobal, parentmod, identifier)
if x isa Module && nameof(x) !== identifier
# HACK: skip the binding resolution logic performed by the `Base.Docs.Binding` constructor
# for modules that are given different names within this context
return $(Expr(:new, Base.Docs.Binding, :parentmod, :identifier))
end
end
return Base.Docs.Binding(parentmod, identifier)
end