luahl: global the luahl

This commit is contained in:
Björn Linse
2020-09-12 10:29:30 +02:00
parent 05c68922d3
commit 4042975df4
9 changed files with 144 additions and 104 deletions

View File

@@ -4,6 +4,8 @@ local a = vim.api
local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {} local TSHighlighter = rawget(vim.treesitter, 'TSHighlighter') or {}
TSHighlighter.__index = TSHighlighter TSHighlighter.__index = TSHighlighter
TSHighlighter.active = TSHighlighter.active or {}
-- These are conventions defined by tree-sitter, though it -- These are conventions defined by tree-sitter, though it
-- needs to be user extensible also. -- needs to be user extensible also.
TSHighlighter.hl_map = { TSHighlighter.hl_map = {
@@ -53,13 +55,16 @@ TSHighlighter.hl_map = {
} }
function TSHighlighter.new(query, bufnr, ft) function TSHighlighter.new(query, bufnr, ft)
if bufnr == nil or bufnr == 0 then
bufnr = a.nvim_get_current_buf()
end
local self = setmetatable({}, TSHighlighter) local self = setmetatable({}, TSHighlighter)
self.parser = vim.treesitter.get_parser( self.parser = vim.treesitter.get_parser(
bufnr, bufnr,
ft, ft,
{ {
on_changedtree = function(...) self:on_changedtree(...) end, on_changedtree = function(...) self:on_changedtree(...) end,
on_bytes = function() self.parser:parse() end
} }
) )
@@ -71,11 +76,8 @@ function TSHighlighter.new(query, bufnr, ft)
self.root = self.parser:parse():root() self.root = self.parser:parse():root()
a.nvim_buf_set_option(self.buf, "syntax", "") a.nvim_buf_set_option(self.buf, "syntax", "")
a.nvim__buf_set_luahl(self.buf, { -- TODO(bfredl): can has multiple highlighters per buffer????
on_start=function(...) return self:on_start(...) end, TSHighlighter.active[bufnr] = self
on_window=function(...) return self:on_window(...) end,
on_line=function(...) return self:on_line(...) end,
})
-- Tricky: if syntax hasn't been enabled, we need to reload color scheme -- Tricky: if syntax hasn't been enabled, we need to reload color scheme
-- but use synload.vim rather than syntax.vim to not enable -- but use synload.vim rather than syntax.vim to not enable
@@ -138,20 +140,13 @@ function TSHighlighter:set_query(query)
a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf)) a.nvim__buf_redraw_range(self.buf, 0, a.nvim_buf_line_count(self.buf))
end end
function TSHighlighter:on_window(_, _win, _buf, _topline, botline) function TSHighlighter._on_line(_, _win, buf, line)
self.iter = nil -- on_line is only called when this is non-nil
self.nextrow = 0 local self = TSHighlighter.active[buf]
self.botline = botline if self.root == nil then
self.redraw_count = self.redraw_count + 1 return -- parser bought the farm already
end end
function TSHighlighter:on_start(_, _buf, _tick)
local tree = self.parser:parse()
self.root = tree:root()
end
function TSHighlighter:on_line(_, _win, buf, line)
if self.iter == nil then if self.iter == nil then
self.iter = self.query:iter_captures(self.root,buf,line,self.botline) self.iter = self.query:iter_captures(self.root,buf,line,self.botline)
end end
@@ -171,4 +166,31 @@ function TSHighlighter:on_line(_, _win, buf, line)
end end
end end
function TSHighlighter._on_start(_, buf, _tick)
local self = TSHighlighter.active[buf]
if self then
local tree = self.parser:parse()
self.root = (tree and tree:root()) or nil
end
end
function TSHighlighter._on_win(_, _win, buf, _topline, botline)
local self = TSHighlighter.active[buf]
if not self then
return false
end
self.iter = nil
self.nextrow = 0
self.botline = botline
self.redraw_count = self.redraw_count + 1
return true
end
a.nvim__set_luahl {
on_start = TSHighlighter._on_start;
on_win = TSHighlighter._on_win;
on_line = TSHighlighter._on_line;
}
return TSHighlighter return TSHighlighter

View File

