Merge pull request #1030 from Guldoman/PR_cache_draw_whitespace
`drawwhitespace`: Cache whitespace location
This commit is contained in:
commit
380cfb9a24
|
@ -22,13 +22,21 @@ function Highlighter:new(doc)
|
||||||
else
|
else
|
||||||
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
|
local max = math.min(self.first_invalid_line + 40, self.max_wanted_line)
|
||||||
|
|
||||||
|
local retokenized_from
|
||||||
for i = self.first_invalid_line, max do
|
for i = self.first_invalid_line, max do
|
||||||
local state = (i > 1) and self.lines[i - 1].state
|
local state = (i > 1) and self.lines[i - 1].state
|
||||||
local line = self.lines[i]
|
local line = self.lines[i]
|
||||||
if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
|
if not (line and line.init_state == state and line.text == self.doc.lines[i]) then
|
||||||
|
retokenized_from = retokenized_from or i
|
||||||
self.lines[i] = self:tokenize_line(i, state)
|
self.lines[i] = self:tokenize_line(i, state)
|
||||||
|
elseif retokenized_from then
|
||||||
|
self:update_notify(retokenized_from, i - retokenized_from - 1)
|
||||||
|
retokenized_from = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if retokenized_from then
|
||||||
|
self:update_notify(retokenized_from, max - retokenized_from)
|
||||||
|
end
|
||||||
|
|
||||||
self.first_invalid_line = max + 1
|
self.first_invalid_line = max + 1
|
||||||
core.redraw = true
|
core.redraw = true
|
||||||
|
@ -71,6 +79,10 @@ function Highlighter:remove_notify(line, n)
|
||||||
common.splice(self.lines, line, n)
|
common.splice(self.lines, line, n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Highlighter:update_notify(line, n)
|
||||||
|
-- plugins can hook here to be notified that lines have been retokenized
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function Highlighter:tokenize_line(idx, state)
|
function Highlighter:tokenize_line(idx, state)
|
||||||
local res = {}
|
local res = {}
|
||||||
|
@ -87,6 +99,7 @@ function Highlighter:get_line(idx)
|
||||||
local prev = self.lines[idx - 1]
|
local prev = self.lines[idx - 1]
|
||||||
line = self:tokenize_line(idx, prev and prev.state)
|
line = self:tokenize_line(idx, prev and prev.state)
|
||||||
self.lines[idx] = line
|
self.lines[idx] = line
|
||||||
|
self:update_notify(idx, 0)
|
||||||
end
|
end
|
||||||
self.max_wanted_line = math.max(self.max_wanted_line, idx)
|
self.max_wanted_line = math.max(self.max_wanted_line, idx)
|
||||||
return line
|
return line
|
||||||
|
|
|
@ -4,6 +4,7 @@ local style = require "core.style"
|
||||||
local DocView = require "core.docview"
|
local DocView = require "core.docview"
|
||||||
local common = require "core.common"
|
local common = require "core.common"
|
||||||
local config = require "core.config"
|
local config = require "core.config"
|
||||||
|
local Highlighter = require "core.doc.highlighter"
|
||||||
|
|
||||||
config.plugins.drawwhitespace = common.merge({
|
config.plugins.drawwhitespace = common.merge({
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
@ -95,6 +96,87 @@ config.plugins.drawwhitespace = common.merge({
|
||||||
}
|
}
|
||||||
}, config.plugins.drawwhitespace)
|
}, config.plugins.drawwhitespace)
|
||||||
|
|
||||||
|
|
||||||
|
local ws_cache
|
||||||
|
local cached_settings
|
||||||
|
local function reset_cache()
|
||||||
|
ws_cache = setmetatable({}, { __mode = "k" })
|
||||||
|
local settings = config.plugins.drawwhitespace
|
||||||
|
cached_settings = {
|
||||||
|
show_leading = settings.show_leading,
|
||||||
|
show_trailing = settings.show_trailing,
|
||||||
|
show_middle = settings.show_middle,
|
||||||
|
show_middle_min = settings.show_middle_min,
|
||||||
|
color = settings.color,
|
||||||
|
leading_color = settings.leading_color,
|
||||||
|
middle_color = settings.middle_color,
|
||||||
|
trailing_color = settings.trailing_color,
|
||||||
|
substitutions = settings.substitutions,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
reset_cache()
|
||||||
|
|
||||||
|
local function reset_cache_if_needed()
|
||||||
|
local settings = config.plugins.drawwhitespace
|
||||||
|
if
|
||||||
|
not ws_cache or
|
||||||
|
cached_settings.show_leading ~= settings.show_leading
|
||||||
|
or cached_settings.show_trailing ~= settings.show_trailing
|
||||||
|
or cached_settings.show_middle ~= settings.show_middle
|
||||||
|
or cached_settings.show_middle_min ~= settings.show_middle_min
|
||||||
|
or cached_settings.color ~= settings.color
|
||||||
|
or cached_settings.leading_color ~= settings.leading_color
|
||||||
|
or cached_settings.middle_color ~= settings.middle_color
|
||||||
|
or cached_settings.trailing_color ~= settings.trailing_color
|
||||||
|
-- we assume that the entire table changes
|
||||||
|
or cached_settings.substitutions ~= settings.substitutions
|
||||||
|
then
|
||||||
|
reset_cache()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Move cache to make space for new lines
|
||||||
|
local prev_insert_notify = Highlighter.insert_notify
|
||||||
|
function Highlighter:insert_notify(line, n, ...)
|
||||||
|
prev_insert_notify(self, line, n, ...)
|
||||||
|
if not ws_cache[self] then
|
||||||
|
ws_cache[self] = {}
|
||||||
|
end
|
||||||
|
local to = math.min(line + n, #self.doc.lines)
|
||||||
|
for i=#self.doc.lines+n,to,-1 do
|
||||||
|
ws_cache[self][i] = ws_cache[self][i - n]
|
||||||
|
end
|
||||||
|
for i=line,to do
|
||||||
|
ws_cache[self][i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Close the cache gap created by removed lines
|
||||||
|
local prev_remove_notify = Highlighter.remove_notify
|
||||||
|
function Highlighter:remove_notify(line, n, ...)
|
||||||
|
prev_remove_notify(self, line, n, ...)
|
||||||
|
if not ws_cache[self] then
|
||||||
|
ws_cache[self] = {}
|
||||||
|
end
|
||||||
|
local to = math.max(line + n, #self.doc.lines)
|
||||||
|
for i=line,to do
|
||||||
|
ws_cache[self][i] = ws_cache[self][i + n]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove changed lines from the cache
|
||||||
|
local prev_update_notify = Highlighter.update_notify
|
||||||
|
function Highlighter:update_notify(line, n, ...)
|
||||||
|
prev_update_notify(self, line, n, ...)
|
||||||
|
if not ws_cache[self] then
|
||||||
|
ws_cache[self] = {}
|
||||||
|
end
|
||||||
|
for i=line,line+n do
|
||||||
|
ws_cache[self][i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local function get_option(substitution, option)
|
local function get_option(substitution, option)
|
||||||
if substitution[option] == nil then
|
if substitution[option] == nil then
|
||||||
return config.plugins.drawwhitespace[option]
|
return config.plugins.drawwhitespace[option]
|
||||||
|
@ -113,67 +195,100 @@ function DocView:draw_line_text(idx, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
local font = (self:get_font() or style.syntax_fonts["whitespace"] or style.syntax_fonts["comment"])
|
local font = (self:get_font() or style.syntax_fonts["whitespace"] or style.syntax_fonts["comment"])
|
||||||
local ty = y + self:get_line_text_y_offset()
|
|
||||||
local tx
|
|
||||||
local text, offset, s, e = self.doc.lines[idx], 1
|
|
||||||
local x1, _, x2, _ = self:get_content_bounds()
|
|
||||||
local _offset = self:get_x_offset_col(idx, x1)
|
|
||||||
|
|
||||||
for _, substitution in pairs(config.plugins.drawwhitespace.substitutions) do
|
reset_cache_if_needed()
|
||||||
local char = substitution.char
|
if
|
||||||
local sub = substitution.sub
|
not ws_cache[self.doc.highlighter]
|
||||||
offset = _offset
|
or ws_cache[self.doc.highlighter].font ~= font
|
||||||
|
then
|
||||||
|
ws_cache[self.doc.highlighter] = {font = font}
|
||||||
|
end
|
||||||
|
|
||||||
local show_leading = get_option(substitution, "show_leading")
|
if not ws_cache[self.doc.highlighter][idx] then -- need to cache line
|
||||||
local show_middle = get_option(substitution, "show_middle")
|
local cache = {}
|
||||||
local show_trailing = get_option(substitution, "show_trailing")
|
|
||||||
|
|
||||||
local show_middle_min = get_option(substitution, "show_middle_min")
|
local tx
|
||||||
|
local text = self.doc.lines[idx]
|
||||||
|
|
||||||
local base_color = get_option(substitution, "color")
|
for _, substitution in pairs(config.plugins.drawwhitespace.substitutions) do
|
||||||
local leading_color = get_option(substitution, "leading_color") or base_color
|
local char = substitution.char
|
||||||
local middle_color = get_option(substitution, "middle_color") or base_color
|
local sub = substitution.sub
|
||||||
local trailing_color = get_option(substitution, "trailing_color") or base_color
|
local offset = 1
|
||||||
|
|
||||||
local pattern = char.."+"
|
local show_leading = get_option(substitution, "show_leading")
|
||||||
while true do
|
local show_middle = get_option(substitution, "show_middle")
|
||||||
s, e = text:find(pattern, offset)
|
local show_trailing = get_option(substitution, "show_trailing")
|
||||||
if not s then break end
|
|
||||||
|
|
||||||
tx = self:get_col_x_offset(idx, s) + x
|
local show_middle_min = get_option(substitution, "show_middle_min")
|
||||||
|
|
||||||
local color = base_color
|
local base_color = get_option(substitution, "color")
|
||||||
local draw = false
|
local leading_color = get_option(substitution, "leading_color") or base_color
|
||||||
|
local middle_color = get_option(substitution, "middle_color") or base_color
|
||||||
|
local trailing_color = get_option(substitution, "trailing_color") or base_color
|
||||||
|
|
||||||
if e == #text - 1 then
|
local pattern = char.."+"
|
||||||
draw = show_trailing
|
while true do
|
||||||
color = trailing_color
|
local s, e = text:find(pattern, offset)
|
||||||
elseif s == 1 then
|
if not s then break end
|
||||||
draw = show_leading
|
|
||||||
color = leading_color
|
|
||||||
else
|
|
||||||
draw = show_middle and (e - s + 1 >= show_middle_min)
|
|
||||||
color = middle_color
|
|
||||||
end
|
|
||||||
|
|
||||||
if draw then
|
tx = self:get_col_x_offset(idx, s)
|
||||||
-- We need to draw tabs one at a time because they might have a
|
|
||||||
-- different size than the substituting character.
|
local color = base_color
|
||||||
-- This also applies to any other char if we use non-monospace fonts
|
local draw = false
|
||||||
-- but we ignore this case for now.
|
|
||||||
if char == "\t" then
|
if e == #text - 1 then
|
||||||
for i = s,e do
|
draw = show_trailing
|
||||||
tx = self:get_col_x_offset(idx, i) + x
|
color = trailing_color
|
||||||
tx = renderer.draw_text(font, sub, tx, ty, color)
|
elseif s == 1 then
|
||||||
end
|
draw = show_leading
|
||||||
|
color = leading_color
|
||||||
else
|
else
|
||||||
tx = renderer.draw_text(font, string.rep(sub, e - s + 1), tx, ty, color)
|
draw = show_middle and (e - s + 1 >= show_middle_min)
|
||||||
|
color = middle_color
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if tx > x + x2 then break end
|
if draw then
|
||||||
offset = e + 1
|
local last_cache_idx = #cache
|
||||||
|
-- We need to draw tabs one at a time because they might have a
|
||||||
|
-- different size than the substituting character.
|
||||||
|
-- This also applies to any other char if we use non-monospace fonts
|
||||||
|
-- but we ignore this case for now.
|
||||||
|
if char == "\t" then
|
||||||
|
for i = s,e do
|
||||||
|
tx = self:get_col_x_offset(idx, i)
|
||||||
|
cache[last_cache_idx + 1] = sub
|
||||||
|
cache[last_cache_idx + 2] = tx
|
||||||
|
cache[last_cache_idx + 3] = font:get_width(sub)
|
||||||
|
cache[last_cache_idx + 4] = color
|
||||||
|
last_cache_idx = last_cache_idx + 4
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cache[last_cache_idx + 1] = string.rep(sub, e - s + 1)
|
||||||
|
cache[last_cache_idx + 2] = tx
|
||||||
|
cache[last_cache_idx + 3] = font:get_width(cache[last_cache_idx + 1])
|
||||||
|
cache[last_cache_idx + 4] = color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
offset = e + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
ws_cache[self.doc.highlighter][idx] = cache
|
||||||
|
end
|
||||||
|
|
||||||
|
-- draw from cache
|
||||||
|
local x1, _, x2, _ = self:get_content_bounds()
|
||||||
|
x1 = x1 + x
|
||||||
|
x2 = x2 + x
|
||||||
|
local cache = ws_cache[self.doc.highlighter][idx]
|
||||||
|
for i=1,#cache,4 do
|
||||||
|
local sub = cache[i]
|
||||||
|
local tx = cache[i + 1] + x
|
||||||
|
local tw = cache[i + 2]
|
||||||
|
local color = cache[i + 3]
|
||||||
|
if tx + tw >= x1 then
|
||||||
|
tx = renderer.draw_text(font, sub, tx, y, color)
|
||||||
|
end
|
||||||
|
if tx > x2 then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
return draw_line_text(self, idx, x, y)
|
return draw_line_text(self, idx, x, y)
|
||||||
|
|
Loading…
Reference in New Issue