83 lines
3.6 KiB
C
83 lines
3.6 KiB
C
/* lineedit.h - REPL 的行编辑器
|
||
*
|
||
* 为什么需要它:原来的 REPL 用 fgets 直接读,终端停在规范模式,
|
||
* 方向键发出的转义序列(\x1b[D 之类)会被当成普通字节读进来,
|
||
* 显示成 ^[[D 而不是移动光标。要有行内编辑就必须自己进 raw 模式接管按键。
|
||
*
|
||
* 缓冲区与编辑操作是纯逻辑,可以脱离终端单测;只有渲染与按键读取需要 tty。
|
||
*/
|
||
#ifndef LIGENT_LINEEDIT_H
|
||
#define LIGENT_LINEEDIT_H
|
||
|
||
#include <stddef.h>
|
||
|
||
/* 可编辑缓冲区。pos 是字节偏移,且始终落在字符边界上 */
|
||
typedef struct {
|
||
char *buf;
|
||
size_t len;
|
||
size_t cap;
|
||
size_t pos;
|
||
} LineBuf;
|
||
|
||
void linebuf_init(LineBuf *lb);
|
||
void linebuf_free(LineBuf *lb);
|
||
|
||
/* 用 text 整体替换内容,光标移到末尾 */
|
||
void linebuf_set(LineBuf *lb, const char *text);
|
||
/* 在光标处插入 n 字节,光标随之后移 */
|
||
void linebuf_insert(LineBuf *lb, const char *text, size_t n);
|
||
|
||
void linebuf_backspace(LineBuf *lb); /* 删除光标前一个字符 */
|
||
void linebuf_delete(LineBuf *lb); /* 删除光标处的字符 */
|
||
void linebuf_left(LineBuf *lb); /* 光标左移一个字符 */
|
||
void linebuf_right(LineBuf *lb); /* 光标右移一个字符 */
|
||
void linebuf_home(LineBuf *lb);
|
||
void linebuf_end(LineBuf *lb);
|
||
void linebuf_kill_to_end(LineBuf *lb); /* 删到行尾 */
|
||
void linebuf_kill_to_start(LineBuf *lb); /* 删到行首 */
|
||
void linebuf_kill_word(LineBuf *lb); /* 往回删一个词 */
|
||
|
||
/* 输入行是否着色(整行:提示符 + 你敲进去的字)。
|
||
* 传进来的 prompt 应当是纯文本 —— 颜色由编辑器统一加在两头。 */
|
||
void line_edit_set_color(int enabled);
|
||
|
||
/* 换输入行的墨色(ANSI 起手式,例如 "\033[35m")。NULL 恢复默认的绿色。
|
||
* meta 模式靠它和主提示符区分开。 */
|
||
void line_edit_set_ink(const char *ansi);
|
||
|
||
/* 读入一行(不含换行符,malloc)。
|
||
* Ctrl-C -> 返回 NULL,*interrupted = 1
|
||
* Ctrl-D 空行 -> 返回 NULL,*interrupted = 0(终止输入)
|
||
* stdin 非终端 -> 退化为按行读取
|
||
* 返回的字符串可能为空串(用户直接回车),调用方负责 free。 */
|
||
char *line_edit(const char *prompt, int *interrupted);
|
||
|
||
/* 进程内的历史记录(上下方向键用) */
|
||
|
||
/* 追加一条。返回 1 表示真的记下了,0 表示被忽略(空行,或与上一条重复)。 */
|
||
int line_history_add(const char *line);
|
||
void line_history_clear(void);
|
||
size_t line_history_len(void);
|
||
const char *line_history_get(size_t i);
|
||
|
||
/* ---- 输入历史的持久化 ----
|
||
*
|
||
* 文件位置由调用方决定(ligent 用 <二进制目录>/ligent.history)。
|
||
* 一条历史在文件里占一行,所以条目里的反斜杠和换行会被转义
|
||
* (\ -> \\,换行 -> \n),保证读回来和写出去一模一样。不含反斜杠的
|
||
* 普通命令在文件里与输入完全一致,肉眼可读。
|
||
*
|
||
* 时间戳之类的元数据刻意不记:目的只是"上下键能翻到",不是审计。 */
|
||
|
||
/* 从 path 读取历史,追加到当前历史之后。最多保留最新的 keep 条;
|
||
* 文件本身更长时会被就地截短(否则会无限增长)。
|
||
* 文件不存在返回 0,读出错返回 -1。 */
|
||
int line_history_load(const char *path, size_t keep);
|
||
|
||
/* 把一条输入实时追加到 path —— 每提交一条就写一次并立刻 flush,
|
||
* 因此另一个 ligent 实例马上就能翻到。line 为空时什么都不做。
|
||
* 成功返回 0,失败返回 -1(失败不打断 REPL)。 */
|
||
int line_history_append(const char *path, const char *line);
|
||
|
||
#endif /* LIGENT_LINEEDIT_H */
|