diff --git a/src/requests/completions.jl b/src/requests/completions.jl index 19b949a0..a4b48868 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -93,6 +93,14 @@ function textDocument_completion_request(params::CompletionParams, server::Langu collect_completions(state.x, "in", state, false) elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.ISA && is_at_end && state.x !== nothing collect_completions(state.x, "isa", state, false) + elseif t isa CSTParser.Tokens.Token && t.kind == CSTParser.Tokens.COMMA && + pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokens.IDENTIFIER && + ppt isa CSTParser.Tokens.Token && ppt.kind == CSTParser.Tokens.LPAREN && + !(parentof(state.x) isa EXPR && CSTParser.iscall(parentof(state.x))) + # method completion for given argument + ptlen = (1 + pt.endbyte - pt.startbyte) + px = get_expr(getcst(state.doc), state.offset - ptlen) + method_completion(px, state, ptlen) end return CompletionList(true, unique(values(state.completions))) @@ -542,3 +550,62 @@ function get_tls_arglist(tls::StaticLint.Scope) error() end end + +function method_completion(x, state, xlen) + scope = scopeof(parentof(parentof(state.x))) + + x_type = refof(x).type.name + + if x_type isa EXPR + typename = x_type.val + elseif x_type isa SymbolServer.FakeTypeName + typename = x_type.name.name + else + return + 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 + + prefix_edit = TextEdit(Range( + Position(state.range.start.line, + state.range.start.character - xlen - 2), + Position(state.range.stop.line, + state.range.stop.character - xlen - 2)), n) + + if siglen_max == 1 # need to close bracket right away + additional_edits = [TextEdit(Range( + Position(state.range.start.line, + state.range.start.character - 1), + Position(state.range.stop.line, + state.range.stop.character)), ""), prefix_edit] + inplace_text = "" + else + inplace_text = " " + additional_edits = [prefix_edit] + end + + inplace_edit = TextEdit(Range( + Position(state.range.start.line, state.range.start.character), + Position(state.range.stop.line, state.range.stop.character)), + inplace_text) + + item = CompletionItem(n, 2, missing, missing, n, + missing, missing, missing, missing, missing, + InsertTextFormats.PlainText, inplace_edit, additional_edits, + missing, missing, missing) + add_completion_item(state, item) + end + end +end diff --git a/test/requests/completions.jl b/test/requests/completions.jl index aaa50787..9874d24c 100644 --- a/test/requests/completions.jl +++ b/test/requests/completions.jl @@ -12,16 +12,16 @@ completion_test(line, char) = LanguageServer.textDocument_completion_request(Lan """) @test completion_test(0, 9).items[1].textEdit.newText == "∴" @test completion_test(0, 9).items[1].textEdit.range == LanguageServer.Range(0, 0, 0, 9) - + @test completion_test(1, 10).items[1].textEdit.newText == "∴" @test completion_test(1, 10).items[1].textEdit.range == LanguageServer.Range(1, 1, 1, 10) - + @test completion_test(2, 10).items[1].textEdit.newText == "∴" @test completion_test(2, 10).items[1].textEdit.range == LanguageServer.Range(2, 1, 2, 10) - + @test completion_test(3, 10).items[1].textEdit.newText == "∴" @test completion_test(3, 10).items[1].textEdit.range == LanguageServer.Range(3, 1, 3, 10) - + @test completion_test(4, 12).items[1].textEdit.newText == "∴" @test completion_test(4, 12).items[1].textEdit.range == LanguageServer.Range(4, 3, 4, 12) @@ -41,7 +41,7 @@ end settestdoc("import ") @test all(item.label in ("Main", "Base", "Core") for item in completion_test(0, 7).items) - + settestdoc("""module M end import .""") @test_broken completion_test(1, 8).items[1].label == "M" @@ -70,7 +70,7 @@ end @test any(item.label == "quot" for item in completion_test(1, 10).items) settestdoc(""" - module M + module M inner = 1 end M. @@ -105,19 +105,19 @@ end settestdoc("@t") @test any(item.label == "@time" for item in completion_test(0, 2).items) - + settestdoc("i") @test any(item.label == "if" for item in completion_test(0, 1).items) - + settestdoc("i") @test any(item.label == "in" for item in completion_test(0, 1).items) - + settestdoc("for") @test any(item.label == "for" for item in completion_test(0, 3).items) settestdoc("in") @test any(item.label == "in" for item in completion_test(0, 2).items) - + settestdoc("isa") @test any(item.label == "isa" for item in completion_test(0, 3).items) end @@ -127,3 +127,9 @@ end myv""") @test any(item.label == "myvar" for item in completion_test(1, 3).items) end + +@testset "method completions" begin + settestdoc("""phi = 1 + (phi,""") + @test any(item.label == "Float64" for item in completion_test(0, 13).items) +end