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

dot methods completion #1210

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
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
121 changes: 95 additions & 26 deletions src/requests/completions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
offset::Int
completions::Dict{String,CompletionItem}
range::Range
x::Union{Nothing, EXPR}
x::Union{Nothing,EXPR}
doc::Document
server::LanguageServerInstance
using_stmts::Dict{String,Any}
end

function add_completion_item(state::CompletionState, completion::CompletionItem)
if haskey(state.completions, completion.label) && ismissing(state.completions[completion.label].data)
if haskey(state.completions, completion.label) && !ismissing(state.completions[completion.label].data)
# For the above statement: we've (1) already got a completion which (2) doesn't require adding an explicit import statement.
return
end
# @info "added", completion.label
state.completions[completion.label] = completion
end

Expand Down Expand Up @@ -48,8 +49,8 @@
CompletionState(offset, Dict{String,CompletionItem}(), rng, x, doc, server, using_stmts)
end

ppt, pt, t, is_at_end = get_partial_completion(state)

ppt, pt, t, is_at_end = get_partial_completion(state)
# @info ppt, pt, t, is_at_end
if pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokenize.Tokens.BACKSLASH
latex_completions(string("\\", CSTParser.Tokenize.untokenize(t)), state)
elseif ppt isa CSTParser.Tokens.Token && ppt.kind == CSTParser.Tokenize.Tokens.BACKSLASH && pt isa CSTParser.Tokens.Token && (pt.kind === CSTParser.Tokens.CIRCUMFLEX_ACCENT || pt.kind === CSTParser.Tokens.COLON)
Expand All @@ -58,20 +59,26 @@
partial = is_latex_comp(t.val, state.offset - t.startbyte)
!isempty(partial) && latex_completions(partial, state)
elseif t isa CSTParser.Tokens.Token && (t.kind in (CSTParser.Tokenize.Tokens.STRING,
CSTParser.Tokenize.Tokens.TRIPLE_STRING,
CSTParser.Tokenize.Tokens.CMD,
CSTParser.Tokenize.Tokens.TRIPLE_CMD))
CSTParser.Tokenize.Tokens.TRIPLE_STRING,
CSTParser.Tokenize.Tokens.CMD,
CSTParser.Tokenize.Tokens.TRIPLE_CMD))
string_completion(t, state)
elseif state.x isa EXPR && is_in_import_statement(state.x)
import_completions(ppt, pt, t, is_at_end, state.x, state)
elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.DOT && pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.IDENTIFIER
# getfield completion, no partial
# @info "enter dot completion"
px = get_expr(getcst(state.doc), state.offset - (1 + t.endbyte - t.startbyte))
_get_dot_completion(px, "", state)
ptlen = (1 + pt.endbyte - pt.startbyte)
# px = get_expr(getcst(state.doc), state.offset - ptlen)
method_completion(px, state, ptlen, "")
elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.IDENTIFIER && pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.DOT && ppt isa CSTParser.Tokens.Token && ppt.kind == CSTParser.Tokens.IDENTIFIER
# getfield completion, partial
px = get_expr(getcst(state.doc), state.offset - (1 + t.endbyte - t.startbyte) - (1 + pt.endbyte - pt.startbyte)) # get offset 2 tokens back
_get_dot_completion(px, t.val, state)
ptlen = (1 + ppt.endbyte - ppt.startbyte)
method_completion(px, state, ptlen, t.val)
elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.IDENTIFIER
# token completion
if is_at_end && state.x !== nothing
Expand Down Expand Up @@ -158,7 +165,7 @@
"try" => "try\n\t\$0\ncatch\nend",
"using" => "using ",
"while" => "while \$1\n\t\$0\nend"
)
)


function texteditfor(state::CompletionState, partial, n)
Expand Down Expand Up @@ -282,9 +289,9 @@

function is_rebinding_of_module(x)
x isa EXPR && refof(x).type === StaticLint.CoreTypes.Module && # binding is a Module
refof(x).val isa EXPR && CSTParser.isassignment(refof(x).val) && # binding expr is an assignment
StaticLint.hasref(refof(x).val.args[2]) && refof(refof(x).val.args[2]).type === StaticLint.CoreTypes.Module &&
refof(refof(x).val.args[2]).val isa EXPR && CSTParser.defines_module(refof(refof(x).val.args[2]).val)# double check the rhs points to a module
refof(x).val isa EXPR && CSTParser.isassignment(refof(x).val) && # binding expr is an assignment
StaticLint.hasref(refof(x).val.args[2]) && refof(refof(x).val.args[2]).type === StaticLint.CoreTypes.Module &&
refof(refof(x).val.args[2]).val isa EXPR && CSTParser.defines_module(refof(refof(x).val.args[2]).val)# double check the rhs points to a module
end

