diff --git a/kilo.c b/kilo.c index c4c16c8..2514ec3 100644 --- a/kilo.c +++ b/kilo.c @@ -38,14 +38,33 @@ enum editorKey { PAGE_DOWN }; +enum editorHighlight { + HL_NORMAL = 0, + HL_COMMENT, + HL_STRING, + HL_NUMBER, + HL_MATCH +}; + +#define HL_HIGHLIGHT_NUMBERS (1<<0) +#define HL_HIGHLIGHT_STRINGS (1<<1) + /*** data ***/ +struct editorSyntax { + char *filetype; + char **filematch; + char *singleline_comment_start; + int flags; +}; + typedef struct erow { int size; int rsize; char *chars; char *render; + unsigned char *hl; } erow; struct editorConfig { @@ -61,11 +80,27 @@ struct editorConfig { char *filename; char statusmsg[80]; time_t statusmsg_time; + struct editorSyntax *syntax; struct termios orig_termios; }; struct editorConfig E; +/*** filetypes ***/ + +char *C_HL_extensions[] = { ".c", ".h", ".cpp", NULL }; + +struct editorSyntax HLDB[] = { + { + "c", + C_HL_extensions, + "//", + HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS + }, +}; + +#define HLDB_ENTRIES (sizeof(HLDB) / sizeof(HLDB[0])) + /*** prototypes ***/ void editorSetStatusMessage(const char *fmt, ...); @@ -189,6 +224,112 @@ int getWindowSize(int *rows, int *cols) { } } +/*** 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->rsize); + memset(row->hl, HL_NORMAL, row->rsize); + + if (E.syntax == NULL) return; + + char *scs = E.syntax->singleline_comment_start; + int scs_len = scs ? strlen(scs) : 0; + + int prev_sep = 1; + int in_string = 0; + + int i = 0; + while (i < row->rsize) { + char c = row->render[i]; + unsigned char prev_hl = (i > 0) ? row->hl[i -1] : HL_NORMAL; + + // Comments + if (scs_len && !in_string) { + if (!strncmp(&row->render[i], scs, scs_len)) { + memset(&row->hl[i], HL_COMMENT, row->rsize - i); + break; + } + } + + // Strings + if (E.syntax->flags & HL_HIGHLIGHT_NUMBERS) { + if (in_string) { + row->hl[i] = HL_STRING; + if (c == '\\' && i + 1 < row->rsize) { + row->hl[i + 1] = HL_STRING; + i += 2; + continue; + } + if (c == in_string) in_string = 0; + i++; + prev_sep = 1; + continue; + } else { + if (c == '"' || c == '\'') { + in_string = c; + row->hl[i] = HL_STRING; + i++; + continue; + } + } + } + + // Numbers + 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_COMMENT: return 36; + case HL_STRING: return 35; + case HL_NUMBER: return 31; + case HL_MATCH: return 34; + default: return 37; + } +} + +void editorSelectSyntaxHighlight() { + E.syntax = NULL; + if (E.filename == NULL) return; + + char *ext = strrchr(E.filename, '.'); + + for (unsigned int j=0; jfilematch[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; + + int filerow; + for (filerow=0; filerowrender[idx] = '\0'; row->rsize = idx; + + editorUpdateSyntax(row); } void editorInsertRow(int at, char *s, size_t len) { @@ -252,6 +395,7 @@ void editorInsertRow(int at, char *s, size_t len) { E.row[at].rsize = 0; E.row[at].render = NULL; + E.row[at].hl = NULL; editorUpdateRow(&E.row[at]); E.numrows++; @@ -261,6 +405,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) { @@ -367,6 +512,8 @@ void editorOpen(char *filename) { free(E.filename); E.filename = strdup(filename); + editorSelectSyntaxHighlight(); + FILE *fp = fopen(filename, "r"); if (!fp) die("fopen"); @@ -406,6 +553,7 @@ void editorSave() { editorSetStatusMessage("%d bytes written to disk", len); return; } + editorSelectSyntaxHighlight(); } close(fd); } @@ -419,6 +567,15 @@ void editorSave() { void editorFindCallback(char *query, int key) { static int last_match = -1; static int direction = 1; + + static int saved_hl_line; + static char *saved_hl = NULL; + + if (saved_hl) { + memcpy(E.row[saved_hl_line].hl, saved_hl, E.row[saved_hl_line].rsize); + free(saved_hl); + saved_hl = NULL; + } if (key == '\r' || key == '\xb') { last_match = -1; @@ -449,6 +606,11 @@ void editorFindCallback(char *query, int key) { E.cy = current; E.cx = editorRowRxToCx(row, match - row->render); // Substract pointers here E.rowoff = E.numrows; + + saved_hl_line = current; + saved_hl = malloc(row->rsize); + memcpy(saved_hl, row->hl, row->rsize); + memset(&row->hl[match - row->render], HL_MATCH, strlen(query)); break; } } @@ -538,16 +700,28 @@ void editorDrawRows(struct abuf *ab) { if (len < 0) len = 0; if (len > E.screencols) len = E.screencols; char *c = &E.row[filerow].render[E.coloff]; + unsigned char *hl = &E.row[filerow].hl[E.coloff]; + int current_color = -1; int j; for (j=0; jfiletype : "no ft", E.cy + 1, E.numrows); if (len > E.screencols) len = E.screencols; abAppend(ab, status, len); while (len < E.screencols) { @@ -791,6 +965,7 @@ void initEditor() { E.filename = NULL; E.statusmsg[0] = '\0'; E.statusmsg_time = 0; + E.syntax = NULL; if (getWindowSize(&E.screenrows, &E.screencols) == -1) die("getWindowSize");