基于WSL的C++文本编辑器光标无法移动问题求助
C++文本编辑器光标无法移动问题排查
我是编程初学者,正在WSL环境下跟着朋友的教程开发C++文本编辑器。方向键的功能逻辑已经实现,但光标完全不动。试过修改editorMoveCursor函数、调整内存相关函数都没用,之前还出现过段错误。相关代码如下:
/*** includes ***/ enum editorKey { ARROW_LEFT = 1000, ARROW_RIGHT, ARROW_UP, ARROW_DOWN, DEL_KEY, HOME_KEY, END_KEY, PAGE_UP, PAGE_DOWN }; struct editorConfig { int cx, cy; int rowoff; // row offset int coloff; // column offset int screenrows; int screencols; int numrows; std::vector<erow> row; struct termios orig_termios; }; editorConfig E; /*** terminal ***/ void killswitch(const char *s) { write(STDOUT_FILENO, "\x1b[2J", 4); write(STDOUT_FILENO, "\x1b[H", 3); perror(s); exit(1); } int editorReadKey() { int nread; char c; while ((nread = read(STDIN_FILENO, &c, 1)) != 1) { if (nread == -1 && errno != EAGAIN) killswitch("read"); } if (c == '\x1b') { char seq[3]; if (read(STDIN_FILENO, &seq[0], 1) != 1) return '\x1b'; if (read(STDIN_FILENO, &seq[1], 1) != 1) return '\x1b'; if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { if (read(STDIN_FILENO, &seq[2], 1) != 1) return '\x1b'; if (seq[2] == '~') { switch (seq[1]) { case '1': return HOME_KEY; case '3': return DEL_KEY; case '4': return END_KEY; case '5': return PAGE_UP; case '6': return PAGE_DOWN; case '7': return HOME_KEY; case '8': return END_KEY; } } } else { switch (seq[1]) { case 'A': return ARROW_UP; case 'B': return ARROW_DOWN; case 'C': return ARROW_RIGHT; case 'D': return ARROW_LEFT; case 'H': return HOME_KEY; case 'F': return END_KEY; } } } else if (seq[0] == 'O') { switch (seq[1]) { case 'H': return HOME_KEY; case 'F': return END_KEY; } } return '\x1b'; } else { return c; } } int getCursorPosition(int *rows, int *cols) { char buf[32]; unsigned int i = 0; if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1; while (i < sizeof(buf) - 1) { if (read(STDIN_FILENO, &buf[i], 1) != 1) break; if (buf[i] == 'R') break; i++; } buf[i] = '\0'; if (buf[0] != '\x1b' || buf[1] != '[') return -1; if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1; return 0; } /*** output ***/ void editorScroll() { // to set the E.rowoff value if (E.cy < E.rowoff) { // check if cursor above window E.rowoff = E.cy; } if (E.cy >= E.rowoff + E.screenrows) { // check if moved outside window E.rowoff = E.cy - E.screenrows + 1; } if (E.cx < E.coloff) { E.coloff = E.cx; } if (E.cx >= E.coloff + E.screencols) { E.coloff = E.cx - E.screencols + 1; } } void editorDrawRows(abuf *ab) { int y; for (y = 0; y < E.screenrows; y++) { int filerow = y + E.rowoff; // to display each y position if (filerow >= E.numrows) { if (E.numrows == 0 && y == E.screenrows / 3) { char welcome[80]; int welcomelen = snprintf(welcome, sizeof(welcome), "Kilo editor -- version %s", KILO_VERSION); if (welcomelen > E.screencols) welcomelen = E.screencols; int padding = (E.screencols - welcomelen) / 2; if (padding) { abAppend(ab, "~", 1); padding--; } while (padding--) abAppend(ab, " ", 1); abAppend(ab, welcome, welcomelen); } else { abAppend(ab, "~", 1); } } else { int len = E.row[filerow].rsize - E.coloff; if (len < 0) len = 0; if (len > E.screencols) len = E.screencols; abAppend(ab, &E.row[filerow].render[E.coloff], len); } abAppend(ab, "\x1b[K", 3); if (y < E.screenrows - 1) { abAppend(ab, "\r\n", 2); } } } void editorRefreshScreen() { editorScroll(); abuf ab; abAppend(&ab, "\x1b[?25l", 6); abAppend(&ab, "\x1b[H", 3); editorDrawRows(&ab); char buf[32]; snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.cx - E.coloff) + 1); abAppend(&ab, buf, strlen(buf)); abAppend(&ab, "\x1b[?25h", 6); write(STDOUT_FILENO, ab.b, ab.len); abFree(&ab); } /*** input ***/ void editorMoveCursor(int key) { erow *row = (E.cy >= E.numrows) ? nullptr : &E.row[E.cy]; switch (key) { case ARROW_LEFT: if (E.cx != 0) { E.cx--; } else if (E.cy > 0) { E.cy--; E.cx = E.row[E.cy].size; } break; case ARROW_RIGHT: if (row && E.cx < row->size) { E.cx++; } else if (row && E.cx == row->size) { E.cy++; E.cx = 0; } break; case ARROW_UP: if (E.cy != 0) { E.cy--; } break; case ARROW_DOWN: if (E.cy < E.numrows - 1) { E.cy++; } break; } row = (E.cy >= E.numrows) ? nullptr : &E.row[E.cy]; int rowlen = row ? row->size : 0; if (E.cx > rowlen) { E.cx = rowlen; } } 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: E.cx = E.screencols - 1; break; case PAGE_UP: case PAGE_DOWN: { 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; } }
问题排查及修复方案
越界访问导致逻辑紊乱:
editorMoveCursor的ARROW_LEFT分支中,当E.cy减1后可能等于E.numrows(空文件或最后一行左移场景),此时直接访问E.row[E.cy].size会触发越界,轻则光标逻辑异常,重则引发段错误。修复时需先判断E.cy < E.numrows再读取行数据。空文件下光标移动限制错误:
ARROW_DOWN的判断条件E.cy < E.numrows -1在空文件时(E.numrows=0)会变成E.cy < -1,永远无法触发移动。改为E.cy < E.numrows即可允许光标移动到虚拟行。END_KEY逻辑错误:原代码将
E.cx设为E.screencols -1,这是错误的,应该定位到当前行的实际末尾,而非屏幕宽度的末尾。修改为根据当前行是否存在,设置E.cx为行长度或0。终端光标定位验证:检查
editorRefreshScreen中的光标定位序列"\x1b[%d;%dH",确保(E.cy - E.rowoff) +1和(E.cx - E.coloff)+1的计算符合终端1起始坐标的规则,若终端模式设置错误,可能导致光标位置不更新。
修改后的关键函数示例
void editorMoveCursor(int key) { erow *row = (E.cy < E.numrows) ? &E.row[E.cy] : nullptr; switch (key) { case ARROW_LEFT: if (E.cx != 0) { E.cx--; } else if (E.cy > 0) { E.cy--; E.cx = (E.cy < E.numrows) ? E.row[E.cy].size : 0; } break; case ARROW_RIGHT: if (row && E.cx < row->size) { E.cx++; } else if (row && E.cx == row->size) { E.cy++; E.cx = 0; } break; case ARROW_UP: if (E.cy != 0) { E.cy--; } break; case ARROW_DOWN: if (E.cy < E.numrows) { E.cy++; } break; } row = (E.cy < E.numrows) ? &E.row[E.cy] : nullptr; int rowlen = row ? row->size : 0; if (E.cx > rowlen) { E.cx = rowlen; } } 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: E.cx = (E.cy < E.numrows) ? E.row[E.cy].size : 0; break; case PAGE_UP: case PAGE_DOWN: { 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; } }
内容的提问来源于stack exchange,提问作者Michael Kraiton
相关产品推荐
相关产品推荐

