Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions src/requests/hover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ function get_tooltip(b::StaticLint.Binding, documentation::String, server, expr
)
end
documentation = if binding_has_preceding_docs(b)
string(documentation, to_codeobject(maybe_get_doc_expr(b.val).args[3]))
string(documentation, to_codeobject(get_doc_payload_expr(maybe_get_doc_expr(b.val))))
elseif const_binding_has_preceding_docs(b)
string(documentation, to_codeobject(maybe_get_doc_expr(parentof(b.val)).args[3]))
string(documentation, to_codeobject(get_doc_payload_expr(maybe_get_doc_expr(parentof(b.val)))))
elseif (doc_expr = maybe_get_doc_expr_from_refs(b)) !== nothing
string(documentation, to_codeobject(get_doc_payload_expr(doc_expr)))
else
documentation
end
Expand Down Expand Up @@ -214,9 +216,9 @@ get_func_hover(x::SymbolServer.SymStore, documentation, server, expr, env) = get

function get_preceding_docs(expr::EXPR, documentation)
if expr_has_preceding_docs(expr)
string(documentation, to_codeobject(maybe_get_doc_expr(expr).args[3]))
string(documentation, to_codeobject(get_doc_payload_expr(maybe_get_doc_expr(expr))))
elseif is_const_expr(parentof(expr)) && expr_has_preceding_docs(parentof(expr))
string(documentation, to_codeobject(maybe_get_doc_expr(parentof(expr)).args[3]))
string(documentation, to_codeobject(get_doc_payload_expr(maybe_get_doc_expr(parentof(expr)))))
else
documentation
end
Expand All @@ -236,11 +238,62 @@ function maybe_get_doc_expr(x)
while CSTParser.hasparent(x) &&
CSTParser.ismacrocall(parentof(x))
x = parentof(x)
headof(x.args[1]) === :globalrefdoc && return x
is_doc_expr(x) && return x
end
return x
end

function is_doc_macro_name(x)
if headof(x) === :globalrefdoc
return true
elseif CSTParser.isidentifier(x)
return valof(x) == "@doc"
elseif CSTParser.is_getfield_w_quotenode(x)
return is_doc_macro_name(CSTParser.unquotenode(CSTParser.rhs_getfield(x)))
else
return false
end
end

function is_string_macro_name(x)
if CSTParser.isidentifier(x)
name = valof(x)
return name isa String && endswith(name, "_str")
elseif CSTParser.is_getfield_w_quotenode(x)
return is_string_macro_name(CSTParser.unquotenode(CSTParser.rhs_getfield(x)))
else
return false
end
end

normalize_doc_payload_expr(x) = x
function normalize_doc_payload_expr(x::EXPR)
if CSTParser.ismacrocall(x) &&
length(x.args) >= 3 &&
is_string_macro_name(x.args[1]) &&
CSTParser.isstring(x.args[3])
return x.args[3]
end
return x
end

get_doc_payload_expr(x::EXPR) = length(x.args) >= 3 ? normalize_doc_payload_expr(x.args[3]) : nothing
get_doc_target_expr(x::EXPR) = length(x.args) >= 4 ? x.args[4] : nothing

function maybe_get_doc_expr_from_refs(b::StaticLint.Binding)
for r in b.refs
r isa EXPR || continue
doc_expr = maybe_get_doc_expr(r)
is_doc_expr(doc_expr) || continue
doc_target = get_doc_target_expr(doc_expr)
doc_target isa EXPR || continue
if doc_target === r || bindingof(doc_target) === b
return doc_expr
end
end
return nothing
end

expr_has_preceding_docs(x) = false
expr_has_preceding_docs(x::EXPR) = is_doc_expr(maybe_get_doc_expr(x))

Expand All @@ -251,8 +304,8 @@ is_doc_expr(x) = false
function is_doc_expr(x::EXPR)
return CSTParser.ismacrocall(x) &&
length(x.args) == 4 &&
headof(x.args[1]) === :globalrefdoc &&
CSTParser.isstring(x.args[3])
is_doc_macro_name(x.args[1]) &&
CSTParser.isstring(get_doc_payload_expr(x))
end

get_fcall_position(x, documentation, visited=nothing) = documentation
Expand Down
20 changes: 20 additions & 0 deletions test/requests/test_hover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,23 @@ end
""")
@test hover_test(3, 5).contents.value == "Argument 1 of 5 in call to `M.f`\n"
end

@testitem "hover docs from @doc macro" begin
include("../test_shared_server.jl")

settestdoc("""
@doc "Function doc via @doc" function docfun() end
@doc "Struct doc via @doc" struct DocType end
@doc "Variable doc via @doc" docvar = 1
@doc raw\"\"\"Raw doc via @doc\"\"\" function rawdocfun() end
docfun
DocType
docvar
rawdocfun
""")

@test occursin("Function doc via @doc", hover_test(4, 6).contents.value)
@test occursin("Struct doc via @doc", hover_test(5, 6).contents.value)
@test occursin("Variable doc via @doc", hover_test(6, 6).contents.value)
@test occursin("Raw doc via @doc", hover_test(7, 8).contents.value)
end
Loading