// Simple one-line text editor with // Emacs keybindings. I was planning // to do more, but this already took // me an hour and a half, and I am // getting sleepy. // Supported keys are left arrow, right // arrow, ^F, ^B, delete (and ^H), ^A, // ^E, ^W (which deletes a word), alt-F, // and alt-B. #include #include #include #include "scbreak.h" char buf[80]; int pos = 0, end = 0; enum { state_normal, state_esc, state_csi } input_state = state_normal; #define CTRL(ch) ((ch) & 31) void redraw() { printf("\r∆"); fflush(stdout); write(1, buf, end); // clear to end of line printf("\033[K"); // then move cursor left if (end > pos) { printf("\033[%dD", end - pos); } fflush(stdout); } void backward_delete_char() { if (pos == 0) return; memmove(buf+pos-1, buf+pos, end-pos); end--; pos--; } static int seeing_wsp() { return pos < end && buf[pos] == ' '; } static int seeing_nonwsp() { return pos < end && !seeing_wsp(); } static int following_wsp() { return pos && buf[pos-1] == ' '; } static int following_nonwsp() { return pos && !following_wsp(); } void dispatch_esc(char c) { input_state = state_normal; if (c == 'b') { while (following_wsp()) pos--; while (following_nonwsp()) pos--; } else if (c == 'f') { while (seeing_wsp()) pos++; while (seeing_nonwsp()) pos++; } else if (c == '[') { input_state = state_csi; } } void dispatch_csi(char c) { input_state = state_normal; if (c == 'C') { if (pos < end) pos++; } else if (c == 'D') { if (pos) pos--; } } int main(int argc, char **argv) { const char *err = enable_cbreak(0); if (err) { fprintf(stderr, "%s: cbreak: %s\n", argv[0], err); return 1; } // switch to alternate screen printf("\033[?1049h"); for (;;) { redraw(); char c; read(0, &c, 1); if (input_state == state_esc) { dispatch_esc(c); } else if (input_state == state_csi) { dispatch_csi(c); } else if (c == 127 || c == CTRL('H')) { backward_delete_char(); } else if (c == CTRL('J')) { printf("\n"); break; } else if (c == CTRL('F')) { if (pos == end) continue; pos++; } else if (c == CTRL('B')) { if (pos) pos--; } else if (c == CTRL('E')) { pos = end; } else if (c == CTRL('A')) { pos = 0; } else if (c == CTRL('W')) { while (following_wsp()) { backward_delete_char(); } while (following_nonwsp()) { backward_delete_char(); } } else if (c == '\033') { input_state = state_esc; } else if (c >= ' ') { memmove(buf+pos+1, buf+pos, end-pos); buf[pos++] = c; end++; } } // restore from alternate screen printf("\033[?1049l"); return 0; }