function _get_dot_completion(px, spartial, state::CompletionState) end
Expand Down Expand Up @@ -359,7 +366,7 @@
function string_completion(t, state::CompletionState)
path_completion(t, state)
# Need to adjust things for quotation marks
if t.kind in (CSTParser.Tokenize.Tokens.STRING,CSTParser.Tokenize.Tokens.CMD)
if t.kind in (CSTParser.Tokenize.Tokens.STRING, CSTParser.Tokenize.Tokens.CMD)
t.startbyte + 1 < state.offset <= t.endbyte || return
relative_offset = state.offset - t.startbyte - 1
content = t.val[2:prevind(t.val, lastindex(t.val))]
Expand Down Expand Up @@ -390,18 +397,18 @@
# latex completions.
# from: UInt8.(sort!(unique(prod([k[2:end] for (k,_) in Iterators.flatten((REPL.REPLCompletions.latex_symbols, REPL.REPLCompletions.emoji_symbols))]))))
u === 0x21 ||
u === 0x28 ||
u === 0x29 ||
u === 0x2b ||
u === 0x2d ||
u === 0x2f ||
0x30 <= u <= 0x39 ||
u === 0x3a ||
u === 0x3d ||
0x41 <= u <= 0x5a ||
u === 0x5e ||
u === 0x5f ||
0x61 <= u <= 0x7a
u === 0x28 ||
u === 0x29 ||
u === 0x2b ||
u === 0x2d ||
u === 0x2f ||
0x30 <= u <= 0x39 ||
u === 0x3a ||
u === 0x3d ||
0x41 <= u <= 0x5a ||
u === 0x5e ||
u === 0x5f ||
0x61 <= u <= 0x7a
end

function path_completion(t, state::CompletionState)
Expand All @@ -426,7 +433,7 @@
if isdir(joinpath(dir, f))
f = string(f, "/")
end
rng1 = Range(state.doc, state.offset - sizeof(partial):state.offset)
rng1 = Range(state.doc, state.offset-sizeof(partial):state.offset)

Check warning on line 436 in src/requests/completions.jl

View check run for this annotation

Codecov / codecov/patch

src/requests/completions.jl#L436

Added line #L436 was not covered by tests
add_completion_item(state, CompletionItem(f, CompletionItemKinds.File, f, TextEdit(rng1, f)))
catch err
isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow()
Expand All @@ -447,7 +454,7 @@
import_root = get_import_root(import_statement)

if (t.kind == CSTParser.Tokens.WHITESPACE && pt.kind ∈ (CSTParser.Tokens.USING, CSTParser.Tokens.IMPORT, CSTParser.Tokens.IMPORTALL, CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON)) ||
(t.kind in (CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON))
(t.kind in (CSTParser.Tokens.COMMA, CSTParser.Tokens.COLON))
# no partial, no dot
if import_root !== nothing && refof(import_root) isa SymbolServer.ModuleStore
for (n, m) in refof(import_root).vals
Expand Down Expand Up @@ -585,3 +592,65 @@
error()
end
end

function method_completion(x, state, xlen, spartial)
# @info parentof(state.x), parentof(parentof(state.x))
scope = scopeof(parentof(parentof(x)))
if isnothing(scope)
return
end
# @info scope, x, refof(x), xlen
x_ref = refof(x)
if !isdefined(x_ref, :type)
return
end
if !isdefined(x_ref.type, :name)
return

Check warning on line 608 in src/requests/completions.jl

View check run for this annotation

Codecov / codecov/patch

src/requests/completions.jl#L608

Added line #L608 was not covered by tests
end
x_type = refof(x).type.name
# @info x_type
if x_type isa EXPR
typename = x_type.val
elseif x_type isa SymbolServer.FakeTypeName
typename = x_type.name.name
else
return

Check warning on line 617 in src/requests/completions.jl

View check run for this annotation

Codecov / codecov/patch

src/requests/completions.jl#L617

Added line #L617 was not covered by tests
end
for m in scope.modules
for val in m[2].vals
n, v = String(val[1]), val[2]
(startswith(n, ".") || startswith(n, "#") || startswith(n, "_")) && continue

!(typeof(v) == SymbolServer.FunctionStore) && continue
siglen_max = 0 # maximum signature length
for m in v.methods
isempty(m.sig) && continue
!(typeof(m.sig[1][2]) == SymbolServer.FakeTypeName) && continue
!(m.sig[1][2].name.name == typename) && continue
siglen_max = max(siglen_max, length(m.sig))
end
(siglen_max == 0) && continue
inplace_edit = TextEdit(Range(
Position(state.range.start.line,
state.range.start.character),
Position(state.range.stop.line,
state.range.stop.character + -xlen + length(n) + 2)), n * "(" * valof(x) * ",)")
additional_edit = TextEdit(Range(
Position(state.range.start.line,
state.range.start.character - xlen - 1 - length(spartial)),
Position(state.range.stop.line,
state.range.stop.character)), "")
# @info "inplace_edit: ", inplace_edit
# @info "additional_edit: ", additional_edit

item = CompletionItem(n, 2, missing, missing, n,
missing, missing, missing, missing, missing,
InsertTextFormats.PlainText, inplace_edit, [additional_edit],
missing, missing, n)

if is_completion_match(n, spartial)
add_completion_item(state, item)
end
end
end
end
9 changes: 4 additions & 5 deletions test/requests/test_completions.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@testitem "latex completions" begin
include("../test_shared_server.jl")
include("../test_shared_server.jl")

settestdoc("""
\\therefor
Expand Down Expand Up @@ -32,8 +32,7 @@
@test completion_test(6, 14).items[1].textEdit.range == LanguageServer.Range(6, 0, 6, 14)
end

@testitem "path completions" begin
end
@testitem "path completions" begin end

@testitem "import completions" begin
include("../test_shared_server.jl")
Expand Down Expand Up @@ -85,7 +84,7 @@ end
x = Expr()
x.
""")
@test all(item.label in ("head", "args") for item in completion_test(1, 2).items)
@test any(item.label in ("head", "args", "findmeta") for item in completion_test(1, 2).items)

settestdoc("""
struct T
Expand Down Expand Up @@ -175,7 +174,7 @@ end

@testitem "completion details" begin
include("../test_shared_server.jl")

settestdoc("""
struct Bar end
struct Foo
Expand Down