Skip to content

Commit

Permalink
fix(winhl): sync inactive highlights for all windows
Browse files Browse the repository at this point in the history
  • Loading branch information
rasulomaroff committed Apr 30, 2024
1 parent 1c91a20 commit faa4bad
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 66 deletions.
31 changes: 19 additions & 12 deletions lua/reactive/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +66,38 @@ function M:init()
group = group,
pattern = '*:*',
desc = 'Reactive: watches for mode changes to update highlights and run callbacks',
callback = function(opts)
local from, to = vim.v.event.old_mode, vim.v.event.new_mode

Snapshot:set_modes(from, to)
callback = function()
Snapshot:set_modes(vim.v.event.old_mode, vim.v.event.new_mode)
local snap = Snapshot:gen { callbacks = true }

Highlight:apply {
hl = snap.hl,
winhl = snap.winhl,
winhl = snap.winhl.current,
winid = api.nvim_get_current_win(),
}
end,
})

-- We need BufWinEnter event to successfully handle cases where you open a window
-- then through telescope go to another one and then go back through Ctrl + o
aucmd({ 'WinEnter', 'BufWinEnter', 'WinLeave' }, {
aucmd('WinEnter', {
group = group,
desc = 'Reactive: applies active/inactive window highlights',
callback = function(opts)
local snap = Snapshot:gen { inactive_win = opts.event == 'WinLeave' }
callback = function()
Highlight:sync()
end,
})

-- We use this autocmd to fix the bug when after entering a file through a telescope/fzf/whatever
-- and then jumping back through ctrl+o mapping we could get highlights for noncurrent windows or
-- highlights for a different mode, for example having a highlights for insert mode while being in normal
-- it may be a neovim issue (or feature?), because I don't see any reasons for it to happen
aucmd('BufWinEnter', {
group = group,
desc = 'Reactive: applies active/inactive window highlights',
callback = function()
local snap = Snapshot:gen()
Highlight:apply {
hl = snap.hl,
winhl = snap.winhl,
winhl = snap.winhl.current,
winid = api.nvim_get_current_win(),
}
end,
Expand All @@ -107,7 +114,7 @@ function M:init()
end,
})

Highlight:sync()
Highlight:sync(true)
end

local function stop_plugin()
Expand Down
41 changes: 20 additions & 21 deletions lua/reactive/highlight.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ local M = {
prev_highlights = {},
}

---@param opts { winid: number, winhl?: table<string, string>, hl?: table<string, table<string, any>>, forced?: boolean }
---@param opts { winid: number, winhl?: table<string, string>, hl?: table<string, table<string, any>> }
function M:apply(opts)
local skip_winhl = self:apply_winhl(opts.winhl, opts.winid)
local skip_hl = self:apply_hl(opts.hl, opts.forced)
local skip_hl = self:apply_hl(opts.hl)

-- redraw pending screen updates
-- without this command some highlights won't be applied immediately
-- or till the next "redraw" caused by nvim itself
-- but we won't redraw if it's not forced or there're no highlights applied or changed
if not skip_winhl or not skip_hl or opts.forced then
if not skip_winhl or not skip_hl then
vim.cmd.redraw()
end
end
Expand Down Expand Up @@ -101,32 +101,31 @@ function M:apply_hl(highlights, forced)
return false
end

function M:sync()
---@param forced boolean? forcely apply highlights
function M:sync(forced)
local Util = require 'reactive.util'
local Snapshot = require 'reactive.snapshot'
local windows = vim.api.nvim_list_wins()
local snapshot = require('reactive.snapshot'):gen()
local current_win = vim.api.nvim_get_current_win()

Util.eachi(windows, function(win)
if win == current_win then
-- we'll only apply global highlights once as it makes no sense to do it
-- several times
local skip_hl = self:apply_hl(snapshot.hl, forced)
local skip_winhl = true

Util.eachi(vim.api.nvim_list_wins(), function(win)
-- we don't apply colours to non-focusable windows
if not vim.api.nvim_win_get_config(win).focusable then
return
end

local snap = Snapshot:gen { inactive_win = true }
self:apply_winhl(snap.winhl, win)
if not self:apply_winhl(win == current_win and snapshot.winhl.current or snapshot.winhl.noncurrent, win) then
skip_winhl = false
end
end)

local current_win_snap = Snapshot:gen()
-- we'll only apply global highlights once as it makes no sense to do it
-- several times
self:apply {
hl = current_win_snap.hl,
winhl = current_win_snap.winhl,
winid = current_win,
-- whenever we 'sync' colors, we should forcely apply new highlights even though there
-- could be the same highlight groups that had been applied before
forced = true,
}
if not skip_hl or not skip_winhl then
vim.cmd.redraw()
end
end

return M
58 changes: 25 additions & 33 deletions lua/reactive/snapshot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,14 @@ function M:set_opfunc(opfunc)
end

---@param opts? { inactive_win?: boolean, callbacks?: boolean }
---@return { winhl: table<string, string>, hl: table<string, table> }
---@return { winhl: { current: table<string, string>, noncurrent: table<string, string> }, hl: table<string, table> }
function M:gen(opts)
local State = require 'reactive.state'

self.snapshot = { winhl = {}, hl = {} }

-- if we're leaving a window, then we just color that window with `inactive` colors, if presented
if opts and opts.inactive_win then
State:iterate_presets(function(preset)
local constraints = {}

if
preset.static
and not vim.tbl_isempty(preset.static)
and not self:process_constraints(preset.skip, constraints)
then
self:form {
preset_name = preset.name,
highlights = {
winhl = preset.static.winhl and preset.static.winhl.inactive,
hl = preset.static.hl,
},
scope = '@static.inactive',
constraints = constraints,
}
end
end)

return self.snapshot
end
self.snapshot = {
winhl = { current = {}, noncurrent = {} },
hl = {},
}

local mode = self.to
local mode_len = #mode
Expand Down Expand Up @@ -196,9 +174,22 @@ function M:gen(opts)
if preset.static and not vim.tbl_isempty(preset.static) then
self:form {
preset_name = preset.name,
highlights = { winhl = preset.static.winhl and preset.static.winhl.active, hl = preset.static.hl },
scope = '@static.active',
highlights = {
winhl = preset.static.winhl and preset.static.winhl.active,
hl = preset.static.hl,
},
scope = '@static.current',
constraints = scope[preset.name] and scope[preset.name].constraints or {},
}

self:form {
preset_name = preset.name,
highlights = {
winhl = preset.static.winhl and preset.static.winhl.inactive,
},
scope = '@static.noncurrent',
constraints = scope[preset.name] and scope[preset.name].constraints or {},
window_scope = 'noncurrent',
}
end

Expand Down Expand Up @@ -278,7 +269,7 @@ local merge_handlers = {
Util.each(highlights, function(hl_group, hl_val)
-- if a group is already applied, then we won't overwrite it
-- meaning that it had a higher priority
if M.snapshot.winhl[hl_group] then
if M.snapshot.winhl[opts.window_scope][hl_group] then
return
end

Expand All @@ -297,9 +288,9 @@ local merge_handlers = {
M.cache.transformed_winhl[key] = rhs
end

M.snapshot.winhl[hl_group] = rhs
M.snapshot.winhl[opts.window_scope][hl_group] = rhs
else
M.snapshot.winhl[hl_group] = cached_hl
M.snapshot.winhl[opts.window_scope][hl_group] = cached_hl
end
end)
end,
Expand All @@ -315,11 +306,12 @@ local merge_handlers = {
end,
}

---@param opts { preset_name: string, highlights: table<string, table | fun(): table>, scope: string, constraints: TriggerConstraints }
---@param opts { preset_name: string, highlights: table<string, table | fun(): table>, scope: string, constraints: TriggerConstraints, window_scope?: 'current' | 'noncurrent' }
function M:form(opts)
local handler_options = {
scope = string.format('@preset.%s.%s', opts.preset_name, opts.scope),
preset_name = opts.preset_name,
window_scope = opts.window_scope or 'current',
}

Util.each(merge_handlers, function(value)
Expand Down

0 comments on commit faa4bad

Please sign in to comment.