From a019a8feae86ee625397538b29be115da6da868c Mon Sep 17 00:00:00 2001 From: Ben Arthur Date: Tue, 5 Jul 2016 09:27:56 -0400 Subject: [PATCH] Dict completions in REPL (#17017) --- base/REPLCompletions.jl | 43 ++++++++++++++++++++++++++++-- test/replcompletions.jl | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/base/REPLCompletions.jl b/base/REPLCompletions.jl index 207be534a0520..473ab5ad28750 100644 --- a/base/REPLCompletions.jl +++ b/base/REPLCompletions.jl @@ -386,12 +386,51 @@ function bslash_completions(string, pos) return (false, (String[], 0:-1, false)) end +function dict_identifier_key(str,tag) + if tag === :string + str_close = str*"\"" + elseif tag === :cmd + str_close = str*"`" + else + str_close = str + end + + frange, end_of_indentifier = find_start_brace(str_close, c_start='[', c_end=']') + isempty(frange) && return (nothing, nothing, nothing) + identifier = Symbol(str[frange[1]:end_of_indentifier]) + isdefined(Main,identifier) || return (nothing, nothing, nothing) + begin_of_key = findnext(x->!in(x,whitespace_chars), str, end_of_indentifier+2) + begin_of_key==0 && return (identifier, nothing, nothing) + partial_key = str[begin_of_key:end] + main_id = getfield(Main,identifier) + typeof(main_id) <: Associative && length(main_id)<1e6 || return (identifier, nothing, nothing) + main_id, partial_key, begin_of_key +end + function completions(string, pos) # First parse everything up to the current position partial = string[1:pos] inc_tag = Base.syntax_deprecation_warnings(false) do Base.incomplete_tag(parse(partial, raise=false)) end + + # if completing a key in a Dict + identifier, partial_key, loc = dict_identifier_key(partial,inc_tag) + if identifier != nothing + if partial_key != nothing + matches = [] + for key in keys(identifier) + rkey = repr(key) + startswith(rkey,partial_key) && push!(matches,rkey) + end + length(matches)==1 && (length(string) <= pos || string[pos+1] != ']') && (matches[1]*="]") + length(matches)>0 && return sort(matches), loc:pos, true + else + return String[], 0:-1, false + end + end + + # otherwise... if inc_tag in [:cmd, :string] m = match(r"[\t\n\r\"'`@\$><=;|&\{]| (?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) @@ -413,7 +452,7 @@ function completions(string, pos) # Make sure that only bslash_completions is working on strings inc_tag==:string && return String[], 0:-1, false - if inc_tag == :other && should_method_complete(partial) + if inc_tag == :other && should_method_complete(partial) frange, method_name_end = find_start_brace(partial) ex = Base.syntax_deprecation_warnings(false) do parse(partial[frange] * ")", raise=false) @@ -484,7 +523,7 @@ function completions(string, pos) elseif c==']' c_start='['; c_end=']' end - frange, end_off_indentifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) + frange, end_of_indentifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) startpos = start(frange) i = prevind(string, startpos) elseif c in ["\'\"\`"...] diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 0bc56bb4cc11b..8abd0c35a01f1 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -661,3 +661,61 @@ c, r, res = test_complete(s) s = "CompletionFoo.tuple." c, r, res = test_complete(s) @test isempty(c) + +# test Dicts +test_dict = Dict("abc"=>1, "abcd"=>10, :bar=>2, :bar2=>9, Base=>3, contains=>4, `ls`=>5, + 66=>7, 67=>8, ("q",3)=>11, "α"=>12, :α=>13) +s="test_dict[\"ab" +c,r = test_complete(s) +@test c == Any["\"abc\"","\"abcd\""] +s="test_dict[\"abcd" +c,r = test_complete(s) +@test c == Any["\"abcd\"]"] +s="test_dict[ \"abcd" # leading whitespace +c,r = test_complete(s) +@test c == Any["\"abcd\"]"] +s="test_dict[\"abcd]" # trailing close bracket +c,r = completions(s,endof(s)-1) +@test c == Any["\"abcd\""] +s="test_dict[:b" +c,r = test_complete(s) +@test c == Any[":bar",":bar2"] +s="test_dict[:bar2" +c,r = test_complete(s) +@test c == Any[":bar2]"] +s="test_dict[Ba" +c,r = test_complete(s) +@test c == Any["Base]"] +s="test_dict[co" +c,r = test_complete(s) +@test c == Any["contains]"] +s="test_dict[`l" +c,r = test_complete(s) +@test c == Any["`ls`]"] +s="test_dict[6" +c,r = test_complete(s) +@test c == Any["66","67"] +s="test_dict[66" +c,r = test_complete(s) +@test c == Any["66]"] +s="test_dict[(" +c,r = test_complete(s) +@test c == Any["(\"q\",3)]"] +s="test_dict[\"\\alp" +c,r = test_complete(s) +@test c == String["\\alpha"] +s="test_dict[\"\\alpha" +c,r = test_complete(s) +@test c == String["α"] +s="test_dict[\"α" +c,r = test_complete(s) +@test c == Any["\"α\"]"] +s="test_dict[:\\alp" +c,r = test_complete(s) +@test c == String["\\alpha"] +s="test_dict[:\\alpha" +c,r = test_complete(s) +@test c == String["α"] +s="test_dict[:α" +c,r = test_complete(s) +@test c == Any[":α]"]