Skip to content

Commit

Permalink
Dict completions in REPL (#17017)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Arthur authored and ivarne committed Jul 5, 2016
1 parent 6ab1b19 commit a019a8f
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
43 changes: 41 additions & 2 deletions base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
Expand Down Expand Up @@ -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 ["\'\"\`"...]
Expand Down
58 changes: 58 additions & 0 deletions test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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[":α]"]

0 comments on commit a019a8f

Please sign in to comment.