Browse Source

Split themes and syntax into separate files

K. Lange 1 year ago
parent
commit
d6d0e2dd95
30 changed files with 2645 additions and 2381 deletions
  1. 1 0
      .gitignore
  2. 17 4
      Makefile
  3. 290 0
      bim-core.h
  4. 27 0
      bim-functions.h
  5. 39 0
      bim-syntax.h
  6. 11 0
      bim-theme.h
  7. 239 2377
      bim.c
  8. 190 0
      syntax/bash.c
  9. 66 0
      syntax/bimcmd.c
  10. 23 0
      syntax/biminfo.c
  11. 35 0
      syntax/bimrc.c
  12. 267 0
      syntax/c.c
  13. 31 0
      syntax/conf.c
  14. 24 0
      syntax/ctags.c
  15. 27 0
      syntax/diff.c
  16. 118 0
      syntax/esh.c
  17. 46 0
      syntax/git.c
  18. 148 0
      syntax/java.c
  19. 59 0
      syntax/json.c
  20. 158 0
      syntax/make.c
  21. 135 0
      syntax/markdown.c
  22. 57 0
      syntax/proto.c
  23. 205 0
      syntax/py.c
  24. 100 0
      syntax/rust.c
  25. 82 0
      syntax/xml.c
  26. 41 0
      themes/ansi.c
  27. 42 0
      themes/citylights.c
  28. 42 0
      themes/solarized.c
  29. 83 0
      themes/sunsmoke.c
  30. 42 0
      themes/wombat.c

+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 bim
+*.o
 *.swp

+ 17 - 4
Makefile

@@ -1,5 +1,5 @@
 TARGET=bim
-CFLAGS=-g -std=c99 -Wvla -pedantic -Wall -Wextra
+CFLAGS=-g -std=c99 -Wvla -pedantic -Wall -Wextra -I.
 
 prefix=/usr/local
 exec_prefix=$(prefix)
@@ -8,12 +8,24 @@ bindir=$(exec_prefix)/bin
 INSTALL=install
 INSTALL_PROGRAM=$(INSTALL)
 
+THEMES=themes/ansi.o themes/citylights.o themes/solarized.o themes/sunsmoke.o themes/wombat.o
+SYNTAXES=syntax/bash.o syntax/bimcmd.o syntax/biminfo.o syntax/bimrc.o syntax/c.o syntax/conf.o syntax/ctags.o \
+         syntax/diff.o syntax/esh.o syntax/git.o syntax/java.o syntax/json.o syntax/make.o syntax/markdown.o \
+         syntax/proto.o syntax/py.o syntax/rust.o syntax/xml.o
+HEADERS=bim-core.h bim-functions.h bim-theme.h bim-syntax.h
+
 .PHONY: all clean distclean install install-strip uninstall
 
 all: $(TARGET)
 
+syntax/*.o: $(HEADERS)
+themes/*.o: $(HEADERS)
+*.o: $(HEADERS)
+
+bim: bim.o $(THEMES) $(SYNTAXES)
+
 clean:
-	-rm -f $(TARGET)
+	-rm -f $(TARGET) bim.o $(THEMES) $(SYNTAXES)
 
 distclean: clean
 
@@ -27,5 +39,6 @@ install-strip: all
 uninstall:
 	rm -f $(DESTDIR)$(bindir)/$(TARGET)
 
-tags: bim.c
-	ctags --c-kinds=+lx bim.c
+.PHONY: tags
+tags:
+	ctags --c-kinds=+lx bim.c themes/* syntax/*

+ 290 - 0
bim-core.h

@@ -0,0 +1,290 @@
+#ifndef _BIM_CORE_H
+#define _BIM_CORE_H
+
+#define _XOPEN_SOURCE 700
+#define _DARWIN_C_SOURCE
+#define _DEFAULT_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <termios.h>
+#include <signal.h>
+#include <locale.h>
+#include <wchar.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <poll.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+
+#define BIM_VERSION   "1.9.0"
+#define BIM_COPYRIGHT "Copyright 2012-2019 K. Lange <\033[3mklange@toaruos.org\033[23m>"
+
+#define BLOCK_SIZE 4096
+#define ENTER_KEY     '\r'
+#define LINE_FEED     '\n'
+#define BACKSPACE_KEY 0x08
+#define DELETE_KEY    0x7F
+
+/**
+ * Syntax highlighting flags.
+ */
+#define FLAG_NONE      0
+#define FLAG_KEYWORD   1
+#define FLAG_STRING    2
+#define FLAG_COMMENT   3
+#define FLAG_TYPE      4
+#define FLAG_PRAGMA    5
+#define FLAG_NUMERAL   6
+#define FLAG_ERROR     7
+#define FLAG_DIFFPLUS  8
+#define FLAG_DIFFMINUS 9
+#define FLAG_NOTICE    10
+#define FLAG_BOLD      11
+#define FLAG_LINK      12
+#define FLAG_ESCAPE    13
+
+#define FLAG_SELECT    (1 << 5)
+#define FLAG_SEARCH    (1 << 6)
+
+/**
+ * Line buffer definitions
+ *
+ * Lines are essentially resizable vectors of char_t structs,
+ * which represent single codepoints in the file.
+ */
+typedef struct {
+	uint32_t display_width:4;
+	uint32_t flags:7;
+	uint32_t codepoint:21;
+} __attribute__((packed)) char_t;
+
+/**
+ * Lines have available and actual lengths, describing
+ * how much space was allocated vs. how much is being
+ * used at the moment.
+ */
+typedef struct {
+	int available;
+	int actual;
+	int istate;
+	int is_current;
+	int rev_status;
+	char_t   text[];
+} line_t;
+
+/**
+ * Global configuration state
+ *
+ * At the moment, this is all in a global, but in the future
+ * this should be passed around to various functions.
+ */
+typedef struct {
+	/* Terminal size */
+	int term_width, term_height;
+	int bottom_size;
+
+	line_t ** yanks;
+	size_t    yank_count;
+	int       yank_is_full_lines;
+
+	int tty_in;
+
+	const char * bimrc_path;
+
+	unsigned int highlight_on_open:1;
+	unsigned int initial_file_is_read_only:1;
+	unsigned int can_scroll:1;
+	unsigned int can_hideshow:1;
+	unsigned int can_altscreen:1;
+	unsigned int can_mouse:1;
+	unsigned int can_unicode:1;
+	unsigned int can_bright:1;
+	unsigned int can_title:1;
+	unsigned int can_bce:1;
+	unsigned int history_enabled:1;
+	unsigned int highlight_parens:1;
+	unsigned int smart_case:1;
+	unsigned int can_24bit:1;
+	unsigned int can_256color:1;
+	unsigned int can_italic:1;
+	unsigned int go_to_line:1;
+	unsigned int highlight_current_line:1;
+	unsigned int shift_scrolling:1;
+	unsigned int check_git:1;
+	unsigned int color_gutter:1;
+	unsigned int relative_lines:1;
+	unsigned int break_from_selection:1;
+	unsigned int numbers:1;
+
+	int cursor_padding;
+	int split_percent;
+	int scroll_amount;
+	const char * syntax_fallback;
+	uint32_t * search;
+} global_config_t;
+
+#define HISTORY_SENTINEL     0
+#define HISTORY_INSERT       1
+#define HISTORY_DELETE       2
+#define HISTORY_REPLACE      3
+#define HISTORY_REMOVE_LINE  4
+#define HISTORY_ADD_LINE     5
+#define HISTORY_REPLACE_LINE 6
+#define HISTORY_MERGE_LINES  7
+#define HISTORY_SPLIT_LINE   8
+
+#define HISTORY_BREAK        10
+
+typedef struct history {
+	struct history * previous;
+	struct history * next;
+	int type;
+	int line;
+	int col;
+	union {
+		struct {
+			int lineno;
+			int offset;
+			int codepoint;
+			int old_codepoint;
+		} insert_delete_replace;
+
+		struct {
+			int lineno;
+			line_t * contents;
+			line_t * old_contents;
+		} remove_replace_line;
+
+		struct {
+			int lineno;
+			int split;
+		} add_merge_split_lines;
+	} contents;
+} history_t;
+
+/**
+ * Buffer data
+ *
+ * A buffer describes a file, and stores
+ * its name as well as the editor state
+ * (cursor offsets, etc.) and the actual
+ * line buffers.
+ */
+typedef struct _env {
+	unsigned int loading:1;
+	unsigned int tabs:1;
+	unsigned int modified:1;
+	unsigned int readonly:1;
+	unsigned int indent:1;
+	unsigned int checkgitstatusonwrite:1;
+	unsigned int crnl:1;
+
+	int highlighting_paren;
+
+	short  mode;
+	short  tabstop;
+
+	char * file_name;
+	int    offset;
+	int    coffset;
+	int    line_no;
+	int    line_count;
+	int    line_avail;
+	int    col_no;
+	int    preferred_column;
+	struct syntax_definition * syntax;
+	line_t ** lines;
+
+	history_t * history;
+	history_t * last_save_history;
+
+	int width;
+	int left;
+
+	int start_line;
+	int sel_col;
+} buffer_t;
+
+struct theme_def {
+	const char * name;
+	void (*load)(void);
+};
+
+extern struct theme_def * themes;
+
+struct syntax_state {
+	line_t * line;
+	int line_no;
+	int state;
+	int i;
+};
+
+struct syntax_definition {
+	char * name;
+	char ** ext;
+	int (*calculate)(struct syntax_state *);
+	int prefers_spaces;
+};
+
+extern struct syntax_definition * syntaxes;
+
+/**
+ * Editor mode states
+ */
+#define MODE_NORMAL 0
+#define MODE_INSERT 1
+#define MODE_LINE_SELECTION 2
+#define MODE_REPLACE 3
+#define MODE_CHAR_SELECTION 4
+#define MODE_COL_SELECTION 5
+#define MODE_COL_INSERT 6
+
+extern global_config_t global_config;
+
+extern const char * COLOR_FG;
+extern const char * COLOR_BG;
+extern const char * COLOR_ALT_FG;
+extern const char * COLOR_ALT_BG;
+extern const char * COLOR_NUMBER_FG;
+extern const char * COLOR_NUMBER_BG;
+extern const char * COLOR_STATUS_FG;
+extern const char * COLOR_STATUS_BG;
+extern const char * COLOR_TABBAR_BG;
+extern const char * COLOR_TAB_BG;
+extern const char * COLOR_ERROR_FG;
+extern const char * COLOR_ERROR_BG;
+extern const char * COLOR_SEARCH_FG;
+extern const char * COLOR_SEARCH_BG;
+extern const char * COLOR_KEYWORD;
+extern const char * COLOR_STRING;
+extern const char * COLOR_COMMENT;
+extern const char * COLOR_TYPE;
+extern const char * COLOR_PRAGMA;
+extern const char * COLOR_NUMERAL;
+extern const char * COLOR_SELECTFG;
+extern const char * COLOR_SELECTBG;
+extern const char * COLOR_RED;
+extern const char * COLOR_GREEN;
+extern const char * COLOR_BOLD;
+extern const char * COLOR_LINK;
+extern const char * COLOR_ESCAPE;
+extern const char * current_theme;
+
+extern buffer_t * env;
+extern buffer_t * left_buffer;
+extern buffer_t * right_buffer;
+#define NAV_BUFFER_MAX 10
+extern char nav_buf[NAV_BUFFER_MAX+1];
+extern int nav_buffer;
+extern int    buffers_len;
+extern int    buffers_avail;
+extern buffer_t ** buffers;
+
+#endif /* _BIM_CORE_H */

