aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--README.md2
-rw-r--r--config.mk14
-rw-r--r--main.c13
-rw-r--r--ui-curses.c3
-rw-r--r--view.c331
-rw-r--r--view.h2
-rw-r--r--vis-cmds.c2
-rw-r--r--vis-core.h1
-rw-r--r--vis-lua.c491
-rw-r--r--vis-lua.h22
-rw-r--r--vis.c118
-rw-r--r--vis.h12
13 files changed, 746 insertions, 273 deletions
diff --git a/Makefile b/Makefile
index 16d62cb..5e71790 100644
--- a/Makefile
+++ b/Makefile
@@ -21,10 +21,10 @@ LIBTERMKEY_SHA1 = 0a78ba7aaa2f3b53f2273268366fef349c9be4ab
#LIBLUA = lua-5.3.1
#LIBLUA_SHA1 = 1676c6a041d90b6982db8cef1e5fb26000ab6dee
-#LIBLUA = lua-5.2.4
-#LIBLUA_SHA1 = ef15259421197e3d85b7d6e4871b8c26fd82c1cf
-LIBLUA = lua-5.1.5
-LIBLUA_SHA1 = b3882111ad02ecc6b972f8c1241647905cb2e3fc
+LIBLUA = lua-5.2.4
+LIBLUA_SHA1 = ef15259421197e3d85b7d6e4871b8c26fd82c1cf
+#LIBLUA = lua-5.1.5
+#LIBLUA_SHA1 = b3882111ad02ecc6b972f8c1241647905cb2e3fc
LIBLPEG = lpeg-1.0.0
LIBLPEG_SHA1 = 64a0920c9243b624a277c987d2219b6c50c43971
diff --git a/README.md b/README.md
index 58a0a6f..a349381 100644
--- a/README.md
+++ b/README.md
@@ -610,7 +610,7 @@ In order to build vis you will need a C99 compiler as well as:
* [libcurses](http://www.gnu.org/software/ncurses/), preferably in the
wide-character version
* [libtermkey](http://www.leonerd.org.uk/code/libtermkey/)
- * [lua](http://www.lua.org/) >= 5.1
+ * [lua](http://www.lua.org/) >= 5.2
* [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/) >= 0.12 (runtime
dependency required for syntax highlighting)
diff --git a/config.mk b/config.mk
index f11e8c1..0391284 100644
--- a/config.mk
+++ b/config.mk
@@ -1,6 +1,7 @@
# optional features
HAVE_ACL=0
HAVE_SELINUX=0
+CONFIG_LUA=1
# vis version
RELEASE = HEAD
@@ -26,17 +27,22 @@ PREFIX ?= /usr/local
MANPREFIX = ${PREFIX}/share/man
SHAREPREFIX = ${PREFIX}/share/vis
-CFLAGS_LUA = $(shell pkg-config --cflags lua5.1 2> /dev/null || echo "-I/usr/include/lua5.1")
-CFLAGS_TERMKEY = $(shell pkg-config --cflags termkey 2> /dev/null || echo "")
-CFLAGS_CURSES = $(shell pkg-config --cflags ncursesw 2> /dev/null || echo "-I/usr/include/ncursesw")
+ifeq (${CONFIG_LUA},1)
+ CFLAGS_LUA = $(shell pkg-config --cflags lua5.2 2> /dev/null || echo "-I/usr/include/lua5.2")
+ LDFLAGS_LUA = $(shell pkg-config --libs lua5.2 2> /dev/null || echo "-llua")
+endif
-LDFLAGS_LUA = $(shell pkg-config --libs lua5.1 2> /dev/null || echo "-llua")
+CFLAGS_TERMKEY = $(shell pkg-config --cflags termkey 2> /dev/null || echo "")
LDFLAGS_TERMKEY = $(shell pkg-config --libs termkey 2> /dev/null || echo "-ltermkey")
+
+CFLAGS_CURSES = $(shell pkg-config --cflags ncursesw 2> /dev/null || echo "-I/usr/include/ncursesw")
LDFLAGS_CURSES = $(shell pkg-config --libs ncursesw 2> /dev/null || echo "-lncursesw")
LIBS = -lm -ldl -lc
OS = $(shell uname)
+CFLAGS += -DCONFIG_LUA=${CONFIG_LUA}
+
ifeq (${OS},Linux)
ifeq (${HAVE_SELINUX},1)
CFLAGS += -DHAVE_SELINUX
diff --git a/main.c b/main.c
index 5bb04d4..b0f5b8d 100644
--- a/main.c
+++ b/main.c
@@ -6,6 +6,7 @@
#include "ui-curses.h"
#include "vis.h"
+#include "vis-lua.h"
#include "text-util.h"
#include "text-motions.h"
#include "text-objects.h"
@@ -1620,7 +1621,17 @@ static void signal_handler(int signum, siginfo_t *siginfo, void *context) {
int main(int argc, char *argv[]) {
- vis = vis_new(ui_curses_new());
+ VisEvent event = {
+ .vis_start = vis_lua_start,
+ .vis_quit = vis_lua_quit,
+ .file_open = vis_lua_file_open,
+ .file_save = vis_lua_file_save,
+ .file_close = vis_lua_file_close,
+ .win_open = vis_lua_win_open,
+ .win_close = vis_lua_win_close,
+ };
+
+ vis = vis_new(ui_curses_new(), &event);
if (!vis)
return EXIT_FAILURE;
diff --git a/ui-curses.c b/ui-curses.c
index 5535dbc..a9ae5f9 100644
--- a/ui-curses.c
+++ b/ui-curses.c
@@ -26,8 +26,9 @@
#include <poll.h>
#include <sys/ioctl.h>
-#include "ui.h"
#include "ui-curses.h"
+#include "vis.h"
+#include "text.h"
#include "util.h"
#include "text-util.h"
diff --git a/view.c b/view.c
index 4e3f869..76d7a45 100644
--- a/view.c
+++ b/view.c
@@ -19,7 +19,7 @@
#include <ctype.h>
#include <errno.h>
#include <regex.h>
-#include "vis.h"
+#include "vis-lua.h"
#include "view.h"
#include "text.h"
#include "text-motions.h"
@@ -115,6 +115,178 @@ static void view_cursors_free(Cursor *c);
/* set/move current cursor position to a given (line, column) pair */
static size_t cursor_set(Cursor *cursor, Line *line, int col);
+#if !CONFIG_LUA
+
+static void view_syntax_color(View *view) { }
+bool view_syntax_set(View *view, const char *name) { return false; }
+
+#else
+
+static void view_syntax_color(View *view) {
+ lua_State *L = view->lua;
+ if (!L || !view->lexer_name)
+ return;
+ lua_getglobal(L, "vis");
+ lua_getfield(L, -1, "lexers");
+ if (lua_isnil(L, -1))
+ return;
+
+ /* maximal number of bytes to consider for syntax highlighting before
+ * the visible area */
+ const size_t lexer_before_max = 16384;
+ /* absolute position to start syntax highlighting */
+ const size_t lexer_start = view->start >= lexer_before_max ? view->start - lexer_before_max : 0;
+ /* number of bytes used for syntax highlighting before visible are */
+ const size_t lexer_before = view->start - lexer_start;
+ /* number of bytes to read in one go */
+ const size_t text_size = lexer_before + (view->end - view->start);
+ /* current buffer to work with */
+ char text[text_size+1];
+ /* bytes to process */
+ const size_t text_len = text_bytes_get(view->text, lexer_start, text_size, text);
+ /* NUL terminate text section */
+ text[text_len] = '\0';
+
+ 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 */
+ /* TODO: start at known state, handle nested lexers */
+ 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 global */
+}
+
+bool view_syntax_set(View *view, const char *name) {
+ 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 = vis.lexers.load(name)
+ * for token_name, id in pairs(lang._TOKENSTYLES) do
+ * ui->syntax_style(id, vis.lexers:get_style(lang, token_name);
+ */
+ lua_getglobal(L, "vis");
+ lua_getfield(L, -1, "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_CURSOR_LINE");
+ view->ui->syntax_style(view->ui, UI_STYLE_CURSOR_LINE, 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, "STYLE_COLOR_COLUMN");
+ view->ui->syntax_style(view->ui, UI_STYLE_COLOR_COLUMN, 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, 4); /* _TOKENSTYLES, grammar, lexers, vis */
+
+ return true;
+}
+
+#endif
+
+
void view_tabwidth_set(View *view, int tabwidth) {
view->tabwidth = tabwidth;
view_draw(view);
@@ -456,86 +628,8 @@ void view_draw(View *view) {
void view_update(View *view) {
if (!view->need_update)
return;
- /* maximal number of bytes to consider for syntax highlighting before
- * the visible area */
- const size_t lexer_before_max = 16384;
- /* absolute position to start syntax highlighting */
- const size_t lexer_start = view->start >= lexer_before_max ? view->start - lexer_before_max : 0;
- /* number of bytes used for syntax highlighting before visible are */
- const size_t lexer_before = view->start - lexer_start;
- /* number of bytes to read in one go */
- const size_t text_size = lexer_before + (view->end - view->start);
- /* current buffer to work with */
- char text[text_size+1];
- /* bytes to process */
- const size_t text_len = text_bytes_get(view->text, lexer_start, text_size, text);
- /* NUL terminate text section */
- text[text_len] = '\0';
-
- lua_State *L = view->lua;
- if (L && view->lexer_name) {
-
- lua_getglobal(L, "vis");
- lua_getfield(L, -1, "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 */
- /* TODO: start at known state, handle nested lexers */
- 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 global */
- }
+ view_syntax_color(view);
if (view->colorcolumn > 0 && view->colorcolumn <= view->width) {
size_t lineno = 0;
@@ -906,83 +1000,6 @@ void view_scroll_to(View *view, size_t pos) {
view_cursors_scroll_to(view->cursor, pos);
}
-bool view_syntax_set(View *view, const char *name) {
- 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 = vis.lexers.load(name)
- * for token_name, id in pairs(lang._TOKENSTYLES) do
- * ui->syntax_style(id, vis.lexers:get_style(lang, token_name);
- */
- lua_getglobal(L, "vis");
- lua_getfield(L, -1, "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_CURSOR_LINE");
- view->ui->syntax_style(view->ui, UI_STYLE_CURSOR_LINE, 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, "STYLE_COLOR_COLUMN");
- view->ui->syntax_style(view->ui, UI_STYLE_COLOR_COLUMN, 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, 4); /* _TOKENSTYLES, grammar, lexers, vis */
-
- return true;
-}
-
const char *view_syntax_get(View *view) {
return view->lexer_name;
}
diff --git a/view.h b/view.h
index 25b186f..ea8bca6 100644
--- a/view.h
+++ b/view.h
@@ -3,7 +3,6 @@
#include <stddef.h>
#include <stdbool.h>
-#include <lua.h>
#include "register.h"
#include "text.h"
#include "ui.h"
@@ -11,6 +10,7 @@
typedef struct View View;
typedef struct Cursor Cursor;
typedef struct Selection Selection;
+typedef struct lua_State lua_State;
typedef struct {
int width; /* display width i.e. number of columns ocupied by this character */
diff --git a/vis-cmds.c b/vis-cmds.c
index 477e29c..3a89d2a 100644
--- a/vis-cmds.c
+++ b/vis-cmds.c
@@ -595,6 +595,8 @@ static bool cmd_write(Vis *vis, Filerange *range, enum CmdOpt opt, const char *a
}
if (strcmp(file->name, *name) == 0)
file->stat = text_stat(text);
+ if (vis->event && vis->event->file_save)
+ vis->event->file_save(vis, file);
}
return true;
}
diff --git a/vis-core.h b/vis-core.h
index 22bec33..6554410 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -156,6 +156,7 @@ struct Vis {
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 */
+ VisEvent *event;
};
/** stuff used by multiple of the vis-* files */
diff --git a/vis-lua.c b/vis-lua.c
new file mode 100644
index 0000000..d597772
--- /dev/null
+++ b/vis-lua.c
@@ -0,0 +1,491 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "vis-lua.h"
+#include "vis-core.h"
+#include "text-motions.h"
+
+#if !CONFIG_LUA
+
+void vis_lua_start(Vis *vis) { }
+void vis_lua_quit(Vis *vis) { }
+void vis_lua_file_open(Vis *vis, File *file) { }
+void vis_lua_file_save(Vis *vis, File *file) { }
+void vis_lua_file_close(Vis *vis, File *file) { }
+void vis_lua_win_open(Vis *vis, Win *win) { }
+void vis_lua_win_close(Vis *vis, Win *win) { }
+bool vis_theme_load(Vis *vis, const char *name) { return true; }
+
+#else
+
+#if 0
+static void stack_dump_entry(lua_State *L, int i) {
+ int t = lua_type(L, i);
+ switch (t) {
+ case LUA_TNIL:
+ printf("nil");
+ break;
+ case LUA_TBOOLEAN:
+ printf(lua_toboolean(L, i) ? "true" : "false");
+ break;
+ case LUA_TLIGHTUSERDATA:
+ printf("lightuserdata(%p)", (void*)lua_touserdata(L, i));
+ break;
+ case LUA_TNUMBER:
+ printf("%g", lua_tonumber(L, i));
+ break;
+ case LUA_TSTRING:
+ printf("`%s'", lua_tostring(L, i));
+ break;
+ case LUA_TTABLE:
+ printf("table[");
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, i > 0 ? i : i - 1)) {
+ stack_dump_entry(L, -2);
+ printf("=");
+ stack_dump_entry(L, -1);
+ printf(",");
+ lua_pop(L, 1); /* remove value, keep key */
+ }
+ printf("]");
+ break;
+ case LUA_TUSERDATA:
+ printf("userdata(%p)", (void*)lua_touserdata(L, i));
+ break;
+ default: /* other values */
+ printf("%s", lua_typename(L, t));
+ break;
+ }
+}
+
+static void stack_dump(lua_State *L, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ int top = lua_gettop(L);
+ for (int i = 1; i <= top; i++) {
+ printf("%d: ", i);
+ stack_dump_entry(L, i);
+ printf("\n");
+ }
+ printf("\n\n");
+}
+
+#endif
+
+/* returns registry["vis.objects"][addr] if it is of correct type */
+static void *obj_get(lua_State *L, void *addr, const char *type) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
+ lua_pushlightuserdata(L, addr);
+ lua_gettable(L, -2);
+ lua_remove(L, -2);
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ return NULL;
+ }
+ return luaL_checkudata(L, -1, type);
+}
+
+/* expects a userdatum at the top of the stack and sets
+ *
+ * registry["vis.objects"][addr] = userdata
+ */
+static void obj_set(lua_State *L, void *addr) {
+ lua_getfield(L, LUA_REGISTRYINDEX, "vis.objects");
+ lua_pushlightuserdata(L, addr);
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ lua_pop(L, 1);
+}
+
+static void obj_del(lua_State *L, void *addr) {
+ lua_pushnil(L);
+ obj_set(L, addr);
+}
+
+static void *obj_new(lua_State *L, void *addr, const char *type) {
+ if (!addr)
+ return NULL;
+ void **handle = (void**)obj_get(L, addr, type);
+ if (!handle) {
+ handle = lua_newuserdata(L, sizeof(addr));
+ obj_set(L, addr);
+ *handle = addr;
+ luaL_getmetatable(L, type);
+ lua_setmetatable(L, -2);
+ lua_newtable(L);
+ lua_setuservalue(L, -2);
+ }
+ return *handle;
+}
+
+static void *obj_arg_get(lua_State *L, int idx, const char *type) {
+ void **addr = luaL_checkudata(L, idx, type);
+ return obj_get(L, *addr, type);
+}
+
+static void *obj_check(lua_State *L, int idx, const char *type) {
+ void **addr = luaL_checkudata(L, idx, type);
+ if (!obj_get(L, *addr, type))
+ return NULL;
+ lua_pop(L, 1);
+ return *addr;
+}
+
+static int index_common(lua_State *L) {
+ lua_getmetatable(L, 1);
+ lua_pushvalue(L, 2);
+ lua_gettable(L, -2);
+ if (lua_isnil(L, -1)) {
+ lua_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_gettable(L, -2);
+ }
+ return 1;
+}
+
+static int newindex_common(lua_State *L) {
+ lua_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, 3);
+ lua_settable(L, -3);
+ return 0;
+}
+
+static int windows_iter(lua_State *L);
+
+static int windows(lua_State *L) {
+ Vis *vis = (Vis*)lua_touserdata(L, lua_upvalueindex(1));
+ Win **handle = lua_newuserdata(L, sizeof *handle);
+ *handle = vis->windows;
+ lua_pushcclosure(L, windows_iter, 1);
+ return 1;
+}
+
+static int windows_iter(lua_State *L) {
+ Win **handle = (Win**)lua_touserdata(L, lua_upvalueindex(1));
+ if (!*handle)
+ return 0;
+ Win *win = obj_new(L, *handle, "vis.window");
+ if (!win)
+ return 0;
+ *handle = win->next;
+ return 1;
+}
+
+static int files_iter(lua_State *L);
+
+static int files(lua_State *L) {
+ Vis *vis = (Vis*)lua_touserdata(L, lua_upvalueindex(1));
+ File **handle = lua_newuserdata(L, sizeof *handle);
+ *handle = vis->files;
+ lua_pushcclosure(L, files_iter, 1);
+ return 1;
+}
+
+static int files_iter(lua_State *L) {
+ File **handle = (File**)lua_touserdata(L, lua_upvalueindex(1));
+ if (!*handle)
+ return 0;
+ File *file = obj_new(L, *handle, "vis.file");
+ if (!file)
+ return 0;
+ *handle = file->next;
+ return 1;
+}
+
+static int command(lua_State *L) {
+ Vis *vis = (Vis*)lua_touserdata(L, lua_upvalueindex(1));
+ const char *cmd = luaL_checkstring(L, 1);
+ bool ret = vis_cmd(vis, cmd);
+ lua_pushboolean(L, ret);
+ return 1;
+}
+
+static int info(lua_State *L) {
+ Vis *vis = (Vis*)lua_touserdata(L, lua_upvalueindex(1));
+ const char *msg = luaL_checkstring(L, 1);
+ vis_info_show(vis, "%s", msg);
+ return 0;
+}
+
+static const struct luaL_Reg vis_lua[] = {
+ { "files", files },
+ { "windows", windows },
+ { "command", command },
+ { "info", info },
+ { NULL, NULL },
+};
+
+static int window_index(lua_State *L) {
+ Win *win = obj_check(L, 1, "vis.window");
+ if (!win) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ if (lua_isstring(L, 2)) {
+ const char *key = lua_tostring(L, 2);
+ if (strcmp(key, "file") == 0) {
+ obj_new(L, win->file, "vis.file");
+ return 1;
+ }
+ }
+
+ return index_common(L);
+}
+
+static int window_newindex(lua_State *L) {
+ Win *win = obj_check(L, 1, "vis.window");
+ if (!win)
+ return 0;
+ return newindex_common(L);
+}
+
+static const struct luaL_Reg window_funcs[] = {
+ { "__index", window_index },
+ { "__newindex", window_newindex },
+ { NULL, NULL },
+};
+
+static int file_index(lua_State *L) {
+ File *file = obj_check(L, 1, "vis.file");
+ if (!file) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ if (lua_isstring(L, 2)) {
+ const char *key = lua_tostring(L, 2);
+ if (strcmp(key, "name") == 0) {
+ lua_pushstring(L, file->name);
+ return 1;
+ }
+ }
+
+ return index_common(L);
+}
+
+static int file_newindex(lua_State *L) {
+ File *file = obj_check(L, 1, "vis.file");
+ if (!file)
+ return 0;
+ return newindex_common(L);
+}
+
+static int file_insert(lua_State *L) {
+ File *file = obj_check(L, 1, "vis.file");
+ if (file) {
+ size_t pos = luaL_checkunsigned(L, 2);
+ size_t len;
+ luaL_checkstring(L, 3);
+ const char *data = lua_tolstring(L, 3, &len);
+ bool ret = text_insert(file->text, pos, data, len);
+ lua_pushboolean(L, ret);
+ } else {
+ lua_pushboolean(L, false);
+ }
+ return 1;
+}
+
+static int file_delete(lua_State *L) {
+ File *file = obj_check(L, 1, "vis.file");
+ if (file) {
+ size_t pos = luaL_checkunsigned(L, 2);
+ size_t len = luaL_checkunsigned(L, 3);
+ bool ret = text_delete(file->text, pos, len);
+ lua_pushboolean(L, ret);
+ } else {
+ lua_pushboolean(L, false);
+ }
+ return 1;
+}
+
+static int file_lines_iter(lua_State *L);
+
+static int file_lines(lua_State *L) {
+ obj_arg_get(L, 1, "vis.file");
+ size_t *pos = lua_newuserdata(L, sizeof *pos);
+ *pos = 0;
+ lua_pushcclosure(L, file_lines_iter, 2);
+ return 1;
+}
+
+static int file_lines_iter(lua_State *L) {
+ File *file = *(File**)lua_touserdata(L, lua_upvalueindex(1));
+ size_t *pos = lua_touserdata(L, lua_upvalueindex(2));
+ size_t new_pos = text_line_next(file->text, *pos);
+ size_t len = new_pos - *pos;
+ if (len == 0)
+ return 0;
+ char *buf = malloc(len);
+ if (!buf)
+ return 0;
+ len = text_bytes_get(file->text, *pos, len, buf);
+ lua_pushlstring(L, buf, len);
+ free(buf);
+ *pos = new_pos;
+ return 1;
+}
+
+static const struct luaL_Reg file_funcs[] = {
+ { "__index", file_index },
+ { "__newindex", file_newindex },
+ { "insert", file_insert },
+ { "delete", file_delete },
+ { "lines", file_lines },
+ { NULL, NULL },
+};
+
+
+static void vis_lua_event(Vis *vis, const char *name) {
+ lua_State *L = vis->lua;
+ lua_getglobal(L, "vis");
+ lua_getfield(L, -1, "events");
+ if (lua_istable(L, -1)) {
+ lua_getfield(L, -1, name);
+ }
+ lua_remove(L, -2);
+}
+
+void vis_lua_start(Vis *vis) {
+ lua_State *L = luaL_newstate();
+ if (!L)
+ return;
+ vis->lua = L;
+ 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 */
+
+ /* table in registry to track lifetimes of C objects */
+ lua_newtable(L);
+ lua_setfield(L, LUA_REGISTRYINDEX, "vis.objects");
+ /* metatable used to type check user data */
+ luaL_newmetatable(L, "vis.file");
+ luaL_setfuncs(L, file_funcs, 0);
+ luaL_newmetatable(L, "vis.window");
+ luaL_setfuncs(L, window_funcs, 0);
+ /* vis module table with up value as the C pointer */
+ luaL_newlibtable(L, vis_lua);
+ lua_pushlightuserdata(L, vis);
+ luaL_setfuncs(L, vis_lua, 1);
+ lua_setglobal(L, "vis");
+
+ lua_getglobal(L, "require");
+ lua_pushstring(L, "visrc");
+ lua_pcall(L, 1, 0, 0);
+}
+
+void vis_lua_quit(Vis *vis) {
+ lua_State *L = vis->lua;
+ if (L)
+ lua_close(L);
+}
+
+void vis_lua_file_open(Vis *vis, File *file) {
+
+}
+
+void vis_lua_file_save(Vis *vis, File *file) {
+
+}
+
+void vis_lua_file_close(Vis *vis, File *file) {
+ lua_State *L = vis->lua;
+ vis_lua_event(vis, "file_close");
+ if (lua_isfunction(L, -1)) {
+ obj_new(L, file, "vis.file");
+ lua_pcall(L, 1, 0, 0);
+ }
+ obj_del(L, file);
+ lua_pop(L, 1);
+}
+
+void vis_lua_win_open(Vis *vis, Win *win) {
+ lua_State *L = vis->lua;
+ vis_lua_event(vis, "win_open");
+ if (lua_isfunction(L, -1)) {
+ obj_new(L, win, "vis.window");
+ lua_pcall(L, 1, 0, 0);
+ }
+ lua_pop(L, 1);
+}
+
+void vis_lua_win_close(Vis *vis, Win *win) {
+ lua_State *L = vis->lua;
+ vis_lua_event(vis, "win_close");
+ if (lua_isfunction(L, -1)) {
+ obj_new(L, win, "vis.window");
+ lua_pcall(L, 1, 0, 0);
+ }
+ obj_del(L, win);
+ lua_pop(L, 1);
+}
+
+bool vis_theme_load(Vis *vis, const char *name) {
+ lua_State *L = vis->lua;
+ if (!L)
+ return false;
+ /* package.loaded['themes/'..name] = nil
+ * require 'themes/'..name */
+ lua_pushstring(L, "themes/");
+ lua_pushstring(L, name);
+ lua_concat(L, 2);
+ lua_getglobal(L, "package");
+ lua_getfield(L, -1, "loaded");
+ lua_pushvalue(L, -3);
+ lua_pushnil(L);
+ lua_settable(L, -3);
+ lua_pop(L, 2);
+ lua_getglobal(L, "require");
+ lua_pushvalue(L, -2);
+ if (lua_pcall(L, 1, 0, 0))
+ return false;
+ for (Win *win = vis->windows; win; win = win->next)
+ view_syntax_set(win->view, view_syntax_get(win->view));
+ return true;
+}
+
+#endif
diff --git a/vis-lua.h b/vis-lua.h
new file mode 100644
index 0000000..b0f1293
--- /dev/null
+++ b/vis-lua.h
@@ -0,0 +1,22 @@
+#ifndef VIS_LUA_H
+#define VIS_LUA_H
+
+#if CONFIG_LUA
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#else
+typedef struct lua_State lua_State;
+#endif
+
+#include "vis.h"
+
+void vis_lua_start(Vis*);
+void vis_lua_quit(Vis*);
+void vis_lua_file_open(Vis*, File*);
+void vis_lua_file_save(Vis*, File*);
+void vis_lua_file_close(Vis*, File*);
+void vis_lua_win_open(Vis*, Win*);
+void vis_lua_win_close(Vis*, Win*);
+
+#endif \ No newline at end of file
diff --git a/vis.c b/vis.c
index 3825500..f23c42e 100644
--- a/vis.c
+++ b/vis.c
@@ -34,9 +34,6 @@
#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"
@@ -67,7 +64,8 @@ static void file_free(Vis *vis, File *file) {
return;
if (--file->refcount > 0)
return;
-
+ if (vis->event && vis->event->file_close)
+ vis->event->file_close(vis, file);
text_free(file->text);
free((char*)file->name);
@@ -120,30 +118,17 @@ static File *file_new(Vis *vis, const char *filename) {
if (filename)
file->name = strdup(filename);
+ if (vis->event && vis->event->file_open)
+ vis->event->file_open(vis, file);
return file;
}
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, "vis");
- lua_getfield(L, -1, "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) {
@@ -189,6 +174,8 @@ static Win *window_new_file(Vis *vis, File *file) {
vis->windows = win;
vis->win = win;
vis->ui->window_focus(win->ui);
+ if (vis->event && vis->event->win_open)
+ vis->event->win_open(vis, win);
return win;
}
@@ -278,6 +265,8 @@ bool vis_window_new(Vis *vis, const char *filename) {
void vis_window_close(Win *win) {
Vis *vis = win->vis;
+ if (vis->event && vis->event->win_close)
+ vis->event->win_close(vis, win);
file_free(vis, win->file);
if (win->prev)
win->prev->next = win->next;
@@ -295,70 +284,14 @@ void vis_window_close(Win *win) {
vis_draw(vis);
}
-Vis *vis_new(Ui *ui) {
+Vis *vis_new(Ui *ui, VisEvent *event) {
if (!ui)
return NULL;
Vis *vis = calloc(1, sizeof(Vis));
if (!vis)
return NULL;
- lua_State *L = luaL_newstate();
- 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_newtable(L); /* vis */
- lua_pushvalue(L, -2); /* require return value */
- lua_setfield(L, -2, "lexers");
- lua_setglobal(L, "vis");
- }
-
+ if (event && event->vis_start)
+ event->vis_start(vis);
vis->ui = ui;
vis->ui->init(vis->ui, vis);
vis->tabwidth = 8;
@@ -381,6 +314,7 @@ Vis *vis_new(Ui *ui) {
if (!(vis->search_pattern = text_regex_new()))
goto err;
vis->mode_prev = vis->mode = &vis_modes[VIS_MODE_NORMAL];
+ vis->event = event;
return vis;
err:
vis_free(vis);
@@ -390,8 +324,9 @@ err:
void vis_free(Vis *vis) {
if (!vis)
return;
- if (vis->lua)
- lua_close(vis->lua);
+ if (vis->event && vis->event->vis_quit)
+ vis->event->vis_quit(vis);
+ vis->event = NULL;
while (vis->windows)
vis_window_close(vis->windows);
file_free(vis, vis->prompt->file);
@@ -1352,26 +1287,3 @@ const char *vis_file_name(File *file) {
return file->name;
}
-bool vis_theme_load(Vis *vis, const char *name) {
- lua_State *L = vis->lua;
- if (!L)
- return false;
- /* package.loaded['themes/'..name] = nil
- * require 'themes/'..name */
- lua_pushstring(L, "themes/");
- lua_pushstring(L, name);
- lua_concat(L, 2);
- lua_getglobal(L, "package");
- lua_getfield(L, -1, "loaded");
- lua_pushvalue(L, -3);
- lua_pushnil(L);
- lua_settable(L, -3);
- lua_pop(L, 2);
- lua_getglobal(L, "require");
- lua_pushvalue(L, -2);
- if (lua_pcall(L, 1, 0, 0))
- return false;
- for (Win *win = vis->windows; win; win = win->next)
- view_syntax_set(win->view, view_syntax_get(win->view));
- return true;
-}
diff --git a/vis.h b/vis.h
index b5c48ea..e007710 100644
--- a/vis.h
+++ b/vis.h
@@ -13,6 +13,16 @@ typedef struct Win Win;
#include "view.h"
#include "register.h"
+typedef struct {
+ void (*vis_start)(Vis*);
+ void (*vis_quit)(Vis*);
+ void (*file_open)(Vis*, File*);
+ void (*file_save)(Vis*, File*);
+ void (*file_close)(Vis*, File*);
+ void (*win_open)(Vis*, Win*);
+ void (*win_close)(Vis*, Win*);
+} VisEvent;
+
typedef union { /* various types of arguments passed to key action functions */
bool b;
int i;
@@ -40,7 +50,7 @@ typedef struct { /* a key binding either refers to an action or an ali
} KeyBinding;
/* creates a new editor instance using the specified user interface */
-Vis *vis_new(Ui*);
+Vis *vis_new(Ui*, VisEvent*);
/* frees all resources associated with this editor instance, terminates ui */
void vis_free(Vis*);
/* instructs the user interface to draw to an internal buffer */