83 lines
3.5 KiB
C
83 lines
3.5 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 */
|