@@ -244,78 +244,6 @@ Boolean nvim_buf_detach(uint64_t channel_id,
return true; return true;
} }
static void buf_clear_luahl(buf_T *buf, bool force)
{
if (buf->b_luahl || force) {
api_free_luaref(buf->b_luahl_start);
api_free_luaref(buf->b_luahl_window);
api_free_luaref(buf->b_luahl_line);
api_free_luaref(buf->b_luahl_end);
}
buf->b_luahl_start = LUA_NOREF;
buf->b_luahl_window = LUA_NOREF;
buf->b_luahl_line = LUA_NOREF;
buf->b_luahl_end = LUA_NOREF;
}
/// Unstabilized interface for defining syntax hl in lua.
///
/// This is not yet safe for general use, lua callbacks will need to
/// be restricted, like textlock and probably other stuff.
///
/// The API on_line/nvim__put_attr is quite raw and not intended to be the
/// final shape. Ideally this should operate on chunks larger than a single
/// line to reduce interpreter overhead, and generate annotation objects
/// (bufhl/virttext) on the fly but using the same representation.
void nvim__buf_set_luahl(uint64_t channel_id, Buffer buffer,
DictionaryOf(LuaRef) opts, Error *err)
FUNC_API_LUA_ONLY
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return;
}
redraw_buf_later(buf, NOT_VALID);
buf_clear_luahl(buf, false);
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
if (strequal("on_start", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
buf->b_luahl_start = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (strequal("on_window", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
buf->b_luahl_window = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (strequal("on_line", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
buf->b_luahl_line = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
goto error;
}
}
buf->b_luahl = true;
return;
error:
buf_clear_luahl(buf, true);
buf->b_luahl = false;
}
void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last,
Error *err) Error *err)
FUNC_API_LUA_ONLY FUNC_API_LUA_ONLY

View File

@@ -1618,3 +1618,15 @@ free_exit:
clear_virttext(&virt_text); clear_virttext(&virt_text);
return virt_text; return virt_text;
} }
bool api_is_truthy(Object obj, const char *what, Error *err)
{
if (obj.type == kObjectTypeBoolean) {
return obj.data.boolean;
} else if (obj.type == kObjectTypeInteger) {
return obj.data.integer; // C semantics: non-zery int is true
} else {
api_set_error(err, kErrorTypeValidation, "%s is not an boolean", what);
return false;
}
}

View File

