Readline:如何在双选项卡上列出所有自动完成匹配项?

Readline: How to list all autocomplete matches on double tab?

我正在使用 "readline" 库为我的程序创建控制台界面。我可以使用 tab 自动完成单词,但是当我有共享相同前缀的单词时,如 (car, card, carbon) 它总是选择最短的一个。这是我的程序(主要取自 link):

#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <iostream>

const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wifes"};

void *xmalloc (int size)
{
    void *buf;
    buf = malloc (size);
    if (!buf)
    {
        fprintf (stderr, "Error: Out of memory. Exiting.\n");
        exit (1);
    }
    return buf;
}

char *dupstr (const char *str)
{
    char *temp;
    temp = (char *) xmalloc (strlen (str) + 1);
    strcpy (temp, str);
    return (temp);
}

char *my_generator (const char *text, int state)
{
    static int list_index, len;
    const char *name;

    if (!state)
    {
        list_index = 0;
        len = strlen (text);
    }

    while (name = words[list_index])
    {
        list_index++;
        if (strncmp (name, text, len) == 0) return dupstr (name);
    }

    // If no names matched, then return NULL.
    return ((char *) NULL);
}

static char **my_completion (const char *text, int start, int end)
{
    char **matches = (char **) NULL;
    if (start == 0)
    {
        matches = rl_completion_matches ((char *) text, &my_generator);
    }
    else rl_bind_key ('\t', rl_abort);
    return matches;
}

int main (int argc, char *argv[])
{
    char *buf;
    rl_attempted_completion_function = my_completion;

    while ((buf = readline(">> ")) != NULL)
    {
        rl_bind_key ('\t', rl_complete);

        if (strcmp (buf, "exit") == 0) break;
        else if (buf[0] == '[=10=]') continue;
        else
        {
            std::cout << buf << std::endl;
            add_history (buf);
        }
    }
    free (buf);

    return 0;
}

是否可以像 ubuntu 终端一样在双 选项卡 上列出所有匹配项?

我设法通过注释掉这两行使其工作:

rl_bind_key ('\t', rl_complete);

和:

else rl_bind_key ('\t', rl_abort);

readline 的默认完成行为与 ubuntu 终端完全一样,一个选项卡完成,两个选项卡列出可能的完成。不确定与 Tab 键绑定的默认完成功能是什么,从 documentation 我认为它是 rl_possible_completions 但它没有给出相同的结果。

我还向 my_completion 函数添加了以下行,以防止在匹配词的末尾添加 space:

rl_completion_append_character = '[=12=]';

我删除了dupstrfunction 并用原生的strdup函数替换了它(这与自动完成问题无关,只是为了删除不必要的代码)。

这是最终代码:

#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>
#include <iostream>

const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wives"};

// Generator function for word completion.
char *my_generator (const char *text, int state)
{
    static int list_index, len;
    const char *name;

    if (!state)
    {
        list_index = 0;
        len = strlen (text);
    }

    while (name = words[list_index])
    {
        list_index++;
        if (strncmp (name, text, len) == 0) return strdup (name);
    }

    // If no names matched, then return NULL.
    return ((char *) NULL);
}

// Custom completion function
static char **my_completion (const char *text, int start, int end)
{
    // This prevents appending space to the end of the matching word
    rl_completion_append_character = '[=13=]';

    char **matches = (char **) NULL;
    if (start == 0)
    {
        matches = rl_completion_matches ((char *) text, &my_generator);
    }
    // else rl_bind_key ('\t', rl_abort);
    return matches;
}

int main (int argc, char *argv[])
{
    char *buf;
    rl_attempted_completion_function = my_completion;

    while ((buf = readline(">> ")) != NULL)
    {
        // rl_bind_key ('\t', rl_complete);

        if (strcmp (buf, "exit") == 0) break;
        else if (buf[0] == '[=13=]')
        {
            free (buf);
            continue;
        }
        else
        {
            std::cout << buf << std::endl;
            add_history (buf);
        }

        free (buf);
        buf = NULL;
    }
    if (buf != NULL) free (buf);

    return 0;
}

razzak 的回答几乎是正确的,但是这个 NULL 必须添加在字符串数组的末尾:

const char *words[] = {"add", "remove", "rm", "update", "child", "children", "wife", "wives", NULL};

my_generator() 函数中非警告编译的一些更改:

while ((name = words[list_index++]))
{
    if (strncmp (name, text, len) == 0) return strdup (name);
}