插入字符是去修改editorConfig.row.chars
中的字符
void editorRowInsertChar(erow *row, int at, int c) {
if (at < 0 || at > row->size) at = row->size;
row->chars = realloc(row->chars, row->size + 2);
//后移一个字符
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
row->size++;
row->chars[at] = c;
editorUpdateRow(row);
}
void editorInsertChar(int c) {
if (E.cy == E.numrows) {
editorAppendRow("", 0);
}
editorRowInsertChar(&E.row[E.cy], E.cx, c);
E.cx++;
}
void editorProcessKeypress() {
int c = editorReadKey();
switch (c) {
case CTRL_KEY('q'):
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
//调用插入字符函数
default:
editorInsertChar(c);
break;
}
}
暂不进行处理。屏幕会在按下任意键的时候进行刷新,所以
enum editorKey {
BACKSPACE = 127,//退格键
ARROW_LEFT = 1000,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
DEL_KEY,
HOME_KEY,
END_KEY,
PAGE_UP,
PAGE_DOWN
};
void editorProcessKeypress() {
int c = editorReadKey();
switch (c) {
case '\r':
/* TODO */
break;
case CTRL_KEY('q'):
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
/* TODO:Delete键 */
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l')://忽略Ctrl-l
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
}
将结构体数组转换为要写到文件里的单个字符串,直接写入文件
更高明的文件编辑器会创建临时文件来应对写入失败
#include
char *editorRowsToString(int *buflen) {
int totlen = 0;
int j;
for (j = 0; j < E.numrows; j++)
//多加的1就是换行符
totlen += E.row[j].size + 1;
*buflen = totlen;
char *buf = malloc(totlen);
char *p = buf;
for (j = 0; j < E.numrows; j++) {
memcpy(p, E.row[j].chars, E.row[j].size);
p += E.row[j].size;
*p = '\n';
p++;
}
return buf;
}
void editorSave() {
if (E.filename == NULL) return;
int len;
char *buf = editorRowsToString(&len);
//第三个参数用来规定对该文件的所有者、文件的用户组及系统中其他用户的访问权限
int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
if (fd != -1) {
if (ftruncate(fd, len) != -1) {//限定文件尺寸
if (write(fd, buf, len) == len) {
close(fd);
free(buf);
//回显消息
editorSetStatusMessage("%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf);
//strerror返回人类可读的错误字符串
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
}
保存快捷键
void editorProcessKeypress() {
int c = editorReadKey();
switch (c) {
case '\r':
/* TODO */
break;
case CTRL_KEY('q'):
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
//保存快捷键
case CTRL_KEY('s'):
editorSave();
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
/* TODO */
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
}
int main(int argc, char *argv[]) {
enableRawMode();
initEditor();
if (argc >= 2) {
editorOpen(argv[1]);
}
editorSetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit");
while (1) {
editorRefreshScreen();
editorProcessKeypress();
}
return 0;
}
用户直接关闭文件时需要检测文件是否脏,从而进行提醒,所以需要在全局配置结构体中添加成员
struct editorConfig {
int cx, cy;
int rx;
int rowoff;
int coloff;
int screenrows;
int screencols;
int numrows;
erow *row;
int dirty;
char *filename;
char statusmsg[80];
time_t statusmsg_time;
struct termios orig_termios;
};
void initEditor() {
E.cx = 0;
E.cy = 0;
E.rx = 0;
E.rowoff = 0;
E.coloff = 0;
E.numrows = 0;
E.row = NULL;
E.dirty = 0;
E.filename = NULL;
E.statusmsg[0] = '\0';
E.statusmsg_time = 0;
if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");
E.screenrows -= 2;
}
在状态栏中显示脏标志
void editorDrawStatusBar(struct abuf *ab) {
abAppend(ab, "\x1b[7m", 4);
char status[80], rstatus[80];
//文件名 + 总行数 + 是否修改过
int len = snprintf(status, sizeof(status), "%.20s - %d lines %s",
E.filename ? E.filename : "[No Name]", E.numrows,
E.dirty ? "(modified)" : "");
int rlen = snprintf(rstatus, sizeof(rstatus), "%d/%d",
E.cy + 1, E.numrows);
if (len > E.screencols) len = E.screencols;
abAppend(ab, status, len);
while (len < E.screencols) {
if (E.screencols - len == rlen) {
abAppend(ab, rstatus, rlen);
break;
} else {
abAppend(ab, " ", 1);
len++;
}
}
abAppend(ab, "\x1b[m", 3);
abAppend(ab, "\r\n", 2);
}
更新脏标志
void editorAppendRow(char *s, size_t len) {
E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
int at = E.numrows;
E.row[at].size = len;
E.row[at].chars = malloc(len + 1);
memcpy(E.row[at].chars, s, len);
E.row[at].chars[len] = '\0';
E.row[at].rsize = 0;
E.row[at].render = NULL;
editorUpdateRow(&E.row[at]);
E.numrows++;
E.dirty++;
}
void editorRowInsertChar(erow *row, int at, int c) {
if (at < 0 || at > row->size) at = row->size;
row->chars = realloc(row->chars, row->size + 2);
memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1);
row->size++;
row->chars[at] = c;
editorUpdateRow(row);
E.dirty++;
}
重置脏标志
void editorOpen(char *filename) {
free(E.filename);
E.filename = strdup(filename);
FILE *fp = fopen(filename, "r");
if (!fp) die("fopen");
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, fp)) != -1) {
while (linelen > 0 && (line[linelen - 1] == '\n' ||
line[linelen - 1] == '\r'))
linelen--;
editorAppendRow(line, linelen);
}
free(line);
fclose(fp);
E.dirty = 0;
}
void editorSave() {
if (E.filename == NULL) return;
int len;
char *buf = editorRowsToString(&len);
int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
if (fd != -1) {
if (ftruncate(fd, len) != -1) {
if (write(fd, buf, len) == len) {
close(fd);
free(buf);
//重置
E.dirty = 0;
editorSetStatusMessage("%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
}
#define KILO_QUIT_TIMES 3
void editorProcessKeypress() {
//按三次才确认退出
static int quit_times = KILO_QUIT_TIMES;
int c = editorReadKey();
switch (c) {
case '\r':
/* TODO */
break;
case CTRL_KEY('q'):
if (E.dirty && quit_times > 0) {
editorSetStatusMessage("WARNING!!! File has unsaved changes. "
"Press Ctrl-Q %d more times to quit.", quit_times);
quit_times--;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case CTRL_KEY('s'):
editorSave();
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
/* TODO */
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
quit_times = KILO_QUIT_TIMES;
}
void editorRowDelChar(erow *row, int at) {
if (at < 0 || at >= row->size) return;
//不进行内存缩小
memmove(&row->chars[at], &row->chars[at + 1], row->size - at);
row->size--;
editorUpdateRow(row);
E.dirty++;
}
void editorDelChar() {
if (E.cy == E.numrows) return;
erow *row = &E.row[E.cy];
if (E.cx > 0) {
editorRowDelChar(row, E.cx - 1);
E.cx--;
}
}
按键映射,BACKSPACE和Ctrl-h直接删除,Delete光标右移后删除
void editorProcessKeypress() {
static int quit_times = KILO_QUIT_TIMES;
int c = editorReadKey();
switch (c) {
case '\r':
/* TODO */
break;
case CTRL_KEY('q'):
if (E.dirty && quit_times > 0) {
editorSetStatusMessage("WARNING!!! File has unsaved changes. "
"Press Ctrl-Q %d more times to quit.", quit_times);
quit_times--;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case CTRL_KEY('s'):
editorSave();
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT);
editorDelChar();
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
quit_times = KILO_QUIT_TIMES;
}
我们需要两个新的行操作:一个用于将字符串附加到行,另一个用于删除行。
void editorFreeRow(erow *row) {
free(row->render);
free(row->chars);
}
void editorDelRow(int at) {
if (at < 0 || at >= E.numrows) return;
editorFreeRow(&E.row[at]);
memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at - 1));
E.numrows--;
E.dirty++;
}
void editorRowAppendString(erow *row, char *s, size_t len) {
row->chars = realloc(row->chars, row->size + len + 1);
memcpy(&row->chars[row->size], s, len);
row->size += len;
row->chars[row->size] = '\0';
editorUpdateRow(row);
E.dirty++;
}
void editorDelChar() {
if (E.cy == E.numrows) return;
if (E.cx == 0 && E.cy == 0) return;
erow *row = &E.row[E.cy];
if (E.cx > 0) {
editorRowDelChar(row, E.cx - 1);
E.cx--;
} else {
//在行首退格
E.cx = E.row[E.cy - 1].size;
editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size);
editorDelRow(E.cy);
E.cy--;
}
}
void editorInsertRow(int at, char *s, size_t len) {
if (at < 0 || at > E.numrows) return;
E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at));
//int at = E.numrows;
E.row[at].size = len;
E.row[at].chars = malloc(len + 1);
memcpy(E.row[at].chars, s, len);
E.row[at].chars[len] = '\0';
E.row[at].rsize = 0;
E.row[at].render = NULL;
editorUpdateRow(&E.row[at]);
E.numrows++;
E.dirty++;
}
void editorInsertChar(int c) {
if (E.cy == E.numrows) {
//在文末插入一行
editorInsertRow(E.numrows, "", 0);
}
editorRowInsertChar(&E.row[E.cy], E.cx, c);
E.cx++;
}
void editorOpen(char *filename) {
free(E.filename);
E.filename = strdup(filename);
FILE *fp = fopen(filename, "r");
if (!fp) die("fopen");
char *line = NULL;
size_t linecap = 0;
ssize_t linelen;
while ((linelen = getline(&line, &linecap, fp)) != -1) {
while (linelen > 0 && (line[linelen - 1] == '\n' || line[linelen - 1] == '\r'))
linelen--;
//打开文件时向结构体数组中插入行
editorInsertRow(E.numrows, line, linelen);
}
free(line);
fclose(fp);
E.dirty = 0;
}
Enter键真正的处理函数
void editorInsertNewline() {
if (E.cx == 0) {
editorInsertRow(E.cy, "", 0);
} else {
//Enter前还有字符
erow *row = &E.row[E.cy];
editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx);
row = &E.row[E.cy];
row->size = E.cx;
row->chars[row->size] = '\0';
editorUpdateRow(row);
}
E.cy++;
E.cx = 0;
}
键盘映射
void editorProcessKeypress() {
static int quit_times = KILO_QUIT_TIMES;
int c = editorReadKey();
switch (c) {
case '\r':
//Enter键换行
editorInsertNewline();
break;
case CTRL_KEY('q'):
if (E.dirty && quit_times > 0) {
editorSetStatusMessage("WARNING!!! File has unsaved changes. "
"Press Ctrl-Q %d more times to quit.", quit_times);
quit_times--;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case CTRL_KEY('s'):
editorSave();
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT);
editorDelChar();
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
quit_times = KILO_QUIT_TIMES;
}
无参运行时在空白文件中进行编辑,但无法保存,需要使用另存为的功能将空白文件保存
//获取输入的另存为的文件名
char *editorPrompt(char *prompt) {
size_t bufsize = 128;
char *buf = malloc(bufsize);
size_t buflen = 0;
buf[0] = '\0';
while (1) {
editorSetStatusMessage(prompt, buf);
editorRefreshScreen();
int c = editorReadKey();
//允许进行删除字符
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
if (buflen != 0) buf[--buflen] = '\0';
} else if (c == '\x1b') {
//ESC取消输入文件名
editorSetStatusMessage("");
free(buf);
return NULL;
} else if (c == '\r') {
//Enter确认文件名
if (buflen != 0) {
editorSetStatusMessage("");
return buf;
}
} else if (!iscntrl(c) && c < 128) {
if (buflen == bufsize - 1) {
bufsize *= 2;
buf = realloc(buf, bufsize);
}
buf[buflen++] = c;
buf[buflen] = '\0';
}
}
}
void editorSave() {
if (E.filename == NULL) {
E.filename = editorPrompt("Save as: %s (ESC to cancel)");
if (E.filename == NULL) {
//回滚保存
editorSetStatusMessage("Save aborted");
return;
}
}
int len;
char *buf = editorRowsToString(&len);
int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
if (fd != -1) {
if (ftruncate(fd, len) != -1) {
if (write(fd, buf, len) == len) {
close(fd);
free(buf);
E.dirty = 0;
editorSetStatusMessage("%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
}
int editorRowRxToCx(erow *row, int rx) {
int cur_rx = 0;
int cx;
for (cx = 0; cx < row->size; cx++) {
if (row->chars[cx] == '\t')
cur_rx += (KILO_TAB_STOP - 1) - (cur_rx % KILO_TAB_STOP);
cur_rx++;
if (cur_rx > rx) return cx;
}
return cx;
}
void editorFind() {
char *query = editorPrompt("Search: %s (ESC to cancel)");
if (query == NULL) return;
int i;
//遍历文件所有行字符串进行遍历
for (i = 0; i < E.numrows; i++) {
erow *row = &E.row[i];
char *match = strstr(row->render, query);
if (match) {
//匹配则光标跳转
E.cy = i;
//
E.cx = editorRowRxToCx(row, match - row->render);
E.rowoff = E.numrows;
break;
}
}
free(query);
}
添加到键盘映射
void editorProcessKeypress() {
static int quit_times = KILO_QUIT_TIMES;
int c = editorReadKey();
switch (c) {
case '\r':
editorInsertNewline();
break;
case CTRL_KEY('q'):
if (E.dirty && quit_times > 0) {
editorSetStatusMessage("WARNING!!! File has unsaved changes. "
"Press Ctrl-Q %d more times to quit.", quit_times);
quit_times--;
return;
}
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
break;
case CTRL_KEY('s'):
editorSave();
break;
case HOME_KEY:
E.cx = 0;
break;
case END_KEY:
if (E.cy < E.numrows)
E.cx = E.row[E.cy].size;
break;
case CTRL_KEY('f'):
//查找
editorFind();
break;
case BACKSPACE:
case CTRL_KEY('h'):
case DEL_KEY:
if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT);
editorDelChar();
break;
case PAGE_UP:
case PAGE_DOWN:
{
if (c == PAGE_UP) {
E.cy = E.rowoff;
} else if (c == PAGE_DOWN) {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--)
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
}
break;
case ARROW_UP:
case ARROW_DOWN:
case ARROW_LEFT:
case ARROW_RIGHT:
editorMoveCursor(c);
break;
case CTRL_KEY('l'):
case '\x1b':
break;
default:
editorInsertChar(c);
break;
}
quit_times = KILO_QUIT_TIMES;
}
int main(int argc, char *argv[]) {
enableRawMode();
initEditor();
if (argc >= 2) {
editorOpen(argv[1]);
}
editorSetStatusMessage("HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find");
while (1) {
editorRefreshScreen();
editorProcessKeypress();
}
return 0;
}
在用户输入时,实时的进行搜索,计划是将搜索的代码放入回调函数,在每次输入目标时调用回调
//在结束目标输入(如另存为的文件名、搜索的目标)时进行回调
char *editorPrompt(char *prompt, void (*callback)(char *, int)) {
size_t bufsize = 128;
char *buf = malloc(bufsize);
size_t buflen = 0;
buf[0] = '\0';
while (1) {
editorSetStatusMessage(prompt, buf);
editorRefreshScreen();
int c = editorReadKey();
if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) {
if (buflen != 0) buf[--buflen] = '\0';
} else if (c == '\x1b') {
editorSetStatusMessage("");
//
if (callback) callback(buf, c);
free(buf);
return NULL;
} else if (c == '\r') {
if (buflen != 0) {
editorSetStatusMessage("");
//
if (callback) callback(buf, c);
return buf;
}
} else if (!iscntrl(c) && c < 128) {
if (buflen == bufsize - 1) {
bufsize *= 2;
buf = realloc(buf, bufsize);
}
buf[buflen++] = c;
buf[buflen] = '\0';
}
//
if (callback) callback(buf, c);
}
}
更新调用格式
void editorSave() {
if (E.filename == NULL) {
E.filename = editorPrompt("Save as: %s (ESC to cancel)", NULL);
if (E.filename == NULL) {
editorSetStatusMessage("Save aborted");
return;
}
}
int len;
char *buf = editorRowsToString(&len);
int fd = open(E.filename, O_RDWR | O_CREAT, 0644);
if (fd != -1) {
if (ftruncate(fd, len) != -1) {
if (write(fd, buf, len) == len) {
close(fd);
free(buf);
E.dirty = 0;
editorSetStatusMessage("%d bytes written to disk", len);
return;
}
}
close(fd);
}
free(buf);
editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno));
}
void editorFindCallback(char *query, int key) {
if (key == '\r' || key == '\x1b') {
return;
}
int i;
for (i = 0; i < E.numrows; i++) {
erow *row = &E.row[i];
char *match = strstr(row->render, query);
if (match) {
E.cy = i;
E.cx = editorRowRxToCx(row, match - row->render);
E.rowoff = E.numrows;
break;
}
}
}
void editorFind() {
//将editorFind()的内容移到回调函数中
char *query = editorPrompt("Search: %s (ESC to cancel)", editorFindCallback);
if (query) {
free(query);
}
}
void editorFind() {
//保存光标位置
int saved_cx = E.cx;
int saved_cy = E.cy;
int saved_coloff = E.coloff;
int saved_rowoff = E.rowoff;
char *query = editorPrompt("Search: %s (ESC to cancel)", editorFindCallback);
if (query) {
free(query);
} else {
//恢复光标位置
E.cx = saved_cx;
E.cy = saved_cy;
E.coloff = saved_coloff;
E.rowoff = saved_rowoff;
}
}
void editorFindCallback(char *query, int key) {
static int last_match = -1;
static int direction = 1;
if (key == '\r' || key == '\x1b') {
last_match = -1;
direction = 1;
return;
} else if (key == ARROW_RIGHT || key == ARROW_DOWN) {
//按右/下键向后搜索
direction = 1;
} else if (key == ARROW_LEFT || key == ARROW_UP) {
//按左/上键向前搜索
direction = -1;
} else {
last_match = -1;
direction = 1;
}
if (last_match == -1) direction = 1;
//当前行恢复为上一个匹配行
int current = last_match;
int i;
for (i = 0; i < E.numrows; i++) {
//当前行不断向上/向下
current += direction;
if (current == -1) current = E.numrows - 1;
else if (current == E.numrows) current = 0;
erow *row = &E.row[current];
char *match = strstr(row->render, query);
if (match) {
//上一个匹配行 = 当前行
last_match = current;
E.cy = current;
E.cx = editorRowRxToCx(row, match - row->render);
E.rowoff = E.numrows;
break;
}
}
}
void editorFind() {
int saved_cx = E.cx;
int saved_cy = E.cy;
int saved_coloff = E.coloff;
int saved_rowoff = E.rowoff;
char *query = editorPrompt("Search: %s (Use ESC/Arrows/Enter)", editorFindCallback);
if (query) {
free(query);
} else {
E.cx = saved_cx;
E.cy = saved_cy;
E.coloff = saved_coloff;
E.rowoff = saved_rowoff;
}
}