From 50f4271f9a57167853eb67b21f19362035059f3f Mon Sep 17 00:00:00 2001 From: Ben Peachey Higdon Date: Thu, 13 Oct 2022 07:39:42 -0400 Subject: [PATCH] conditionally add parens to function completions --- src/languageserverinstance.jl | 2 ++ src/requests/completions.jl | 33 +++++++++++++++++-------------- src/requests/workspace.jl | 5 ++++- test/requests/test_completions.jl | 16 +++++++++++++++ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/languageserverinstance.jl b/src/languageserverinstance.jl index 751e9e38..435e1874 100644 --- a/src/languageserverinstance.jl +++ b/src/languageserverinstance.jl @@ -43,6 +43,7 @@ mutable struct LanguageServerInstance lint_missingrefs::Symbol lint_disableddirs::Vector{String} completion_mode::Symbol + complete_func_parens::Bool combined_msg_queue::Channel{Any} @@ -83,6 +84,7 @@ mutable struct LanguageServerInstance :all, LINT_DIABLED_DIRS, :qualify, # options: :import or :qualify, anything else turns this off + false, Channel{Any}(Inf), err_handler, :created, diff --git a/src/requests/completions.jl b/src/requests/completions.jl index ee264ca9..68abe5c2 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -109,7 +109,7 @@ function latex_completions(partial::String, state::CompletionState) for (k, v) in Iterators.flatten((REPL.REPLCompletions.latex_symbols, REPL.REPLCompletions.emoji_symbols)) if is_completion_match(string(k), partial) # t1 = TextEdit(Range(state.doc, (state.offset - sizeof(partial)):state.offset), v) - add_completion_item(state, CompletionItem(k, CompletionItemKinds.Unit, missing, v, v, missing, missing, missing, missing, missing, missing, texteditfor(state, partial, v), missing, missing, missing, missing)) + add_completion_item(state, CompletionItem(k, CompletionItemKinds.Unit, missing, v, v, missing, missing, missing, missing, missing, missing, texteditfor(state, partial, v, CompletionItemKinds.Unit), missing, missing, missing, missing)) end end end @@ -119,7 +119,7 @@ function kw_completion(partial::String, state::CompletionState) for (kw, comp) in snippet_completions if startswith(kw, partial) kind = occursin("\$0", comp) ? CompletionItemKinds.Snippet : CompletionItemKinds.Keyword - add_completion_item(state, CompletionItem(kw, kind, missing, missing, kw, missing, missing, missing, missing, missing, InsertTextFormats.Snippet, texteditfor(state, partial, comp), missing, missing, missing, missing)) + add_completion_item(state, CompletionItem(kw, kind, missing, missing, kw, missing, missing, missing, missing, missing, InsertTextFormats.Snippet, texteditfor(state, partial, comp, kind), missing, missing, missing, missing)) end end end @@ -161,8 +161,11 @@ const snippet_completions = Dict{String,String}( ) -function texteditfor(state::CompletionState, partial, n) - TextEdit(Range(Position(state.range.start.line, max(state.range.start.character - length(partial), 0)), state.range.stop), n) +function texteditfor(state::CompletionState, partial, newtext, kind) + if state.server.complete_func_parens && (kind == CompletionItemKinds.Method || kind == CompletionItemKinds.Function) + newtext = newtext * "()" + end + TextEdit(Range(Position(state.range.start.line, max(state.range.start.character - length(partial), 0)), state.range.stop), newtext) end function string_macro_altname(s) @@ -196,11 +199,11 @@ function collect_completions(m::SymbolServer.ModuleStore, spartial, state::Compl end if StaticLint.isexportedby(canonical_name, m) || inclexported foreach(possible_names) do n - add_completion_item(state, CompletionItem(n, _completion_kind(v), get_typed_definition(v), MarkupContent(sanitize_docstring(v.doc)), texteditfor(state, spartial, n))) + add_completion_item(state, CompletionItem(n, _completion_kind(v), get_typed_definition(v), MarkupContent(sanitize_docstring(v.doc)), texteditfor(state, spartial, n, _completion_kind(v)))) end elseif dotcomps foreach(possible_names) do n - push!(state.completions, CompletionItem(n, _completion_kind(v), get_typed_definition(v), MarkupContent(sanitize_docstring(v.doc)), texteditfor(state, spartial, string(m.name, ".", n)))) + push!(state.completions, CompletionItem(n, _completion_kind(v), get_typed_definition(v), MarkupContent(sanitize_docstring(v.doc)), texteditfor(state, spartial, string(m.name, ".", n), _completion_kind(v)))) end elseif length(spartial) > 3 && !variable_already_imported(m, canonical_name, state) if state.server.completion_mode === :import @@ -209,14 +212,14 @@ function collect_completions(m::SymbolServer.ModuleStore, spartial, state::Compl foreach(possible_names) do n ci = CompletionItem(n, _completion_kind(v), missing, "This is an unexported symbol and will be explicitly imported.", MarkupContent(sanitize_docstring(v.doc)), missing, missing, missing, missing, missing, InsertTextFormats.PlainText, - texteditfor(state, spartial, n), textedit_to_insert_using_stmt(m, canonical_name, state), missing, missing, "import") + texteditfor(state, spartial, n, _completion_kind(v)), textedit_to_insert_using_stmt(m, canonical_name, state), missing, missing, "import") add_completion_item(state, ci) end elseif state.server.completion_mode === :qualify foreach(possible_names) do n add_completion_item(state, CompletionItem(string(m.name, ".", n), _completion_kind(v), missing, missing, MarkupContent(sanitize_docstring(v.doc)), missing, - missing, string(n), missing, missing, InsertTextFormats.PlainText, texteditfor(state, spartial, string(m.name, ".", n)), + missing, string(n), missing, missing, InsertTextFormats.PlainText, texteditfor(state, spartial, string(m.name, ".", n), _completion_kind(v)), missing, missing, missing, missing)) end end @@ -272,7 +275,7 @@ function collect_completions(x::StaticLint.Scope, spartial, state::CompletionSta sanitize_docstring(documentation) end foreach(possible_names) do nn - add_completion_item(state, CompletionItem(nn, _completion_kind(n[2]), get_typed_definition(n[2]), MarkupContent(documentation), texteditfor(state, spartial, nn))) + add_completion_item(state, CompletionItem(nn, _completion_kind(n[2]), get_typed_definition(n[2]), MarkupContent(documentation), texteditfor(state, spartial, nn, _completion_kind(n[2])))) end end end @@ -301,14 +304,14 @@ function _get_dot_completion(px::EXPR, spartial, state::CompletionState) for a in refof(px).type.fieldnames a = String(a) if is_completion_match(a, spartial) - add_completion_item(state, CompletionItem(a, CompletionItemKinds.Method, get_typed_definition(a), MarkupContent(a), texteditfor(state, spartial, a))) + add_completion_item(state, CompletionItem(a, CompletionItemKinds.Method, get_typed_definition(a), MarkupContent(a), texteditfor(state, spartial, a, CompletionItemKinds.Method))) end end elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa SymbolServer.DataTypeStore for a in refof(px).type.val.fieldnames a = String(a) if is_completion_match(a, spartial) - add_completion_item(state, CompletionItem(a, CompletionItemKinds.Method, get_typed_definition(a), MarkupContent(a), texteditfor(state, spartial, a))) + add_completion_item(state, CompletionItem(a, CompletionItemKinds.Method, get_typed_definition(a), MarkupContent(a), texteditfor(state, spartial, a, CompletionItemKinds.Method))) end end elseif refof(px).type isa StaticLint.Binding && refof(px).type.val isa EXPR && CSTParser.defines_struct(refof(px).type.val) && scopeof(refof(px).type.val) isa StaticLint.Scope @@ -453,7 +456,7 @@ function import_completions(ppt, pt, t, is_at_end, x, state::CompletionState) for (n, m) in refof(import_root).vals n = String(n) if is_completion_match(n, t.val) && !startswith(n, "#") - add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n))) + add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n, _completion_kind(m)))) end end else @@ -476,7 +479,7 @@ function import_completions(ppt, pt, t, is_at_end, x, state::CompletionState) for (n, m) in rootmod.vals n = String(n) if is_completion_match(n, t.val) && !startswith(n, "#") - add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n))) + add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n, _completion_kind(m)))) end end end @@ -485,14 +488,14 @@ function import_completions(ppt, pt, t, is_at_end, x, state::CompletionState) for (n, m) in refof(import_root).vals n = String(n) if is_completion_match(n, t.val) && !startswith(n, "#") - add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n))) + add_completion_item(state, CompletionItem(n, _completion_kind(m), get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? sanitize_docstring(m.doc) : n), texteditfor(state, t.val, n, _completion_kind(m)))) end end else for (n, m) in StaticLint.getsymbols(getenv(state)) n = String(n) if is_completion_match(n, t.val) - add_completion_item(state, CompletionItem(n, CompletionItemKinds.Module, get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? m.doc : n), texteditfor(state, t.val, n))) + add_completion_item(state, CompletionItem(n, CompletionItemKinds.Module, get_typed_definition(m), MarkupContent(m isa SymbolServer.SymStore ? m.doc : n), texteditfor(state, t.val, n, CompletionItemKinds.Module,))) end end end diff --git a/src/requests/workspace.jl b/src/requests/workspace.jl index 1f67028f..b97f6a73 100644 --- a/src/requests/workspace.jl +++ b/src/requests/workspace.jl @@ -107,7 +107,8 @@ function request_julia_config(server::LanguageServerInstance, conn) ConfigurationItem(missing, "julia.lint.run"), ConfigurationItem(missing, "julia.lint.missingrefs"), ConfigurationItem(missing, "julia.lint.disabledDirs"), - ConfigurationItem(missing, "julia.completionmode") + ConfigurationItem(missing, "julia.completionmode"), + ConfigurationItem(missing, "julia.completeFunctionParens") ])) new_runlinter = something(response[11], true) @@ -116,6 +117,7 @@ function request_julia_config(server::LanguageServerInstance, conn) new_lint_missingrefs = Symbol(something(response[12], :all)) new_lint_disableddirs = something(response[13], LINT_DIABLED_DIRS) new_completion_mode = Symbol(something(response[14], :import)) + new_complete_func_parens = something(response[15], false) rerun_lint = begin any(getproperty(server.lint_options, opt) != getproperty(new_SL_opts, opt) for opt in fieldnames(StaticLint.LintOptions)) || @@ -129,6 +131,7 @@ function request_julia_config(server::LanguageServerInstance, conn) server.lint_missingrefs = new_lint_missingrefs server.lint_disableddirs = new_lint_disableddirs server.completion_mode = new_completion_mode + server.complete_func_parens = new_complete_func_parens if rerun_lint relintserver(server) diff --git a/test/requests/test_completions.jl b/test/requests/test_completions.jl index 6a0f5258..dcb2bd83 100644 --- a/test/requests/test_completions.jl +++ b/test/requests/test_completions.jl @@ -193,3 +193,19 @@ end @test any(i -> i.label == "yyy" && occursin("yyy::Bar", i.detail), items1) @test any(i -> i.label == "xxx" && occursin("xxx::Bar = f.yyy", i.detail), items2) end + +@testitem "complete function parens" begin + include("../test_shared_server.jl") + + server.complete_func_parens = true + settestdoc(""" + foo_func() = 1 + foo_var = 2 + fo + """) + items = completion_test(2, 2).items + @test any(i -> i.label == "foo_func" && i.textEdit.newText == "foo_func()", items) + @test any(i -> i.label == "foo_var" && i.textEdit.newText == "foo_var", items) + @test any(i -> i.label == "foldl" && i.textEdit.newText == "foldl()", items) + server.complete_func_parens = false +end