Adds syntax highlighting and more vim controls

This commit is contained in:
sloumdrone 2019-12-31 15:54:43 -08:00
parent b754278f2e
commit b05771bc75
5 changed files with 291 additions and 47 deletions

View File

@ -1,5 +1,9 @@
static const int tabwidth = 4;
#define UNSAVED_QUIT_COUNT 1 #define UNSAVED_QUIT_COUNT 1
static const int tabwidth = 4;
static const int HL_NUMBER_COLOR = 31;
static const int HL_DEFAULT_COLOR = 37;

BIN
csedit

Binary file not shown.

319
csedit.c
View File

@ -24,12 +24,19 @@
// ----------- // -----------
#define CSEDIT_VERSION "0.0.1" #define CSEDIT_VERSION "0.0.1"
#define CTRL_KEY(k) ((k) & 0x1f) #define CTRL_KEY(k) ((k) & 0x1f)
#define HL_HIGHLIGHT_NUMBERS (1<<0)
// //
// definitions for config file // definitions for config file
// --------------------------- // ---------------------------
enum editorMode {CommandMode, InputMode, VisualMode}; enum editorMode {CommandMode, InputMode, VisualMode};
enum editorHighlight {
HL_NORMAL,
HL_NUMBER
};
enum editorKey { enum editorKey {
BACKSPACE = 127, BACKSPACE = 127,
ARROW_LEFT = 1000, ARROW_LEFT = 1000,
@ -52,6 +59,7 @@ typedef struct erow {
int rendersize; int rendersize;
char *chars; char *chars;
char *render; char *render;
unsigned char *hl;
} erow; } erow;
struct editorConfig { struct editorConfig {
@ -68,10 +76,33 @@ struct editorConfig {
char statusmsg[80]; char statusmsg[80];
time_t statusmsg_time; time_t statusmsg_time;
struct termios orig_termios; struct termios orig_termios;
struct editorSyntax *syntax;
};
struct editorSyntax {
char *filetype;
char **filematch;
int flags;
}; };
struct editorConfig E; 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 // prototypes
// ---------- // ----------
@ -194,6 +225,68 @@ int getWindowSize(int *rows, int *cols) {
return 0; 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 // row ops
// ------- // -------
@ -228,6 +321,8 @@ void editorUpdateRow(erow *row) {
} }
row->render[idx] = '\0'; row->render[idx] = '\0';
row->rendersize = idx; row->rendersize = idx;
editorUpdateSyntax(row);
} }
void editorInsertRow(int at, char *s, size_t len) { 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].rendersize = 0;
E.row[at].render = NULL; E.row[at].render = NULL;
E.row[at].hl = NULL;
editorUpdateRow(&E.row[at]); editorUpdateRow(&E.row[at]);
E.numRows++; E.numRows++;
@ -252,6 +348,7 @@ void editorInsertRow(int at, char *s, size_t len) {
void editorFreeRow(erow *row) { void editorFreeRow(erow *row) {
free(row->render); free(row->render);
free(row->chars); free(row->chars);
free(row->hl);
} }
void editorDelRow(int at) { void editorDelRow(int at) {
@ -357,6 +454,8 @@ void editorOpen(char *filename) {
free(E.filename); free(E.filename);
E.filename = strdup(filename); E.filename = strdup(filename);
editorSelectSyntaxHighlight();
FILE *fp = fopen(filename, "r"); FILE *fp = fopen(filename, "r");
if (!fp) die("Open file"); if (!fp) die("Open file");
@ -374,14 +473,15 @@ void editorOpen(char *filename) {
E.unsaved = 0; E.unsaved = 0;
} }
void editorSave() { void editorSave(int saveAs) {
if (E.filename == NULL) { if (E.filename == NULL || saveAs) {
E.filename = editorPrompt("Save as: %s"); E.filename = editorPrompt("Save as: %s");
if (E.filename == NULL) { if (E.filename == NULL) {
E.filename = NULL; E.filename = NULL;
editorStatusMessage("Save cancelled"); editorStatusMessage("Save cancelled");
return; return;
} }
editorSelectSyntaxHighlight();
} }
int len; int len;
@ -473,7 +573,29 @@ void editorDrawRows(struct abuf *ab) {
int len = E.row[filerow].rendersize - E.coloff; int len = E.row[filerow].rendersize - E.coloff;
if (len < 0) len = 0; if (len < 0) len = 0;
if (len > E.screenCols) len = E.screenCols; 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); abAppend(ab, "\033[K", 3);
@ -500,8 +622,8 @@ void editorDrawStatusBar(struct abuf *ab) {
int len = snprintf(status, sizeof(status), "%.20s - %d lines %s", int len = snprintf(status, sizeof(status), "%.20s - %d lines %s",
E.filename ? E.filename : "[No name]", E.numRows, E.unsaved ? "(modified)" : ""); E.filename ? E.filename : "[No name]", E.numRows, E.unsaved ? "(modified)" : "");
int rlen = snprintf(rstatus, sizeof(rstatus), "%d:%d/%d [%c]", int rlen = snprintf(rstatus, sizeof(rstatus), "<%s> %d:%d/%d [%c] ",
E.cx, E.cy + 1, E.numRows, editorMode); E.syntax ? E.syntax->filetype : "generic" ,E.cx, E.cy + 1, E.numRows, editorMode);
if (len > E.screenCols) len = E.screenCols; if (len > E.screenCols) len = E.screenCols;
abAppend(ab, status, len); abAppend(ab, status, len);
while (len < E.screenCols) { while (len < E.screenCols) {
@ -558,7 +680,7 @@ void editorPrevWord() {
int x = E.cx; int x = E.cx;
int y = E.cy; int y = E.cy;
int moved = 0; 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) { while (row) {
if (moved && row->chars[x] != ' ' && row->chars[x] != '\t' && (x == 0 || row->chars[x - 1] == ' ' || row->chars[x - 1] == '\t')) { 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; break;
} else if (x == 0) { } else if (x == 0) {
y--; y--;
row = (y <= 0) ? NULL : &E.row[y]; row = (y >= E.numRows || y < 0) ? NULL : &E.row[y];
if (!row) return; if (!row) return;
x = row->size; x = row->size;
moved++; moved++;
@ -583,7 +705,7 @@ void editorNextWord() {
int ws = 0; int ws = 0;
int x = E.cx; int x = E.cx;
int y = E.cy; 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) { while (row) {
if (ws > 0 && row->chars[x] != ' ' && row->chars[x] != '\t') { if (ws > 0 && row->chars[x] != ' ' && row->chars[x] != '\t') {
@ -597,7 +719,7 @@ void editorNextWord() {
y++; y++;
x = 0; x = 0;
ws++; ws++;
row = (y >= E.numRows) ? NULL : &E.row[y]; row = (y >= E.numRows || y < 0) ? NULL : &E.row[y];
if (!row) return; if (!row) return;
} else { } else {
x++; x++;
@ -685,9 +807,12 @@ void editorMoveCursor(int key) {
break; break;
case 'l': case 'l':
case ARROW_RIGHT: case ARROW_RIGHT:
if (row && E.cx < row->size) {
E.cx++; int mod = E.mode == CommandMode ? 1 : 0;
break; if (row && E.cx < row->size - mod)
E.cx++;
break;
}
} }
row = (E.cy >= E.numRows) ? NULL : &E.row[E.cy]; row = (E.cy >= E.numRows) ? NULL : &E.row[E.cy];
@ -727,6 +852,7 @@ void editorInputKp(int c) {
switch (c) { switch (c) {
case '\033': case '\033':
case CTRL_KEY('l'): case CTRL_KEY('l'):
if (E.cx == E.row[E.cy].size) editorMoveCursor(ARROW_LEFT);
E.mode = CommandMode; E.mode = CommandMode;
break; break;
case '\r': case '\r':
@ -745,12 +871,18 @@ void editorInputKp(int c) {
} }
void editorCommandKp(int c) { void editorCommandKp(int c) {
static int deleting = 0;
static int counter = 1;
static int place = 1;
switch (c) { switch (c) {
case 'j': case 'j':
case 'k': case 'k':
case 'h': case 'h':
case 'l': case 'l':
editorMoveCursor(c); while (counter > 0) {
editorMoveCursor(c);
counter--;
}
break; break;
case 'g': case 'g':
E.cy = 0; E.cy = 0;
@ -765,48 +897,144 @@ void editorCommandKp(int c) {
break; break;
case 'o': case 'o':
editorCommandKp('$'); editorCommandKp('$');
editorInsertNewline();
editorCommandKp('i'); editorCommandKp('i');
editorMoveCursor(ARROW_RIGHT);
editorInsertNewline();
break; break;
case 'O': case 'O':
editorCommandKp('0'); editorCommandKp('^');
editorInsertNewline(); editorInsertNewline();
editorMoveCursor(ARROW_UP); editorMoveCursor(ARROW_UP);
editorCommandKp('i'); editorCommandKp('i');
break; break;
case 'x': case 'x':
editorMoveCursor(ARROW_RIGHT); while (counter > 0) {
editorDeleteChar(); E.mode = InputMode;
editorMoveCursor(ARROW_RIGHT);
E.mode = CommandMode;
editorDeleteChar();
if (E.cx == E.row[E.cy].size) editorMoveCursor(ARROW_LEFT);
}
break; break;
case 'i': case 'i':
case 'a': case 'a':
if (c == 'a') editorMoveCursor('l');
E.mode = InputMode; E.mode = InputMode;
break; if (c == 'a') editorMoveCursor('l');
case 'u':
editorPageCursor(PAGE_UP);
break; break;
case 'd': case 'd':
editorPageCursor(PAGE_DOWN); if (!deleting) {
break; 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 '$': case '$':
if (E.cy < E.numRows) if (deleting) {
E.cx = E.row[E.cy].size; 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; break;
case 'w': 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; break;
case 'b': case 'b':
editorPrevWord(); while (counter > 0) {
if (deleting) {
// TODO
} else {
editorPrevWord();
}
counter--;
}
break; break;
case 'e': 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; break;
case '0': case '0':
case '^': if (counter == 1) {
E.cx = 0; if (deleting) {
break; 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() { void editorProcessKeypress() {
@ -838,7 +1066,10 @@ void editorProcessKeypress() {
editorPageCursor(PAGE_DOWN); editorPageCursor(PAGE_DOWN);
return; return;
case CTRL_KEY('s'): case CTRL_KEY('s'):
editorSave(); editorSave(0);
return;
case CTRL_KEY('a'):
editorSave(1);
return; return;
case HOME_KEY: case HOME_KEY:
E.cx = 0; E.cx = 0;
@ -862,18 +1093,20 @@ void editorProcessKeypress() {
// init // init
// ---- // ----
void initEditor() { void initEditor() {
E.mode = CommandMode; E.mode = CommandMode;
E.rowoff = 0; E.rowoff = 0;
E.coloff = 0; E.coloff = 0;
E.numRows = 0; E.numRows = 0;
E.unsaved = 0; E.unsaved = 0;
E.row = NULL;
E.filename = NULL;
E.statusmsg[0] = '\0';
E.statusmsg_time = 0; E.statusmsg_time = 0;
E.cx = 0; E.cx = 0;
E.cy = 0; E.cy = 0;
E.rx = 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"); if (getWindowSize(&E.screenRows, &E.screenCols) == -1) die("getWindowSize");
E.screenRows -= 2; E.screenRows -= 2;
} }

10
filetypesdb.h Normal file
View File

@ -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
}
}

View File

@ -1,3 +0,0 @@
Hello, world!
typing.
and such.