From 228c992c84f361648411bfca58e1af4bd1949779 Mon Sep 17 00:00:00 2001 From: Aaron Fischer Date: Fri, 19 Oct 2018 13:24:58 +0200 Subject: [PATCH] Split up the codebase into files, Fixes #8 --- Makefile | 2 +- file.c | 79 ++++++ file.h | 8 + fn | Bin 28664 -> 28848 bytes fn.c | 839 +------------------------------------------------------ fn.h | 5 + hl.c | 202 ++++++++++++++ hl.h | 10 + input.c | 180 ++++++++++++ input.h | 9 + output.c | 207 ++++++++++++++ output.h | 20 ++ row.c | 165 +++++++++++ row.h | 24 ++ search.c | 86 ++++++ search.h | 8 + 16 files changed, 1014 insertions(+), 830 deletions(-) create mode 100644 file.c create mode 100644 file.h create mode 100644 hl.c create mode 100644 hl.h create mode 100644 input.c create mode 100644 input.h create mode 100644 output.c create mode 100644 output.h create mode 100644 row.c create mode 100644 row.h create mode 100644 search.c create mode 100644 search.h diff --git a/Makefile b/Makefile index 06614dd..192a0d1 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ kilo: fn.c terminal.c - $(CC) fn.c terminal.c -o fn \ + $(CC) fn.c terminal.c output.c row.c hl.c search.c file.c input.c -o fn \ -Wall \ -Wextra \ -Wbad-function-cast \ diff --git a/file.c b/file.c new file mode 100644 index 0000000..48fe525 --- /dev/null +++ b/file.c @@ -0,0 +1,79 @@ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "file.h" +#include "hl.h" +#include "terminal.h" +#include "row.h" + +void editorOpen(char *filename) { + free(E.filename); + // TODO: Maybe replace strdup (string.h), becaus it is no strict posix c lib + E.filename = strdup(filename); + + editorSelectSyntaxHighlight(); + + FILE *fp = fopen(filename, "r"); + if (!fp) die("fopen"); + + char *line = NULL; + size_t linecap = 0; + ssize_t linelen; + while ((linelen = getline(&line, &linecap, fp)) != -1) { + while (linelen > 0 && (line[linelen - 1] == '\n' || + line[linelen - 1] == '\r')) + linelen--; + editorInsertRow(E.numrows, line, linelen); + } + free(line); + fclose(fp); + E.dirty = 0; +} + +void editorSave() { + if (E.filename == NULL) { + E.filename = editorPrompt("Save as: %s (ESC to cancel)", NULL); + if (E.filename == NULL) { + editorSetStatusMessage("Save aborted"); + return; + } + } + + int len; + + char *buf = editorRowsToString(&len); + + int fd = open(E.filename, O_RDWR | O_CREAT, 0644); + if (fd != -1) { + if (ftruncate(fd, len) != -1) { + if (write(fd, buf, len) == len) { + close(fd); + free(buf); + E.dirty = 0; + editorSetStatusMessage("%d bytes written to disk", len); + return; + } + editorSelectSyntaxHighlight(); + } + close(fd); + } + + free(buf); + editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..6aab45e --- /dev/null +++ b/file.h @@ -0,0 +1,8 @@ +#ifndef _FILE_H_ +#define _FILE_H_ + +void editorOpen(char *filename); +void editorSave(); + +#endif + diff --git a/fn b/fn index 957b7451c82629d875bd46a8fbd47d0df47aac83..82c13dcbd58afdf74b6fdd39751ad252c95031bc 100755 GIT binary patch delta 9263 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 diff --git a/fn.c b/fn.c index 1037cff..ae39d3b 100644 --- a/fn.c +++ b/fn.c @@ -1,5 +1,3 @@ -/*** includes ***/ - #define _DEFAULT_SOURCE #define _BSD_SOURCE #define _GNU_SOURCE @@ -20,834 +18,17 @@ #include "fn.h" #include "terminal.h" - - -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])) - - -/*** 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 **keywords = E.syntax->keywords; - - char *scs = E.syntax->singleline_comment_start; - char *mcs = E.syntax->multiline_comment_start; - char *mce = E.syntax->multiline_comment_end; - - int scs_len = scs ? strlen(scs) : 0; - int mcs_len = mcs ? strlen(mcs) : 0; - int mce_len = mce ? strlen(mce) : 0; - - int prev_sep = 1; - int in_string = 0; - int in_comment = (row->idx > 0 && E.row[row->idx-1].hl_open_comment); - - 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 && !in_comment) { - if (!strncmp(&row->render[i], scs, scs_len)) { - memset(&row->hl[i], HL_COMMENT, row->rsize - i); - break; - } - } - - // Multi line comments - if (mcs_len && mce_len && !in_string) { - if (in_comment) { - row->hl[i] = HL_MLCOMMENT; - if (!strncmp(&row->render[i], mce, mce_len)) { - memset(&row->hl[i], HL_MLCOMMENT, mce_len); - i += mce_len; - in_comment = 0; - prev_sep = 1; - continue; - } else { - i++; - continue; - } - } else if (!strncmp(&row->render[i], mcs, mcs_len)) { - memset(&row->hl[i], HL_MLCOMMENT, mcs_len); - i += mcs_len; - in_comment = 1; - continue; - } - } - - // 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; - } - } - - // Keywords - 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); - i++; - } - - int changed = (row->hl_open_comment != in_comment); - row->hl_open_comment = in_comment; - if (changed && row->idx+1 < E.numrows) { - editorUpdateSyntax(&E.row[row->idx+1]); - } -} - -int editorSyntaxToColor(int hl) { - switch (hl) { - case HL_COMMENT: - case HL_MLCOMMENT: return 36; - case HL_KEYWORD1: return 33; - case HL_KEYWORD2: return 32; - 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; filerowchars[j] == '\t') { - rx += (FN_TAB_STOP - 1) - (rx % FN_TAB_STOP); - } - rx++; - } - return rx; -} - -int editorRowRxToCx(erow *row, int rx) { - int cur_rx = 0; - int cx; - for (cx=0; cx < row->size; cx++) { - if (row->chars[cx] == '\t') - cur_rx += (FN_TAB_STOP - 1) - (cur_rx % FN_TAB_STOP); - cur_rx++; - - if (cur_rx > rx) return cx; - } - return cx; -} - -void editorUpdateRow(erow *row) { - int tabs = 0; - int j; - for (j=0; jsize; j++) { - if (row->chars[j] == '\t') tabs++; - } - - free(row->render); - row->render = malloc(row->size + tabs*(FN_TAB_STOP - 1) + 1); - - int idx = 0; - for (j=0; jsize; j++) { - if (row->chars[j] == '\t') { - row->render[idx++] = ' '; - while (idx % FN_TAB_STOP != 0) row->render[idx++] = ' '; - } else { - row->render[idx++] = row->chars[j]; - } - } - row->render[idx] = '\0'; - row->rsize = idx; - - editorUpdateSyntax(row); -} - -void editorInsertRow(int at, char *s, size_t len) { - if (at < 0 || at > E.numrows) return; - - E.row = realloc(E.row, sizeof(erow) * (E.numrows +1)); - memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at)); - for (int j=at+1; j<= E.numrows; j++) E.row[j].idx++; - - E.row[at].idx = at; - - E.row[at].size = len; - E.row[at].chars = malloc(len + 1); - memcpy(E.row[at].chars, s, len); - E.row[at].chars[len] = '\0'; - - E.row[at].rsize = 0; - E.row[at].render = NULL; - E.row[at].hl = NULL; - E.row[at].hl_open_comment = 0; - editorUpdateRow(&E.row[at]); - - E.numrows++; - E.dirty++; -} - -void editorFreeRow(erow *row) { - free(row->render); - free(row->chars); - free(row->hl); -} - -void editorDelRow(int at) { - if (at < 0 || at >= E.numrows) return; - editorFreeRow(&E.row[at]); - memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at -1)); - for (int j=at; j<= E.numrows-1; j++) E.row[j].idx--; - E.numrows--; - E.dirty++; -} - -void editorRowInsertChar(erow *row, int at, int c) { - if (at < 0 || at > row->size) at = row->size; - row->chars = realloc(row->chars, row->size + 2); - memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); - row->size++; - row->chars[at] = c; - editorUpdateRow(row); - E.dirty++; -} - -void editorRowAppendString(erow *row, char *s, size_t len) { - row->chars = realloc(row->chars, row->size + len + 1); - memcpy(&row->chars[row->size], s, len); - row->size += len; - row->chars[row->size] = '\0'; - editorUpdateRow(row); - E.dirty++; -} - -void editorRowDelChar(erow *row, int at) { - if (at < 0 || at >= row->size) return; - memmove(&row->chars[at], &row->chars[at + 1], row->size - at); - row->size--; - editorUpdateRow(row); - E.dirty++; -} - - -/*** editor operations ***/ - -void editorInsertChar(int c) { - if (E.cy == E.numrows) { - editorInsertRow(E.numrows, "", 0); - } - editorRowInsertChar(&E.row[E.cy], E.cx, c); - E.cx++; -} - -void editorInsertNewLine() { - if (E.cx == 0) { - editorInsertRow(E.cy, "", 0); - } else { - erow *row = &E.row[E.cy]; - editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx); - row = &E.row[E.cy]; - row->size = E.cx; - row->chars[row->size] = '\0'; - editorUpdateRow(row); - } - E.cy++; - E.cx = 0; -} - - -void editorDelChar() { - if (E.cy == E.numrows) return; - if (E.cx == 0 && E.cy == 0) return; - - erow *row = &E.row[E.cy]; - if (E.cx > 0) { - editorRowDelChar(row, E.cx - 1); - E.cx--; - } else { - E.cx = E.row[E.cy - 1].size; - editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size); - editorDelRow(E.cy); - E.cy--; - } -} - -/*** file i/o ***/ - -char *editorRowsToString(int *buflen) { - int totlen = 0; - int j; - for (j=0; j 0 && (line[linelen - 1] == '\n' || - line[linelen - 1] == '\r')) - linelen--; - editorInsertRow(E.numrows, line, linelen); - } - free(line); - fclose(fp); - E.dirty = 0; -} - -void editorSave() { - if (E.filename == NULL) { - E.filename = editorPrompt("Save as: %s (ESC to cancel)", NULL); - if (E.filename == NULL) { - editorSetStatusMessage("Save aborted"); - return; - } - } - - int len; - char *buf = editorRowsToString(&len); - - int fd = open(E.filename, O_RDWR | O_CREAT, 0644); - if (fd != -1) { - if (ftruncate(fd, len) != -1) { - if (write(fd, buf, len) == len) { - close(fd); - free(buf); - E.dirty = 0; - editorSetStatusMessage("%d bytes written to disk", len); - return; - } - editorSelectSyntaxHighlight(); - } - close(fd); - } - - free(buf); - editorSetStatusMessage("Can't save! I/O error: %s", strerror(errno)); -} - -/** find **/ - -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; - } +#include "output.h" +#include "row.h" +#include "hl.h" +#include "search.h" +#include "file.h" +#include "input.h" + +// TODO: Put this somewhere else? +void windowResizeCallback(int signum); +void initEditor(); - if (key == '\r' || key == '\xb') { - last_match = -1; - direction = 1; - return; - } else if (key == ARROW_RIGHT || key == ARROW_DOWN) { - direction = 1; - } else if (key == ARROW_LEFT || key == ARROW_UP) { - direction = -1; - } else { - last_match = -1; - direction = 1; - } - - if (last_match == -1) direction = 1; - int current = last_match; - - int i; - for (i=0; i < E.numrows; i++) { - current += direction; - if (current == -1) current = E.numrows -1; - else if (current == E.numrows) current = 0; - - erow *row = &E.row[current]; - char *match = strstr(row->render, query); - if (match) { - last_match = current; - 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; - } - } -} - -void editorFind() { - int saved_cx = E.cx; - int saved_cy = E.cy; - int saved_coloff = E.coloff; - int saved_rowoff = E.rowoff; - - char *query = editorPrompt("Search: %s (Use ESC/Arrows/Enter)", editorFindCallback); - if (query) { - free(query); - } else { - E.cx = saved_cx; - E.cy = saved_cy; - E.coloff = saved_coloff; - E.rowoff = saved_rowoff; - } -} - -/*** append buffer ***/ - -struct abuf { - char *b; - int len; -}; - -#define ABUF_INIT {NULL, 0} - -void abAppend(struct abuf *ab, const char *s, int len) { - char *new = realloc(ab->b, ab->len + len); - if (new == NULL) return; - memcpy(&new[ab->len], s, len); - ab->b = new; - ab->len += len; -} -void abFree(struct abuf *ab) { - free(ab->b); -} - -/*** output ***/ - -void editorScroll() { - E.rx = 0; - if (E.cy < E.numrows) { - E.rx = editorRowCxToRx(&E.row[E.cy], E.cx); - } - - if (E.cy < E.rowoff) { - E.rowoff = E.cy; - } - if (E.cy >= E.rowoff + E.screenrows) { - E.rowoff = E.cy - E.screenrows + 1; - } - if (E.rx < E.coloff) { - E.coloff = E.rx; - } - if (E.rx >= E.coloff + E.screencols) { - E.coloff = E.rx - E.screencols + 1; - } -} - -void editorDrawRows(struct abuf *ab) { - int y; - for (y = 0; y < E.screenrows; y++) { - int filerow = y + E.rowoff; - if (filerow >= E.numrows) { - if (E.numrows == 0 && y == E.screenrows / 3) { - char welcome[80]; - int welcomelen = snprintf(welcome, sizeof(welcome), - "FuNote v%s", FN_VERSION); - if (welcomelen > E.screencols) welcomelen = E.screencols; - int padding = (E.screencols - welcomelen) / 2; - if (padding) { - abAppend(ab, "~", 1); - padding--; - } - while(padding--) abAppend(ab, " ", 1); - abAppend(ab, welcome, welcomelen); - } else { - abAppend(ab, "~", 1); - } - } else { - int len = E.row[filerow].rsize - E.coloff; - 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) { - if (E.screencols - len == rlen) { - abAppend(ab, rstatus, rlen); - break; - } else { - abAppend(ab, " ", 1); - len++; - } - } - abAppend(ab, "\x1b[m", 3); - abAppend(ab, "\r\n", 2); -} - -void editorDrawMessageBar(struct abuf *ab) { - abAppend(ab, "\x1b[K", 3); - int msglen = strlen(E.statusmsg); - if (msglen > E.screencols) msglen = E.screencols; - if (msglen && time(NULL) - E.statusmsg_time < 5) - abAppend(ab, E.statusmsg, msglen); -} - -void editorRefreshScreen() { - editorScroll(); - - struct abuf ab = ABUF_INIT; - - abAppend(&ab, "\x1b[?25l", 6); // Hide cursor - abAppend(&ab, "\x1b[H", 3); // CUP (cursor position) - - editorDrawRows(&ab); - editorDrawStatusBar(&ab); - editorDrawMessageBar(&ab); - - // Update cursor position - char buf[32]; - snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.rx - E.coloff) + 1); - abAppend(&ab, buf, strlen(buf)); - - abAppend(&ab, "\x1b[?25h", 6); // Show cursor - - write(STDOUT_FILENO, ab.b, ab.len); - abFree(&ab); -} - -void editorSetStatusMessage(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap); - va_end(ap); - E.statusmsg_time = time(NULL); -} - -/*** input ***/ - -char *editorPrompt(char *prompt, void (*callback)(char *, int)) { - size_t bufsize = 128; - char *buf = malloc(bufsize); - - size_t buflen = 0; - buf[0] = '\0'; - - while (1) { - editorSetStatusMessage(prompt, buf); - editorRefreshScreen(); - - int c = editorReadKey(); - if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) { - if (buflen != 0) buf[--buflen] = '\0'; - } else if (c == '\x1b') { - editorSetStatusMessage(""); - if (callback) callback(buf, c); - free(buf); - return NULL; - } else if (c == '\r') { - if (buflen != 0) { - editorSetStatusMessage(""); - if (callback) callback(buf, c); - return buf; - } - } else if (!iscntrl(c) && c < 128) { - if (buflen == bufsize -1) { - bufsize *= 2; - buf = realloc(buf, bufsize); - } - buf[buflen++] = c; - buf[buflen] = '\0'; - } - - if (callback) callback(buf, c); - } -} - -void editorMoveCursor(int key) { - erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; - - switch (key) { - case ARROW_LEFT: - if (E.cx != 0) { - E.cx--; - } else if (E.cy > 0) { - E.cy--; - E.cx = E.row[E.cy].size; - } - break; - case ARROW_RIGHT: - if (row && E.cx < row->size) { - E.cx++; - } else if (row && E.cx == row->size) { - E.cy++; - E.cx = 0; - } - break; - case ARROW_UP: - if (E.cy != 0) { - E.cy--; - } - break; - case ARROW_DOWN: - if (E.cy < E.numrows) { - E.cy++; - } - break; - } - - row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; - int rowlen = row ? row->size : 0; - if (E.cx > rowlen) { - E.cx = rowlen; - } -} - - -void editorProcessKeypress() { - static int quit_times = FN_QUIT_TIMES; - - int c = editorReadKey(); // Blocking! - - switch (c) { - case '\r': - editorInsertNewLine(); - break; - - case CTRL_KEY('q'): - if (E.dirty && quit_times > 0) { - editorSetStatusMessage("WARNING!!! File has unsaved changes. " - "Press CTRL-Q %d more times to quit.", quit_times); - quit_times--; - return; - } - write(STDOUT_FILENO, "\x1b[2J", 4); - write(STDOUT_FILENO, "\x1b[H", 3); - exit(0); - break; - - case CTRL_KEY('s'): - editorSave(); - break; - - case HOME_KEY: - E.cx = 0; - break; - - case END_KEY: - if (E.cy < E.numrows) { - E.cx = E.row[E.cy].size; - } - break; - - case CTRL_KEY('f'): - editorFind(); - break; - - case BACKSPACE: - case CTRL_KEY('h'): - case DEL_KEY: - if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT); - editorDelChar(); - break; - - case PAGE_UP: - case PAGE_DOWN: - { - if (c == PAGE_UP) { - E.cy = E.rowoff; - } else if (c == PAGE_DOWN) { - E.cy = E.rowoff + E.screenrows - 1; - if (E.cy > E.numrows) E.cy = E.numrows; - } - int times = E.screenrows; - while(times--) - editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); - } - break; - case ARROW_UP: - case ARROW_DOWN: - case ARROW_LEFT: - case ARROW_RIGHT: - editorMoveCursor(c); - break; - - case CTRL_KEY('l'): - case '\x1b': - break; - - default: - editorInsertChar(c); - break; - } - - quit_times = FN_QUIT_TIMES; -} - -/*** init ***/ - void windowResizeCallback(int signum) { if (signum != SIGWINCH) { return; diff --git a/fn.h b/fn.h index 32786b2..a0345d0 100644 --- a/fn.h +++ b/fn.h @@ -76,6 +76,11 @@ struct editorConfig { struct editorConfig E; +struct abuf { + char *b; + int len; +}; + /*** filetypes ***/ diff --git a/hl.c b/hl.c new file mode 100644 index 0000000..e4f1534 --- /dev/null +++ b/hl.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "hl.h" + +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])) + + +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 **keywords = E.syntax->keywords; + + char *scs = E.syntax->singleline_comment_start; + char *mcs = E.syntax->multiline_comment_start; + char *mce = E.syntax->multiline_comment_end; + + int scs_len = scs ? strlen(scs) : 0; + int mcs_len = mcs ? strlen(mcs) : 0; + int mce_len = mce ? strlen(mce) : 0; + + int prev_sep = 1; + int in_string = 0; + int in_comment = (row->idx > 0 && E.row[row->idx-1].hl_open_comment); + + 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 && !in_comment) { + if (!strncmp(&row->render[i], scs, scs_len)) { + memset(&row->hl[i], HL_COMMENT, row->rsize - i); + break; + } + } + + // Multi line comments + if (mcs_len && mce_len && !in_string) { + if (in_comment) { + row->hl[i] = HL_MLCOMMENT; + if (!strncmp(&row->render[i], mce, mce_len)) { + memset(&row->hl[i], HL_MLCOMMENT, mce_len); + i += mce_len; + in_comment = 0; + prev_sep = 1; + continue; + } else { + i++; + continue; + } + } else if (!strncmp(&row->render[i], mcs, mcs_len)) { + memset(&row->hl[i], HL_MLCOMMENT, mcs_len); + i += mcs_len; + in_comment = 1; + continue; + } + } + + // 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; + } + } + + // Keywords + 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); + i++; + } + + int changed = (row->hl_open_comment != in_comment); + row->hl_open_comment = in_comment; + if (changed && row->idx+1 < E.numrows) { + editorUpdateSyntax(&E.row[row->idx+1]); + } +} + +int editorSyntaxToColor(int hl) { + switch (hl) { + case HL_COMMENT: + case HL_MLCOMMENT: return 36; + case HL_KEYWORD1: return 33; + case HL_KEYWORD2: return 32; + 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; filerow +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "row.h" +#include "terminal.h" +#include "input.h" +#include "output.h" +#include "file.h" +#include "search.h" + + +char *editorPrompt(char *prompt, void (*callback)(char *, int)) { + size_t bufsize = 128; + char *buf = malloc(bufsize); + + size_t buflen = 0; + buf[0] = '\0'; + + while (1) { + editorSetStatusMessage(prompt, buf); + editorRefreshScreen(); + + int c = editorReadKey(); + if (c == DEL_KEY || c == CTRL_KEY('h') || c == BACKSPACE) { + if (buflen != 0) buf[--buflen] = '\0'; + } else if (c == '\x1b') { + editorSetStatusMessage(""); + if (callback) callback(buf, c); + free(buf); + return NULL; + } else if (c == '\r') { + if (buflen != 0) { + editorSetStatusMessage(""); + if (callback) callback(buf, c); + return buf; + } + } else if (!iscntrl(c) && c < 128) { + if (buflen == bufsize -1) { + bufsize *= 2; + buf = realloc(buf, bufsize); + } + buf[buflen++] = c; + buf[buflen] = '\0'; + } + + if (callback) callback(buf, c); + } +} + +void editorMoveCursor(int key) { + erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; + + switch (key) { + case ARROW_LEFT: + if (E.cx != 0) { + E.cx--; + } else if (E.cy > 0) { + E.cy--; + E.cx = E.row[E.cy].size; + } + break; + case ARROW_RIGHT: + if (row && E.cx < row->size) { + E.cx++; + } else if (row && E.cx == row->size) { + E.cy++; + E.cx = 0; + } + break; + case ARROW_UP: + if (E.cy != 0) { + E.cy--; + } + break; + case ARROW_DOWN: + if (E.cy < E.numrows) { + E.cy++; + } + break; + } + + row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy]; + int rowlen = row ? row->size : 0; + if (E.cx > rowlen) { + E.cx = rowlen; + } +} + + +void editorProcessKeypress() { + static int quit_times = FN_QUIT_TIMES; + + int c = editorReadKey(); // Blocking! + + switch (c) { + case '\r': + editorInsertNewLine(); + break; + + case CTRL_KEY('q'): + if (E.dirty && quit_times > 0) { + editorSetStatusMessage("WARNING!!! File has unsaved changes. " + "Press CTRL-Q %d more times to quit.", quit_times); + quit_times--; + return; + } + write(STDOUT_FILENO, "\x1b[2J", 4); + write(STDOUT_FILENO, "\x1b[H", 3); + exit(0); + break; + + case CTRL_KEY('s'): + editorSave(); + break; + + case HOME_KEY: + E.cx = 0; + break; + + case END_KEY: + if (E.cy < E.numrows) { + E.cx = E.row[E.cy].size; + } + break; + + case CTRL_KEY('f'): + editorFind(); + break; + + case BACKSPACE: + case CTRL_KEY('h'): + case DEL_KEY: + if (c == DEL_KEY) editorMoveCursor(ARROW_RIGHT); + editorDelChar(); + break; + + case PAGE_UP: + case PAGE_DOWN: + { + if (c == PAGE_UP) { + E.cy = E.rowoff; + } else if (c == PAGE_DOWN) { + E.cy = E.rowoff + E.screenrows - 1; + if (E.cy > E.numrows) E.cy = E.numrows; + } + int times = E.screenrows; + while(times--) + editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN); + } + break; + case ARROW_UP: + case ARROW_DOWN: + case ARROW_LEFT: + case ARROW_RIGHT: + editorMoveCursor(c); + break; + + case CTRL_KEY('l'): + case '\x1b': + break; + + default: + editorInsertChar(c); + break; + } + + quit_times = FN_QUIT_TIMES; +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..418418b --- /dev/null +++ b/input.h @@ -0,0 +1,9 @@ +#ifndef _INPUT_H_ +#define _INPUT_H_ + +char *editorPrompt(char *prompt, void (*callback)(char *, int)); +void editorMoveCursor(int key); +void editorProcessKeypress(); + +#endif + diff --git a/output.c b/output.c new file mode 100644 index 0000000..f32b944 --- /dev/null +++ b/output.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "row.h" +#include "hl.h" +#include "output.h" + +void editorScroll() { + E.rx = 0; + if (E.cy < E.numrows) { + E.rx = editorRowCxToRx(&E.row[E.cy], E.cx); + } + + if (E.cy < E.rowoff) { + E.rowoff = E.cy; + } + if (E.cy >= E.rowoff + E.screenrows) { + E.rowoff = E.cy - E.screenrows + 1; + } + if (E.rx < E.coloff) { + E.coloff = E.rx; + } + if (E.rx >= E.coloff + E.screencols) { + E.coloff = E.rx - E.screencols + 1; + } +} + +void editorDrawRows(struct abuf *ab) { + int y; + for (y = 0; y < E.screenrows; y++) { + int filerow = y + E.rowoff; + if (filerow >= E.numrows) { + if (E.numrows == 0 && y == E.screenrows / 3) { + char welcome[80]; + int welcomelen = snprintf(welcome, sizeof(welcome), + "FuNote v%s", FN_VERSION); + if (welcomelen > E.screencols) welcomelen = E.screencols; + int padding = (E.screencols - welcomelen) / 2; + if (padding) { + abAppend(ab, "~", 1); + padding--; + } + while(padding--) abAppend(ab, " ", 1); + abAppend(ab, welcome, welcomelen); + } else { + abAppend(ab, "~", 1); + } + } else { + int len = E.row[filerow].rsize - E.coloff; + 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) { + if (E.screencols - len == rlen) { + abAppend(ab, rstatus, rlen); + break; + } else { + abAppend(ab, " ", 1); + len++; + } + } + abAppend(ab, "\x1b[m", 3); + abAppend(ab, "\r\n", 2); +} + +void editorDrawMessageBar(struct abuf *ab) { + abAppend(ab, "\x1b[K", 3); + int msglen = strlen(E.statusmsg); + if (msglen > E.screencols) msglen = E.screencols; + if (msglen && time(NULL) - E.statusmsg_time < 5) + abAppend(ab, E.statusmsg, msglen); +} + +void editorRefreshScreen() { + editorScroll(); + + struct abuf ab = ABUF_INIT; + + abAppend(&ab, "\x1b[?25l", 6); // Hide cursor + abAppend(&ab, "\x1b[H", 3); // CUP (cursor position) + + editorDrawRows(&ab); + editorDrawStatusBar(&ab); + editorDrawMessageBar(&ab); + + // Update cursor position + char buf[32]; + snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.rx - E.coloff) + 1); + abAppend(&ab, buf, strlen(buf)); + + abAppend(&ab, "\x1b[?25h", 6); // Show cursor + + write(STDOUT_FILENO, ab.b, ab.len); + abFree(&ab); +} + +void editorSetStatusMessage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap); + va_end(ap); + E.statusmsg_time = time(NULL); +} + + +// Line oriented actions + +void editorInsertChar(int c) { + if (E.cy == E.numrows) { + editorInsertRow(E.numrows, "", 0); + } + editorRowInsertChar(&E.row[E.cy], E.cx, c); + E.cx++; +} + +void editorInsertNewLine() { + if (E.cx == 0) { + editorInsertRow(E.cy, "", 0); + } else { + erow *row = &E.row[E.cy]; + editorInsertRow(E.cy + 1, &row->chars[E.cx], row->size - E.cx); + row = &E.row[E.cy]; + row->size = E.cx; + row->chars[row->size] = '\0'; + editorUpdateRow(row); + } + E.cy++; + E.cx = 0; +} + + +void editorDelChar() { + if (E.cy == E.numrows) return; + if (E.cx == 0 && E.cy == 0) return; + + erow *row = &E.row[E.cy]; + if (E.cx > 0) { + editorRowDelChar(row, E.cx - 1); + E.cx--; + } else { + E.cx = E.row[E.cy - 1].size; + editorRowAppendString(&E.row[E.cy - 1], row->chars, row->size); + editorDelRow(E.cy); + E.cy--; + } +} diff --git a/output.h b/output.h new file mode 100644 index 0000000..373ae35 --- /dev/null +++ b/output.h @@ -0,0 +1,20 @@ +#ifndef _OUTPUT_H +#define _OUTPUT_H + +void editorScroll(); + +void editorDrawRows(struct abuf *ab); +void editorDrawStatusBar(struct abuf *ab); +void editorDrawMessageBar(struct abuf *ab); + +void editorRefreshScreen(); + +void editorSetStatusMessage(const char *fmt, ...); + +// Line oriented actions +void editorInsertChar(int c); +void editorInsertNewLine(); +void editorDelChar(); + +#endif + diff --git a/row.c b/row.c new file mode 100644 index 0000000..23b1225 --- /dev/null +++ b/row.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "hl.h" +#include "row.h" + +void abAppend(struct abuf *ab, const char *s, int len) { + char *new = realloc(ab->b, ab->len + len); + if (new == NULL) return; + memcpy(&new[ab->len], s, len); + ab->b = new; + ab->len += len; +} +void abFree(struct abuf *ab) { + free(ab->b); +} + + +char *editorRowsToString(int *buflen) { + int totlen = 0; + int j; + for (j=0; jchars[j] == '\t') { + rx += (FN_TAB_STOP - 1) - (rx % FN_TAB_STOP); + } + rx++; + } + return rx; +} + +int editorRowRxToCx(erow *row, int rx) { + int cur_rx = 0; + int cx; + for (cx=0; cx < row->size; cx++) { + if (row->chars[cx] == '\t') + cur_rx += (FN_TAB_STOP - 1) - (cur_rx % FN_TAB_STOP); + cur_rx++; + + if (cur_rx > rx) return cx; + } + return cx; +} + +void editorUpdateRow(erow *row) { + int tabs = 0; + int j; + for (j=0; jsize; j++) { + if (row->chars[j] == '\t') tabs++; + } + + free(row->render); + row->render = malloc(row->size + tabs*(FN_TAB_STOP - 1) + 1); + + int idx = 0; + for (j=0; jsize; j++) { + if (row->chars[j] == '\t') { + row->render[idx++] = ' '; + while (idx % FN_TAB_STOP != 0) row->render[idx++] = ' '; + } else { + row->render[idx++] = row->chars[j]; + } + } + row->render[idx] = '\0'; + row->rsize = idx; + + editorUpdateSyntax(row); +} + +void editorInsertRow(int at, char *s, size_t len) { + if (at < 0 || at > E.numrows) return; + + E.row = realloc(E.row, sizeof(erow) * (E.numrows +1)); + memmove(&E.row[at + 1], &E.row[at], sizeof(erow) * (E.numrows - at)); + for (int j=at+1; j<= E.numrows; j++) E.row[j].idx++; + + E.row[at].idx = at; + + E.row[at].size = len; + E.row[at].chars = malloc(len + 1); + memcpy(E.row[at].chars, s, len); + E.row[at].chars[len] = '\0'; + + E.row[at].rsize = 0; + E.row[at].render = NULL; + E.row[at].hl = NULL; + E.row[at].hl_open_comment = 0; + editorUpdateRow(&E.row[at]); + + E.numrows++; + E.dirty++; +} + +void editorFreeRow(erow *row) { + free(row->render); + free(row->chars); + free(row->hl); +} + +void editorDelRow(int at) { + if (at < 0 || at >= E.numrows) return; + editorFreeRow(&E.row[at]); + memmove(&E.row[at], &E.row[at + 1], sizeof(erow) * (E.numrows - at -1)); + for (int j=at; j<= E.numrows-1; j++) E.row[j].idx--; + E.numrows--; + E.dirty++; +} + +void editorRowInsertChar(erow *row, int at, int c) { + if (at < 0 || at > row->size) at = row->size; + row->chars = realloc(row->chars, row->size + 2); + memmove(&row->chars[at + 1], &row->chars[at], row->size - at + 1); + row->size++; + row->chars[at] = c; + editorUpdateRow(row); + E.dirty++; +} + +void editorRowAppendString(erow *row, char *s, size_t len) { + row->chars = realloc(row->chars, row->size + len + 1); + memcpy(&row->chars[row->size], s, len); + row->size += len; + row->chars[row->size] = '\0'; + editorUpdateRow(row); + E.dirty++; +} + +void editorRowDelChar(erow *row, int at) { + if (at < 0 || at >= row->size) return; + memmove(&row->chars[at], &row->chars[at + 1], row->size - at); + row->size--; + editorUpdateRow(row); + E.dirty++; +} diff --git a/row.h b/row.h new file mode 100644 index 0000000..de04a68 --- /dev/null +++ b/row.h @@ -0,0 +1,24 @@ +#ifndef _ROW_H +#define _ROW_H + +#define ABUF_INIT {NULL, 0} + +void abAppend(struct abuf *ab, const char *s, int len); +void abFree(struct abuf *ab); + +char *editorRowsToString(int *buflen); + +int editorRowCxToRx(erow *row, int cx); +int editorRowRxToCx(erow *row, int rx); + +void editorUpdateRow(erow *row); +void editorInsertRow(int at, char *s, size_t len); +void editorFreeRow(erow *row); +void editorDelRow(int at); + +void editorRowInsertChar(erow *row, int at, int c); +void editorRowAppendString(erow *row, char *s, size_t len); +void editorRowDelChar(erow *row, int at); + +#endif + diff --git a/search.c b/search.c new file mode 100644 index 0000000..25db62c --- /dev/null +++ b/search.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fn.h" +#include "search.h" +#include "row.h" + +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; + direction = 1; + return; + } else if (key == ARROW_RIGHT || key == ARROW_DOWN) { + direction = 1; + } else if (key == ARROW_LEFT || key == ARROW_UP) { + direction = -1; + } else { + last_match = -1; + direction = 1; + } + + if (last_match == -1) direction = 1; + int current = last_match; + + int i; + for (i=0; i < E.numrows; i++) { + current += direction; + if (current == -1) current = E.numrows -1; + else if (current == E.numrows) current = 0; + + erow *row = &E.row[current]; + char *match = strstr(row->render, query); + if (match) { + last_match = current; + 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; + } + } +} + +void editorFind() { + int saved_cx = E.cx; + int saved_cy = E.cy; + int saved_coloff = E.coloff; + int saved_rowoff = E.rowoff; + + char *query = editorPrompt("Search: %s (Use ESC/Arrows/Enter)", editorFindCallback); + if (query) { + free(query); + } else { + E.cx = saved_cx; + E.cy = saved_cy; + E.coloff = saved_coloff; + E.rowoff = saved_rowoff; + } +} diff --git a/search.h b/search.h new file mode 100644 index 0000000..6f4f1fc --- /dev/null +++ b/search.h @@ -0,0 +1,8 @@ +#ifndef _SEARCH_H_ +#define _SEARCH_H_ + +void editorFindCallback(char *query, int key); +void editorFind(); + +#endif +