aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgi Kirilov <in.the@repo>2020-02-17 14:42:24 +0200
committerMarc André Tanner <mat@brain-dump.org>2020-02-24 11:10:55 +0100
commit15d213e4b6e33670cb50d472ad3f532245ebcc3b (patch)
tree166e249426a946beb3b64b3653e99cde6f651153
parentf63f26aac0be25b36959112af412773561daab5e (diff)
downloadvis-15d213e4b6e33670cb50d472ad3f532245ebcc3b.tar.gz
vis-15d213e4b6e33670cb50d472ad3f532245ebcc3b.tar.xz
lua: add `redrawtime` option
Upper bound lexing time and cancel highlighting if it is exceeded.
-rw-r--r--lua/lexers/lexer.lua23
-rw-r--r--lua/vis-std.lua16
-rw-r--r--man/vis.13
3 files changed, 35 insertions, 7 deletions
diff --git a/lua/lexers/lexer.lua b/lua/lexers/lexer.lua
index d4af90c..2973ea6 100644
--- a/lua/lexers/lexer.lua
+++ b/lua/lexers/lexer.lua
@@ -881,7 +881,7 @@ module('lexer')]=]
lpeg = require('lpeg')
local lpeg_P, lpeg_R, lpeg_S, lpeg_V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
local lpeg_Ct, lpeg_Cc, lpeg_Cp = lpeg.Ct, lpeg.Cc, lpeg.Cp
-local lpeg_Cmt, lpeg_C = lpeg.Cmt, lpeg.C
+local lpeg_Cmt, lpeg_C, lpeg_Carg = lpeg.Cmt, lpeg.C, lpeg.Carg
local lpeg_match = lpeg.match
M.LEXERPATH = package.path
@@ -988,7 +988,13 @@ local function build_grammar(lexer, initial_rule)
lexer._INITIALRULE = initial_rule
lexer._GRAMMAR = lpeg_Ct(lpeg_P(grammar))
else
- lexer._GRAMMAR = lpeg_Ct(join_tokens(lexer)^0)
+ local function tmout(_, _, t1, redrawtime_max, flag)
+ if not redrawtime_max or os.clock() - t1 < redrawtime_max then return true end
+ if flag then flag.timedout = true end
+ end
+ local tokens = join_tokens(lexer)
+ -- every 500 tokens (approx. a screenful), check whether we have exceeded the timeout
+ lexer._GRAMMAR = lpeg_Ct((tokens * tokens^-500 * lpeg_Cmt(lpeg_Carg(1) * lpeg_Carg(2) * lpeg_Carg(3), tmout))^0)
end
end
@@ -1120,9 +1126,12 @@ end
-- @param text The text in the buffer to lex.
-- @param init_style The current style. Multiple-language lexers use this to
-- determine which language to start lexing in.
+-- @param redrawtime_max Stop lexing after that many seconds and set the second return value (timedout) to true.
+-- @param init Start lexing from this offset in *text* (default is 1).
-- @return table of token names and positions.
+-- @return whether the lexing timed out.
-- @name lex
-function M.lex(lexer, text, init_style)
+function M.lex(lexer, text, init_style, redrawtime_max, init)
if not lexer._GRAMMAR then return {M.DEFAULT, #text + 1} end
if not lexer._LEXBYLINE then
-- For multilang lexers, build a new grammar whose initial_rule is the
@@ -1138,7 +1147,8 @@ function M.lex(lexer, text, init_style)
end
end
end
- return lpeg_match(lexer._GRAMMAR, text)
+ local flag = {}
+ return lpeg_match(lexer._GRAMMAR, text, init, os.clock(), redrawtime_max, flag), flag.timedout
else
local tokens = {}
local function append(tokens, line_tokens, offset)
@@ -1149,8 +1159,9 @@ function M.lex(lexer, text, init_style)
end
local offset = 0
local grammar = lexer._GRAMMAR
+ local flag = {}
for line in text:gmatch('[^\r\n]*\r?\n?') do
- local line_tokens = lpeg_match(grammar, line)
+ local line_tokens = lpeg_match(grammar, line, init, os.clock(), redrawtime_max, flag)
if line_tokens then append(tokens, line_tokens, offset) end
offset = offset + #line
-- Use the default style to the end of the line if none was specified.
@@ -1158,7 +1169,7 @@ function M.lex(lexer, text, init_style)
tokens[#tokens + 1], tokens[#tokens + 2] = 'default', offset + 1
end
end
- return tokens
+ return tokens, flag.timedout
end
end
diff --git a/lua/vis-std.lua b/lua/vis-std.lua
index 31439c2..470872f 100644
--- a/lua/vis-std.lua
+++ b/lua/vis-std.lua
@@ -37,6 +37,17 @@ vis:option_register("horizon", "number", function(horizon)
return true
end, "Number of bytes to consider for syntax highlighting")
+vis:option_register("redrawtime", "string", function(redrawtime)
+ if not vis.win then return false end
+ local value = tonumber(redrawtime)
+ if not value or value <= 0 then
+ vis:info("A positive real number expected")
+ return false
+ end
+ vis.win.redrawtime = value
+ return true
+end, "Seconds to wait for syntax highlighting before aborting it")
+
vis.events.subscribe(vis.events.WIN_HIGHLIGHT, function(win)
if not win.syntax or not vis.lexers.load then return end
local lexer = vis.lexers.load(win.syntax, nil, true)
@@ -45,6 +56,7 @@ vis.events.subscribe(vis.events.WIN_HIGHLIGHT, function(win)
-- TODO: improve heuristic for initial style
local viewport = win.viewport
if not viewport then return end
+ local redrawtime_max = win.redrawtime or 1.0
local horizon_max = win.horizon or 32768
local horizon = viewport.start < horizon_max and viewport.start or horizon_max
local view_start = viewport.start
@@ -52,9 +64,11 @@ vis.events.subscribe(vis.events.WIN_HIGHLIGHT, function(win)
viewport.start = lex_start
local data = win.file:content(viewport)
local token_styles = lexer._TOKENSTYLES
- local tokens = lexer:lex(data, 1)
+ local tokens, timedout = lexer:lex(data, 1, redrawtime_max)
local token_end = lex_start + (tokens[#tokens] or 1) - 1
+ if timedout then return end
+
for i = #tokens - 1, 1, -2 do
local token_start = lex_start + (tokens[i-1] or 1) - 1
if token_end < view_start then
diff --git a/man/vis.1 b/man/vis.1
index a60d4fb..eb6907f 100644
--- a/man/vis.1
+++ b/man/vis.1
@@ -1375,6 +1375,9 @@ Highlight a fixed column.
.It Ic horizon Op Ar 32768
How many bytes back the lexer will look to synchronize parsing.
.
+.It Ic redrawtime Op Ar 1.0
+Maximum time (in seconds) to wait for syntax highlighting before aborting it.
+.
.It Ic theme Op Do default-16 Dc or Do default-256 Dc
Color theme to use, name without file extension.
.