From 8efb53f3551294664eadff5939da217db9bc828d Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Wed, 1 Mar 2023 17:20:45 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9D=87=EF=B8=8F=20dot=20methods=20comple?= =?UTF-8?q?tion=20attempt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/requests/completions.jl | 108 +++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/src/requests/completions.jl b/src/requests/completions.jl index ee264ca9..8a8cb062 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -6,17 +6,18 @@ struct CompletionState 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 @@ -48,7 +49,7 @@ function textDocument_completion_request(params::CompletionParams, server::Langu 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) if pt isa CSTParser.Tokens.Token && pt.kind == CSTParser.Tokenize.Tokens.BACKSLASH latex_completions(string("\\", CSTParser.Tokenize.untokenize(t)), state) @@ -58,9 +59,9 @@ function textDocument_completion_request(params::CompletionParams, server::Langu 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) @@ -68,6 +69,9 @@ function textDocument_completion_request(params::CompletionParams, server::Langu # getfield completion, no partial 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 @@ -158,7 +162,7 @@ const snippet_completions = Dict{String,String}( "try" => "try\n\t\$0\ncatch\nend", "using" => "using ", "while" => "while \$1\n\t\$0\nend" - ) +) function texteditfor(state::CompletionState, partial, n) @@ -282,9 +286,9 @@ end 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 @@ -359,7 +363,7 @@ end 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))] @@ -390,18 +394,18 @@ function is_latex_comp_char(u) # 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) @@ -426,7 +430,7 @@ function path_completion(t, state::CompletionState) 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) add_completion_item(state, CompletionItem(f, CompletionItemKinds.File, f, TextEdit(rng1, f))) catch err isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow() @@ -447,7 +451,7 @@ function import_completions(ppt, pt, t, is_at_end, x, state::CompletionState) 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 @@ -585,3 +589,57 @@ function get_tls_arglist(tls::StaticLint.Scope) error() end end + +function method_completion(x, state, xlen) + scope = scopeof(parentof(parentof(state.x))) + # @info scope, x, refof(x) + x_ref = refof(x) + if !hasproperty(x_ref, :type) + return + end + if !hasproperty(x_ref.type, :name) + return + 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 + 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), + 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) + add_completion_item(state, item) + end + end +end From ac4fa29d96dc55f0e95b59800dd2eb6dd3dc5c23 Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Sat, 4 Mar 2023 12:12:33 +0800 Subject: [PATCH 2/5] test fix --- test/requests/test_completions.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/requests/test_completions.jl b/test/requests/test_completions.jl index 6a0f5258..d2ff3731 100644 --- a/test/requests/test_completions.jl +++ b/test/requests/test_completions.jl @@ -1,5 +1,5 @@ @testitem "latex completions" begin - include("../test_shared_server.jl") + include("../test_shared_server.jl") settestdoc(""" \\therefor @@ -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") @@ -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 @@ -175,7 +174,7 @@ end @testitem "completion details" begin include("../test_shared_server.jl") - + settestdoc(""" struct Bar end struct Foo From 6e551924aa497f70e4f9ae5fba496ecc3b82f4d2 Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Sun, 5 Mar 2023 10:05:00 +0800 Subject: [PATCH 3/5] fix test --- src/requests/completions.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/requests/completions.jl b/src/requests/completions.jl index 8a8cb062..895221e1 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -594,10 +594,10 @@ function method_completion(x, state, xlen) scope = scopeof(parentof(parentof(state.x))) # @info scope, x, refof(x) x_ref = refof(x) - if !hasproperty(x_ref, :type) + if !isdefined(x_ref, :type) return end - if !hasproperty(x_ref.type, :name) + if !isdefined(x_ref.type, :name) return end x_type = refof(x).type.name From 23cb38036bd00ef71425202dad0d569bd947bcfe Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Tue, 7 Mar 2023 22:54:27 +0800 Subject: [PATCH 4/5] fix search --- src/requests/completions.jl | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/requests/completions.jl b/src/requests/completions.jl index 895221e1..2bcbdefe 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -50,7 +50,7 @@ function textDocument_completion_request(params::CompletionParams, server::Langu end 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) @@ -67,15 +67,18 @@ function textDocument_completion_request(params::CompletionParams, server::Langu 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) + 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 @@ -590,9 +593,10 @@ function get_tls_arglist(tls::StaticLint.Scope) end end -function method_completion(x, state, xlen) - scope = scopeof(parentof(parentof(state.x))) - # @info scope, x, refof(x) +function method_completion(x, state, xlen, spartial) + # @info parentof(state.x), parentof(parentof(state.x)) + scope = scopeof(parentof(parentof(x))) + # @info scope, x, refof(x), xlen x_ref = refof(x) if !isdefined(x_ref, :type) return @@ -630,16 +634,20 @@ function method_completion(x, state, xlen) 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), + 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) - add_completion_item(state, item) + + if is_completion_match(n, spartial) + add_completion_item(state, item) + end end end end From a8f4fc6f07847ab1a35e45a8498f44663fb9f029 Mon Sep 17 00:00:00 2001 From: xgdgsc Date: Fri, 10 Mar 2023 18:22:51 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=90=9B=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/requests/completions.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/requests/completions.jl b/src/requests/completions.jl index 2bcbdefe..9f7138dc 100644 --- a/src/requests/completions.jl +++ b/src/requests/completions.jl @@ -596,6 +596,9 @@ 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)