Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adjust for SL changes #870

Merged
merged 4 commits into from
Jan 26, 2021
Merged
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ JSON = "0.20, 0.21"
julia = "1"
CSTParser = "3.1"
DocumentFormat = "3.2.2"
StaticLint = "6.0.1"
StaticLint = "7"
Tokenize = "0.5.10"
JSONRPC = "1.1"
SymbolServer = "6"
Expand Down
1 change: 1 addition & 0 deletions src/LanguageServer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ include("requests/completions.jl")
include("requests/workspace.jl")
include("requests/actions.jl")
include("requests/init.jl")
include("requests/signatures.jl")
include("utilities.jl")

end
151 changes: 26 additions & 125 deletions src/requests/features.jl
Original file line number Diff line number Diff line change
@@ -1,93 +1,4 @@
function get_signatures(b, tls, sigs, server, visited=nothing) end # Fallback

function get_signatures(b::StaticLint.Binding, tls::StaticLint.Scope, sigs::Vector{SignatureInformation}, server, visited=StaticLint.Binding[])
if b in visited # TODO: remove
throw(LSInfiniteLoop("Possible infinite loop.")) # TODO: remove
else # TODO: remove
push!(visited, b) # TODO: remove
end # TODO: remove
if b.type == StaticLint.CoreTypes.Function && b.val isa EXPR && CSTParser.defines_function(b.val)
get_siginfo_from_call(b.val, sigs)
elseif b.val isa EXPR && CSTParser.defines_struct(b.val)
args = b.val.args[3]
if length(args) > 0
inner_constructor_i = findfirst(a -> CSTParser.defines_function(a), args.args)
if inner_constructor_i !== nothing
get_siginfo_from_call(args.args[inner_constructor_i], sigs)
else
params = ParameterInformation[]
for field in args.args
field_name = CSTParser.rem_decl(field)
push!(params, ParameterInformation(field_name isa EXPR && CSTParser.isidentifier(field_name) ? valof(field_name) : "", missing))
end
push!(sigs, SignatureInformation(string(Expr(b.val)), "", params))
end
end
return
elseif b.val isa SymbolServer.SymStore
return get_signatures(b.val, tls, sigs, server)
else
return
end

get_signatures(b.prev, tls, sigs, server, visited)
end

function get_signatures(b::T, tls::StaticLint.Scope, sigs::Vector{SignatureInformation}, server, visited=nothing) where T <: Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore}
StaticLint.iterate_over_ss_methods(b, tls, server, function (m)
push!(sigs, SignatureInformation(string(m), "", (a -> ParameterInformation(string(a[1]), string(a[2]))).(m.sig)))
return false
end)
end


function get_siginfo_from_call(call, sigs) end # Fallback

function get_siginfo_from_call(call::EXPR, sigs)
sig = CSTParser.rem_where_decl(CSTParser.get_sig(call))
params = ParameterInformation[]
if sig isa EXPR && sig.args !== nothing
for i = 2:length(sig.args)
if (argbinding = bindingof(sig.args[i])) !== nothing
push!(params, ParameterInformation(valof(argbinding.name) isa String ? valof(argbinding.name) : "", missing))
end
end
push!(sigs, SignatureInformation(string(Expr(sig)), "", params))
end
end

function textDocument_signatureHelp_request(params::TextDocumentPositionParams, server::LanguageServerInstance, conn)
doc = getdocument(server, URI2(params.textDocument.uri))
sigs = SignatureInformation[]
offset = get_offset(doc, params.position)
rng = Range(doc, offset:offset)
x = get_expr(getcst(doc), offset)
arg = 0
if x isa EXPR && parentof(x) isa EXPR && CSTParser.iscall(parentof(x))
if CSTParser.isidentifier(parentof(x).args[1])
call_name = parentof(x).args[1]
elseif CSTParser.iscurly(parentof(x).args[1]) && CSTParser.isidentifier(parentof(x).args[1].args[1])
call_name = parentof(x).args[1].args[1]
elseif CSTParser.is_getfield_w_quotenode(parentof(x).args[1])
call_name = parentof(x).args[1].args[2].args[1]
else
call_name = nothing
end
if call_name !== nothing && (f_binding = refof(call_name)) !== nothing && (tls = StaticLint.retrieve_toplevel_scope(call_name)) !== nothing
get_signatures(f_binding, tls, sigs, server)
end
end
if (isempty(sigs) || (headof(x) === :RPAREN))
return SignatureHelp(SignatureInformation[], 0, 0)
end

