Подавить печать новой подсказки при нажатии на вкладку с Readline

При использовании автодополнения с библиотекой Readline в C приглашение перепечатывается при двойном нажатии клавиши tab:

(prompt) view NAME_OF_F (user presses tab twice)
NAME_OF_FILE1   NAME_OF_FILE2   (suggestions by Readline)
(prompt) view NAME_OF_F 

Я хотел бы подавить повторную печать подсказки в 3-й строке, сохранив первую строку с предложениями под ней, например:

(prompt) view NAME_OF_F (user presses tab twice)
NAME_OF_FILE1   NAME_OF_FILE2   (suggestions by Readline)

Я бы хотел, чтобы курсор вернулся в конец первой строки с подсказкой.

Скомпилировано с помощью gcc -Wall -O0 -ggdb -fno-builtin rline.c -o rline -lreadline -ltermcap.

Вот пример кода:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>

int execute_line(char *line);
void initialize_readline();
static char **fileman_completion(char *text, int start, int end);
static char *command_generator(char *text, int state);

char *command[] = { "view", "quit", (char *)NULL };

int done; /* When non-zero, this global means the user is done using this program. */

int main(int argc, char **argv)
{
    char *line;
    initialize_readline();  /* Bind our completer. */
    for ( ; done == 0; ) {
        line = readline("> ");

        if (!line) 
            break;

        if (*line) 
            execute_line(line);
        free(line);
    }
    return 0;
}

/* String to pass to system().  This is for the VIEW command. */
static char syscom[1024];

int execute_line(char *line)
{
    int i = 0;
    char *word;
    /* Isolate the command word. */
    while (line[i] && whitespace(line[i]))
        i++;
    word = line + i;

    while (line[i] && !whitespace(line[i])) i++;

    if (line[i]) line[i++] = '\0';

    if (strcmp(word, "quit") == 0) {
        done = 1;
        return 0;
    } else if (strcmp(word, "view")) {
        fprintf(stderr, "%s: Choose only \"view FILE\" or \"quit\" as your command.\n", word);
        return -1;
    }

    /* Get argument to command, if any. */
    while (whitespace(line[i])) i++;

    word = line + i;
    if(!word || !*word) {
        fprintf(stderr, "view: Argument required.\n");
        return -1;
    }
    sprintf(syscom, "more %s", word);
    return system(syscom);
}

void initialize_readline()
{
    rl_readline_name = "rline";
    rl_attempted_completion_function = (rl_completion_func_t *)fileman_completion;
}

static char **fileman_completion(char *text, int start, int end)
{
    if (start == 0)
        return rl_completion_matches(text, (rl_compentry_func_t *)*command_generator);
    return NULL;
}

static char *command_generator(char *text, int state)
{
    static int list_index, len;
    char *name;
    if (!state) {
        list_index = 0;
        len = strlen(text);
    }
    while ((name = command[list_index++]))
        if (strncmp(name, text, len) == 0)
            return strdup(name);
    return NULL;
}

Программа принимает только команды view FILE_NAME для просмотра содержимого файла и quit для выхода из программы. Пример представляет собой сокращенную версию примера программы, которую можно найти здесь.


person tham mes    schedule 05.10.2020    source источник
comment
Примечание: strncmp(name, text, len) является UB, когда state истинно, поскольку len не установлено.   -  person chux - Reinstate Monica    schedule 05.10.2020
comment
Из любопытства, почему -fno-builtin?   -  person rici    schedule 05.10.2020
comment
@rici Чтобы я мог перемещаться по функциям строки чтения во время отладки с помощью gdb, я не знаю, правильно ли это. Я также скомпилировал readline7 с теми же параметрами.   -  person tham mes    schedule 05.10.2020
comment
Честно говоря, я не понимаю, почему это может повлиять на отладку. По крайней мере, мне никогда не приходилось проходить через встроенный апдейт.   -  person rici    schedule 05.10.2020


Ответы (2)


Я не думаю, что в readline встроено что-то подобное, но он предоставляет множество возможностей настройки, если вы хотите попробовать написать логику самостоятельно.

Вы можете попробовать написать собственный rl_completion_display_matches_hook для отображения списка завершения. . Но мне не совсем понятно, как вы потом будете восстанавливать позицию курсора. Я не думаю, что у readline есть общедоступный интерфейс для поиска или сброса позиции курсора. (И, конечно, возможно, что список завершения был настолько большим, что исходная команда прокручивалась за пределы экрана.)

В качестве альтернативы я мог использовать хук для печати списка завершения поверх текущей строки, а затем повторно отображать подсказку после списка завершения (хотя я обманул, предполагая, что текущий ввод всегда представляет собой только одну строку). Это не совсем то, о чем вы просили, но это может быть полезно для демонстрационных целей. Я использовал следующий пользовательский спичечный принтер:

static void display_matches(char** matches, int len, int max) {
    putp(carriage_return);
    putp(clr_eol);
    putp(cursor_up);
    rl_display_match_list(matches, len, max);
    rl_forced_update_display();
}

Я также добавил следующее в функцию инициализации:

    rl_completion_display_matches_hook = display_matches;
    setupterm(NULL, 1, (int*)0);
person rici    schedule 05.10.2020

Спасибо @rici за вдохновение. Я получил его работу с его функцией с некоторыми изменениями. Для правильной работы вам необходимо скачать библиотеку readline. В файле rlprivate.h из readline я удалил строки char **lines;, а строку #include "realdine.h" из display.c. Тогда в вашем собственном .c у вас должен быть #include </PATH/TO/display.c>. В этом display.c #include указывает на измененный rlprivate.h. Все это для того, чтобы у меня был доступ к _rl_move_vert(1).

static void display_matches(char** matches, int len, int max)
{
    int saved_point = rl_point;
    char *saved_line = rl_copy_text(0, rl_end);
    rl_save_prompt();
    rl_replace_line("", 0); // Clear the previous text
    putp(cursor_up);
    _rl_move_vert(1);
    rl_display_match_list(matches, len, max);
    putp(cursor_up);
    rl_restore_prompt();
    rl_replace_line(saved_line, 0);
    rl_point = saved_point;
    rl_redisplay();
    putp(cursor_down);
    free(saved_line);
}

person tham mes    schedule 10.10.2020