diff --git a/config.h b/config.h index a50047e..5d0f04b 100644 --- a/config.h +++ b/config.h @@ -1,5 +1,9 @@ -static const int tabwidth = 4; #define UNSAVED_QUIT_COUNT 1 + +static const int tabwidth = 4; + +static const int HL_NUMBER_COLOR = 31; +static const int HL_DEFAULT_COLOR = 37; diff --git a/csedit b/csedit index b767356..531bcc8 100755 Binary files a/csedit and b/csedit differ diff --git a/csedit.c b/csedit.c index aaec4c6..a76a382 100644 --- a/csedit.c +++ b/csedit.c @@ -24,12 +24,19 @@ // ----------- #define CSEDIT_VERSION "0.0.1" #define CTRL_KEY(k) ((k) & 0x1f) +#define HL_HIGHLIGHT_NUMBERS (1<<0) // // definitions for config file // --------------------------- enum editorMode {CommandMode, InputMode, VisualMode}; + +enum editorHighlight { + HL_NORMAL, + HL_NUMBER +}; + enum editorKey { BACKSPACE = 127, ARROW_LEFT = 1000, @@ -52,6 +59,7 @@ typedef struct erow { int rendersize; char *chars; char *render; + unsigned char *hl; } erow; struct editorConfig { @@ -68,10 +76,33 @@ struct editorConfig { char statusmsg[80]; time_t statusmsg_time; struct termios orig_termios; + struct editorSyntax *syntax; +}; + +struct editorSyntax { + char *filetype; + char **filematch; + int flags; }; struct editorConfig E; + +// +// filetypes +// --------- +char *C_HL_extensions[] = {".c", ".h", ".cpp", NULL}; + +struct editorSyntax HLDB[] = { + { + "c", + C_HL_extensions, + HL_HIGHLIGHT_NUMBERS + }, +}; + +#define HLDB_ENTRIES (sizeof(HLDB) / sizeof(HLDB[0])) + // // prototypes // ---------- @@ -194,6 +225,68 @@ int getWindowSize(int *rows, int *cols) { return 0; } +// +// syntax highlighting +// ------------------- +int is_separator(int c) { + return isspace(c) || c == '\0' || strchr(",.()+-/*=~%<>[];", c) != NULL; +} + +void editorUpdateSyntax(erow *row) { + row->hl = realloc(row->hl, row->rendersize); + memset(row->hl, HL_NORMAL, row->rendersize); + + if (E.syntax == NULL) return; + + int prev_sep = 1; + + int i; + while (i < row->size) { + char c = row->render[i]; + unsigned char prev_hl = (i > 0) ? row->hl[i - 1] : HL_NORMAL; + + if (E.syntax->flags & HL_HIGHLIGHT_NUMBERS) { + if ((isdigit(c) && (prev_sep || prev_hl == HL_NUMBER)) || + (c == '.' && prev_hl == HL_NUMBER)) { + row->hl[i] = HL_NUMBER; + i++; + prev_sep = 0; + continue; + } + } + prev_sep = is_separator(c); + i++; + } +} + +int editorSyntaxToColor(int hl) { + switch (hl) { + case HL_NUMBER: return HL_NUMBER_COLOR; + default: return HL_DEFAULT_COLOR; + } +} + +void editorSelectSyntaxHighlight() { + E.syntax = NULL; + if (E.filename == NULL) return; + + char *ext = strrchr(E.filename, '.'); + + for (unsigned int j = 0; j < HLDB_ENTRIES; j++) { + struct editorSyntax *s = &HLDB[j]; + unsigned int i = 0; + while (s->filematch[i]) { + int is_ext = (s->filematch[i][0] == '.'); + if ((is_ext && ext && !strcmp(ext, s->filematch[i])) || + (!is_ext && strstr(E.filename, s->filematch[i]))) { + E.syntax = s; + return; + } + i++; + } + } +} + // // row ops // ------- @@ -228,6 +321,8 @@ void editorUpdateRow(erow *row) { } row->render[idx] = '\0'; row->rendersize = idx; + + editorUpdateSyntax(row); } void editorInsertRow(int at, char *s, size_t len) { @@ -243,6 +338,7 @@ void editorInsertRow(int at, char *s, size_t len) { E.row[at].rendersize = 0; E.row[at].render = NULL; + E.row[at].hl = NULL; editorUpdateRow(&E.row[at]); E.numRows++; @@ -252,6 +348,7 @@ void editorInsertRow(int at, char *s, size_t len) { void editorFreeRow(erow *row) { free(row->render); free(row->chars); + free(row->hl); } void editorDelRow(int at) { @@ -357,6 +454,8 @@ void editorOpen(char *filename) { free(E.filename); E.filename = strdup(filename); + editorSelectSyntaxHighlight(); + FILE *fp = fopen(filename, "r"); if (!fp) die("Open file"); @@ -374,14 +473,15 @@ void editorOpen(char *filename) { E.unsaved = 0; } -void editorSave() { - if (E.filename == NULL) { +void editorSave(int saveAs) { + if (E.filename == NULL || saveAs) { E.filename = editorPrompt("Save as: %s"); if (E.filename == NULL) { E.filename = NULL; editorStatusMessage("Save cancelled"); return; } + editorSelectSyntaxHighlight(); } int len; @@ -473,7 +573,29 @@ void editorDrawRows(struct abuf *ab) { int len = E.row[filerow].rendersize - E.coloff; if (len < 0) len = 0; if (len > E.screenCols) len = E.screenCols; - abAppend(ab, &E.row[filerow].render[E.coloff], len); + char *c = &E.row[filerow].render[E.coloff]; + int current_color = -1; + unsigned char *hl = &E.row[filerow].hl[E.coloff]; + int j; + for (j = 0; j < len; j++) { + if (hl[j] == HL_NORMAL) { + if (current_color != -1) { + abAppend(ab, "\033[39m", 5); + current_color = -1; + } + abAppend(ab, &c[j], 1); + } else { + int color = editorSyntaxToColor(hl[j]); + if (color != current_color) { + current_color = color; + char buf[16]; + int clen = snprintf(buf, sizeof(buf), "\033[%dm", color); + abAppend(ab, buf, clen); + } + abAppend(ab, &c[j], 1); + } + } + abAppend(ab, "\033[39m", 5); } abAppend(ab, "\033[K", 3); @@ -500,8 +622,8 @@ void editorDrawStatusBar(struct abuf *ab) { int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", E.filename ? E.filename : "[No name]", E.numRows, E.unsaved ? "(modified)" : ""); - int rlen = snprintf(rstatus, sizeof(rstatus), "%d:%d/%d [%c]", - E.cx, E.cy + 1, E.numRows, editorMode); + int rlen = snprintf(rstatus, sizeof(rstatus), "<%s> %d:%d/%d [%c] ", + E.syntax ? E.syntax->filetype : "generic" ,E.cx, E.cy + 1, E.numRows, editorMode); if (len > E.screenCols) len = E.screenCols; abAppend(ab, status, len); while (len < E.screenCols) { @@ -558,7 +680,7 @@ void editorPrevWord() { int x = E.cx; int y = E.cy; int moved = 0; - erow *row = (y >= E.numRows) ? NULL : &E.row[y]; + erow *row = (y >= E.numRows || y < 0) ? NULL : &E.row[y]; while (row) { if (moved && row->chars[x] != ' ' && row->chars[x] != '\t' && (x == 0 || row->chars[x - 1] == ' ' || row->chars[x - 1] == '\t')) { @@ -568,7 +690,7 @@ void editorPrevWord() { break; } else if (x == 0) { y--; - row = (y <= 0) ? NULL : &E.row[y]; + row = (y >= E.numRows || y < 0) ? NULL : &E.row[y]; if (!row) return; x = row->size; moved++; @@ -583,7 +705,7 @@ void editorNextWord() { int ws = 0; int x = E.cx; int y = E.cy; - erow *row = (y >= E.numRows) ? NULL : &E.row[y]; + erow *row = (y >= E.numRows || y < 0) ? NULL : &E.row[y]; while (row) { if (ws > 0 && row->chars[x] != ' ' && row->chars[x] != '\t') { @@ -597,7 +719,7 @@ void editorNextWord() { y++; x = 0; ws++; - row = (y >= E.numRows) ? NULL : &E.row[y]; + row = (y >= E.numRows || y < 0) ? NULL : &E.row[y]; if (!row) return; } else { x++; @@ -685,9 +807,12 @@ void editorMoveCursor(int key) { break; case 'l': case ARROW_RIGHT: - if (row && E.cx < row->size) - E.cx++; - break; + { + int mod = E.mode == CommandMode ? 1 : 0; + if (row && E.cx < row->size - mod) + E.cx++; + break; + } } row = (E.cy >= E.numRows) ? NULL : &E.row[E.cy]; @@ -727,6 +852,7 @@ void editorInputKp(int c) { switch (c) { case '\033': case CTRL_KEY('l'): + if (E.cx == E.row[E.cy].size) editorMoveCursor(ARROW_LEFT); E.mode = CommandMode; break; case '\r': @@ -745,12 +871,18 @@ void editorInputKp(int c) { } void editorCommandKp(int c) { + static int deleting = 0; + static int counter = 1; + static int place = 1; switch (c) { case 'j': case 'k': case 'h': case 'l': - editorMoveCursor(c); + while (counter > 0) { + editorMoveCursor(c); + counter--; + } break; case 'g': E.cy = 0; @@ -765,48 +897,144 @@ void editorCommandKp(int c) { break; case 'o': editorCommandKp('$'); - editorInsertNewline(); editorCommandKp('i'); + editorMoveCursor(ARROW_RIGHT); + editorInsertNewline(); break; case 'O': - editorCommandKp('0'); + editorCommandKp('^'); editorInsertNewline(); editorMoveCursor(ARROW_UP); editorCommandKp('i'); break; case 'x': - editorMoveCursor(ARROW_RIGHT); - editorDeleteChar(); + while (counter > 0) { + E.mode = InputMode; + editorMoveCursor(ARROW_RIGHT); + E.mode = CommandMode; + editorDeleteChar(); + if (E.cx == E.row[E.cy].size) editorMoveCursor(ARROW_LEFT); + } break; case 'i': case 'a': - if (c == 'a') editorMoveCursor('l'); E.mode = InputMode; - break; - case 'u': - editorPageCursor(PAGE_UP); + if (c == 'a') editorMoveCursor('l'); break; case 'd': - editorPageCursor(PAGE_DOWN); - break; + if (!deleting) { + deleting++; + return; + } else { + while (counter > 0) { + editorDelRow(E.cy); + if (E.cy == E.numRows) E.cy--; + if (E.cy < 0) E.cy = 0; + counter--; + } + break; + } case '$': - if (E.cy < E.numRows) - E.cx = E.row[E.cy].size; + if (deleting) { + int diff = E.row[E.cy].size - E.cx; + E.cx = E.row[E.cy].size - 1; + if (E.cx < 0) E.cx = 0; + while (diff > 0) { + E.mode = InputMode; + editorMoveCursor(ARROW_RIGHT); + E.mode = CommandMode; + editorDeleteChar(); + if (E.cx == E.row[E.cy].size) editorMoveCursor(ARROW_LEFT); + diff--; + } + } else if (E.cy < E.numRows) { + E.cx = E.row[E.cy].size - 1; + if (E.cx < 0) E.cx = 0; + } break; case 'w': - editorNextWord(); + while (counter > 0) { + counter--; + if (deleting) { + int orig = E.cx; + editorNextWord(); + int diff = E.cx - orig; + if (diff < 0) { + editorPrevWord(); + editorEndOfWord(); + editorMoveCursor(ARROW_RIGHT); + diff = E.cx - orig; + } + while (diff > 0) { + editorDeleteChar(); + diff--; + } + } else { + editorNextWord(); + } + } break; case 'b': - editorPrevWord(); + while (counter > 0) { + if (deleting) { + // TODO + } else { + editorPrevWord(); + } + counter--; + } break; case 'e': - editorEndOfWord(); + while (counter > 0) { + if (deleting) { + // TODO + } else { + editorEndOfWord(); + } + counter--; + } + break; + case '^': + if (deleting) { + while (E.cx > 0) { + editorDeleteChar(); + } + } else { + E.cx = 0; + } break; case '0': - case '^': - E.cx = 0; - break; + if (counter == 1) { + if (deleting) { + while (E.cx > 0) { + editorDeleteChar(); + } + } else { + E.cx = 0; + } + break; + } + /* fall through */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + if (counter <= 1 && place == 1) counter--; + int num = c - 48; + counter += num * place; + place *= 10; + } + return; } + deleting = 0; + place = 1; + counter = 1; } void editorProcessKeypress() { @@ -838,7 +1066,10 @@ void editorProcessKeypress() { editorPageCursor(PAGE_DOWN); return; case CTRL_KEY('s'): - editorSave(); + editorSave(0); + return; + case CTRL_KEY('a'): + editorSave(1); return; case HOME_KEY: E.cx = 0; @@ -862,18 +1093,20 @@ void editorProcessKeypress() { // init // ---- void initEditor() { - E.mode = CommandMode; - E.rowoff = 0; - E.coloff = 0; - E.numRows = 0; - E.unsaved = 0; - E.row = NULL; - E.filename = NULL; - E.statusmsg[0] = '\0'; + E.mode = CommandMode; + E.rowoff = 0; + E.coloff = 0; + E.numRows = 0; + E.unsaved = 0; E.statusmsg_time = 0; - E.cx = 0; - E.cy = 0; - E.rx = 0; + E.cx = 0; + E.cy = 0; + E.rx = 0; + E.row = NULL; + E.filename = NULL; + E.syntax = NULL; + E.statusmsg[0] = '\0'; + if (getWindowSize(&E.screenRows, &E.screenCols) == -1) die("getWindowSize"); E.screenRows -= 2; } diff --git a/filetypesdb.h b/filetypesdb.h new file mode 100644 index 0000000..3532b03 --- /dev/null +++ b/filetypesdb.h @@ -0,0 +1,10 @@ +// File extensions by language +char *C_HL_extensions[] = {".c", ".h", ".cpp", NULL}; + +struct editorSyntax HLDB[] { + { + "c", + C_HL_extensions, + HL_HIGHLIGHT_NUMBERS + } +} diff --git a/test.txt b/test.txt deleted file mode 100644 index 6de1fe6..0000000 --- a/test.txt +++ /dev/null @@ -1,3 +0,0 @@ -Hello, world! - typing. -and such.