if headof(x) === :LPAREN
arg = 0
else
arg = sum(headof(a) === :COMMA for a in parentof(x).trivia)
end
return SignatureHelp(filter(s -> length(s.parameters) > arg, sigs), 0, arg)
end

# TODO: should be in StaticLint. visited check is costly.
resolve_shadow_binding(b) = b
Expand All @@ -104,15 +15,15 @@ function resolve_shadow_binding(b::StaticLint.Binding, visited=StaticLint.Bindin
end
end

function get_definitions(x, tls, server, locations, visited=nothing) end # Fallback
function get_definitions(x, tls, server, locations) end # Fallback

function get_definitions(x::SymbolServer.ModuleStore, tls, server, locations, visited=nothing)
function get_definitions(x::SymbolServer.ModuleStore, tls, server, locations)
if haskey(x.vals, :eval) && x[:eval] isa SymbolServer.FunctionStore
get_definitions(x[:eval], tls, server, locations, visited)
get_definitions(x[:eval], tls, server, locations)
end
end

function get_definitions(x::T, tls, server, locations, visited=nothing) where T <: Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore}
function get_definitions(x::Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore}, tls, server, locations)
StaticLint.iterate_over_ss_methods(x, tls, server, function (m)
try
if isfile(m.file)
Expand All @@ -125,23 +36,26 @@ function get_definitions(x::T, tls, server, locations, visited=nothing) where T
end)
end

function get_definitions(b::StaticLint.Binding, tls, server, locations, visited=StaticLint.Binding[])
if b in visited # TODO: remove
throw(LSInfiniteLoop("Possible infinite loop.")) # TODO: remove
else # TODO: remove
push!(visited, b) # TODO: remove
end # TODO: remove

function get_definitions(b::StaticLint.Binding, tls, server, locations)
if !(b.val isa EXPR)
return get_definitions(b.val, tls, server, locations, visited)
get_definitions(b.val, tls, server, locations)
end
doc1, o = get_file_loc(b.val)
if doc1 isa Document
push!(locations, Location(doc1._uri, Range(doc1, o .+ (0:b.val.span))))
if b.type === StaticLint.CoreTypes.Function || b.type === StaticLint.CoreTypes.DataType
for ref in b.refs
method = StaticLint.get_method(ref)
if method !== nothing
get_definitions(method, tls, server, locations)
end
end
elseif b.val isa EXPR
get_definitions(b.val, tls, server, locations)
end
end

if b.type === StaticLint.CoreTypes.Function && b.prev isa StaticLint.Binding && (b.prev.type === StaticLint.CoreTypes.Function || b.prev.type === StaticLint.CoreTypes.DataType)
return get_definitions(b.prev, tls, server, locations, visited)
function get_definitions(x::EXPR, tls::StaticLint.Scope, server, locations)
doc1, o = get_file_loc(x)
if doc1 isa Document
push!(locations, Location(doc1._uri, Range(doc1, o .+ (0:x.span))))
end
end