@@ -2702,3 +2702,68 @@ void nvim__screenshot(String path)
{ {
ui_call_screenshot(path); ui_call_screenshot(path);
} }
static void clear_luahl(bool force)
{
if (luahl_active || force) {
api_free_luaref(luahl_start);
api_free_luaref(luahl_win);
api_free_luaref(luahl_line);
api_free_luaref(luahl_end);
}
luahl_start = LUA_NOREF;
luahl_win = LUA_NOREF;
luahl_line = LUA_NOREF;
luahl_end = LUA_NOREF;
luahl_active = false;
}
/// Unstabilized interface for defining syntax hl in lua.
///
/// This is not yet safe for general use, lua callbacks will need to
/// be restricted, like textlock and probably other stuff.
///
/// The API on_line/nvim__put_attr is quite raw and not intended to be the
/// final shape. Ideally this should operate on chunks larger than a single
/// line to reduce interpreter overhead, and generate annotation objects
/// (bufhl/virttext) on the fly but using the same representation.
void nvim__set_luahl(DictionaryOf(LuaRef) opts, Error *err)
FUNC_API_LUA_ONLY
{
redraw_later(NOT_VALID);
clear_luahl(false);
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
if (strequal("on_start", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
luahl_start = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (strequal("on_win", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
luahl_win = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else if (strequal("on_line", k.data)) {
if (v->type != kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "callback is not a function");
goto error;
}
luahl_line = v->data.luaref;
v->data.luaref = LUA_NOREF;
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
goto error;
}
}
luahl_active = true;
return;
error:
clear_luahl(true);
}

View File

@@ -842,12 +842,6 @@ struct file_buffer {
// The number for times the current line has been flushed in the memline. // The number for times the current line has been flushed in the memline.
int flush_count; int flush_count;
bool b_luahl;
LuaRef b_luahl_start;
LuaRef b_luahl_window;
LuaRef b_luahl_line;
LuaRef b_luahl_end;
int b_diff_failed; // internal diff failed for this buffer int b_diff_failed; // internal diff failed for this buffer
}; };

View File

@@ -845,7 +845,7 @@ bool decorations_redraw_reset(buf_T *buf, DecorationRedrawState *state)
{ {
state->row = -1; state->row = -1;
kv_size(state->active) = 0; kv_size(state->active) = 0;
return buf->b_extmark_index || buf->b_luahl; return buf->b_extmark_index;
} }

View File

@@ -405,6 +405,12 @@ EXTERN int sys_menu INIT(= false);
// ('lines' and 'rows') must not be changed. // ('lines' and 'rows') must not be changed.
EXTERN int updating_screen INIT(= 0); EXTERN int updating_screen INIT(= 0);
EXTERN bool luahl_active INIT(= false);
EXTERN LuaRef luahl_start INIT(= LUA_NOREF);
EXTERN LuaRef luahl_win INIT(= LUA_NOREF);
EXTERN LuaRef luahl_line INIT(= LUA_NOREF);
EXTERN LuaRef luahl_end INIT(= LUA_NOREF);
// All windows are linked in a list. firstwin points to the first entry, // All windows are linked in a list. firstwin points to the first entry,
// lastwin to the last entry (can be the same as firstwin) and curwin to the // lastwin to the last entry (can be the same as firstwin) and curwin to the
// currently active window. // currently active window.

View File

@@ -7,6 +7,8 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <lua.h>
#include <lauxlib.h>
#include <msgpack.h> #include <msgpack.h>
#include "nvim/ascii.h" #include "nvim/ascii.h"

View File

@@ -158,6 +158,8 @@ static bool msg_grid_invalid = false;
static bool resizing = false; static bool resizing = false;
static bool do_luahl_line = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h" # include "screen.c.generated.h"
#endif #endif
@@ -508,12 +510,12 @@ int update_screen(int type)
} }
buf_T *buf = wp->w_buffer; buf_T *buf = wp->w_buffer;
if (buf->b_luahl && buf->b_luahl_window != LUA_NOREF) { if (luahl_active && luahl_start != LUA_NOREF) {
Error err = ERROR_INIT; Error err = ERROR_INIT;
FIXED_TEMP_ARRAY(args, 2); FIXED_TEMP_ARRAY(args, 2);
args.items[0] = BUFFER_OBJ(buf->handle); args.items[0] = BUFFER_OBJ(buf->handle);
args.items[1] = INTEGER_OBJ(display_tick); args.items[1] = INTEGER_OBJ(display_tick);
nlua_call_ref(buf->b_luahl_start, "start", args, false, &err); nlua_call_ref(luahl_start, "start", args, false, &err);
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
ELOG("error in luahl start: %s", err.msg); ELOG("error in luahl start: %s", err.msg);
api_clear_error(&err); api_clear_error(&err);
@@ -1239,7 +1241,9 @@ static void win_update(win_T *wp)
decorations_active = decorations_redraw_reset(buf, &decorations); decorations_active = decorations_redraw_reset(buf, &decorations);
if (buf->b_luahl && buf->b_luahl_window != LUA_NOREF) { do_luahl_line = false;
if (luahl_win != LUA_NOREF) {
Error err = ERROR_INIT; Error err = ERROR_INIT;
FIXED_TEMP_ARRAY(args, 4); FIXED_TEMP_ARRAY(args, 4);
linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE) linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE)
@@ -1252,7 +1256,13 @@ static void win_update(win_T *wp)
args.items[3] = INTEGER_OBJ(knownmax); args.items[3] = INTEGER_OBJ(knownmax);
// TODO(bfredl): we could allow this callback to change mod_top, mod_bot. // TODO(bfredl): we could allow this callback to change mod_top, mod_bot.
// For now the "start" callback is expected to use nvim__buf_redraw_range. // For now the "start" callback is expected to use nvim__buf_redraw_range.
nlua_call_ref(buf->b_luahl_window, "window", args, false, &err); Object ret = nlua_call_ref(luahl_win, "win", args, true, &err);
if (!ERROR_SET(&err) && api_is_truthy(ret, "luahl_window retval", &err)) {
do_luahl_line = true;
decorations_active = true;
}
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
ELOG("error in luahl window: %s", err.msg); ELOG("error in luahl window: %s", err.msg);
api_clear_error(&err); api_clear_error(&err);
@@ -2349,7 +2359,7 @@ win_line (
} }
if (decorations_active) { if (decorations_active) {
if (buf->b_luahl && buf->b_luahl_line != LUA_NOREF) { if (do_luahl_line && luahl_line != LUA_NOREF) {
Error err = ERROR_INIT; Error err = ERROR_INIT;
FIXED_TEMP_ARRAY(args, 3); FIXED_TEMP_ARRAY(args, 3);
args.items[0] = WINDOW_OBJ(wp->handle); args.items[0] = WINDOW_OBJ(wp->handle);
@@ -2357,8 +2367,9 @@ win_line (
args.items[2] = INTEGER_OBJ(lnum-1); args.items[2] = INTEGER_OBJ(lnum-1);
lua_attr_active = true; lua_attr_active = true;
extra_check = true; extra_check = true;
nlua_call_ref(buf->b_luahl_line, "line", args, false, &err); nlua_call_ref(luahl_line, "line", args, false, &err);
lua_attr_active = false; lua_attr_active = false;
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
ELOG("error in luahl line: %s", err.msg); ELOG("error in luahl line: %s", err.msg);
luatext = err.msg; luatext = err.msg;