diff --git a/README.md b/README.md index ada954c..118725c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ Tyrel's Text Editor next: -https://viewsourcecode.org/snaptoken/kilo/07.syntaxHighlighting.html + +https://viewsourcecode.org/snaptoken/kilo/07.syntaxHighlighting.html#colorful-multiline-comments diff --git a/tted.c b/tted.c index 45b3fea..08c998b 100644 --- a/tted.c +++ b/tted.c @@ -37,17 +37,23 @@ enum editorKey { enum editorHighlight { HL_NORMAL = 0, + HL_COMMENT, + HL_KEYWORD1, + HL_KEYWORD2, + 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 **keywords; + char *singleline_comment_start; int flags; }; @@ -72,10 +78,33 @@ 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}; +char *C_HL_keywords[] = { + "switch", "if", "while", "for", "break", "continue", "return", "else", + "struct", "union", "typedef", "static", "enum", "class", "case", + "int|", "long|", "double|", "float|", "char|", "unsigned|", "signed|", + "void|", NULL +}; + +struct editorSyntax HLDB[] = { + { + "c", + C_HL_extensions, + C_HL_keywords, + "//", + HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS + }, +}; + +#define HLDB_ENTRIES (sizeof(HLDB) / sizeof(HLDB[0])) + + /** prototypes */ void editorSetStatusMessage(const char *fmt, ...); void editorRefreshScreen(); @@ -99,7 +128,7 @@ void disableRawMode() { void enableRawMode() { if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1) die("tcgetattr"); - atexit(disableRawMode); + atexit(disableRawMode); // run on exit struct termios raw = E.orig_termios; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); @@ -212,19 +241,80 @@ void editorUpdateSyntax(erow *row){ row->hl = realloc(row->hl, row->rsize); memset(row->hl, HL_NORMAL, row->rsize); + if (E.syntax == NULL) return; + + char **keywords = E.syntax->keywords; + + 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; - if ((isdigit(c) && (prev_sep || prev_hl == HL_NUMBER))|| - (c == '.' && prev_hl == HL_NUMBER)){ - row->hl[i] = HL_NUMBER; - i++; - prev_sep = 0; - continue; + if (scs_len && !in_string){ + if (!strncmp(&row->render[i], scs, scs_len)) { + memset(&row->hl[i], HL_COMMENT, row->rsize - i); + break; + } + } + + if (E.syntax->flags & HL_HIGHLIGHT_STRINGS) { + 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; + } + } + } + + + 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; + } + } + + if (prev_sep) { + int j; + for (j=0; keywords[j]; j++){ + int klen = strlen(keywords[j]); + int kw2 = keywords[j][klen - 1] == '|'; + if (kw2) klen--; + + if (!strncmp(&row->render[i], keywords[j], klen) && + is_separator(row->render[i + klen])){ + memset(&row->hl[i], kw2 ? HL_KEYWORD2 : HL_KEYWORD1, klen); + i += klen; + break; + } + } + if (keywords[j] != NULL){ + prev_sep = 0; + continue; + } + } prev_sep = is_separator(c); @@ -234,12 +324,42 @@ void editorUpdateSyntax(erow *row){ int editorSyntaxToColor(int hl){ switch(hl){ + case HL_COMMENT: return 36; + case HL_KEYWORD1: return 33; + case HL_KEYWORD2: return 32; case HL_NUMBER: return 31; + case HL_STRING: return 35; 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; 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; + + int filerow; + for (filerow = 0; filerow < E.numrows; filerow++){ + editorUpdateSyntax(&E.row[filerow]); + } + return; + } + i++; + } + } +} + /** row operations */ int editorRowCxToRx(erow *row, int cx) { int rx = 0; @@ -419,6 +539,9 @@ char *editorRowsToString(int *buflen) { void editorOpen(char *filename) { free(E.filename); E.filename = strdup(filename); + + editorSelectSyntaxHighlight(); + FILE *fp = fopen(filename, "r"); if (!fp) die("fopen"); @@ -444,6 +567,7 @@ void editorSave() { editorSetStatusMessage("Save aborted"); return; } + editorSelectSyntaxHighlight(); } int len; @@ -615,7 +739,17 @@ void editorDrawRows(struct abuf *ab) { int current_color = -1; int j; for (j=0;j< len;j++){ - if (hl[j] == HL_NORMAL){ + if (iscntrl(c[j])){ + char sym = (c[j] <= 26) ? '@' + c[j] : '?'; + abAppend(ab, "\x1b[7m", 4); + abAppend(ab, &sym, 1); + abAppend(ab, "\x1b[m", 3); + if (current_color != -1){ + char buf[16]; + int clen = snprintf(buf, sizeof(buf), "\x1b[%dm", current_color); + abAppend(ab, buf, clen); + } + } else if (hl[j] == HL_NORMAL){ if (current_color != -1){ abAppend(ab, "\x1b[39m", 5); current_color = -1; @@ -646,7 +780,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.dirty ? "(modified)" : ""); - int rlen = snprintf(rstatus, sizeof(rstatus), "%d/%d", E.cy + 1, E.numrows); + int rlen = snprintf(rstatus, sizeof(rstatus), "%s | %d/%d", + E.syntax ? E.syntax->filetype : "no ft", E.cy + 1, E.numrows); if (len > E.screencols) len = E.screencols; abAppend(ab, status, len); while (len < E.screencols) { @@ -870,6 +1005,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"); E.screenrows -= 2;