Adds syntax highlighting and more vim controls
This commit is contained in:
parent
b754278f2e
commit
b05771bc75
6
config.h
6
config.h
|
@ -1,5 +1,9 @@
|
|||
|
||||
|
||||
|
||||
#define UNSAVED_QUIT_COUNT 1
|
||||
|
||||
static const int tabwidth = 4;
|
||||
|
||||
#define UNSAVED_QUIT_COUNT 1
|
||||
static const int HL_NUMBER_COLOR = 31;
|
||||
static const int HL_DEFAULT_COLOR = 37;
|
||||
|
|
309
csedit.c
309
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,10 +807,13 @@ void editorMoveCursor(int key) {
|
|||
break;
|
||||
case 'l':
|
||||
case ARROW_RIGHT:
|
||||
if (row && E.cx < row->size)
|
||||
{
|
||||
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];
|
||||
int rowlen = row ? row->size : 0;
|
||||
|
@ -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':
|
||||
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':
|
||||
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;
|
||||
case '$':
|
||||
if (E.cy < E.numRows)
|
||||
E.cx = E.row[E.cy].size;
|
||||
break;
|
||||
case 'w':
|
||||
editorNextWord();
|
||||
break;
|
||||
case 'b':
|
||||
editorPrevWord();
|
||||
break;
|
||||
case 'e':
|
||||
editorEndOfWord();
|
||||
break;
|
||||
case '0':
|
||||
case '^':
|
||||
E.cx = 0;
|
||||
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 (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':
|
||||
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':
|
||||
while (counter > 0) {
|
||||
if (deleting) {
|
||||
// TODO
|
||||
} else {
|
||||
editorPrevWord();
|
||||
}
|
||||
counter--;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
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':
|
||||
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;
|
||||
|
@ -867,13 +1098,15 @@ void initEditor() {
|
|||
E.coloff = 0;
|
||||
E.numRows = 0;
|
||||
E.unsaved = 0;
|
||||
E.row = NULL;
|
||||
E.filename = NULL;
|
||||
E.statusmsg[0] = '\0';
|
||||
E.statusmsg_time = 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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue