lib/exec.o \
lib/view.o \
lib/x11.o \
- lib/win.o
+ lib/win.o \
+ lib/colors.o
TEST_BINS = \
tests/tide \
void binload(Buf* buf, FMap file);
void binsave(Buf* buf, FILE* file);
+/* Syntax Highlighting
+ *****************************************************************************/
+typedef struct {
+ char* name;
+ char** extensions;
+ struct {
+ char* line_beg;
+ char* multi_beg;
+ char* multi_end;
+ } comments;
+} SyntaxDef;
+
+typedef struct SyntaxSpan {
+ size_t beg;
+ size_t end;
+ size_t color;
+ struct SyntaxSpan* prev;
+ struct SyntaxSpan* next;
+} SyntaxSpan;
+
+SyntaxDef* colors_find(char* path);
+SyntaxSpan* colors_scan(SyntaxDef* syntax, Buf* buf);
+
/* Screen management functions
*****************************************************************************/
typedef struct {
} Row;
typedef struct {
- bool sync_needed; /* whether the view needs to be synced with cursor */
- bool sync_center; /* cursor should be centered on screen if possible */
- bool sync_lines; /* whether the line numbers should be recalculated */
- size_t nrows; /* number of rows in the view */
- size_t ncols; /* number of columns in the view */
- Row** rows; /* array of row data structures */
- Buf buffer; /* the buffer used to populate the view */
- Sel selection; /* range of currently selected text */
- size_t prev_csr; /* previous cursor location */
+ bool sync_needed; /* whether the view needs to be synced with cursor */
+ bool sync_center; /* cursor should be centered on screen if possible */
+ bool sync_lines; /* whether the line numbers should be recalculated */
+ size_t nrows; /* number of rows in the view */
+ size_t ncols; /* number of columns in the view */
+ Row** rows; /* array of row data structures */
+ Buf buffer; /* the buffer used to populate the view */
+ Sel selection; /* range of currently selected text */
+ size_t prev_csr; /* previous cursor location */
+ SyntaxDef* syntax; /* syntax rules object */
+ SyntaxSpan* spans; /* list of colored regions */
} View;
enum {
--- /dev/null
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
+#include <config.h>
+
+static SyntaxDef Syntaxes[] = {
+ {
+ .name = "Text",
+ .extensions = (char*[]){ 0 },
+ .comments = { .line_beg = "#" }
+ },
+ {
+ .name = "C",
+ .extensions = (char*[]){
+ ".c", ".h", ".C", ".cpp", ".CPP", ".hpp", ".cc", ".c++", ".cxx", 0 },
+ .comments = {
+ .line_beg = "//", .multi_beg = "/*", .multi_end = "*/" }
+ },
+ {
+ .name = "Ruby",
+ .extensions = (char*[]){ ".rb", 0 },
+ .comments = { .line_beg = "#" }
+ },
+ {
+ .name = "Shell",
+ .extensions = (char*[]){ ".sh", 0 },
+ .comments = { .line_beg = "#" }
+ }
+};
+
+SyntaxDef* colors_find(char* path) {
+ char* ext = strrchr(path, '.');
+ for (int i = 0; i < nelem(Syntaxes); i++) {
+ char** synext = Syntaxes[i].extensions;
+ for (; ext && *synext; synext++) {
+ if (!strcmp(*synext, ext))
+ return &Syntaxes[i];
+ }
+ }
+ return &Syntaxes[0];
+}
+
+static bool matches(Buf* buf, size_t* off, char* str) {
+ size_t curr = *off;
+ if (str) {
+ while (*str && *str == buf_get(buf, curr))
+ curr++, str++;
+ if (*str == '\0') {
+ *off = curr;
+ return true;
+ }
+ }
+ return false;
+}
+
+static SyntaxSpan* mkspan(size_t beg, size_t end, size_t clr, SyntaxSpan* span) {
+ SyntaxSpan* newspan = malloc(sizeof(SyntaxSpan));
+ newspan->beg = beg;
+ newspan->end = end;
+ newspan->color = clr;
+ newspan->prev = span;
+ newspan->next = NULL;
+ if (span)
+ span->next = newspan;
+ return newspan;
+}
+
+SyntaxSpan* colors_scan(SyntaxDef* syntax, Buf* buf) {
+ SyntaxSpan* firstspan = NULL;
+ SyntaxSpan* spans = NULL;
+ if (!syntax) return spans;
+ for (size_t end = buf_end(buf), off = 0; off < end; off++) {
+ size_t start = off;
+ if (matches(buf, &off, syntax->comments.line_beg))
+ for (; off < end && !buf_iseol(buf, off); off++);
+ else if (matches(buf, &off, syntax->comments.multi_beg))
+ for (; off < end && !matches(buf, &off, syntax->comments.multi_end); off++);
+ if (start != off) {
+ spans = mkspan(start, ++off, CLR_Cursor, spans);
+ }
+ if (!firstspan && spans)
+ firstspan = spans;
+ }
+ return firstspan;
+}
view->prev_csr = 0;
/* load the file and jump to the address returned from the load function */
buf_init(&(view->buffer), errfn);
- if (file)
+ if (file) {
view_jumpto(view, false, buf_load(&(view->buffer), file));
+ view->syntax = colors_find(view->buffer.path);
+ }
}
void view_reload(View* view) {
view_scrollto(view, csr);
/* locate the cursor if visible */
find_cursor(view, csrx, csry);
+
+ view->spans = colors_scan(view->syntax, &(view->buffer));
+ SyntaxSpan* curr = view->spans;
+ for (size_t r = 0; curr && r < view->nrows; r++) {
+ Row* row = view->rows[r];
+ for (; curr && curr->end < row->off; curr = curr->next);
+ if (curr) {
+ size_t off = row->off, col = 0;
+ while (col < row->len) {
+ if (curr->beg <= off && off < curr->end) {
+ uint32_t attr = row->cols[col].attr;
+ row->cols[col].attr = (attr & 0xF0) | curr->color;
+ }
+ off++, col++;
+ while (col < row->len && row->cols[col].rune == '\0')
+ col++;
+ }
+ }
+ }
+
+ while (view->spans) {
+ SyntaxSpan* deadite = view->spans;
+ view->spans = deadite->next;
+ free(deadite);
+ }
}
Row* view_getrow(View* view, size_t row) {
}
static void draw_line_num(size_t x, size_t y, size_t gcols, size_t num) {
- UGlyph glyphs[gcols];
if (ShowLineNumbers) {
+ UGlyph glyphs[gcols];
for (int i = gcols-1; i >= 0; i--) {
glyphs[i].attr = CLR_GutterText;
if (num > 0) {