我在C中编写一个解释器,它在ubuntu控制台中运行。我想在打字时更改int
,double
,function
等关键字的颜色。
示例:functio
应为白色,但只要输入关键字function
,其颜色应更改为蓝色。
回答我的问题的简单例子:我想在字母数为奇数时将终端中的文本颜色设置为蓝色,在偶数时将其设置为红色,并且应该在键入时(不是在按下回车后)进行。
如何操作终端中的文本并在打字时改变颜色?
终端控制台上的实时文本编辑是一种黑色艺术,因为没有可靠的方法来确保控制台支持您需要的任何和所有转义序列。对于全屏编辑器,您可以使用ncurses
,但“内联”比它看起来更难。一个例子:我的其他相当兼容的Mac OS X自己的终端可以优雅地处理单行输入,但是当文本字符串包含非超出终端宽度的非打印或跨度时会感到困惑。
以下不那么快(但仍然很脏)的C程序允许非常基本的输入。它需要跳过的一些箍:在OS X上,getchar
将其输入回送到命令行;在输入Enter之前,终端本身(!)不会将其输入转发到正在运行的程序。其他控制台可能有其他怪癖。
通过单线编辑工作,唯一值得注意的是如何处理运行线以显示颜色的部件。剩下的就是在显示时在字符串上移动,使用ANSI Escape代码来打开和关闭颜色。我的语法高亮方法非常基础;如果您有更多关键字,您可能希望以更快的方式查找它们。它也无法处理通配符,因此您无法突出显示“任何十进制数”之类的内容。
#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
的来源,并检查是否更容易调整其重绘例程以允许自定义语法突出显示,而不是重新发明轮子。
除此之外,它的工作原理很好: