diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2015-10-12 17:09:41 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2015-11-08 13:37:23 +0100 |
| commit | c339bb3ae6646c3dbef8e73f41c1e797ed5ec99b (patch) | |
| tree | b27016384ad5cad67377a318f8f130fb40c3aab5 | |
| parent | 5f5699fb3a93f2a478f3888236f7c25bc7975fbb (diff) | |
| download | vis-c339bb3ae6646c3dbef8e73f41c1e797ed5ec99b.tar.gz vis-c339bb3ae6646c3dbef8e73f41c1e797ed5ec99b.tar.xz | |
vis: experimental support for lua/lpeg based syntax highlighting
The lua based lexers are searched in the following order:
$VIS_PATH/lexers
$HOME/.vis/lexers
/usr/share/vis/lexers
followed by the standard lua package.path
| -rw-r--r-- | lexers/diff.lua | 6 | ||||
| -rw-r--r-- | lexers/lexer.lua | 128 | ||||
| l--------- | lexers/themes/default.lua | 1 | ||||
| -rw-r--r-- | lexers/themes/solarized.lua | 54 | ||||
| -rw-r--r-- | ui-curses.c | 78 | ||||
| -rw-r--r-- | ui.h | 9 | ||||
| -rw-r--r-- | view.c | 181 | ||||
| -rw-r--r-- | view.h | 3 | ||||
| -rw-r--r-- | vis-cmds.c | 2 | ||||
| -rw-r--r-- | vis-core.h | 1 | ||||
| -rw-r--r-- | vis.c | 84 |
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); } - @@ -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" @@ -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) { @@ -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*); @@ -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 : ""); @@ -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 */ @@ -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); |