Expand Down Expand Up @@ -208,31 +122,18 @@ function find_references(textDocument::TextDocumentIdentifier, position::Positio
offset = get_offset(doc, position)
x = get_expr1(getcst(doc), offset)
if x isa EXPR && StaticLint.hasref(x) && refof(x) isa StaticLint.Binding
refs = find_references(refof(x))
for r in refs
doc1, o = get_file_loc(r)
if doc1 isa Document
push!(locations, Location(doc1._uri, Range(doc1, o .+ (0:r.span))))
for r in refof(x).refs
if r isa EXPR
doc1, o = get_file_loc(r)
if doc1 isa Document
push!(locations, Location(doc1._uri, Range(doc1, o .+ (0:r.span))))
end
end
end
end
return locations
end

function find_references(b::StaticLint.Binding, refs=EXPR[], from_end=false)
if !from_end && (b.type === StaticLint.CoreTypes.Function || b.type === StaticLint.CoreTypes.DataType)
b = StaticLint.last_method(b)
end
for r in b.refs
r isa EXPR && push!(refs, r)
end
if b.prev isa StaticLint.Binding && (b.prev.type === StaticLint.CoreTypes.Function || b.prev.type === StaticLint.CoreTypes.DataType)
return find_references(b.prev, refs, true)
else
return refs
end
end

function textDocument_references_request(params::ReferenceParams, server::LanguageServerInstance, conn)
return find_references(params.textDocument, params.position, server)
end
Expand Down
60 changes: 26 additions & 34 deletions src/requests/hover.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,24 @@ function get_hover(x::EXPR, documentation::String, server)
end

function get_hover(b::StaticLint.Binding, documentation::String, server)
if b.val isa EXPR
if b.val isa StaticLint.Binding
documentation = get_hover(b.val, documentation, server)
elseif b.val isa EXPR
if CSTParser.defines_function(b.val) || CSTParser.defines_datatype(b.val)
documentation = get_func_hover(b, documentation, server)
for r in b.refs
method = StaticLint.get_method(r)
if method isa EXPR
documentation = get_preceding_docs(method, documentation)
if CSTParser.defines_function(method)
documentation = string(ensure_ends_with(documentation), "```julia\n", Expr(CSTParser.get_sig(method)), "\n```\n")
elseif CSTParser.defines_datatype(method)
documentation = string(ensure_ends_with(documentation), "```julia\n", Expr(method), "\n```\n")
end
elseif method isa SymbolServer.SymStore
documentation = get_hover(method, documentation, server)
end
end
else
try
documentation = if binding_has_preceding_docs(b)
Expand All @@ -42,13 +57,11 @@ function get_hover(b::StaticLint.Binding, documentation::String, server)
documentation = string(documentation, "```julia\n", Expr(b.val), "\n```\n")
catch err
doc1, offset1 = get_file_loc(b.val)
throw(LSHoverError(string("get_hover failed to convert the following to coode: ", String(codeunits(get_text(doc1))[offset1 .+ (1:b.val.span)]))))
throw(LSHoverError(string("get_hover failed to convert the following to code: ", String(codeunits(get_text(doc1))[offset1 .+ (1:b.val.span)]))))
end
end
elseif b.val isa SymbolServer.SymStore
documentation = get_hover(b.val, documentation, server)
elseif b.val isa StaticLint.Binding
documentation = get_hover(b.val, documentation, server)
end
return documentation
end
Expand Down Expand Up @@ -93,38 +106,17 @@ function get_hover(f::SymbolServer.FunctionStore, documentation::String, server)
end


get_func_hover(x, documentation, server, visited=nothing) = documentation
get_func_hover(x::SymbolServer.SymStore, documentation, server, visited=nothing) = get_hover(x, documentation, server)
get_func_hover(x, documentation, server) = documentation
get_func_hover(x::SymbolServer.SymStore, documentation, server) = get_hover(x, documentation, server)

function get_func_hover(b::StaticLint.Binding, documentation, server, visited=StaticLint.Binding[])
if b in visited # TODO: remove
# throw(LSInfiniteLoop("Possible infinite loop.")) # TODO: remove
# There is a cycle in the links between Bindings. Root cause is in StaticLint but there is no reason to allow it to crash the language server. If we have done a complete circuit here then we have all the information we need and can return.
return documentation
else # TODO: remove
push!(visited, b) # TODO: remove
end # TODO: remove
if b.val isa EXPR
documentation = if binding_has_preceding_docs(b)
# Binding has preceding docs so use them..
string(documentation, Expr(parentof(b.val).args[3]))
elseif const_binding_has_preceding_docs(b)
string(documentation, Expr(parentof(parentof(b.val)).args[3]))
else
documentation
end
if CSTParser.defines_function(b.val)
documentation = string(ensure_ends_with(documentation), "```julia\n", Expr(CSTParser.get_sig(b.val)), "\n```\n")
elseif CSTParser.defines_datatype(b.val)
documentation = string(ensure_ends_with(documentation), "```julia\n", Expr(b.val), "\n```\n")
end
elseif b.val isa SymbolServer.SymStore
return get_hover(b.val, documentation, server)
end
if b.prev isa StaticLint.Binding && (b.prev.type == StaticLint.CoreTypes.Function || b.prev.type == StaticLint.CoreTypes.DataType || b.prev.val isa Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore}) || (b.prev isa SymbolServer.FunctionStore || b.prev isa SymbolServer.DataTypeStore)
return get_func_hover(b.prev, documentation, server, visited)
function get_preceding_docs(expr::EXPR, documentation)
if expr_has_preceding_docs(expr)
string(documentation, Expr(parentof(expr).args[3]))
elseif is_const_expr(parentof(expr)) && expr_has_preceding_docs(parentof(expr))
string(documentation, Expr(parentof(parentof(expr)).args[3]))
else
documentation
end
return documentation
end

