aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lexers/diff.lua6
-rw-r--r--lexers/lexer.lua128
l---------lexers/themes/default.lua1
-rw-r--r--lexers/themes/solarized.lua54
-rw-r--r--ui-curses.c78
-rw-r--r--ui.h9
-rw-r--r--view.c181
-rw-r--r--view.h3
-rw-r--r--vis-cmds.c2
-rw-r--r--vis-core.h1
-rw-r--r--vis.c84
11 files changed, 499 insertions, 48 deletions
diff --git a/lexers/diff.lua b/lexers/diff.lua
index 53b7f17..ee680aa 100644
--- a/lexers/diff.lua
+++ b/lexers/diff.lua
@@ -34,9 +34,9 @@ M._rules = {
M._tokenstyles = {
header = l.STYLE_COMMENT,
- addition = 'fore:$(color.green)',
- deletion = 'fore:$(color.red)',
- change = 'fore:$(color.yellow)'
+ addition = 'fore:'..l.colors.green,
+ deletion = 'fore:'..l.colors.red,
+ change = 'fore:'..l.colors.yellow,
}
M._LEXBYLINE = true
diff --git a/lexers/lexer.lua b/lexers/lexer.lua
index 0dc9674..4d7eb60 100644
--- a/lexers/lexer.lua
+++ b/lexers/lexer.lua
@@ -974,7 +974,10 @@ local default = {
'function', 'class', 'type', 'label', 'regex', 'embedded'
}
for _, v in ipairs(default) do
- M[string_upper(v)], M['STYLE_'..string_upper(v)] = v, '$(style.'..v..')'
+ M[string_upper(v)] = v
+ if not M['STYLE_'..string_upper(v)] then
+ M['STYLE_'..string_upper(v)] = ''
+ end
end
-- Predefined styles.
local predefined = {
@@ -982,7 +985,10 @@ local predefined = {
'indentguide', 'calltip'
}
for _, v in ipairs(predefined) do
- M[string_upper(v)], M['STYLE_'..string_upper(v)] = v, '$(style.'..v..')'
+ M[string_upper(v)] = v
+ if not M['STYLE_'..string_upper(v)] then
+ M['STYLE_'..string_upper(v)] = ''
+ end
end
---
@@ -1584,4 +1590,122 @@ M.property_expanded = setmetatable({}, {
local lexer
]]
+function M.get_style(lexer, lang, token_name)
+ return lexer['STYLE_'..string_upper(token_name)] or lang._EXTRASTYLES[token_name]
+end
+
+local files = {
+ [".as|.asc"] = "actionscript",
+ [".adb|.ads"] = "ada",
+ [".g|.g4"] = "antlr",
+ [".ans|.inp|.mac"] = "apdl",
+ [".applescript"] = "applescript",
+ [".asm|.ASM|.s|.S"] = "asm",
+ [".asa|.asp|.hta"] = "asp",
+ [".awk"] = "awk",
+ [".bat|.cmd"] = "batch",
+ [".bib"] = "bibtex",
+ [".boo"] = "boo",
+ [".cs"] = "csharp",
+ [".c|.cc|.C"] = "ansi_c",
+ [".cpp|.cxx|.c++|.h|.hh|.hpp|.hxx|.h++"] = "cpp",
+ [".ck"] = "chuck",
+ [".cmake|.cmake.in|.ctest|.ctest.in"] = "cmake",
+ [".coffee"] = "coffeescript",
+ [".css"] = "css",
+ [".cu|.cuh"] = "cuda",
+ [".d|.di"] = "dmd",
+ [".dart"] = "dart",
+ [".desktop"] = "desktop",
+ [".diff|.patch"] = "diff",
+ [".dot"] = "dot",
+ [".e|.eif"] = "eiffel",
+ [".ex|.exs"] = "elixir",
+ [".erl|.hrl"] = "erlang",
+ [".fs"] = "fsharp",
+ [".fish"] = "fish",
+ [".forth|.frt|.fs"] = "forth",
+ [".f|.for|.ftn|.fpp|.f77|.f90|.f95|.f03|.f08"] = "fortran",
+ [".g|.gd|.gi|.gap"] = "gap",
+ [".po|.pot"] = "gettext",
+ [".glslf|.glslv"] = "glsl",
+ [".dem|.plt"] = "gnuplot",
+ [".go"] = "go",
+ [".groovy|.gvy"] = "groovy",
+ [".gtkrc"] = "gtkrc",
+ [".hs"] = "haskell",
+ [".htm|.html|.shtm|.shtml|.xhtml"] = "html",
+ [".idl|.odl"] = "idl",
+ [".inf|.ni"] = "inform",
+ [".cfg|.cnf|.inf|.ini|.reg"] = "ini",
+ [".io"] = "io_lang",
+ [".bsh|.java"] = "java",
+ [".js|.jsfl"] = "javascript",
+ [".json"] = "json",
+ [".jsp"] = "jsp",
+ [".bbl|.dtx|.ins|.ltx|.tex|.sty"] = "latex",
+ [".less"] = "less",
+ [".lily|.ly"] = "lilypond",
+ [".cl|.el|.lisp|.lsp"] = "lisp",
+ [".litcoffee"] = "litcoffee",
+ [".lua"] = "lua",
+ ["GNUmakefile|.iface|.mak|.mk|makefile|Makefile"] = "makefile",
+ [".md"] = "markdown",
+ [".n"] = "nemerle",
+ [".nim"] = "nim",
+ [".nsh|.nsi|.nsis"] = "nsis",
+ [".m|.mm|.objc"] = "objective_c",
+ [".caml|.ml|.mli|.mll|.mly"] = "caml",
+ [".dpk|.dpr|.p|.pas"] = "pascal",
+ [".al|.perl|.pl|.pm|.pod"] = "perl",
+ [".inc|.php|.php3|.php4|.phtml"] = "php",
+ [".pike|.pmod"] = "pike",
+ [".PKGBUILD"] = "pkgbuild",
+ [".ps1"] = "powershell",
+ [".eps|.ps"] = "ps",
+ [".prolog"] = "prolog",
+ [".props|.properties"] = "props",
+ [".sc|.py|.pyw"] = "python",
+ [".R|.Rout|.Rhistory|.Rt|Rout.save|Rout.fail"] = "rstats",
+ [".r|.reb"] = "rebol",
+ [".rst"] = "rest",
+ [".orx|.rex"] = "rexx",
+ [".erb|.rhtml"] = "rhtml",
+ [".Rakefile|.rake|.rb|.rbw"] = "ruby",
+ [".rs"] = "rust",
+ [".sass|.scss"] = "sass",
+ [".scala"] = "scala",
+ [".sch|.scm"] = "scheme",
+ [".bash|.bashrc|.bash_profile|.configure|.csh|.sh|.zsh"] = "bash",
+ [".changes|.st|.sources"] = "smalltalk",
+ [".ddl|.sql"] = "sql",
+ [".tcl|.tk"] = "tcl",
+ [".texi"] = "texinfo",
+ [".toml"] = "toml",
+ [".vala"] = "vala",
+ [".vcf|.vcard"] = "vcard",
+ [".v|.ver"] = "verilog",
+ [".vh|.vhd|.vhdl"] = "vhdl",
+ [".asa|.bas|.cls|.ctl|.dob|.dsm|.dsr|.frm|.pag|.vb|.vba|.vbs"] = "vb",
+ [".wsf"] = "wsf",
+ [".dtd|.svg|.xml|.xsd|.xsl|.xslt|.xul"] = "xml",
+ [".xtend"] = "xtend",
+ [".yaml"] = "yaml",
+}
+
+function M.lexer_name(filename)
+ -- filename = string.lower(filename)
+ for patterns, lang in pairs(files) do
+ for pattern in string.gmatch(patterns, '[^|]+') do
+ if #filename >= #pattern then
+ local s, e = string.find(filename, pattern, -#pattern, true)
+ if s ~= e and e == #filename then
+ return lang
+ end
+ end
+ end
+ end
+ return nil
+end
+
return M
diff --git a/lexers/themes/default.lua b/lexers/themes/default.lua
new file mode 120000
index 0000000..8479b1b
--- /dev/null
+++ b/lexers/themes/default.lua
@@ -0,0 +1 @@
+solarized.lua \ No newline at end of file
diff --git a/lexers/themes/solarized.lua b/lexers/themes/solarized.lua
new file mode 100644
index 0000000..0575056
--- /dev/null
+++ b/lexers/themes/solarized.lua
@@ -0,0 +1,54 @@
+-- Solarized color codes Copyright (c) 2011 Ethan Schoonover
+local colors = {
+ ['base03'] = '#002b36',
+ ['base02'] = '#073642',
+ ['base01'] = '#586e75',
+ ['base00'] = '#657b83',
+ ['base0'] = '#839496',
+ ['base1'] = '#93a1a1',
+ ['base2'] = '#eee8d5',
+ ['base3'] = '#fdf6e3',
+ ['yellow'] = '#b58900',
+ ['orange'] = '#cb4b16',
+ ['red'] = '#dc322f',
+ ['magenta'] = '#d33682',
+ ['violet'] = '#6c71c4',
+ ['blue'] = '#268bd2',
+ ['cyan'] = '#2aa198',
+ ['green'] = '#859900',
+}
+
+lexers.colors = colors
+-- dark
+local fg = ',fore:'..colors.base0..','
+local bg = ',back:'..colors.base03..','
+-- light
+-- local fg = ',fore:'..colors.base03..','
+-- local bg = ',back:'..colors.base3..','
+
+lexers.STYLE_DEFAULT = bg..fg
+lexers.STYLE_NOTHING = bg
+lexers.STYLE_CLASS = 'fore:yellow'
+lexers.STYLE_COMMENT = 'fore:'..colors.base01
+lexers.STYLE_CONSTANT = 'fore:'..colors.cyan
+lexers.STYLE_DEFINITION = 'fore:'..colors.blue
+lexers.STYLE_ERROR = 'fore:'..colors.red..',italics'
+lexers.STYLE_FUNCTION = 'fore:'..colors.blue
+lexers.STYLE_KEYWORD = 'fore:'..colors.green
+lexers.STYLE_LABEL = 'fore:'..colors.green
+lexers.STYLE_NUMBER = 'fore:'..colors.cyan
+lexers.STYLE_OPERATOR = 'fore:'..colors.green
+lexers.STYLE_REGEX = 'fore:green'
+lexers.STYLE_STRING = 'fore:'..colors.cyan
+lexers.STYLE_PREPROCESSOR = 'fore:'..colors.orange
+lexers.STYLE_TAG = 'fore:'..colors.red
+lexers.STYLE_TYPE = 'fore:'..colors.yellow
+lexers.STYLE_VARIABLE = 'fore:'..colors.blue
+lexers.STYLE_WHITESPACE = ''
+lexers.STYLE_EMBEDDED = 'back:blue'
+lexers.STYLE_IDENTIFIER = fg
+
+lexers.STYLE_LINENUMBER = fg
+lexers.STYLE_CURSOR = 'fore:'..colors.base03..',back:'..colors.base0
+-- lexers.STYLE_SELECTION = 'back:'..colors.base02
+lexers.STYLE_SELECTION = 'back:white'
diff --git a/ui-curses.c b/ui-curses.c
index 7c1f5b3..d00dbf3 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -59,6 +59,11 @@ int ESCDELAY;
} while (0);
#endif
+typedef struct {
+ attr_t attr;
+ short fg, bg;
+} CellStyle;
+
typedef struct UiCursesWin UiCursesWin;
typedef struct {
@@ -88,7 +93,7 @@ struct UiCursesWin {
int sidebar_width; /* width of the sidebar showing line numbers etc. */
UiCursesWin *next, *prev; /* pointers to neighbouring windows */
enum UiOption options; /* display settings for this window */
- attr_t styles[UI_STYLES_MAX];
+ CellStyle styles[UI_STYLE_MAX];
};
static volatile sig_atomic_t need_resize; /* TODO */
@@ -454,7 +459,7 @@ static int color_fromstring(const char *s)
return -1;
}
-static unsigned int color_pair_hash(short fg, short bg) {
+static inline unsigned int color_pair_hash(short fg, short bg) {
if (fg == -1)
fg = COLORS;
if (bg == -1)
@@ -510,41 +515,47 @@ static short color_pair_get(short fg, short bg) {
return color2palette[index];
}
+static inline attr_t style_to_attr(CellStyle *style) {
+ return style->attr | COLOR_PAIR(color_pair_get(style->fg, style->bg));
+}
+
static bool ui_window_syntax_style(UiWin *w, int id, const char *style) {
UiCursesWin *win = (UiCursesWin*)w;
- if (id >= UI_STYLES_MAX)
+ if (id >= UI_STYLE_MAX)
return false;
- short fg = -1, bg = -1;
- attr_t attr = A_NORMAL;
+ if (!style)
+ return true;
+ CellStyle cell_style = win->styles[UI_STYLE_DEFAULT];
char *style_copy = strdup(style), *option = style_copy, *next, *p;
while (option) {
if ((next = strchr(option, ',')))
*next++ = '\0';
if ((p = strchr(option, ':')))
*p++ = '\0';
- if (!strcasecmp(option, "bold")) {
- attr |= A_BOLD;
+ if (!strcasecmp(option, "reverse")) {
+ cell_style.attr |= A_REVERSE;
+ } else if (!strcasecmp(option, "bold")) {
+ cell_style.attr |= A_BOLD;
} else if (!strcasecmp(option, "notbold")) {
- attr &= ~A_BOLD;
+ cell_style.attr &= ~A_BOLD;
#ifdef A_ITALIC
} else if (!strcasecmp(option, "italics")) {
- attr |= A_ITALIC;
+ cell_style.attr |= A_ITALIC;
} else if (!strcasecmp(option, "notitalics")) {
- attr &= ~A_ITALIC;
+ cell_style.attr &= ~A_ITALIC;
#endif
} else if (!strcasecmp(option, "underlined")) {
- attr |= A_UNDERLINE;
+ cell_style.attr |= A_UNDERLINE;
} else if (!strcasecmp(option, "notunderlined")) {
- attr &= ~A_UNDERLINE;
+ cell_style.attr &= ~A_UNDERLINE;
} else if (!strcasecmp(option, "fore")) {
- fg = color_fromstring(p);
+ cell_style.fg = color_fromstring(p);
} else if (!strcasecmp(option, "back")) {
- bg = color_fromstring(p);
+ cell_style.bg = color_fromstring(p);
}
option = next;
}
- attr |= COLOR_PAIR(color_pair_get(fg, bg));
- win->styles[id] = attr;
+ win->styles[id] = cell_style;
free(style_copy);
return true;
}
@@ -585,6 +596,8 @@ static bool ui_window_draw_sidebar(UiCursesWin *win) {
size_t prev_lineno = 0;
size_t cursor_lineno = view_cursor_getpos(win->view).line;
werase(win->winside);
+ wbkgd(win->winside, style_to_attr(&win->styles[UI_STYLE_DEFAULT]));
+ wattrset(win->winside, style_to_attr(&win->styles[UI_STYLE_LINENUMBER]));
for (const Line *l = line; l; l = l->next, i++) {
if (l->lineno && l->lineno != prev_lineno) {
if (win->options & UI_OPTION_LINE_NUMBERS_ABSOLUTE) {
@@ -632,15 +645,25 @@ static void ui_window_draw(UiWin *w) {
UiCursesWin *win = (UiCursesWin*)w;
if (!ui_window_draw_sidebar(win))
return;
+ wbkgd(win->win, style_to_attr(&win->styles[UI_STYLE_DEFAULT]));
wmove(win->win, 0, 0);
int width = view_width_get(win->view);
+ CellStyle *prev_style = NULL;
+ short selection_bg = win->styles[UI_STYLE_SELECTION].bg;
+ attr_t attr;
for (const Line *l = view_lines_get(win->view); l; l = l->next) {
for (int x = 0; x < width; x++) {
- int attr = win->styles[l->cells[x].attr];
- if (l->cells[x].cursor && (win->ui->selwin == win || win->ui->prompt_win == win))
- attr = A_NORMAL | A_REVERSE;
- if (l->cells[x].selected)
- attr |= A_REVERSE;
+ CellStyle *style = &win->styles[l->cells[x].attr];
+ if (l->cells[x].cursor && (win->ui->selwin == win || win->ui->prompt_win == win)) {
+ attr = style_to_attr(&win->styles[UI_STYLE_CURSOR]);
+ prev_style = NULL;
+ } else if (l->cells[x].selected) {
+ attr = style->attr | COLOR_PAIR(color_pair_get(style->fg, selection_bg));
+ prev_style = NULL;
+ } else if (style != prev_style) {
+ attr = style_to_attr(style);
+ prev_style = style;
+ }
wattrset(win->win, attr);
waddstr(win->win, l->cells[x].data);
}
@@ -841,6 +864,18 @@ static UiWin *ui_window_new(Ui *ui, View *view, File *file) {
return NULL;
}
+ CellStyle style = (CellStyle) {
+ .fg = -1, .bg = -1, .attr = A_NORMAL,
+ };
+
+ for (int i = 0; i < UI_STYLE_MAX; i++) {
+ win->styles[i] = style;
+ }
+
+ style.attr |= A_REVERSE;
+ win->styles[UI_STYLE_CURSOR] = style;
+ win->styles[UI_STYLE_SELECTION] = style;
+
win->ui = uic;
win->view = view;
win->file = file;
@@ -1071,4 +1106,3 @@ void ui_curses_free(Ui *ui) {
termkey_destroy(uic->termkey);
free(uic);
}
-
diff --git a/ui.h b/ui.h
index 8c94d51..656f5a8 100644
--- a/ui.h
+++ b/ui.h
@@ -23,7 +23,14 @@ enum UiOption {
UI_OPTION_SYMBOL_EOF = 1 << 6,
};
-#define UI_STYLES_MAX 64
+enum UiStyles {
+ UI_STYLE_LEXER_MAX = 64,
+ UI_STYLE_DEFAULT,
+ UI_STYLE_CURSOR,
+ UI_STYLE_SELECTION,
+ UI_STYLE_LINENUMBER,
+ UI_STYLE_MAX,
+};
#include "text.h"
#include "view.h"
diff --git a/view.c b/view.c
index 2f5aa47..80e940b 100644
--- a/view.c
+++ b/view.c
@@ -71,6 +71,8 @@ struct View {
int tabwidth; /* how many spaces should be used to display a tab character */
Cursor *cursors; /* all cursors currently active */
Selection *selections; /* all selected regions */
+ lua_State *lua; /* lua state used for syntax highlighting */
+ char *lexer_name;
};
static const SyntaxSymbol symbols_none[] = {
@@ -333,20 +335,31 @@ void view_cursor_to(View *view, size_t pos) {
* stop once the screen is full, update view->end, view->lastline */
void view_draw(View *view) {
view_clear(view);
- /* current absolute file position */
- size_t pos = view->start;
+ /* absolute start of visible area */
+ size_t start = view->start;
+ /* maximal number of bytes to consider for syntax highlighting before
+ * the visible area */
+ const size_t lexer_before_max = 4096;
+ /* absolute position to start syntax highlighting */
+ size_t lexer_start = start >= lexer_before_max ? start - lexer_before_max : 0;
+ /* number of bytes used for syntax highlighting before visible are */
+ size_t lexer_before = start - lexer_start;
/* number of bytes to read in one go */
- size_t text_len = view->width * view->height;
+ const size_t text_size = lexer_before_max + (view->width * view->height);
/* current buffer to work with */
- char text[text_len+1];
+ char text[text_size+1];
/* remaining bytes to process in buffer*/
- size_t rem = text_bytes_get(view->text, pos, text_len, text);
- /* NUL terminate because regex(3) function expect it */
- text[rem] = '\0';
+ size_t text_len = text_bytes_get(view->text, lexer_start, text_size, text);
+ /* NUL terminate text section */
+ text[text_len] = '\0';
+ /* remaining bytes to process */
+ size_t rem = text_len;
+ if (rem >= lexer_before)
+ rem -= lexer_before;
+ /* absolute position of character currently being added to display */
+ size_t pos = start;
/* current position into buffer from which to interpret a character */
- char *cur = text;
- /* default and current curses attributes to use */
- int default_attrs = 0, attrs = default_attrs;
+ char *cur = text + lexer_before;
/* start from known multibyte state */
mbstate_t mbstate = { 0 };
@@ -369,7 +382,7 @@ void view_draw(View *view) {
* wide character. advance file position and read
* another junk into buffer.
*/
- rem = text_bytes_get(view->text, pos, text_len, text);
+ rem = text_bytes_get(view->text, pos, text_size, text);
text[rem] = '\0';
cur = text;
continue;
@@ -392,7 +405,6 @@ void view_draw(View *view) {
cell = (Cell){ .data = "\n", .len = 2, .width = 1, .istab = false };
}
- cell.attr = attrs;
if (!view_addch(view, &cell))
break;
@@ -404,6 +416,70 @@ void view_draw(View *view) {
/* set end of vieviewg region */
view->end = pos;
view->lastline = view->line ? view->line : view->bottomline;
+
+ lua_State *L = view->lua;
+ if (L && view->lexer_name) {
+
+ lua_getglobal(L, "lexers");
+ lua_getfield(L, -1, "load");
+ lua_pushstring(L, view->lexer_name);
+ lua_pcall(L, 1, 1, 0);
+
+ lua_getfield(L, -1, "_TOKENSTYLES");
+ lua_getfield(L, -2, "lex");
+
+ lua_pushvalue(L, -3); /* lexer obj */
+
+ const char *lex_text = text;
+ if (lexer_start > 0) {
+ /* try to start lexing at a line boundry */
+ const char *newline = memchr(text, '\n', lexer_before);
+ if (newline)
+ lex_text = newline;
+ }
+
+ lua_pushlstring(L, lex_text, text_len - (lex_text - text));
+ lua_pushinteger(L, 1 /* inital style: whitespace */);
+
+ int token_count;
+
+ if (lua_isfunction(L, -4) && !lua_pcall(L, 3, 1, 0) && lua_istable(L, -1) &&
+ (token_count = lua_objlen(L, -1)) > 0) {
+
+ size_t pos = lexer_before - (lex_text - text);
+ Line *line = view->topline;
+ int col = 0;
+
+ for (int i = 1; i < token_count; i += 2) {
+ lua_rawgeti(L, -1, i);
+ //const char *name = lua_tostring(L, -1);
+ lua_gettable(L, -3); /* _TOKENSTYLES[token] */
+ size_t token_style = lua_tointeger(L, -1);
+ lua_pop(L, 1); /* style */
+ lua_rawgeti(L, -1, i + 1);
+ size_t token_end = lua_tointeger(L, -1) - 1;
+ lua_pop(L, 1); /* pos */
+
+ for (bool token_next = false; line; line = line->next, col = 0) {
+ for (; col < line->width; col++) {
+ if (pos < token_end) {
+ line->cells[col].attr = token_style;
+ pos += line->cells[col].len;
+ } else {
+ token_next = true;
+ break;
+ }
+ }
+ if (token_next)
+ break;
+ }
+ }
+ lua_pop(L, 1);
+ }
+
+ lua_pop(L, 3); /* _TOKENSTYLES, language specific lexer, lexers globale */
+ }
+
if (view->line) {
for (int x = view->col; x < view->width; x++)
view->line->cells[x] = cell_blank;
@@ -507,6 +583,7 @@ void view_free(View *view) {
while (view->selections)
view_selections_free(view->selections);
free(view->lines);
+ free(view->lexer_name);
free(view);
}
@@ -516,7 +593,7 @@ void view_reload(View *view, Text *text) {
view_cursor_to(view, 0);
}
-View *view_new(Text *text, ViewEvent *events) {
+View *view_new(Text *text, lua_State *lua, ViewEvent *events) {
if (!text)
return NULL;
View *view = calloc(1, sizeof(View));
@@ -528,6 +605,7 @@ View *view_new(Text *text, ViewEvent *events) {
}
view->text = text;
+ view->lua = lua;
view->events = events;
view->tabwidth = 8;
view_options_set(view, 0);
@@ -782,12 +860,85 @@ void view_scroll_to(View *view, size_t pos) {
view_cursors_scroll_to(view->cursor, pos);
}
+#include <stdio.h>
+
+
bool view_syntax_set(View *view, const char *name) {
- return false;
+ if (!name) {
+ free(view->lexer_name);
+ view->lexer_name = NULL;
+ return true;
+ }
+
+ lua_State *L = view->lua;
+ if (!L)
+ return false;
+
+ /* Try to load the specified lexer and parse its token styles.
+ * Roughly equivalent to the following lua code:
+ *
+ * lang = lexers.load(name)
+ * for token_name, id in pairs(lang._TOKENSTYLES) do
+ * ui->syntax_style(id, lexers:get_style(lang, token_name);
+ */
+ lua_getglobal(L, "lexers");
+
+ lua_getfield(L, -1, "STYLE_DEFAULT");
+ view->ui->syntax_style(view->ui, UI_STYLE_DEFAULT, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "STYLE_CURSOR");
+ view->ui->syntax_style(view->ui, UI_STYLE_CURSOR, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "STYLE_SELECTION");
+ view->ui->syntax_style(view->ui, UI_STYLE_SELECTION, lua_tostring(L, -1));
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "STYLE_LINENUMBER");
+ view->ui->syntax_style(view->ui, UI_STYLE_LINENUMBER, lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+
+ lua_getfield(L, -1, "load");
+ lua_pushstring(L, name);
+
+ if (lua_pcall(L, 1, 1, 0))
+ return false;
+
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 2);
+ return false;
+ }
+
+ view->lexer_name = strdup(name);
+ /* loop through all _TOKENSTYLES and parse them */
+ lua_getfield(L, -1, "_TOKENSTYLES");
+ lua_pushnil(L); /* first key */
+
+ while (lua_next(L, -2)) {
+ size_t id = lua_tointeger(L, -1);
+ //const char *name = lua_tostring(L, -2);
+ lua_pop(L, 1); /* remove value (=id), keep key (=name) */
+ lua_getfield(L, -4, "get_style");
+ lua_pushvalue(L, -5); /* lexer */
+ lua_pushvalue(L, -5); /* lang */
+ lua_pushvalue(L, -4); /* token_name */
+ if (lua_pcall(L, 3, 1, 0))
+ return false;
+ const char *style = lua_tostring(L, -1);
+ //printf("%s\t%d\t%s\n", name, id, style);
+ view->ui->syntax_style(view->ui, id, style);
+ lua_pop(L, 1); /* style */
+ }
+
+ lua_pop(L, 1); /* _TOKENSTYLES */
+ lua_pop(L, 1); /* grammar */
+
+ lua_pop(L, 1); /* lexers */
+
+ return true;
}
const char *view_syntax_get(View *view) {
- return NULL;
+ return view->lexer_name;
}
void view_options_set(View *view, enum UiOption options) {
diff --git a/view.h b/view.h
index a9f7ec9..facccb1 100644
--- a/view.h
+++ b/view.h
@@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdbool.h>
+#include <lua.h>
#include "register.h"
#include "text.h"
#include "ui.h"
@@ -45,7 +46,7 @@ typedef struct {
size_t col;
} CursorPos;
-View *view_new(Text*, ViewEvent*);
+View *view_new(Text*, lua_State*, ViewEvent*);
void view_ui(View*, UiWin*);
/* change associated text displayed in this window */
void view_reload(View*, Text*);
diff --git a/vis-cmds.c b/vis-cmds.c
index 8b4e805..b407e1d 100644
--- a/vis-cmds.c
+++ b/vis-cmds.c
@@ -336,7 +336,7 @@ static const char *file_open_dialog(Vis *vis, const char *pattern) {
Text *txt_orig = file->text;
View *view_orig = win->view;
Text *txt = text_load(NULL);
- View *view = view_new(txt, NULL);
+ View *view = view_new(txt, NULL, NULL);
filename[0] = '\0';
snprintf(vis_open, sizeof(vis_open)-1, "vis-open %s", pattern ? pattern : "");
diff --git a/vis-core.h b/vis-core.h
index ceb1311..1333eab 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -156,6 +156,7 @@ struct Vis {
volatile sig_atomic_t sigbus; /* one of the memory mapped region became unavailable (SIGBUS) */
sigjmp_buf sigbus_jmpbuf; /* used to jump back to a known good state in the mainloop after (SIGBUS) */
Map *actions; /* registered editor actions / special keys commands */
+ lua_State *lua; /* lua context used for syntax highligthing */
};
/** stuff used by multiple of the vis-* files */
diff --git a/vis.c b/vis.c
index 377b6a9..9684fac 100644
--- a/vis.c
+++ b/vis.c
@@ -33,8 +33,12 @@
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
-
+#include <pwd.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <lua.h>
#include <termkey.h>
+
#include "vis.h"
#include "text-util.h"
#include "text-motions.h"
@@ -120,11 +124,25 @@ static File *file_new(Vis *vis, const char *filename) {
}
void vis_window_name(Win *win, const char *filename) {
+ lua_State *L = win->vis->lua;
File *file = win->file;
if (filename != file->name) {
free((char*)file->name);
file->name = filename ? strdup(filename) : NULL;
}
+
+ if (filename && L) {
+ lua_getglobal(L, "lexers");
+ lua_getfield(L, -1, "lexer_name");
+ lua_pushstring(L, filename);
+ lua_pcall(L, 1, 1, 0);
+ if (lua_isstring(L, -1)) {
+ const char *lexer_name = lua_tostring(L, -1);
+ if (lexer_name)
+ view_syntax_set(win->view, lexer_name);
+ }
+ lua_pop(L, 2); /* return value: lexer name, lexers variable */
+ }
}
static void windows_invalidate(Vis *vis, size_t start, size_t end) {
@@ -169,7 +187,7 @@ static Win *window_new_file(Vis *vis, File *file) {
.selection = window_selection_changed,
};
win->jumplist = ringbuf_alloc(31);
- win->view = view_new(file->text, &win->events);
+ win->view = view_new(file->text, vis->lua, &win->events);
win->ui = vis->ui->window_new(vis->ui, win->view, file);
if (!win->jumplist || !win->view || !win->ui) {
window_free(win);
@@ -287,6 +305,64 @@ Vis *vis_new(Ui *ui) {
Vis *vis = calloc(1, sizeof(Vis));
if (!vis)
return NULL;
+ lua_State *L = lua_open();
+ if (!(vis->lua = L))
+ goto err;
+ luaL_openlibs(L);
+
+ /* try to get users home directory */
+ const char *home = getenv("HOME");
+ if (!home || !*home) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw)
+ home = pw->pw_dir;
+ }
+
+ /* extends lua's package.path with:
+ * - $VIS_PATH/lexers
+ * - $HOME/.vis/lexers
+ * - /usr/local/share/vis/lexers
+ * - /usr/share/vis/lexers
+ * - package.path (standard lua search path)
+ */
+ int paths = 3;
+ lua_getglobal(L, "package");
+
+ const char *vis_path = getenv("VIS_PATH");
+ if (vis_path) {
+ lua_pushstring(L, vis_path);
+ lua_pushstring(L, "/lexers/?.lua;");
+ lua_concat(L, 2);
+ paths++;
+ }
+
+ if (home && *home) {
+ lua_pushstring(L, home);
+ lua_pushstring(L, "/.vis/lexers/?.lua;");
+ lua_concat(L, 2);
+ paths++;
+ }
+
+ lua_pushstring(L, "/usr/local/share/vis/lexers/?.lua;");
+ lua_pushstring(L, "/usr/share/vis/lexers/?.lua;");
+ lua_getfield(L, -paths, "path");
+ lua_concat(L, paths);
+ lua_setfield(L, -2, "path");
+ lua_pop(L, 1); /* package */
+
+ /* try to load the lexer module */
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "lexer");
+ if (lua_pcall(L, 1, 1, 0)) {
+ lua_close(L);
+ vis->lua = L = NULL;
+ } else {
+ lua_setglobal(L, "lexers");
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "themes/default");
+ lua_pcall(L, 1, 0, 0);
+ }
+
vis->ui = ui;
vis->ui->init(vis->ui, vis);
vis->tabwidth = 8;
@@ -302,7 +378,7 @@ Vis *vis_new(Ui *ui) {
goto err;
if (!(vis->prompt->file->text = text_load(NULL)))
goto err;
- if (!(vis->prompt->view = view_new(vis->prompt->file->text, NULL)))
+ if (!(vis->prompt->view = view_new(vis->prompt->file->text, NULL, NULL)))
goto err;
if (!(vis->prompt->ui = vis->ui->prompt_new(vis->ui, vis->prompt->view, vis->prompt->file)))
goto err;
@@ -318,6 +394,8 @@ err:
void vis_free(Vis *vis) {
if (!vis)
return;
+ if (vis->lua)
+ lua_close(vis->lua);
while (vis->windows)
vis_window_close(vis->windows);
file_free(vis, vis->prompt->file);