+ 27 - 0
bim-functions.h

@@ -0,0 +1,27 @@
+#ifndef _BIM_FUNCTIONS_H
+#define _BIM_FUNCTIONS_H
+
+extern const char * flag_to_color(int _flag);
+extern void redraw_line(int x);
+extern int git_examine(char * filename);
+extern void search_next(void);
+extern void set_preferred_column(void);
+extern void quit(const char * message);
+extern void close_buffer(void);
+extern void set_syntax_by_name(const char * name);
+extern void rehighlight_search(line_t * line);
+extern void try_to_center();
+extern int read_one_character(char * message);
+extern void bim_unget(int c);
+#define bim_getch() bim_getch_timeout(200)
+extern int bim_getch_timeout(int timeout);
+extern buffer_t * buffer_new(void);
+extern FILE * open_biminfo(void);
+extern int fetch_from_biminfo(buffer_t * buf);
+extern int update_biminfo(buffer_t * buf);
+extern buffer_t * buffer_close(buffer_t * buf);
+
+extern void add_colorscheme(const char * name, void (*load)(void));
+extern void add_syntax(struct syntax_definition def);
+
+#endif /* _BIM_FUNCTIONS_H */

+ 39 - 0
bim-syntax.h

@@ -0,0 +1,39 @@
+#ifndef _BIM_SYNTAX_H
+#define _BIM_SYNTAX_H
+
+#define BIM_SYNTAX(name, spaces) \
+	__attribute__((constructor)) static void _load_ ## name (void) { \
+		add_syntax((struct syntax_definition){#name, syn_ ## name ## _ext, syn_ ## name ## _calculate, spaces}); \
+	} \
+
+#define paint(length, flag) do { for (int i = 0; i < (length) && state->i < state->line->actual; i++, state->i++) { state->line->text[state->i].flags = (flag); } } while (0)
+#define charat() (state->i < state->line->actual ? state->line->text[(state->i)].codepoint : -1)
+#define nextchar() (state->i + 1 < state->line->actual ? state->line->text[(state->i+1)].codepoint : -1)
+#define lastchar() (state->i - 1 >= 0 ? state->line->text[(state->i-1)].codepoint : -1)
+#define skip() (state->i++)
+#define charrel(x) (state->i + (x) < state->line->actual ? state->line->text[(state->i+(x))].codepoint : -1)
+
+extern int find_keywords(struct syntax_state * state, char ** keywords, int flag, int (*keyword_qualifier)(int c));
+extern int match_and_paint(struct syntax_state * state, const char * keyword, int flag, int (*keyword_qualifier)(int c));
+extern void paint_simple_string(struct syntax_state * state);
+extern int common_comment_buzzwords(struct syntax_state * state);
+extern int paint_comment(struct syntax_state * state);
+extern int match_forward(struct syntax_state * state, char * c);
+extern struct syntax_definition * find_syntax_calculator(const char * name);
+
+#define nest(lang, low) \
+	do { \
+		state->state = (state->state < 1 ? 0 : state->state - low); \
+		do { state->state = lang(state); } while (state->state == 0); \
+		if (state->state == -1) return low; \
+		return state->state + low; \
+	} while (0)
+
+/* Some of the C stuff is widely used */
+extern int c_keyword_qualifier(int c);
+extern int paint_c_numeral(struct syntax_state * state);
+extern int paint_c_comment(struct syntax_state * state);
+extern void paint_c_char(struct syntax_state * state);
+
+
+#endif /* _BIM_SYNTAX_H */

+ 11 - 0
bim-theme.h

@@ -0,0 +1,11 @@
+#ifndef _BIM_THEME_H
+#define _BIM_THEME_H
+
+#define BIM_THEME(name) \
+	static void load_colorscheme_ ## name (void); \
+	__attribute__((constructor)) static void _load_ ## name (void) { \
+		add_colorscheme(#name, load_colorscheme_ ## name); \
+	} \
+	static void load_colorscheme_ ## name (void)
+
+#endif /* _BIM_THEME_H */

File diff suppressed because it is too large
+ 239 - 2377
bim.c


+ 190 - 0
syntax/bash.c

@@ -0,0 +1,190 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_bash_keywords[] = {
+	/* Actual bash keywords */
+	"if","then","else","elif","fi","case","esac","for","coproc",
+	"select","while","until","do","done","in","function","time",
+	/* Other keywords */
+	"exit","return","source","function","export","alias","complete","shopt","local","eval",
+	/* Common Unix utilities */
+	"echo","cd","pushd","popd","printf","sed","rm","mv",
+	NULL
+};
+
+int bash_pop_state(int state) {
+	int new_state = state / 100;
+	return new_state * 10;
+}
+
+int bash_push_state(int state, int new) {
+	return state * 10 + new;
+}
+
+int bash_paint_tick(struct syntax_state * state, int out_state) {
+	int last = -1;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == '\'') {
+			paint(1, FLAG_STRING);
+			return bash_pop_state(out_state);
+		} else if (last == '\\') {
+			paint(1, FLAG_STRING);
+			last = -1;
+		} else if (charat() != -1) {
+			last = charat();
+			paint(1, FLAG_STRING);
+		}
+	}
+	return out_state;
+}
+
+int bash_paint_braced_variable(struct syntax_state * state) {
+	while (charat() != -1) {
+		if (charat() == '}') {
+			paint(1, FLAG_NUMERAL);
+			return 0;
+		}
+		paint(1, FLAG_NUMERAL);
+	}
+	return 0;
+}
+
+int bash_special_variable(int c) {
+	return (c == '@' || c == '?');
+}
+
+int bash_paint_string(struct syntax_state * state, char terminator, int out_state, int color) {
+	int last = -1;
+	state->state = out_state;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == terminator) {
+			paint(1, color);
+			return bash_pop_state(state->state);
+		} else if (last == '\\') {
+			paint(1, color);
+			last = -1;
+		} else if (terminator != '`' && charat() == '`') {
+			paint(1, FLAG_ESCAPE);
+			state->state = bash_paint_string(state,'`',bash_push_state(out_state, 20),FLAG_ESCAPE);
+		} else if (terminator != ')' && charat() == '$' && nextchar() == '(') {
+			paint(2, FLAG_TYPE);
+			state->state = bash_paint_string(state,')',bash_push_state(out_state, 30),FLAG_TYPE);
+		} else if (charat() == '$' && nextchar() == '{') {
+			paint(2, FLAG_NUMERAL);
+			bash_paint_braced_variable(state);
+		} else if (charat() == '$') {
+			paint(1, FLAG_NUMERAL);
+			if (bash_special_variable(charat())) { paint(1, FLAG_NUMERAL); continue; }
+			while (c_keyword_qualifier(charat())) paint(1, FLAG_NUMERAL);
+		} else if (terminator != '"' && charat() == '"') {
+			paint(1, FLAG_STRING);
+			state->state = bash_paint_string(state,'"',bash_push_state(out_state, 40),FLAG_STRING);
+		} else if (terminator != '"' && charat() == '\'') { /* No single quotes in regular quotes */
+			paint(1, FLAG_STRING);
+			state->state = bash_paint_tick(state, out_state);
+		} else if (charat() != -1) {
+			last = charat();
+			paint(1, color);
+		}
+	}
+	return state->state;
+}
+
+int syn_bash_calculate(struct syntax_state * state) {
+	if (state->state < 1) {
+		if (charat() == '#') {
+			while (charat() != -1) {
+				if (common_comment_buzzwords(state)) continue;
+				else paint(1, FLAG_COMMENT);
+			}
+			return -1;
+		} else if (charat() == '\'') {
+			paint(1, FLAG_STRING);
+			return bash_paint_tick(state, 10);
+		} else if (charat() == '`') {
+			paint(1, FLAG_ESCAPE);
+			return bash_paint_string(state,'`',20,FLAG_ESCAPE);
+		} else if (charat() == '$' && nextchar() == '(') {
+			paint(2, FLAG_TYPE);
+			return bash_paint_string(state,')',30,FLAG_TYPE);
+		} else if (charat() == '"') {
+			paint(1, FLAG_STRING);
+			return bash_paint_string(state,'"',40,FLAG_STRING);
+		} else if (charat() == '$' && nextchar() == '{') {
+			paint(2, FLAG_NUMERAL);
+			bash_paint_braced_variable(state);
+			return 0;
+		} else if (charat() == '$') {
+			paint(1, FLAG_NUMERAL);
+			if (bash_special_variable(charat())) { paint(1, FLAG_NUMERAL); return 0; }
+			while (c_keyword_qualifier(charat())) paint(1, FLAG_NUMERAL);
+			return 0;
+		} else if (find_keywords(state, syn_bash_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+			return 0;
+		} else if (charat() == ';') {
+			paint(1, FLAG_KEYWORD);
+			return 0;
+		} else if (c_keyword_qualifier(charat())) {
+			for (int i = 0; charrel(i) != -1; ++i) {
+				if (charrel(i) == ' ') break;
+				if (charrel(i) == '=') {
+					for (int j = 0; j < i; ++j) {
+						paint(1, FLAG_TYPE);
+					}
+					skip(); /* equals sign */
+					return 0;
+				}
+			}
+			for (int i = 0; charrel(i) != -1; ++i) {
+				if (charrel(i) == '(') {
+					for (int j = 0; j < i; ++j) {
+						paint(1, FLAG_TYPE);
+					}
+					return 0;
+				}
+				if (!c_keyword_qualifier(charrel(i)) && charrel(i) != '-' && charrel(i) != ' ') break;
+			}
+			skip();
+			return 0;
+		} else if (charat() != -1) {
+			skip();
+			return 0;
+		}
+	} else if (state->state < 10) {
+		/*
+		 * TODO: I have an idea of how to do up to `n` (here... 8?) heredocs
+		 * by storing them in a static table and using the index into that table
+		 * for the state, but it's iffy. It would work well in situations where
+		 * someoen used the same heredoc repeatedly throughout their document.
+		 */
+	} else if (state->state >= 10) {
+		/* Nested string states */
+		while (charat() != -1) {
+			int s = (state->state / 10) % 10;
+			if (s == 1) {
+				state->state = bash_paint_string(state,'\'',state->state,FLAG_STRING);
+			} else if (s == 2) {
+				state->state = bash_paint_string(state,'`',state->state,FLAG_ESCAPE);
+			} else if (s == 3) {
+				state->state = bash_paint_string(state,')',state->state,FLAG_TYPE);
+			} else if (s == 4) {
+				state->state = bash_paint_string(state,'"',state->state,FLAG_STRING);
+			} else if (!s) {
+				return -1;
+			}
+		}
+		return state->state;
+	}
+	return -1;
+}
+
+char * syn_bash_ext[] = {
+#ifndef __toaru__
+	".sh",
+#endif
+	".bash",".bashrc",
+	NULL
+};
+
+BIM_SYNTAX(bash, 0)

+ 66 - 0
syntax/bimcmd.c

@@ -0,0 +1,66 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_bimcmd_keywords[] = {
+	"help","recalc","syntax","tabn","tabp","tabnew","theme","colorscheme",
+	"tabs","tabstop","spaces","noh","clearyank","indent","noindent",
+	"padding","hlparen","hlcurrent","relativenumber","cursorcolumn",
+	"smartcase","split","splitpercent","unsplit","git","colorgutter",
+	"tohtml","buffers","s/","e","w","q","qa","q!","qa!","history","crnl",
+	"numbers","version",
+	NULL
+};
+
+int cmd_qualifier(int c) { return c != -1 && c != ' '; }
+
+extern int syn_bash_calculate(struct syntax_state * state);
+extern int syn_py_calculate(struct syntax_state * state);
+
+int syn_bimcmd_calculate(struct syntax_state * state) {
+	if (state->i == 0) {
+		if (match_and_paint(state, "theme", FLAG_KEYWORD, cmd_qualifier) ||
+			match_and_paint(state, "colorscheme", FLAG_KEYWORD, cmd_qualifier)) {
+			while (charat() == ' ') skip();
+			for (struct theme_def * s = themes; themes && s->name; ++s) {
+				if (match_and_paint(state, s->name, FLAG_TYPE, cmd_qualifier)) break;
+			}
+		} else if (match_and_paint(state, "syntax", FLAG_KEYWORD, cmd_qualifier)) {
+			while (charat() == ' ') skip();
+			for (struct syntax_definition * s = syntaxes; syntaxes && s->name; ++s) {
+				if (match_and_paint(state, s->name, FLAG_TYPE, cmd_qualifier)) return -1;
+			}
+			if (match_and_paint(state, "none", FLAG_TYPE, cmd_qualifier)) return -1;
+		} else if (charat() == 's' && !isalpha(nextchar())) {
+			paint(1, FLAG_KEYWORD);
+			char special = charat();
+			paint(1, FLAG_TYPE);
+			while (charat() != -1 && charat() != special) {
+				paint(1, FLAG_DIFFMINUS);
+			}
+			if (charat() == special) paint(1, FLAG_TYPE);
+			while (charat() != -1 && charat() != special) {
+				paint(1, FLAG_DIFFPLUS);
+			}
+			if (charat() == special) paint(1, FLAG_TYPE);
+			while (charat() != -1) paint(1, FLAG_NUMERAL);
+		} else if (find_keywords(state, syn_bimcmd_keywords, FLAG_KEYWORD, cmd_qualifier)) {
+			return -1;
+		} else if (charat() == '!') {
+			paint(1, FLAG_NUMERAL);
+			nest(syn_bash_calculate, 1);
+		} else if (charat() == '`') {
+			paint(1, FLAG_NUMERAL);
+			nest(syn_py_calculate, 1);
+		} else if (isdigit(charat()) || charat() == '-' || charat() == '+') {
+			paint(1, FLAG_NUMERAL);
+			while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			return -1;
+		}
+	}
+	return -1;
+}
+
+char * syn_bimcmd_ext[] = {NULL}; /* no files */
+
+BIM_SYNTAX(bimcmd, 1)

+ 23 - 0
syntax/biminfo.c

@@ -0,0 +1,23 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_biminfo_calculate(struct syntax_state * state) {
+	if (state->i == 0) {
+		if (charat() == '#') {
+			while (charat() != -1) paint(1, FLAG_COMMENT);
+		} else if (charat() == '>') {
+			paint(1, FLAG_KEYWORD);
+			while (charat() != ' ') paint(1, FLAG_TYPE);
+			skip();
+			while (charat() != -1) paint(1, FLAG_NUMERAL);
+		} else {
+			while (charat() != -1) paint(1, FLAG_ERROR);
+		}
+	}
+	return -1;
+}
+
+char * syn_biminfo_ext[] = {".biminfo",NULL};
+
+BIM_SYNTAX(biminfo, 0)

+ 35 - 0
syntax/bimrc.c

@@ -0,0 +1,35 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_bimrc_keywords[] = {
+	"history","padding","hlparen","hlcurrent","splitpercent","numbers",
+	"shiftscrolling","scrollamount","git","colorgutter","relativenumber",
+	NULL
+};
+
+int syn_bimrc_calculate(struct syntax_state * state) {
+	/* No states */
+	if (state->i == 0) {
+		if (charat() == '#') {
+			while (charat() != -1) {
+				if (common_comment_buzzwords(state)) continue;
+				else paint(1, FLAG_COMMENT);
+			}
+		} else if (match_and_paint(state, "theme", FLAG_KEYWORD, c_keyword_qualifier)) {
+			if (charat() == '=') {
+				skip();
+				for (struct theme_def * s = themes; themes && s->name; ++s) {
+					if (match_and_paint(state, s->name, FLAG_TYPE, c_keyword_qualifier)) break;
+				}
+			}
+		} else if (find_keywords(state, syn_bimrc_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+			return -1;
+		}
+	}
+	return -1;
+}
+
+char * syn_bimrc_ext[] = {".bimrc",NULL};
+
+BIM_SYNTAX(bimrc, 0)

+ 267 - 0
syntax/c.c

@@ -0,0 +1,267 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+/**
+ * Syntax definition for C
+ */
+char * syn_c_keywords[] = {
+	"while","if","for","continue","return","break","switch","case","sizeof",
+	"struct","union","typedef","do","default","else","goto",
+	"alignas","alignof","offsetof","asm","__asm__",
+	/* C++ stuff */
+	"public","private","class","using","namespace","virtual","override","protected",
+	"template","typename","static_cast","throw",
+	NULL
+};
+
+char * syn_c_types[] = {
+	"static","int","char","short","float","double","void","unsigned","volatile","const",
+	"register","long","inline","restrict","enum","auto","extern","bool","complex",
+	"uint8_t","uint16_t","uint32_t","uint64_t",
+	"int8_t","int16_t","int32_t","int64_t","FILE",
+	"ssize_t","size_t","uintptr_t","intptr_t","__volatile__",
+	"constexpr",
+	NULL
+};
+
+char * syn_c_special[] = {
+	"NULL",
+	"stdin","stdout","stderr",
+	"STDIN_FILENO","STDOUT_FILENO","STDERR_FILENO",
+	NULL
+};
+
+int c_keyword_qualifier(int c) {
+	return isalnum(c) || (c == '_');
+}
+
+/**
+ * Paints a basic C-style quoted string.
+ */
+void paint_c_string(struct syntax_state * state) {
+	/* Assumes you came in from a check of charat() == '"' */
+	paint(1, FLAG_STRING);
+	int last = -1;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == '"') {
+			paint(1, FLAG_STRING);
+			return;
+		} else if (charat() == '\\' && (nextchar() == '\\' || nextchar() == 'n' || nextchar() == 'r')) {
+			paint(2, FLAG_ESCAPE);
+			last = -1;
+		} else if (charat() == '\\' && nextchar() >= '0' && nextchar() <= '7') {
+			paint(2, FLAG_ESCAPE);
+			if (charat() >= '0' && charat() <= '7') {
+				paint(1, FLAG_ESCAPE);
+				if (charat() >= '0' && charat() <= '7') {
+					paint(1, FLAG_ESCAPE);
+				}
+			}
+			last = -1;
+		} else if (charat() == '%') {
+			paint(1, FLAG_ESCAPE);
+			if (charat() == '%') {
+				paint(1, FLAG_ESCAPE);
+			} else {
+				while (charat() == '-' || charat() == '#' || charat() == '*' || charat() == '0' || charat() == '+') paint(1, FLAG_ESCAPE);
+				while (isdigit(charat())) paint(1, FLAG_ESCAPE);
+				if (charat() == '.') {
+					paint(1, FLAG_ESCAPE);
+					if (charat() == '*') paint(1, FLAG_ESCAPE);
+					else while (isdigit(charat())) paint(1, FLAG_ESCAPE);
+				}
+				while (charat() == 'l' || charat() == 'z') paint(1, FLAG_ESCAPE);
+				paint(1, FLAG_ESCAPE);
+			}
+		} else if (charat() == '\\' && nextchar() == 'x') {
+			paint(2, FLAG_ESCAPE);
+			while (isxdigit(charat())) paint(1, FLAG_ESCAPE);
+		} else {
+			last = charat();
+			paint(1, FLAG_STRING);
+		}
+	}
+}
+
+/**
+ * Paint a C character numeral. Can be arbitrarily large, so
+ * it supports multibyte chars for things like defining weird
+ * ASCII multibyte integer constants.
+ */
+void paint_c_char(struct syntax_state * state) {
+	/* Assumes you came in from a check of charat() == '\'' */
+	paint(1, FLAG_NUMERAL);
+	int last = -1;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == '\'') {
+			paint(1, FLAG_NUMERAL);
+			return;
+		} else if (last == '\\' && charat() == '\\') {
+			paint(1, FLAG_NUMERAL);
+			last = -1;
+		} else {
+			last = charat();
+			paint(1, FLAG_NUMERAL);
+		}
+	}
+}
+
+/**
+ * Paint a classic C comment which continues until terminated.
+ * Assumes you've already painted the starting / and *.
+ */
+int paint_c_comment(struct syntax_state * state) {
+	int last = -1;
+	while (charat() != -1) {
+		if (common_comment_buzzwords(state)) continue;
+		else if (last == '*' && charat() == '/') {
+			paint(1, FLAG_COMMENT);
+			return 0;
+		} else {
+			last = charat();
+			paint(1, FLAG_COMMENT);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Paint a generic C pragma, eg. a #define statement.
+ */
+int paint_c_pragma(struct syntax_state * state) {
+	while (state->i < state->line->actual) {
+		if (charat() == '"') {
+			/* Paint C string */
+			paint_c_string(state);
+		} else if (charat() == '\'') {
+			paint_c_char(state);
+		} else if (charat() == '\\' && state->i == state->line->actual - 1) {
+			paint(1, FLAG_PRAGMA);
+			return 2;
+		} else if (find_keywords(state, syn_c_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+			continue;
+		} else if (find_keywords(state, syn_c_types, FLAG_TYPE, c_keyword_qualifier)) {
+			continue;
+		} else if (charat() == '/' && nextchar() == '/') {
+			/* C++-style comments */
+			paint_comment(state);
+			return -1;
+		} else if (charat() == '/' && nextchar() == '*') {
+			/* C-style comments */
+			if (paint_c_comment(state) == 1) return 3;
+			continue;
+		} else {
+			paint(1, FLAG_PRAGMA);
+		}
+	}
+	return 0;
+}
+
+/**
+ * Paint integers and floating point values with some handling for suffixes.
+ */
+int paint_c_numeral(struct syntax_state * state) {
+	if (charat() == '0' && (nextchar() == 'x' || nextchar() == 'X')) {
+		paint(2, FLAG_NUMERAL);
+		while (isxdigit(charat())) paint(1, FLAG_NUMERAL);
+	} else if (charat() == '0' && nextchar() == '.') {
+		paint(2, FLAG_NUMERAL);
+		while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		if (charat() == 'f') paint(1, FLAG_NUMERAL);
+		return 0;
+	} else if (charat() == '0') {
+		paint(1, FLAG_NUMERAL);
+		while (charat() >= '0' && charat() <= '7') paint(1, FLAG_NUMERAL);
+	} else {
+		while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		if (charat() == '.') {
+			paint(1, FLAG_NUMERAL);
+			while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			if (charat() == 'f') paint(1, FLAG_NUMERAL);
+			return 0;
+		}
+	}
+	while (charat() == 'u' || charat() == 'U' || charat() == 'l' || charat() == 'L') paint(1, FLAG_NUMERAL);
+	return 0;
+}
+
+int syn_c_calculate(struct syntax_state * state) {
+	switch (state->state) {
+		case -1:
+		case 0:
+			if (charat() == '#') {
+				/* Must be first thing on line, but can have spaces before */
+				for (int i = 0; i < state->i; ++i) {
+					if (state->line->text[i].codepoint != ' ' && state->line->text[i].codepoint != '\t') {
+						skip();
+						return 0;
+					}
+				}
+				/* Handle preprocessor functions */
+				paint(1, FLAG_PRAGMA);
+				while (charat() == ' ') paint(1, FLAG_PRAGMA);
+				if (match_and_paint(state, "include", FLAG_PRAGMA, c_keyword_qualifier)) {
+					/* Put quotes around <includes> */
+					while (charat() == ' ') paint(1, FLAG_PRAGMA);
+					if (charat() == '<') {
+						paint(1, FLAG_STRING);
+						while (charat() != '>' && state->i < state->line->actual) {
+							paint(1, FLAG_STRING);
+						}
+						if (charat() != -1) {
+							paint(1, FLAG_STRING);
+						}
+					}
+					/* (for "includes", normal pragma highlighting covers that. */
+				} else if (match_and_paint(state, "if", FLAG_PRAGMA, c_keyword_qualifier)) {
+					/* These are to prevent #if and #else from being highlighted as keywords */
+				} else if (match_and_paint(state, "else", FLAG_PRAGMA, c_keyword_qualifier)) {
+					/* ... */
+				}
+				return paint_c_pragma(state);
+			} else if (charat() == '/' && nextchar() == '/') {
+				/* C++-style comments */
+				paint_comment(state);
+			} else if (charat() == '/' && nextchar() == '*') {
+				/* C-style comments */
+				if (paint_c_comment(state) == 1) return 1;
+				return 0;
+			} else if (find_keywords(state, syn_c_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_c_types, FLAG_TYPE, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_c_special, FLAG_NUMERAL, c_keyword_qualifier)) {
+				return 0;
+			} else if (charat() == '\"') {
+				paint_c_string(state);
+				return 0;
+			} else if (charat() == '\'') {
+				paint_c_char(state);
+				return 0;
+			} else if (!c_keyword_qualifier(lastchar()) && isdigit(charat())) {
+				paint_c_numeral(state);
+				return 0;
+			} else if (charat() != -1) {
+				skip();
+				return 0;
+			}
+			break;
+		case 1:
+			/* In a block comment */
+			if (paint_c_comment(state) == 1) return 1;
+			return 0;
+		case 2:
+			/* In an unclosed preprocessor statement */
+			return paint_c_pragma(state);
+		case 3:
+			/* In a block comment within an unclosed preprocessor statement */
+			if (paint_c_comment(state) == 1) return 3;
+			return paint_c_pragma(state);
+	}
+	return -1;
+}
+
+char * syn_c_ext[] = {".c",".h",".cpp",".hpp",".c++",".h++",".cc",".hh",NULL};
+
+BIM_SYNTAX(c, 0)

+ 31 - 0
syntax/conf.c

@@ -0,0 +1,31 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_conf_calculate(struct syntax_state * state) {
+	if (state->i == 0) {
+		if (charat() == ';') {
+			while (charat() != -1) {
+				if (common_comment_buzzwords(state)) continue;
+				else paint(1, FLAG_COMMENT);
+			}
+		} else if (charat() == '#') {
+			while (charat() != -1) {
+				if (common_comment_buzzwords(state)) continue;
+				else paint(1, FLAG_COMMENT);
+			}
+		} else if (charat() == '[') {
+			paint(1, FLAG_KEYWORD);
+			while (charat() != ']' && charat() != -1) paint(1, FLAG_KEYWORD);
+			if (charat() == ']') paint(1, FLAG_KEYWORD);
+		} else {
+			while (charat() != '=' && charat() != -1) paint(1, FLAG_TYPE);
+		}
+	}
+
+	return -1;
+}
+
+char * syn_conf_ext[] = {".conf",".ini",".git/config",NULL};
+
+BIM_SYNTAX(conf, 1)

+ 24 - 0
syntax/ctags.c

@@ -0,0 +1,24 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_ctags_calculate(struct syntax_state * state) {
+	if (state->i == 0) {
+		if (charat() == '!') {
+			paint_comment(state);
+			return -1;
+		} else {
+			while (charat() != -1 && charat() != '\t') paint(1, FLAG_TYPE);
+			if (charat() == '\t') skip();
+			while (charat() != -1 && charat() != '\t') paint(1, FLAG_NUMERAL);
+			if (charat() == '\t') skip();
+			while (charat() != -1 && !(charat() == ';' && nextchar() == '"')) paint(1, FLAG_KEYWORD);
+			return -1;
+		}
+	}
+	return -1;
+}
+
+char * syn_ctags_ext[] = { "tags", NULL };
+
+BIM_SYNTAX(ctags, 0)

+ 27 - 0
syntax/diff.c

@@ -0,0 +1,27 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_diff_calculate(struct syntax_state * state) {
+	/* No states to worry about */
+	if (state->i == 0) {
+		int flag = 0;
+		if (charat() == '+') {
+			flag = FLAG_DIFFPLUS;
+		} else if (charat() == '-') {
+			flag = FLAG_DIFFMINUS;
+		} else if (charat() == '@') {
+			flag = FLAG_TYPE;
+		} else if (charat() != ' ') {
+			flag = FLAG_KEYWORD;
+		} else {
+			return -1;
+		}
+		while (charat() != -1) paint(1, flag);
+	}
+	return -1;
+}
+
+char * syn_diff_ext[] = {".patch",".diff",NULL};
+
+BIM_SYNTAX(diff, 1)

+ 118 - 0
syntax/esh.c

@@ -0,0 +1,118 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int esh_variable_qualifier(int c) {
+	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_');
+}
+
+int paint_esh_variable(struct syntax_state * state) {
+	if (charat() == '{') {
+		paint(1, FLAG_TYPE);
+		while (charat() != '}') paint(1, FLAG_TYPE);
+		if (charat() == '}') paint(1, FLAG_TYPE);
+	} else {
+		if (charat() == '?' || charat() == '$' || charat() == '#') {
+			paint(1, FLAG_TYPE);
+		} else {
+			while (esh_variable_qualifier(charat())) paint(1, FLAG_TYPE);
+		}
+	}
+	return 0;
+}
+
+int paint_esh_string(struct syntax_state * state) {
+	int last = -1;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == '"') {
+			paint(1, FLAG_STRING);
+			return 0;
+		} else if (charat() == '$') {
+			paint(1, FLAG_TYPE);
+			paint_esh_variable(state);
+			last = -1;
+		} else if (charat() != -1) {
+			last = charat();
+			paint(1, FLAG_STRING);
+		}
+	}
+	return 2;
+}
+
+int paint_esh_single_string(struct syntax_state * state) {
+	int last = -1;
+	while (charat() != -1) {
+		if (last != '\\' && charat() == '\'') {
+			paint(1, FLAG_STRING);
+			return 0;
+		} else if (charat() != -1) {
+			last = charat();
+			paint(1, FLAG_STRING);
+		}
+	}
+	return 1;
+}
+
+int esh_keyword_qualifier(int c) {
+	return (isalnum(c) || c == '?' || c == '_' || c == '-'); /* technically anything that isn't a space should qualify... */
+}
+
+char * esh_keywords[] = {
+	"cd","exit","export","help","history","if","empty?",
+	"equals?","return","export-cmd","source","exec","not","while",
+	"then","else","echo",
+	NULL
+};
+
+int syn_esh_calculate(struct syntax_state * state) {
+	if (state->state == 1) {
+		return paint_esh_single_string(state);
+	} else if (state->state == 2) {
+		return paint_esh_string(state);
+	}
+	if (charat() == '#') {
+		while (charat() != -1) {
+			if (common_comment_buzzwords(state)) continue;
+			else paint(1, FLAG_COMMENT);
+		}
+		return -1;
+	} else if (charat() == '$') {
+		paint(1, FLAG_TYPE);
+		paint_esh_variable(state);
+		return 0;
+	} else if (charat() == '\'') {
+		paint(1, FLAG_STRING);
+		return paint_esh_single_string(state);
+	} else if (charat() == '"') {
+		paint(1, FLAG_STRING);
+		return paint_esh_string(state);
+	} else if (match_and_paint(state, "export", FLAG_KEYWORD, esh_keyword_qualifier)) {
+		while (charat() == ' ') skip();
+		while (esh_keyword_qualifier(charat())) paint(1, FLAG_TYPE);
+		return 0;
+	} else if (match_and_paint(state, "export-cmd", FLAG_KEYWORD, esh_keyword_qualifier)) {
+		while (charat() == ' ') skip();
+		while (esh_keyword_qualifier(charat())) paint(1, FLAG_TYPE);
+		return 0;
+	} else if (find_keywords(state, esh_keywords, FLAG_KEYWORD, esh_keyword_qualifier)) {
+		return 0;
+	} else if (isdigit(charat())) {
+		while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		return 0;
+	} else if (charat() != -1) {
+		skip();
+		return 0;
+	}
+	return -1;
+}
+
+/* Only enable esh highlighting by default on ToaruOS */
+char * syn_esh_ext[] = {
+#ifdef __toaru__
+	".sh",
+#endif
+	".eshrc",".yutanirc",
+	NULL
+};
+
+BIM_SYNTAX(esh, 0)

+ 46 - 0
syntax/git.c

@@ -0,0 +1,46 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_gitcommit_calculate(struct syntax_state * state) {
+	if (state->i == 0 && charat() == '#') {
+		while (charat() != -1) paint(1, FLAG_COMMENT);
+	} else if (state->line_no == 0) {
+		/* First line is special */
+		while (charat() != -1 && state->i < 50) paint(1, FLAG_KEYWORD);
+		while (charat() != -1) paint(1, FLAG_DIFFMINUS);
+	} else if (state->line_no == 1) {
+		/* No text on second line */
+		while (charat() != -1) paint(1, FLAG_DIFFMINUS);
+	} else if (charat() != -1) {
+		skip();
+		return 0;
+	}
+	return -1;
+}
+
+char * syn_gitcommit_ext[] = {"COMMIT_EDITMSG", NULL};
+
+char * syn_gitrebase_commands[] = {
+	"p","r","e","s","f","x","d",
+	"pick","reword","edit","squash","fixup",
+	"exec","drop",
+	NULL
+};
+
+int syn_gitrebase_calculate(struct syntax_state * state) {
+	if (state->i == 0 && charat() == '#') {
+		while (charat() != -1) paint(1, FLAG_COMMENT);
+	} else if (state->i == 0 && find_keywords(state, syn_gitrebase_commands, FLAG_KEYWORD, c_keyword_qualifier)) {
+		while (charat() == ' ') skip();
+		while (isxdigit(charat())) paint(1, FLAG_NUMERAL);
+		return -1;
+	}
+
+	return -1;
+}
+
+char * syn_gitrebase_ext[] = {"git-rebase-todo",NULL};
+
+BIM_SYNTAX(gitcommit, 1)
+BIM_SYNTAX(gitrebase, 1)

+ 148 - 0
syntax/java.c

@@ -0,0 +1,148 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+static char * syn_java_keywords[] = {
+	"assert","break","case","catch","class","continue",
+	"default","do","else","enum","exports","extends","finally",
+	"for","if","implements","instanceof","interface","module","native",
+	"new","requires","return","throws",
+	"strictfp","super","switch","synchronized","this","throw","try","while",
+	NULL
+};
+
+static char * syn_java_types[] = {
+	"var","boolean","void","short","long","int","double","float","enum","char",
+	"private","protected","public","static","final","transient","volatile","abstract",
+	NULL
+};
+
+static char * syn_java_special[] = {
+	"true","false","import","package","null",
+	NULL
+};
+
+static char * syn_java_at_comments[] = {
+	"@author","@see","@since","@return","@throws",
+	"@version","@exception","@deprecated",
+	/* @param is special */
+	NULL
+};
+
+static int at_keyword_qualifier(int c) {
+	return isalnum(c) || (c == '_') || (c == '@');
+}
+
+static char * syn_java_brace_comments[] = {
+	"{@docRoot","{@inheritDoc","{@link","{@linkplain",
+	"{@value","{@code","{@literal","{@serial",
+	"{@serialData","{@serialField",
+	NULL
+};
+
+static int brace_keyword_qualifier(int c) {
+	return isalnum(c) || (c == '{') || (c == '@');
+}
+
+static int paint_java_comment(struct syntax_state * state) {
+	int last = -1;
+	while (charat() != -1) {
+		if (common_comment_buzzwords(state)) continue;
+		else if (charat() == '@') {
+			if (!find_keywords(state, syn_java_at_comments, FLAG_ESCAPE, at_keyword_qualifier)) {
+				if (match_and_paint(state, "@param", FLAG_ESCAPE, at_keyword_qualifier)) {
+					while (charat() == ' ') skip();
+					while (c_keyword_qualifier(charat())) paint(1, FLAG_TYPE);
+				} else {
+					/* Paint the @ */
+					paint(1, FLAG_COMMENT);
+				}
+			}
+		} else if (charat() == '{') {
+			/* see if this terminates */
+			if (find_keywords(state, syn_java_brace_comments, FLAG_ESCAPE, brace_keyword_qualifier)) {
+				while (charat() != '}' && charat() != -1) {
+					paint(1, FLAG_ESCAPE);
+				}
+				if (charat() == '}') paint(1, FLAG_ESCAPE);
+			} else {
+				paint(1, FLAG_COMMENT);
+			}
+		} else if (charat() == '<') {
+			int is_tag = 0;
+			for (int i = 1; charrel(i) != -1; ++i) {
+				if (charrel(i) == '>') {
+					is_tag = 1;
+					break;
+				}
+				if (!isalnum(charrel(i)) && charrel(i) != '/') {
+					is_tag = 0;
+					break;
+				}
+			}
+			if (is_tag) {
+				paint(1, FLAG_TYPE);
+				while (charat() != -1 && charat() != '>') {
+					if (charat() == '/') paint(1, FLAG_TYPE);
+					else paint(1, FLAG_KEYWORD);
+				}
+				if (charat() == '>') paint(1, FLAG_TYPE);
+			} else {
+				/* Paint the < */
+				paint(1, FLAG_COMMENT);
+			}
+		} else if (last == '*' && charat() == '/') {
+			paint(1, FLAG_COMMENT);
+			return 0;
+		} else {
+			last = charat();
+			paint(1, FLAG_COMMENT);
+		}
+	}
+	return 1;
+}
+
+int syn_java_calculate(struct syntax_state * state) {
+	switch (state->state) {
+		case -1:
+		case 0:
+			if (!c_keyword_qualifier(lastchar()) && isdigit(charat())) {
+				paint_c_numeral(state);
+				return 0;
+			} else if (charat() == '/' && nextchar() == '/') {
+				/* C++-style comments */
+				paint_comment(state);
+			} else if (charat() == '/' && nextchar() == '*') {
+				/* C-style comments; TODO: Needs special stuff for @author; <html>; etc. */
+				if (paint_java_comment(state) == 1) return 1;
+			} else if (find_keywords(state, syn_java_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_java_types, FLAG_TYPE, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_java_special, FLAG_NUMERAL, c_keyword_qualifier)) {
+				return 0;
+			} else if (charat() == '\"') {
+				paint_simple_string(state);
+				return 0;
+			} else if (charat() == '\'') {
+				paint_c_char(state);
+				return 0;
+			} else if (charat() == '@') {
+				paint(1, FLAG_PRAGMA);
+				while (c_keyword_qualifier(charat())) paint(1, FLAG_PRAGMA);
+				return 0;
+			} else if (charat() != -1) {
+				skip();
+				return 0;
+			}
+			break;
+		case 1:
+			if (paint_java_comment(state) == 1) return 1;
+			return 0;
+	}
+	return -1;
+}
+
+char * syn_java_ext[] = {".java",NULL};
+
+BIM_SYNTAX(java, 1)

+ 59 - 0
syntax/json.c

@@ -0,0 +1,59 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_json_keywords[] = {
+	"true","false","null",
+	NULL
+};
+
+int syn_json_calculate(struct syntax_state * state) {
+	while (charat() != -1) {
+		if (charat() == '"') {
+			int backtrack = state->i;
+			paint_simple_string(state);
+			int backtrack_end = state->i;
+			while (charat() == ' ') skip();
+			if (charat() == ':') {
+				/* This is dumb. */
+				state->i = backtrack;
+				paint(1, FLAG_ESCAPE);
+				while (state->i < backtrack_end-1) {
+					paint(1, FLAG_KEYWORD);
+				}
+				if (charat() == '"') {
+					paint(1, FLAG_ESCAPE);
+				}
+			}
+			return 0;
+		} else if (charat() == '-' || isdigit(charat())) {
+			if (charat() == '-') paint(1, FLAG_NUMERAL);
+			if (charat() == '0') {
+				paint(1, FLAG_NUMERAL);
+			} else {
+				while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			}
+			if (charat() == '.') {
+				paint(1, FLAG_NUMERAL);
+				while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			}
+			if (charat() == 'e' || charat() == 'E') {
+				paint(1, FLAG_NUMERAL);
+				if (charat() == '+' || charat() == '-') {
+					paint(1, FLAG_NUMERAL);
+				}
+				while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			}
+		} else if (find_keywords(state,syn_json_keywords,FLAG_NUMERAL,c_keyword_qualifier)) {
+			/* ... */
+		} else {
+			skip();
+			return 0;
+		}
+	}
+	return -1;
+}
+
+char * syn_json_ext[] = {".json",NULL}; // TODO other stuff that uses json
+
+BIM_SYNTAX(json, 1)

+ 158 - 0
syntax/make.c

@@ -0,0 +1,158 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int make_command_qualifier(int c) {
+	return isalnum(c) || c == '_' || c == '-' || c == '.';
+}
+
+char * syn_make_commands[] = {
+	"define","endef","undefine","ifdef","ifndef","ifeq","ifneq","else","endif",
+	"include","sinclude","override","export","unexport","private","vpath",
+	"-include",
+	NULL
+};
+
+char * syn_make_functions[] = {
+	"subst","patsubst","findstring","filter","filter-out",
+	"sort","word","words","wordlist","firstword","lastword",
+	"dir","notdir","suffix","basename","addsuffix","addprefix",
+	"join","wildcard","realpath","abspath","error","warning",
+	"shell","origin","flavor","foreach","if","or","and",
+	"call","eval","file","value",
+	NULL
+};
+
+char * syn_make_special_targets[] = {
+	"all", /* Not really special, but highlight it 'cause I feel like it. */
+	".PHONY", ".SUFFIXES", ".DEFAULT", ".PRECIOUS", ".INTERMEDIATE",
+	".SECONDARY", ".SECONDEXPANSION", ".DELETE_ON_ERROR", ".IGNORE",
+	".LOW_RESOLUTION_TIME", ".SILENT", ".EXPORT_ALL_VARIABLES",
+	".NOTPARALLEL", ".ONESHELL", ".POSIX",
+	NULL
+};
+
+int make_close_paren(struct syntax_state * state) {
+	paint(2, FLAG_TYPE);
+	find_keywords(state, syn_make_functions, FLAG_KEYWORD, c_keyword_qualifier);
+	int i = 1;
+	while (charat() != -1) {
+		if (charat() == '(') {
+			i++;
+		} else if (charat() == ')') {
+			i--;
+			if (i == 0) {
+				paint(1,FLAG_TYPE);
+				return 0;
+			}
+		} else if (charat() == '"') {
+			paint_simple_string(state);
+		}
+		paint(1,FLAG_TYPE);
+	}
+	return 0;
+}
+
+int make_close_brace(struct syntax_state * state) {
+	paint(2, FLAG_TYPE);
+	while (charat() != -1) {
+		if (charat() == '}') {
+			paint(1, FLAG_TYPE);
+			return 0;
+		}
+		paint(1, FLAG_TYPE);
+	}
+	return 0;
+}
+
+int make_variable_or_comment(struct syntax_state * state, int flag) {
+	while (charat() != -1) {
+		if (charat() == '$') {
+			switch (nextchar()) {
+				case '(':
+					make_close_paren(state);
+					break;
+				case '{':
+					make_close_brace(state);
+					break;
+				default:
+					paint(2, FLAG_TYPE);
+					break;
+			}
+		} else if (charat() == '#') {
+			while (charat() != -1) paint(1, FLAG_COMMENT);
+		} else {
+			paint(1, flag);
+		}
+	}
+	return 0;
+}
+
+int syn_make_calculate(struct syntax_state * state) {
+	if (state->i == 0 && charat() == '\t') {
+		make_variable_or_comment(state, FLAG_NUMERAL);
+	} else {
+		while (charat() == ' ') { skip(); }
+		/* Peek forward to see if this is a rule or a variable */
+		int whatisit = 0;
+		for (int i = 0; charrel(i) != -1; ++i) {
+			if (charrel(i) == ':' && charrel(i+1) != '=') {
+				whatisit = 1;
+				break;
+			} else if (charrel(i) == '=') {
+				whatisit = 2;
+				break;
+			} else if (charrel(i) == '#') {
+				break;
+			}
+		}
+		if (!whatisit) {
+			/* Check for functions */
+			while (charat() != -1) {
+				if (charat() == '#') {
+					while (charat() != -1) {
+						if (common_comment_buzzwords(state)) continue;
+						else paint(1, FLAG_COMMENT);
+					}
+				} else if (find_keywords(state, syn_make_commands, FLAG_KEYWORD, make_command_qualifier)) {
+					continue;
+				} else if (charat() == '$') {
+					make_variable_or_comment(state, FLAG_NONE);
+				} else {
+					skip();
+				}
+			}
+		} else if (whatisit == 1) {
+			/* It's a rule */
+			while (charat() != -1) {
+				if (charat() == '#') {
+					while (charat() != -1) {
+						if (common_comment_buzzwords(state)) continue;
+						else paint(1, FLAG_COMMENT);
+					}
+				} else if (charat() == ':') {
+					paint(1, FLAG_TYPE);
+					make_variable_or_comment(state, FLAG_NONE);
+				} else if (find_keywords(state, syn_make_special_targets, FLAG_KEYWORD, make_command_qualifier)) {
+						continue;
+				} else {
+					paint(1, FLAG_TYPE);
+				}
+			}
+		} else if (whatisit == 2) {
+			/* It's a variable definition */
+			match_and_paint(state, "export", FLAG_KEYWORD, c_keyword_qualifier);
+			while (charat() != -1 && charat() != '+' && charat() != '=' && charat() != ':' && charat() != '?') {
+				paint(1, FLAG_TYPE);
+			}
+			while (charat() != -1 && charat() != '=') skip();
+			/* Highlight variable expansions */
+			make_variable_or_comment(state, FLAG_NONE);
+		}
+	}
+	return -1;
+}
+
+char * syn_make_ext[] = {"Makefile","makefile","GNUmakefile",".mak",NULL};
+
+BIM_SYNTAX(make, 0)

+ 135 - 0
syntax/markdown.c

@@ -0,0 +1,135 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+static struct syntax_definition * syn_c = NULL;
+static struct syntax_definition * syn_py = NULL;
+static struct syntax_definition * syn_java = NULL;
+static struct syntax_definition * syn_json = NULL;
+static struct syntax_definition * syn_xml = NULL;
+static struct syntax_definition * syn_make = NULL;
+static struct syntax_definition * syn_diff = NULL;
+static struct syntax_definition * syn_rust = NULL;
+
+static int _initialized = 0;
+
+int syn_markdown_calculate(struct syntax_state * state) {
+	if (!_initialized) {
+		_initialized = 1;
+		syn_c    = find_syntax_calculator("c");
+		syn_py   = find_syntax_calculator("py");
+		syn_java = find_syntax_calculator("java");
+		syn_json = find_syntax_calculator("json");
+		syn_xml  = find_syntax_calculator("xml");
+		syn_make = find_syntax_calculator("make");
+		syn_diff = find_syntax_calculator("diff");
+		syn_rust = find_syntax_calculator("rust");
+	}
+	if (state->state < 1) {
+		while (charat() != -1) {
+			if (state->i == 0 && charat() == '#') {
+				while (charat() == '#') paint(1, FLAG_KEYWORD);
+				while (charat() != -1) paint(1, FLAG_BOLD);
+				return -1;
+			} else if (state->i == 0) {
+				while (charat() == ' ') skip();
+				if (charat() == '`' && nextchar() == '`' && charrel(2) == '`') {
+					paint(3, FLAG_STRING);
+					if (syn_c &&match_forward(state, "c")) {
+						nest(syn_c->calculate, 100);
+					} else if (syn_c && match_forward(state,"c++")) {
+						nest(syn_c->calculate, 100);
+					} else if (syn_py && (match_forward(state,"py") || match_forward(state,"python"))) {
+						nest(syn_py->calculate, 200);
+					} else if (syn_java && match_forward(state, "java")) {
+						nest(syn_java->calculate, 300);
+					} else if (syn_json && match_forward(state,"json")) {
+						nest(syn_json->calculate, 400);
+					} else if (syn_xml && match_forward(state,"xml")) {
+						nest(syn_xml->calculate, 500);
+					} else if (syn_xml && match_forward(state,"html")) {
+						nest(syn_xml->calculate, 500); // TODO this will be a different highlighter later
+					} else if (syn_make && match_forward(state,"make")) {
+						nest(syn_make->calculate, 600);
+					} else if (syn_diff && match_forward(state, "diff")) {
+						nest(syn_diff->calculate, 700);
+					} else if (syn_rust && match_forward(state, "rust")) {
+						nest(syn_rust->calculate, 800); /* Keep this at the end for now */
+					}
+					return 1;
+				}
+			}
+			if (charat() == '`') {
+				paint(1, FLAG_STRING);
+				while (charat() != -1) {
+					if (charat() == '`') {
+						paint(1, FLAG_STRING);
+						return 0;
+					}
+					paint(1, FLAG_STRING);
+				}
+			} else if (charat() == '[') {
+				skip();
+				while (charat() != -1 && charat() != ']') {
+					paint(1, FLAG_LINK);
+				}
+				if (charat() == ']') skip();
+				if (charat() == '(') {
+					skip();
+					while (charat() != -1 && charat() != ')') {
+						paint(1, FLAG_NUMERAL);
+					}
+				}
+			} else {
+				skip();
+				return 0;
+			}
+		}
+		return -1;
+	} else if (state->state >= 1) {
+		/* Continuing generic triple-` */
+		if (state->i == 0) {
+			/* Go backwards until we find the source ``` */
+			int count = 0;
+			for (int i = state->line_no; i > 0; i--) {
+				if (env->lines[i]->istate < 1) {
+					while (env->lines[i]->text[count].codepoint == ' ') {
+						if (charrel(count) != ' ') goto _nope;
+						count++;
+					}
+					break;
+				}
+			}
+			if (charrel(count) == '`' && charrel(count+1) == '`' && charrel(count+2) == '`' && charrel(count+3) == -1) {
+				paint(count+3,FLAG_STRING);
+				return -1;
+			}
+		}
+_nope:
+		if (state->state == 1) {
+			while (charat() != -1) paint(1, FLAG_STRING);
+			return 1;
+		} else if (state->state < 199) {
+			nest(syn_c->calculate, 100);
+		} else if (state->state < 299) {
+			nest(syn_py->calculate, 200);
+		} else if (state->state < 399) {
+			nest(syn_java->calculate, 300);
+		} else if (state->state < 499) {
+			nest(syn_json->calculate, 400);
+		} else if (state->state < 599) {
+			nest(syn_xml->calculate, 500);
+		} else if (state->state < 699) {
+			nest(syn_make->calculate, 600);
+		} else if (state->state < 799) {
+			nest(syn_diff->calculate, 700);
+		} else {
+			nest(syn_rust->calculate, 800);
+		}
+	}
+	return -1;
+}
+
+char * syn_markdown_ext[] = {".md",".markdown",NULL};
+
+BIM_SYNTAX(markdown, 1)

+ 57 - 0
syntax/proto.c

@@ -0,0 +1,57 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_proto_keywords[] = {
+	"syntax","import","option","package","message","group","oneof",
+	"optional","required","repeated","default","extend","extensions","to","max","reserved",
+	"service","rpc","returns","stream",
+	NULL
+};
+
+char * syn_proto_types[] = {
+	"int32","int64","uint32","uint64","sint32","sint64",
+	"fixed32","fixed64","sfixed32","sfixed64",
+	"float","double","bool","string","bytes",
+	"enum",
+	NULL
+};
+
+char * syn_proto_special[] = {
+	"true","false",
+	NULL
+};
+
+int syn_proto_calculate(struct syntax_state * state) {
+	if (state->state < 1) {
+		if (charat() == '/' && nextchar() == '/') {
+			paint_comment(state);
+		} else if (charat() == '/' && nextchar() == '*') {
+			if (paint_c_comment(state) == 1) return 1;
+			return 0;
+		} else if (find_keywords(state, syn_proto_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+			return 0;
+		} else if (find_keywords(state, syn_proto_types, FLAG_TYPE, c_keyword_qualifier)) {
+			return 0;
+		} else if (find_keywords(state, syn_proto_special, FLAG_NUMERAL, c_keyword_qualifier)) {
+			return 0;
+		} else if (charat() == '"') {
+			paint_simple_string(state);
+			return 0;
+		} else if (!c_keyword_qualifier(lastchar()) && isdigit(charat())) {
+			paint_c_numeral(state);
+			return 0;
+		} else if (charat() != -1) {
+			skip();
+			return 0;
+		}
+		return -1;
+	} else {
+		if (paint_c_comment(state) == 1) return 1;
+		return 0;
+	}
+}
+
+char * syn_proto_ext[] = {".proto",NULL};
+
+BIM_SYNTAX(proto, 1)

+ 205 - 0
syntax/py.c

@@ -0,0 +1,205 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+char * syn_py_keywords[] = {
+	"class","def","return","del","if","else","elif","for","while","continue",
+	"break","assert","as","and","or","except","finally","from","global",
+	"import","in","is","lambda","with","nonlocal","not","pass","raise","try","yield",
+	NULL
+};
+
+char * syn_py_types[] = {
+	/* built-in functions */
+	"abs","all","any","ascii","bin","bool","breakpoint","bytes",
+	"bytearray","callable","compile","complex","delattr","chr",
+	"dict","dir","divmod","enumerate","eval","exec","filter","float",
+	"format","frozenset","getattr","globals","hasattr","hash","help",
+	"hex","id","input","int","isinstance","issubclass","iter","len",
+	"list","locals","map","max","memoryview","min","next","object",
+	"oct","open","ord","pow","print","property","range","repr","reverse",
+	"round","set","setattr","slice","sorted","staticmethod","str","sum",
+	"super","tuple","type","vars","zip",
+	NULL
+};
+
+char * syn_py_special[] = {
+	"True","False","None",
+	NULL
+};
+
+int paint_py_triple_double(struct syntax_state * state) {
+	while (charat() != -1) {
+		if (charat() == '"') {
+			paint(1, FLAG_STRING);
+			if (charat() == '"' && nextchar() == '"') {
+				paint(2, FLAG_STRING);
+				return 0;
+			}
+		} else {
+			paint(1, FLAG_STRING);
+		}
+	}
+	return 1; /* continues */
+}
+
+int paint_py_triple_single(struct syntax_state * state) {
+	while (charat() != -1) {
+		if (charat() == '\'') {
+			paint(1, FLAG_STRING);
+			if (charat() == '\'' && nextchar() == '\'') {
+				paint(2, FLAG_STRING);
+				return 0;
+			}
+		} else {
+			paint(1, FLAG_STRING);
+		}
+	}
+	return 2; /* continues */
+}
+
+int paint_py_single_string(struct syntax_state * state) {
+	paint(1, FLAG_STRING);
+	while (charat() != -1) {
+		if (charat() == '\\' && nextchar() == '\'') {
+			paint(2, FLAG_ESCAPE);
+		} else if (charat() == '\'') {
+			paint(1, FLAG_STRING);
+			return 0;
+		} else if (charat() == '\\') {
+			paint(2, FLAG_ESCAPE);
+		} else {
+			paint(1, FLAG_STRING);
+		}
+	}
+	return 0;
+}
+
+int paint_py_numeral(struct syntax_state * state) {
+	if (charat() == '0' && (nextchar() == 'x' || nextchar() == 'X')) {
+		paint(2, FLAG_NUMERAL);
+		while (isxdigit(charat())) paint(1, FLAG_NUMERAL);
+	} else if (charat() == '0' && nextchar() == '.') {
+		paint(2, FLAG_NUMERAL);
+		while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		if ((charat() == '+' || charat() == '-') && (nextchar() == 'e' || nextchar() == 'E')) {
+			paint(2, FLAG_NUMERAL);
+			while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		} else if (charat() == 'e' || charat() == 'E') {
+			paint(1, FLAG_NUMERAL);
+			while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		}
+		if (charat() == 'j') paint(1, FLAG_NUMERAL);
+		return 0;
+	} else {
+		while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+		if (charat() == '.') {
+			paint(1, FLAG_NUMERAL);
+			while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			if ((charat() == '+' || charat() == '-') && (nextchar() == 'e' || nextchar() == 'E')) {
+				paint(2, FLAG_NUMERAL);
+				while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			} else if (charat() == 'e' || charat() == 'E') {
+				paint(1, FLAG_NUMERAL);
+				while (isdigit(charat())) paint(1, FLAG_NUMERAL);
+			}
+			if (charat() == 'j') paint(1, FLAG_NUMERAL);
+			return 0;
+		}
+		if (charat() == 'j') paint(1, FLAG_NUMERAL);
+	}
+	while (charat() == 'l' || charat() == 'L') paint(1, FLAG_NUMERAL);
+	return 0;
+}
+
+void paint_py_format_string(struct syntax_state * state, char type) {
+	paint(1, FLAG_STRING);
+	while (charat() != -1) {
+		if (charat() == '\\' && nextchar() == type) {
+			paint(2, FLAG_ESCAPE);
+		} else if (charat() == type) {
+			paint(1, FLAG_STRING);
+			return;
+		} else if (charat() == '\\') {
+			paint(2, FLAG_ESCAPE);
+		} else if (charat() == '{') {
+			paint(1, FLAG_NUMERAL);
+			if (charat() == '}') {
+				state->i--;
+				paint(2, FLAG_ERROR); /* Can't do that. */
+			} else {
+				while (charat() != -1 && charat() != '}') {
+					paint(1, FLAG_NUMERAL);
+				}
+				paint(1, FLAG_NUMERAL);
+			}
+		} else {
+			paint(1, FLAG_STRING);
+		}
+	}
+}
+
+int syn_py_calculate(struct syntax_state * state) {
+	switch (state->state) {
+		case -1:
+		case 0:
+			if (charat() == '#') {
+				paint_comment(state);
+			} else if (state->i == 0 && match_and_paint(state, "import", FLAG_PRAGMA, c_keyword_qualifier)) {
+				return 0;
+			} else if (charat() == '@') {
+				paint(1, FLAG_PRAGMA);
+				while (c_keyword_qualifier(charat())) paint(1, FLAG_PRAGMA);
+				return 0;
+			} else if (charat() == '"') {
+				if (nextchar() == '"' && charrel(2) == '"') {
+					paint(3, FLAG_STRING);
+					return paint_py_triple_double(state);
+				} else if (lastchar() == 'f') {
+					/* I don't like backtracking like this, but it makes this parse easier */
+					state->i--;
+					paint(1,FLAG_TYPE);
+					paint_py_format_string(state,'"');
+					return 0;
+				} else {
+					paint_simple_string(state);
+					return 0;
+				}
+			} else if (find_keywords(state, syn_py_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+				return 0;
+			} else if (lastchar() != '.' && find_keywords(state, syn_py_types, FLAG_TYPE, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_py_special, FLAG_NUMERAL, c_keyword_qualifier)) {
+				return 0;
+			} else if (charat() == '\'') {
+				if (nextchar() == '\'' && charrel(2) == '\'') {
+					paint(3, FLAG_STRING);
+					return paint_py_triple_single(state);
+				} else if (lastchar() == 'f') {
+					/* I don't like backtracking like this, but it makes this parse easier */
+					state->i--;
+					paint(1,FLAG_TYPE);
+					paint_py_format_string(state,'\'');
+					return 0;
+				} else {
+					return paint_py_single_string(state);
+				}
+			} else if (!c_keyword_qualifier(lastchar()) && isdigit(charat())) {
+				paint_py_numeral(state);
+				return 0;
+			} else if (charat() != -1) {
+				skip();
+				return 0;
+			}
+			break;
+		case 1: /* multiline """ string */
+			return paint_py_triple_double(state);
+		case 2: /* multiline ''' string */
+			return paint_py_triple_single(state);
+	}
+	return -1;
+}
+
+char * syn_py_ext[] = {".py",NULL};
+
+BIM_SYNTAX(py, 1)

+ 100 - 0
syntax/rust.c

@@ -0,0 +1,100 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+static char * syn_rust_keywords[] = {
+	"as","break","const","continue","crate","else","enum","extern",
+	"false","fn","for","if","impl","in","let","loop","match","mod",
+	"move","mut","pub","ref","return","Self","self","static","struct",
+	"super","trait","true","type","unsafe","use","where","while",
+	NULL,
+};
+
+static char * syn_rust_types[] = {
+	"bool","char","str",
+	"i8","i16","i32","i64",
+	"u8","u16","u32","u64",
+	"isize","usize",
+	"f32","f64",
+	NULL,
+};
+
+static int paint_rs_comment(struct syntax_state * state) {
+	while (charat() != -1) {
+		if (common_comment_buzzwords(state)) continue;
+		else if (charat() == '*' && nextchar() == '/') {
+			paint(2, FLAG_COMMENT);
+			state->state--;
+			if (state->state == 0) return 0;
+		} else if (charat() == '/' && nextchar() == '*') {
+			state->state++;
+			paint(2, FLAG_COMMENT);
+		} else {
+			paint(1, FLAG_COMMENT);
+		}
+	}
+	return state->state;
+}
+
+int paint_rust_numeral(struct syntax_state * state) {
+	if (charat() == '0' && nextchar() == 'b') {
+		paint(2, FLAG_NUMERAL);
+		while (charat() == '0' || charat() == '1' || charat() == '_') paint(1, FLAG_NUMERAL);
+	} else if (charat() == '0' && nextchar() == 'o') {
+		paint(2, FLAG_NUMERAL);
+		while ((charat() >= '0' && charat() <= '7') || charat() == '_') paint(1, FLAG_NUMERAL);
+	} else if (charat() == '0' && nextchar() == 'x') {
+		paint(2, FLAG_NUMERAL);
+		while (isxdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
+	} else if (charat() == '0' && nextchar() == '.') {
+		paint(2, FLAG_NUMERAL);
+		while (isdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
+	} else {
+		while (isdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
+		if (charat() == '.') {
+			paint(1, FLAG_NUMERAL);
+			while (isdigit(charat()) || charat() == '_') paint(1, FLAG_NUMERAL);
+		}
+	}
+	return 0;
+}
+
+static int syn_rust_calculate(struct syntax_state * state) {
+	switch (state->state) {
+		case -1:
+		case 0:
+			if (charat() == '/' && nextchar() == '/') {
+				/* C++-style comments */
+				paint_comment(state);
+			} else if (charat() == '/' && nextchar() == '*') {
+				paint(2, FLAG_COMMENT);
+				state->state = 1;
+				return paint_rs_comment(state);
+			} else if (find_keywords(state, syn_rust_keywords, FLAG_KEYWORD, c_keyword_qualifier)) {
+				return 0;
+			} else if (find_keywords(state, syn_rust_types, FLAG_TYPE, c_keyword_qualifier)) {
+				return 0;
+			} else if (charat() == '\"') {
+				paint_simple_string(state);
+				return 0;
+			} else if (charat() == '\'') {
+				paint_c_char(state);
+				return 0;
+			} else if (!c_keyword_qualifier(lastchar()) && isdigit(charat())) {
+				paint_rust_numeral(state);
+				return 0;
+			} else if (charat() != -1) {
+				skip();
+				return 0;
+			}
+			break;
+		default: /* Nested comments */
+			return paint_rs_comment(state);
+	}
+
+	return -1;
+}
+
+char * syn_rust_ext[] = {".rs",NULL};
+
+BIM_SYNTAX(rust, 1)

+ 82 - 0
syntax/xml.c

@@ -0,0 +1,82 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-syntax.h"
+
+int syn_xml_calculate(struct syntax_state * state) {
+	switch (state->state) {
+		case -1:
+		case 0:
+			if (charat() == -1) return -1;
+			if (charat() != '<') {
+				skip();
+				return 0;
+			}
+			/* Opening brace */
+			if (charat() == '<' && nextchar() == '!' && charrel(2) == '-' && charrel(3) == '-') {
+				paint(4, FLAG_COMMENT);
+				goto _comment;
+			}
+			paint(1, FLAG_TYPE);
+			/* Fall through */
+		case 1:
+			/* State 1: We saw an opening brace. */
+			while (charat() != -1) {
+				if (charat() == '/') paint(1, FLAG_TYPE);
+				if (charat() == '?') paint(1, FLAG_TYPE);
+				if (charat() == ' ' || charat() == '\t') skip();
+				if (isalnum(charat())) {
+					while (isalnum(charat()) || charat() == '-') paint(1, FLAG_KEYWORD);
+					if (charat() == -1) return 2;
+					goto _in_tag;
+				} else {
+					paint(1, FLAG_TYPE);
+				}
+			}
+			return -1;
+_in_tag:
+		case 2:
+			while (charat() != -1) {
+				if (charat() == '>') {
+					paint(1, FLAG_TYPE);
+					return 0;
+				} else if (charat() == '"') {
+					paint_simple_string(state);
+					if (charat() == -1 && lastchar() != '"') {
+						return 3;
+					}
+				} else {
+					paint(1, FLAG_TYPE);
+				}
+			}
+			return 2;
+		case 3:
+			/* In a string in tag */
+			if (charat() == '"') {
+				paint(1, FLAG_STRING);
+				return 2;
+			} else {
+				paint_simple_string(state);
+				if (charat() == -1 && lastchar() != '"') {
+					return 3;
+				}
+			}
+			break;
+_comment:
+		case 4:
+			while (charat() != -1) {
+				if (charat() == '-' && nextchar() == '-' && charrel(2) == '>') {
+					paint(3, FLAG_COMMENT);
+					return 0;
+				} else {
+					if (common_comment_buzzwords(state)) continue;
+					else paint(1, FLAG_COMMENT);
+				}
+			}
+			return 4;
+	}
+	return -1;
+}
+
+char * syn_xml_ext[] = {".xml",".htm",".html",NULL}; // TODO other stuff that uses xml (it's a lot!); FIXME htm/html are temporary; make dedicated SGML ones for this
+
+BIM_SYNTAX(xml, 1)

+ 41 - 0
themes/ansi.c

@@ -0,0 +1,41 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-theme.h"
+
+/* 16-color theme, default */
+BIM_THEME(ansi) {
+	COLOR_FG        = global_config.can_bright ? "@17" : "@7";
+	COLOR_BG        = global_config.can_bright ? "@9"  : "@0";
+	COLOR_ALT_FG    = global_config.can_bright ? "@10" : "@5";
+	COLOR_ALT_BG    = "@9";
+	COLOR_NUMBER_FG = "@3";
+	COLOR_NUMBER_BG = "@9";
+	COLOR_STATUS_FG = global_config.can_bright ? "@17" : "@7";
+	COLOR_STATUS_BG = "@4";
+	COLOR_TABBAR_BG = "@4";
+	COLOR_TAB_BG    = "@4";
+	COLOR_KEYWORD   = global_config.can_bright ? "@14" : "@4";
+	COLOR_STRING    = "@2";
+	COLOR_COMMENT   = global_config.can_bright ? "@10" : "@5";
+	COLOR_TYPE      = "@3";
+	COLOR_PRAGMA    = "@1";
+	COLOR_NUMERAL   = "@1";
+
+	COLOR_ERROR_FG  = global_config.can_bright ? "@17" : "@7";
+	COLOR_ERROR_BG  = "@1";
+	COLOR_SEARCH_FG = "@0";
+	COLOR_SEARCH_BG = global_config.can_bright ? "@13" : "@3";
+
+	COLOR_SELECTBG  = global_config.can_bright ? "@17" : "@7";
+	COLOR_SELECTFG  = "@0";
+
+	COLOR_RED       = "@1";
+	COLOR_GREEN     = "@2";
+
+	COLOR_BOLD      = COLOR_FG; /* @ doesn't support extra args; FIXME */
+	COLOR_LINK      = global_config.can_bright ? "@14" : "@4";
+	COLOR_ESCAPE    = global_config.can_bright ? "@12" : "@2";
+
+	current_theme = "ansi";
+}
+

+ 42 - 0
themes/citylights.c

@@ -0,0 +1,42 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-theme.h"
+
+/* "City Lights" based on citylights.xyz */
+BIM_THEME(citylights) {
+	if (!global_config.can_24bit) return;
+	COLOR_FG        = "2;151;178;198";
+	COLOR_BG        = "2;29;37;44";
+	COLOR_ALT_FG    = "2;45;55;65";
+	COLOR_ALT_BG    = "2;33;42;50";
+	COLOR_NUMBER_FG = "2;71;89;103";
+	COLOR_NUMBER_BG = "2;37;47;56";
+	COLOR_STATUS_FG = "2;116;144;166";
+	COLOR_STATUS_BG = "2;53;67;78";
+	COLOR_TABBAR_BG = "2;37;47;56";
+	COLOR_TAB_BG    = "2;29;37;44";
+	COLOR_KEYWORD   = "2;94;196;255";
+	COLOR_STRING    = "2;83;154;252";
+	COLOR_COMMENT   = "2;107;133;153;3";
+	COLOR_TYPE      = "2;139;212;156";
+	COLOR_PRAGMA    = "2;0;139;148";
+	COLOR_NUMERAL   = "2;207;118;132";
+
+	COLOR_ERROR_FG  = "5;15";
+	COLOR_ERROR_BG  = "5;196";
+	COLOR_SEARCH_FG = "5;234";
+	COLOR_SEARCH_BG = "5;226";
+
+	COLOR_SELECTFG  = "2;29;37;44";
+	COLOR_SELECTBG  = "2;151;178;198";
+
+	COLOR_RED       = "2;222;53;53";
+	COLOR_GREEN     = "2;55;167;0";
+
+	COLOR_BOLD      = "2;151;178;198;1";
+	COLOR_LINK      = "2;94;196;255;4";
+	COLOR_ESCAPE    = "2;133;182;249";
+
+	current_theme = "citylights";
+}
+

+ 42 - 0
themes/solarized.c

@@ -0,0 +1,42 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-theme.h"
+
+/* Solarized Dark, popular theme */
+BIM_THEME(solarized_dark) {
+	if (!global_config.can_24bit) return;
+	COLOR_FG        = "2;147;161;161";
+	COLOR_BG        = "2;0;43;54";
+	COLOR_ALT_FG    = "2;147;161;161";
+	COLOR_ALT_BG    = "2;7;54;66";
+	COLOR_NUMBER_FG = "2;131;148;149";
+	COLOR_NUMBER_BG = "2;7;54;66";
+	COLOR_STATUS_FG = "2;131;148;150";
+	COLOR_STATUS_BG = "2;7;54;66";
+	COLOR_TABBAR_BG = "2;7;54;66";
+	COLOR_TAB_BG    = "2;131;148;150";
+	COLOR_KEYWORD   = "2;133;153;0";
+	COLOR_STRING    = "2;42;161;152";
+	COLOR_COMMENT   = "2;101;123;131";
+	COLOR_TYPE      = "2;181;137;0";
+	COLOR_PRAGMA    = "2;203;75;22";
+	COLOR_NUMERAL   = "2;220;50;47";
+
+	COLOR_ERROR_FG  = "5;15";
+	COLOR_ERROR_BG  = "5;196";
+	COLOR_SEARCH_FG = "5;234";
+	COLOR_SEARCH_BG = "5;226";
+
+	COLOR_SELECTFG  = "2;0;43;54";
+	COLOR_SELECTBG  = "2;147;161;161";
+
+	COLOR_RED       = "2;222;53;53";
+	COLOR_GREEN     = "2;55;167;0";
+
+	COLOR_BOLD      = "2;147;161;161;1";
+	COLOR_LINK      = "2;42;161;152;4";
+	COLOR_ESCAPE    = "2;133;153;0";
+
+	current_theme = "solarized-dark";
+}
+

+ 83 - 0
themes/sunsmoke.c

@@ -0,0 +1,83 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-theme.h"
+
+BIM_THEME(sunsmoke256) {
+	if (!global_config.can_256color) return;
+	COLOR_FG        = "5;188";
+	COLOR_BG        = "5;234";
+	COLOR_ALT_FG    = "5;244";
+	COLOR_ALT_BG    = "5;236";
+	COLOR_NUMBER_FG = "5;101";
+	COLOR_NUMBER_BG = "5;232";
+	COLOR_STATUS_FG = "5;188";
+	COLOR_STATUS_BG = "5;59";
+	COLOR_TABBAR_BG = "5;59";
+	COLOR_TAB_BG    = "5;59";
+	COLOR_KEYWORD   = "5;74";
+	COLOR_STRING    = "5;71";
+	COLOR_COMMENT   = global_config.can_italic ? "5;102;3" : "5;102";
+	COLOR_TYPE      = "5;221";
+	COLOR_PRAGMA    = "5;160";
+	COLOR_NUMERAL   = "5;161";
+
+	COLOR_ERROR_FG  = "5;15";
+	COLOR_ERROR_BG  = "5;196";
+	COLOR_SEARCH_FG = "5;234";
+	COLOR_SEARCH_BG = "5;226";
+
+	COLOR_SELECTFG  = "5;17";
+	COLOR_SELECTBG  = "5;109";
+
+	COLOR_RED       = "@1";
+	COLOR_GREEN     = "@2";
+
+	COLOR_BOLD      = "5;188;1";
+	COLOR_LINK      = "5;74;4";
+	COLOR_ESCAPE    = "5;79";
+
+	current_theme = "sunsmoke256";
+}
+
+/* Custom theme */
+BIM_THEME(sunsmoke) {
+	if (!global_config.can_24bit) {
+		load_colorscheme_sunsmoke256();
+		return;
+	}
+	COLOR_FG        = "2;230;230;230";
+	COLOR_BG        = "2;31;31;31";
+	COLOR_ALT_FG    = "2;122;122;122";
+	COLOR_ALT_BG    = "2;46;43;46";
+	COLOR_NUMBER_FG = "2;150;139;57";
+	COLOR_NUMBER_BG = "2;0;0;0";
+	COLOR_STATUS_FG = "2;230;230;230";
+	COLOR_STATUS_BG = "2;71;64;58";
+	COLOR_TABBAR_BG = "2;71;64;58";
+	COLOR_TAB_BG    = "2;71;64;58";
+	COLOR_KEYWORD   = "2;51;162;230";
+	COLOR_STRING    = "2;72;176;72";
+	COLOR_COMMENT   = "2;158;153;129;3";
+	COLOR_TYPE      = "2;230;206;110";
+	COLOR_PRAGMA    = "2;194;70;54";
+	COLOR_NUMERAL   = "2;230;43;127";
+
+	COLOR_ERROR_FG  = "5;15";
+	COLOR_ERROR_BG  = "5;196";
+	COLOR_SEARCH_FG = "5;234";
+	COLOR_SEARCH_BG = "5;226";
+
+	COLOR_SELECTFG  = "2;0;43;54";
+	COLOR_SELECTBG  = "2;147;161;161";
+
+	COLOR_RED       = "2;222;53;53";
+	COLOR_GREEN     = "2;55;167;0";
+
+	COLOR_BOLD      = "2;230;230;230;1";
+	COLOR_LINK      = "2;51;162;230;4";
+	COLOR_ESCAPE    = "2;113;203;173";
+
+	current_theme = "sunsmoke";
+}
+
+

+ 42 - 0
themes/wombat.c

@@ -0,0 +1,42 @@
+#include "bim-core.h"
+#include "bim-functions.h"
+#include "bim-theme.h"
+
+/* Based on the wombat256 theme for vim */
+BIM_THEME(wombat) {
+	if (!global_config.can_256color) return;
+	COLOR_FG        = "5;230";
+	COLOR_BG        = "5;235";
+	COLOR_ALT_FG    = "5;244";
+	COLOR_ALT_BG    = "5;236";
+	COLOR_NUMBER_BG = "5;232";
+	COLOR_NUMBER_FG = "5;101";
+	COLOR_STATUS_FG = "5;230";
+	COLOR_STATUS_BG = "5;238";
+	COLOR_TABBAR_BG = "5;230";
+	COLOR_TAB_BG    = "5;248";
+	COLOR_KEYWORD   = "5;117";
+	COLOR_STRING    = "5;113";
+	COLOR_COMMENT   = global_config.can_italic ? "5;102;3" : "5;102";
+	COLOR_TYPE      = "5;186";
+	COLOR_PRAGMA    = "5;173";
+	COLOR_NUMERAL   = COLOR_PRAGMA;
+
+	COLOR_ERROR_FG  = "5;15";
+	COLOR_ERROR_BG  = "5;196";
+	COLOR_SEARCH_FG = "5;234";
+	COLOR_SEARCH_BG = "5;226";
+
+	COLOR_SELECTFG  = "5;235";
+	COLOR_SELECTBG  = "5;230";
+
+	COLOR_RED       = "@1";
+	COLOR_GREEN     = "@2";
+
+	COLOR_BOLD      = "5;230;1";
+	COLOR_LINK      = "5;117;4";
+	COLOR_ESCAPE    = "5;194";
+
+	current_theme = "wombat";
+}
+