Tab-completion (expansion) and edlin_expand:expand/1,2,3

Hello, all.

Could you explain to me, please.
The function io:setopts/1,2 and option {expand_fun, expand_fun()} provide a function for tab-completion (expansion) from here io:setopts
For example,

...
io:setopts([{expand_fun,                                                                                 
             fun("h") ->                                                        
                     {yes, _Completion = "ello", _Expansions = ["hello", "world"]};                     
                ("w") ->                                                        
                     {yes, _Completion = "orld", _Expansions = ["hello", "world"]};                       
                (_) ->                                                             
                     {no, _Completion = "", _Expansions = ["hello", "world"]}                               
             end                                                                   
            }]),                                                                   
{ok, [Input]}  = io:fread("Hello or world: ", "~s"),
...

It’s inconvenient because if you type Hello or world: he or Hello or world: wo and press tab the tab-completion stops working. Also, the input string gets into the function expand_fun() as a reversed string.
So let’s rewrite the function expand_fun() a little.

...
expand(_String, {[], Expansions}) ->                                             
    {no, _Completion = "", Expansions};                                          
expand(String, {[E | Es], Expansions}) ->                                        
    case string:prefix(E, String) of                                             
        nomatch ->                                                               
            expand(String, {Es, Expansions});                                    
        Completion ->                                                            
            {yes, Completion, Expansions}                                        
    end.
...
io:setopts([{expand_fun,                                                                                 
             fun(S) ->                                                           
                     Expansions = ["hello", "world"],                               
                     expand(lists:reverse(S), {Expansions, Expansions})          
             end                                                                 
            }]),                                                                 
{ok, [Input]} = io:fread("Hello or world: ", "~s"),
...                    

This is better. The tab-completion works on: "h", "he", "w", "wo", and so on.
(1) Here the question arises, how to return the tab-completion to the default state?
This disables the tab-completion at all.

...
io:setopts([{expand_fun,  fun(_) -> {no, "", []} end}]),
...

It says here io:setopts
The list of possible expansions can be formatted in different ways to make more advanced expansion suggestions more readable to the user, see edlin_expand:expand/2 for documentation of that.
and
The edlin_expand module provides an expand_fun for the erlang shell expand/1,2. It is possible to override this expand_fun io:setopts/1,2.
(2) Could you show a small example of usage expand_fun edlin_expand:expand/2 for the case shown abobe to override expand_fun io:setopts/1,2?
I didn’t understand how it (expand_fun edlin_expand:expand/2) works.

There are two more questions about this function expand_fun edlin_expand:expand/1,2,3:
(3) What does this abbreviation mean Bef0?
(4) Why this function edlin_expand:expand/3 is undocumented.
I saw that the function edlin_expand:expand/3 is used here /path/to/otp/lib/stdlib/test/edlin_expand_SUITE.erl.

1 Like

Hi,

It’s inconvenient because if you type Hello or world: he or Hello or world: wo and press tab the tab-completion stops working. Also, the input string gets into the function expand_fun() as a reversed string.

It might be inconvenient in your usecase, but for the shell its great, because then you always have the latest word (before the cursor) in the beginning of the string: “ow olleh”.

  1. If you want to set it back to the default expand function again you can do it like this io:setopts([{expand_fun, fun edlin_expand:expand/2}]),

The list of possible expansions can be formatted in different ways to make more advanced expansion suggestions more readable to the user,

This is something documented in the later paragraphs of expand/2, and basically makes it possible to format your suggestions:

   {no, "", "#{title := "*** Suggestions ***", elems := ["hello", "world"]}}
hello or world: he<tab>
*** Suggestions ***
hello            world
  1. Your second function above already solves your usecase.
  2. Bef0 is a bit cryptic, but it stand for Before, The text before the cursor (in reverse).
  3. We needed edlin_expand:expand/3 in a test suite, to set a custom shell state. It being undocumented means that it should not be used outside OTP.

Hope that helps, let me know if you have more questions!

3 Likes

Hello, @frazze.

Thanks a lot.