ensure_ends_with(s, c = "\n") = endswith(s, c) ? s : string(s, c)
Expand Down
80 changes: 80 additions & 0 deletions src/requests/signatures.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
function textDocument_signatureHelp_request(params::TextDocumentPositionParams, server::LanguageServerInstance, conn)
doc = getdocument(server, URI2(params.textDocument.uri))
sigs = SignatureInformation[]
offset = get_offset(doc, params.position)
x = get_expr(getcst(doc), offset)
arg = 0
if x isa EXPR && parentof(x) isa EXPR && CSTParser.iscall(parentof(x))
if CSTParser.isidentifier(parentof(x).args[1])
call_name = parentof(x).args[1]
elseif CSTParser.iscurly(parentof(x).args[1]) && CSTParser.isidentifier(parentof(x).args[1].args[1])
call_name = parentof(x).args[1].args[1]
elseif CSTParser.is_getfield_w_quotenode(parentof(x).args[1])
call_name = parentof(x).args[1].args[2].args[1]
else
call_name = nothing
end
if call_name !== nothing && (f_binding = refof(call_name)) !== nothing && (tls = StaticLint.retrieve_toplevel_scope(call_name)) !== nothing
get_signatures(f_binding, tls, sigs, server)
end
end
if (isempty(sigs) || (headof(x) === :RPAREN))
return SignatureHelp(SignatureInformation[], 0, 0)
end

if headof(x) === :LPAREN
arg = 0
else
arg = sum(headof(a) === :COMMA for a in parentof(x).trivia)
end
return SignatureHelp(filter(s -> length(s.parameters) > arg, sigs), 0, arg)
end

function get_signatures(b::StaticLint.Binding, tls::StaticLint.Scope, sigs::Vector{SignatureInformation}, server)
if b.val isa StaticLint.Binding
get_signatures(b.val, tls, sigs, server)
end
if b.type == StaticLint.CoreTypes.Function || b.type == StaticLint.CoreTypes.DataType
b.val isa SymbolServer.SymStore && get_signatures(b.val, tls, sigs, server)
for ref in b.refs
method = StaticLint.get_method(ref)
if method !== nothing
get_signatures(method, tls, sigs, server)
end
end
end
end

function get_signatures(b::T, tls::StaticLint.Scope, sigs::Vector{SignatureInformation}, server) where T <: Union{SymbolServer.FunctionStore,SymbolServer.DataTypeStore}
StaticLint.iterate_over_ss_methods(b, tls, server, function (m)
push!(sigs, SignatureInformation(string(m), "", (a -> ParameterInformation(string(a[1]), string(a[2]))).(m.sig)))
return false
end)
end

function get_signatures(x::EXPR, tls::StaticLint.Scope, sigs::Vector{SignatureInformation}, server)
if CSTParser.defines_function(x)
sig = CSTParser.rem_where_decl(CSTParser.get_sig(x))
params = ParameterInformation[]
if sig isa EXPR && sig.args !== nothing
for i = 2:length(sig.args)
if (argbinding = bindingof(sig.args[i])) !== nothing
push!(params, ParameterInformation(valof(argbinding.name) isa String ? valof(argbinding.name) : "", missing))
end
end
push!(sigs, SignatureInformation(string(Expr(sig)), "", params))
end
elseif CSTParser.defines_struct(x)
args = x.args[3]
if length(args) > 0
if !any(CSTParser.defines_function, args.args)
params = ParameterInformation[]
for field in args.args
field_name = CSTParser.rem_decl(field)
push!(params, ParameterInformation(field_name isa EXPR && CSTParser.isidentifier(field_name) ? valof(field_name) : "", missing))
end
push!(sigs, SignatureInformation(string(Expr(x)), "", params))
end
end
end
end