% File: read_with_description.sl                            -*- mode: SLang -*-
%
% Copyright (c)
%       2006      Jörg Sommer <joerg@alea.gnuu.de>
%       $Id: read_with_description.sl 160 2007-06-28 11:11:34Z joerg $
%
% Description:   This is an extention of the read_with_completion() function
%                to print a description beside the proposal in the completion
%                buffer to give the user an idea of what he selects.
%
% License: This program is free software; you can redistribute it and/or
%	   modify it under the terms of the GNU General Public License as
%	   published by the Free Software Foundation; either version 2 of
%	   the License, or (at your option) any later version.
%
%          This program is distributed in the hope that it will be
%	   useful, but WITHOUT ANY WARRANTY; without even the implied
%	   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
%	   PURPOSE.  See the GNU General Public License for more details.

#ifnexists profile_on
% profiling does not work with stack check enabled.
autoload("enable_stack_check", "stkcheck");
try
{
    enable_stack_check();
}
catch OpenError: {} % stkcheck not found in the path
#endif

%!%+
%\variable{Integer_Type RWD_Match}
%\synopsis{Defines what and how read_with_description() makes a match}
%\description
%  This variable defines how a completion should get found for
%  read_with_description().
%  0 means the matching is case insensitiv and only with the text
%  1 means the matching is case sensitiv and only with the text
%  2 means the matching is case insensitiv and with the text and the
%          description
%  3 means the matching is case sensitiv and with the text and the
%          description
%
%  The default is 1.
%\seealso{read_with_completion()}
%!%-
custom_variable("RWD_Match", 1);

private variable completion_list, max_compl_width;

private define rwd_complete()
{
    variable old_buf = whatbuf();

    variable line = line_as_string(), buffer_new = not bufferp("*Completions*");
    setbuf("*Completions*");

    if (buffer_new)
    {
        if (max_compl_width > SCREEN_WIDTH/2)
          max_compl_width = SCREEN_WIDTH/2;

        insert("!!! Use Page Up/Down keys to scroll this window. !!!\n");
        foreach (completion_list)
        {
            variable item = ();
            !if ( strlen(item.compl) )
              continue;

            insert(item.compl);
            whitespace(1 + max_compl_width - strlen(item.compl));
            insert("--");
            if ( strlen(item.desc) )
            {
                insert_char(' ');
                insert(item.desc);
            }
            newline();
        }
        set_buffer_modified_flag(0);
        CASE_SEARCH = RWD_Match & 1;
        set_status_line(" Completion buffer ", 0);

        __uninitialize(&completion_list);
    }

    variable possible_completions;
    if (LAST_KBD_COMMAND == "%rwd_complete%")
    {
        pop2buf("*Completions*");
        if ( eobp() )
        {
            bob();
            () = down(1);
        }

        if ( is_line_hidden() )
          skip_hidden_lines_forward(1);

        % if not all lines are hidden, we have a completion
        possible_completions = not eobp();
    }
    else
    {
        bob();
        possible_completions = 0;
        variable search_fun;
        if ( (RWD_Match & 2) != 0 )
          search_fun = &ffind();
        else
          search_fun = &looking_at();

        while ( down(1) )
        {
            if ( orelse {strlen(line) == 0} {@search_fun(line)} )
            {
                set_line_hidden(0);
                ++possible_completions;
            }
            else
              set_line_hidden(1);
        }
        bob();
        () = down(1);

        if (possible_completions == 1 and is_line_hidden() )
          skip_hidden_lines_forward(1);
    }

    variable completion;
    if (possible_completions == 1)
    {
        bol();
        push_mark();
        skip_chars("^ \t");
        completion = bufsubstr();
        skip_hidden_lines_forward(1);
    }

    pop2buf(old_buf);
    setbuf(old_buf);

    switch (possible_completions)
    { case 0: % no completion error
        flush("No possible completion!");
    }
    { case 1: % excactly on completion
        if (whatbuf() != old_buf)
        {
            % Fixme: find the real reason
            % this is a hack to see in the *traceback* buffer the buffer name
            variable buf = whatbuf();
            throw InternalError;
        }
        delete_line();
        insert(completion);
    }

    set_current_kbd_command("%rwd_complete%");
}

%!%+
%\function{read_with_description}
%\synopsis{Prompt the user for a string and give him a description of the completion}
%\usage{String_Type read_with_description(prompt, dflt, init, list)}
%\description
% This is an extention of the read_with_completion() function to print a
% description beside the proposal in the completion buffer to give the user an
% idea of what he selects.
% 
% The argument prompt is the prompt for the user, dflt is returned if the user
% simply hits return, init is th value to use as initialisation of the answer
% and list is an List_Type of Struct_Type with two componentes "compl" for the
% completions and "desc" for the description of the completion. The values are
% presented in the order they are in the list, e. g. you can determine the
% sorting order.
%\seealso{read_with_completion()}
%!%-
public define read_with_description(prompt, dflt, init, list)
{
    max_compl_width = 0;
    foreach (list)
    {
        variable item = (), compl = item.compl, desc = item.desc;
        if ( strlen(compl) != strlen(str_delete_chars(compl, " \t\n\r")) )
          throw InvalidParmError, "The completion \"" + compl +
                                               "\" contains a whitespace";

        if (strlen(compl) > max_compl_width)
          max_compl_width = strlen(compl);

        if ( strlen(desc) != strlen(str_delete_chars(desc, "\r\n")) )
          throw InvalidParmError, "The description \"" + desc +
                "\" of completion \"" + compl + "\" contains a linebreak";
    }
    completion_list = list;

    if ( bufferp("*Completions*") )
    {
        variable orig_buf = whatbuf();

        setbuf("*Completions*");
        set_buffer_modified_flag(0);
        % jump back that delbuf() does not delete the current buffer and
        % jump to an arbitrary buffer
        setbuf(orig_buf);

        delbuf("*Completions*");
    }

    % mangle the keymap of the mini buffer
    definekey(&rwd_complete, "\t", "Mini_Map");
    definekey(&rwd_complete, " ", "Mini_Map");

    % mangle the color of hidden line indicator
    variable old_fg, old_bg;
    (old_fg, old_bg) = get_color("...");
    set_color("...", old_bg, old_bg);

    try
      return read_with_completion("", prompt, dflt, init, 's');
    finally
    {
        definekey("mini_complete", "\t", "Mini_Map");
        definekey("mini_complete", " ", "Mini_Map");
        set_color("...", old_fg, old_bg);

        if ( bufferp("*Completions*") )
        {
            variable old_buf = whatbuf();

            if ( buffer_visible("*Completions*") )
            {
                pop2buf("*Completions*");
                call("delete_window");
                pop2buf(old_buf);
            }

            delbuf("*Completions*");
        }
    }
}

provide("read_with_description");
