【问题标题】:Change colors of keywords in linux terminal while typing键入时更改linux终端中关键字的颜色
【发布时间】:2018-06-06 17:16:40
【问题描述】:

我正在用 C 语言编写一个解释器,它在 ubuntu 控制台中运行。我想在输入时更改 intdoublefunction 等关键字的颜色。

例如:functio 应该是白色的,但是当我输入完关键字 function 后,它的颜色应该变成蓝色。

可以回答我的问题的简单示例:当字母数为奇数时,我想将终端中的文本颜色设置为蓝色,当字母数为偶数时设置为红色,并且应该在键入时发生(而不是在按下 enter 之后)。

如何在终端中对文本进行操作并在输入时更改其颜色?

【问题讨论】:

  • ncurses
  • 你可能想看看这个:askubuntu.com/questions/558280/…
  • 目前如何处理文本输入?如果它是面向行的(正如您的“按 Enter 后”所建议的那样),那么放弃它并重新开始使用基于字符的输入。有了它,这是一个标准的“我如何做语法高亮”问题。
  • 这比看起来要困难得多,我不建议这样做。您需要先提供自己的行编辑功能。如果您问如何在终端中为文本着色,您很可能也不知道如何进行行编辑。查看 GNU Readlineeditline 或类似的库,以了解行编辑的复杂性。您将重新实现此功能并在顶部添加颜色,因为现有库不允许轻松集成颜色。
  • 经过一番琢磨,我认为可以在 GNU Readline 中集成颜色。您必须提供自己的重新显示功能(请参阅rl_redisplay_function),该功能将调用rl_show_char 根据需要设置终端颜色。

标签: c linux terminal


【解决方案1】:

终端控制台上的实时文本编辑在很大程度上是一种黑色艺术,因为没有可靠的方法来确保控制台支持您需要的任何和所有转义序列。对于全屏编辑器,您可以使用ncurses,但“内联”比看起来更难。一个例子:我原本相当兼容的 Mac OS X 自己的终端可以优雅地处理单行输入,但是当文本字符串包含不可打印的内容或跨越的范围超过终端的宽度时,它会变得混乱。

以下不那么快(但仍然很脏)的 C 程序允许非常基本的输入。它需要跳过一些障碍:在 OS X 上,getchar 将其输入回显到命令行;在输入 Enter 之前,终端本身 (!) 不会将其输入转发给正在运行的程序。其他控制台可能有其他怪癖。

在单行编辑工作中,唯一值得注意的是如何处理运行的行以显示彩色部分。剩下的就是在显示字符串时遍历字符串,使用 ANSI 转义码来打开和关闭颜色。我的语法高亮方法非常基础;如果您有更多关键字,您可能希望以更快的方式查找它们。它也无法处理通配符,因此您无法突出显示“任何十进制数”之类的内容。

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <unistd.h>
#include <termios.h>

#define PP_INVERSE  "\x1b[7m"

#define PP_RED      "\x1b[31m"
#define PP_BLUE     "\x1b[34m"

#define PP_RESET    "\x1b[39m\x1b[0m"

const char *keyword_list[] = {
    "int",
    "double",
    "function"
};


/* ha ha */
#define SURELY_LARGE_ENOUGH 256

char line_buf[SURELY_LARGE_ENOUGH] = "";

void display_line (char *text, int cursor)
{
    char *startptr, *endptr;
    int i;

    printf ("\r>");

    startptr = text;

    do
    {
        /* hop from one 'word' to another */

        /* check for non-word first */
        while (*startptr && !isalnum(*startptr))
        {
            putchar (*startptr);
            startptr++;
        }

        if (*startptr)
        {
            endptr = startptr;

            /* gather next word */
            while (*endptr && isalnum(*endptr))
                endptr++;

            /* check if it's a known keyword */
            for (i=0; i<sizeof(keyword_list)/sizeof(keyword_list[0]); i++)
            {
                if (keyword_list[i][0] == *startptr && strlen(keyword_list[i]) == endptr - startptr)
                {
                    if (!strncmp (startptr, keyword_list[i], strlen(keyword_list[i])))
                    {
                        break;
                    }
                }
            }

            if (i<sizeof(keyword_list)/sizeof(keyword_list[0]))
            {
                printf (PP_INVERSE "%s" PP_RESET, keyword_list[i]);
                startptr = endptr;
            } else
            {
                if ((endptr - startptr) & 1)
                {
                    printf (PP_BLUE);
                } else
                {
                    printf (PP_RED);
                }
                while (startptr < endptr)
                {
                    putchar (*startptr);
                    startptr++;
                }
                printf (PP_RESET);
            }
        }
    } while (*startptr);

    /* erase any following leftovers */
    printf ("\x1B[K");

    /* position cursor */
    printf ("\r\x1B[%dC", cursor+1);
    fflush (stdout);
}

/* turn off echo for getchar
    see https://stackoverflow.com/questions/558009/ansi-c-no-echo-keyboard-input
*/
void getch_disable_echo (void)
{
    struct termios t;

    tcgetattr(STDIN_FILENO, &t);
    t.c_lflag &= ~ECHO;
    t.c_lflag &= ~ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
}

void getch_enable_echo (void)
{
    struct termios t;

    tcgetattr(STDIN_FILENO, &t);
    t.c_lflag |= ECHO;
    t.c_lflag |= ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
}

int get_input (void)
{
    int cursor_pos = 0;
    int key;

    do
    {
        display_line (line_buf, cursor_pos);
        key = getchar();
        if (isprint(key))
        {
            if (!line_buf[cursor_pos])
                line_buf[cursor_pos+1] = 0;
            line_buf[cursor_pos] = key;
            cursor_pos++;
        }
        /* poor man's backspace */
        if (key == 127)
        {
            if (cursor_pos)
            {
                cursor_pos--;
                line_buf[cursor_pos] = 0;
            }
        }
        if (key == '\n')
        {
            printf ("\r%s \n", line_buf);
            line_buf[0] = 0;
            cursor_pos = 0;
        }
    } while (key != EOF);

    return 0;
}

int main (void)
{
    getch_disable_echo();

    strcpy (line_buf, "this is a function test");
    get_input ();

    getch_enable_echo();
    return 0;
}

如您所见,这已经是一个相当大的程序,并且只允许进行基本的编辑。我省略了制表符完成、光标移动、撤消以及用户可能期望从全行编辑器获得的各种其他细节。您可以查看 GNU readline 的源代码,而不是重新发明轮子,并检查是否可以更轻松地调整其重绘例程以允许您的自定义语法突出显示。

除此之外,它工作得很好:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    • 2015-06-06
    • 1970-01-01
    • 2021-04-13
    相关资源
    最近更新 更多