Files
ligent/src/lineedit.h
T
2026-10-03 14:56:56 +08:00

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 */