2020-05-07 22:14:46 +02:00
|
|
|
local core = require "core"
|
2021-11-22 06:23:16 +01:00
|
|
|
local common = require "core.common"
|
2020-05-07 22:14:46 +02:00
|
|
|
local config = require "core.config"
|
|
|
|
local tokenizer = require "core.tokenizer"
|
|
|
|
local Object = require "core.object"
|
|
|
|
|
|
|
|
|
|
|
|
local Highlighter = Object:extend()
|
|
|
|
|
|
|
|
|
|
|
|
function Highlighter:new(doc)
|
|
|
|
self.doc = doc
|
2022-11-06 03:38:10 +01:00
|
|
|
self.running = false
|
2020-05-09 17:23:07 +02:00
|
|
|
self:reset()
|
2022-11-06 03:38:10 +01:00
|
|
|
end
|
2020-05-07 22:14:46 +02:00
|
|
|
|
2022-11-06 03:38:10 +01:00
|
|
|
-- init incremental syntax highlighting
|
|
|
|
function Highlighter:start()
|
|
|
|
if self.running then return end
|
|
|
|
self.running = true
|
2020-05-07 22:14:46 +02:00
|
|
|
core.add_thread(function()
|
2023-04-01 18:12:39 +02:00
|
|
|
while self.first_invalid_line <= self.max_wanted_line do
|
2022-11-06 03:38:10 +01:00
|
|
|
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
|
|
|
|
local retokenized_from
|
|
|
|
for i = self.first_invalid_line, max do
|
|
|
|
local state = (i > 1) and self.lines[i - 1].state
|
|
|
|
local line = self.lines[i]
|
2023-04-01 18:12:39 +02:00
|
|
|
if line and line.resume and (line.init_state ~= state or line.text ~= self.doc.lines[i]) then
|
|
|
|
-- Reset the progress if no longer valid
|
|
|
|
line.resume = nil
|
|
|
|
end
|
|
|
|
if not (line and line.init_state == state and line.text == self.doc.lines[i] and not line.resume) then
|
2022-11-06 03:38:10 +01:00
|
|
|
retokenized_from = retokenized_from or i
|
2023-04-01 18:12:39 +02:00
|
|
|
self.lines[i] = self:tokenize_line(i, state, line and line.resume)
|
|
|
|
if self.lines[i].resume then
|
|
|
|
self.first_invalid_line = i
|
|
|
|
goto yield
|
|
|
|
end
|
2022-11-06 03:38:10 +01:00
|
|
|
elseif retokenized_from then
|
|
|
|
self:update_notify(retokenized_from, i - retokenized_from - 1)
|
|
|
|
retokenized_from = nil
|
2022-06-11 06:21:55 +02:00
|
|
|
end
|
2020-05-07 22:14:46 +02:00
|
|
|
end
|
2023-04-01 18:12:39 +02:00
|
|
|
|
|
|
|
self.first_invalid_line = max + 1
|
|
|
|
::yield::
|
2022-11-06 03:38:10 +01:00
|
|
|
if retokenized_from then
|
|
|
|
self:update_notify(retokenized_from, max - retokenized_from)
|
|
|
|
end
|
|
|
|
core.redraw = true
|
2024-02-11 18:51:12 +01:00
|
|
|
coroutine.yield()
|
2020-05-07 22:14:46 +02:00
|
|
|
end
|
2022-11-06 03:38:10 +01:00
|
|
|
self.max_wanted_line = 0
|
|
|
|
self.running = false
|
2020-05-07 22:14:46 +02:00
|
|
|
end, self)
|
|
|
|
end
|
|
|
|
|
2022-11-06 03:38:10 +01:00
|
|
|
local function set_max_wanted_lines(self, amount)
|
|
|
|
self.max_wanted_line = amount
|
2023-04-01 18:12:39 +02:00
|
|
|
if self.first_invalid_line <= self.max_wanted_line then
|
2022-11-06 03:38:10 +01:00
|
|
|
self:start()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-07 22:14:46 +02:00
|
|
|
|
2020-05-09 17:23:07 +02:00
|
|
|
function Highlighter:reset()
|
|
|
|
self.lines = {}
|
2021-11-20 01:15:13 +01:00
|
|
|
self:soft_reset()
|
|
|
|
end
|
|
|
|
|
|
|
|
function Highlighter:soft_reset()
|
|
|
|
for i=1,#self.lines do
|
|
|
|
self.lines[i] = false
|
|
|
|
end
|
2020-05-09 17:23:07 +02:00
|
|
|
self.first_invalid_line = 1
|
|
|
|
self.max_wanted_line = 0
|
|
|
|
end
|
|
|
|
|
2020-05-07 22:14:46 +02:00
|
|
|
function Highlighter:invalidate(idx)
|
2020-06-02 23:50:03 +02:00
|
|
|
self.first_invalid_line = math.min(self.first_invalid_line, idx)
|
2022-11-06 03:38:10 +01:00
|
|
|
set_max_wanted_lines(self, math.min(self.max_wanted_line, #self.doc.lines))
|
2020-05-07 22:14:46 +02:00
|
|
|
end
|
|
|
|
|
2021-10-07 19:19:03 +02:00
|
|
|
function Highlighter:insert_notify(line, n)
|
|
|
|
self:invalidate(line)
|
2021-11-22 06:23:16 +01:00
|
|
|
local blanks = { }
|
2021-10-07 19:19:03 +02:00
|
|
|
for i = 1, n do
|
2021-11-22 06:23:16 +01:00
|
|
|
blanks[i] = false
|
2021-10-07 19:19:03 +02:00
|
|
|
end
|
2021-11-22 06:23:16 +01:00
|
|
|
common.splice(self.lines, line, 0, blanks)
|
2021-10-07 19:19:03 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function Highlighter:remove_notify(line, n)
|
|
|
|
self:invalidate(line)
|
2021-11-22 06:23:16 +01:00
|
|
|
common.splice(self.lines, line, n)
|
2021-10-07 19:19:03 +02:00
|
|
|
end
|
|
|
|
|
2022-06-11 06:21:55 +02:00
|
|
|
function Highlighter:update_notify(line, n)
|
|
|
|
-- plugins can hook here to be notified that lines have been retokenized
|
|
|
|
end
|
|
|
|
|
2020-05-07 22:14:46 +02:00
|
|
|
|
2023-04-01 18:12:39 +02:00
|
|
|
function Highlighter:tokenize_line(idx, state, resume)
|
2020-05-08 21:29:22 +02:00
|
|
|
local res = {}
|
|
|
|
res.init_state = state
|
|
|
|
res.text = self.doc.lines[idx]
|
2023-04-01 18:12:39 +02:00
|
|
|
res.tokens, res.state, res.resume = tokenizer.tokenize(self.doc.syntax, res.text, state, resume)
|
2020-05-08 21:29:22 +02:00
|
|
|
return res
|
2020-05-07 22:14:46 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function Highlighter:get_line(idx)
|
|
|
|
local line = self.lines[idx]
|
|
|
|
if not line or line.text ~= self.doc.lines[idx] then
|
|
|
|
local prev = self.lines[idx - 1]
|
|
|
|
line = self:tokenize_line(idx, prev and prev.state)
|
|
|
|
self.lines[idx] = line
|
2022-06-11 06:21:55 +02:00
|
|
|
self:update_notify(idx, 0)
|
2020-05-07 22:14:46 +02:00
|
|
|
end
|
2022-11-06 03:38:10 +01:00
|
|
|
set_max_wanted_lines(self, math.max(self.max_wanted_line, idx))
|
2020-05-07 22:14:46 +02:00
|
|
|
return line
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function Highlighter:each_token(idx)
|
|
|
|
return tokenizer.each_token(self:get_line(idx).tokens)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return Highlighter
|