Derive default highlights if they aren't defined

This commit is contained in:
Lewis Russell 2021-03-13 11:47:35 +00:00
parent ed60dad2e4
commit ab16e46397
11 changed files with 356 additions and 64 deletions

View File

@ -1,9 +1,12 @@
export PJ_ROOT=$(PWD)
FILTER=.*
BUSTED_ARGS = \
--lpath=$(PJ_ROOT)/lua/?.lua \
--lpath=$(PJ_ROOT)/plenary.nvim/lua/?.lua
--lpath=$(PJ_ROOT)/plenary.nvim/lua/?.lua \
--filter=$(FILTER)
TEST_FILE = $(PJ_ROOT)/test/gitsigns_spec.lua

View File

@ -73,13 +73,14 @@ the default settings:
```lua
require('gitsigns').setup {
signs = {
add = {hl = 'DiffAdd' , text = '│', numhl='GitSignsAddNr'},
change = {hl = 'DiffChange', text = '│', numhl='GitSignsChangeNr'},
delete = {hl = 'DiffDelete', text = '_', numhl='GitSignsDeleteNr'},
topdelete = {hl = 'DiffDelete', text = '‾', numhl='GitSignsDeleteNr'},
changedelete = {hl = 'DiffChange', text = '~', numhl='GitSignsChangeNr'},
add = {hl = 'GitSignsAdd' , text = '│', numhl='GitSignsAddNr' , linehl='GitSignsAddLn},
change = {hl = 'GitSignsChange', text = '│', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn},
delete = {hl = 'GitSignsDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn},
topdelete = {hl = 'GitSignsDelete', text = '‾', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn},
changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn},
},
numhl = false,
linehl = false,
keymaps = {
-- Default keymap options
noremap = true,
@ -102,7 +103,9 @@ require('gitsigns').setup {
interval = 1000
},
sign_priority = 6,
update_debounce = 200,
status_formatter = nil, -- Use default
use_decoration_api = false
}
```
@ -122,24 +125,6 @@ set statusline+=%{get(b:,'gitsigns_status','')}
For the current branch use the variable `b:gitsigns_head`.
## FAQ
#### The default signs set the background and not the foreground. How do I get my signs to look like the GIF?
By default Gitsigns uses the highlight groups `DiffAdd`, `DiffChange` and `DiffDelete` for signs as these are the most appropriate highlights that are builtin to Neovim. In many colorschemes, these default highlights set the background but not the foreground. To make the signs looks more like the GIF then you need to set the highlights appropriately. Many colorschemes have specific highlights for Gitgutter (`GitGutterAdd`, `GitGutterChange` and `GitGutterDelete`), so you may use these highlights instead if you have them.
The following configuration will make Gitsigns look like GitGutters defaults:
```lua
require('gitsigns').setup {
signs = {
add = {hl = 'GitGutterAdd' , text = '+'},
change = {hl = 'GitGutterChange', text = '~'},
delete = {hl = 'GitGutterDelete', text = '_'},
topdelete = {hl = 'GitGutterDelete', text = '‾'},
changedelete = {hl = 'GitGutterChange', text = '~'},
}
}
```
## TODO
- [x] Add action for undoing a stage of a hunk

View File

@ -155,11 +155,11 @@ signs *gitsigns-config-signs*
Type: `table`, Default:
>
{
add = {hl = 'DiffAdd' , text = '│', numhl='GitSignsAddNr' , linehl='GitSignsAddLr' },
change = {hl = 'DiffChange', text = '│', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
delete = {hl = 'DiffDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
topdelete = {hl = 'DiffDelete', text = '‾', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
changedelete = {hl = 'DiffChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
add = {hl = 'GitSignsAdd' , text = '│', numhl='GitSignsAddNr' , linehl='GitSignsAddLn' },
change = {hl = 'GitSignsChange', text = '│', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
delete = {hl = 'GitSignsDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
topdelete = {hl = 'GitSignsDelete', text = '‾', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
}
<
Configuration for signs:
@ -172,6 +172,18 @@ signs *gitsigns-config-signs*
• `show_count` to enable showing count of hunk, e.g. number of deleted
lines.
Note if `hl`, `numhl` or `linehl` use a `GitSigns*` highlight and it is
not defined, it will be automatically derived by searching for other
defined highlights in the following order:
• `GitGutter*`
• `Signify*`
• `Diff*`
For example if `signs.add.hl = GitSignsAdd` and `GitSignsAdd` is not
defined but `GitGutterAdd` is defined, then `GitSignsAdd` will be linked
to `GitGutterAdd`.
keymaps *gitsigns-config-keymaps*
Type: `table`, Default:
>

View File

@ -10,6 +10,7 @@ local throttle_leading = gs_debounce.throttle_leading
local debounce_trailing = gs_debounce.debounce_trailing
local gs_popup = require('gitsigns/popup')
local gs_hl = require('gitsigns/highlight')
local sign_define = require('gitsigns/signs').sign_define
local process_config = require('gitsigns/config').process
@ -555,14 +556,12 @@ local function setup(cfg)
for t, sign_name in pairs(sign_map) do
local cs = config.signs[t]
local HlTy = {}
gs_hl.setup_highlight(cs.hl)
for _, hl in ipairs({ 'numhl', 'linehl' }) do
if config[hl] then
local hl_exists, _ = pcall(api.nvim_get_hl_by_name, cs[hl], false)
if not hl_exists then
vim.cmd(('highlight link %s %s'):format(cs[hl], cs.hl))
end
local HlTy = {}
for _, hlty in ipairs({ 'numhl', 'linehl' }) do
if config[hlty] then
gs_hl.setup_other_highlight(cs[hlty], cs.hl)
end
end

View File

@ -12,11 +12,11 @@ local schema = {
type = 'table',
deep_extend = true,
default = [[{
add = {hl = 'DiffAdd' , text = '', numhl='GitSignsAddNr' , linehl='GitSignsAddLr' },
change = {hl = 'DiffChange', text = '', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
delete = {hl = 'DiffDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
topdelete = {hl = 'DiffDelete', text = '', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
changedelete = {hl = 'DiffChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
add = {hl = 'GitSignsAdd' , text = '', numhl='GitSignsAddNr' , linehl='GitSignsAddLn' },
change = {hl = 'GitSignsChange', text = '', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
delete = {hl = 'GitSignsDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
topdelete = {hl = 'GitSignsDelete', text = '', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
}]],
description = [[
Configuration for signs:
@ -28,6 +28,18 @@ local schema = {
(see |gitsigns-config.linehl|).
`show_count` to enable showing count of hunk, e.g. number of deleted
lines.
Note if `hl`, `numhl` or `linehl` use a `GitSigns*` highlight and it is
not defined, it will be automatically derived by searching for other
defined highlights in the following order:
`GitGutter*`
`Signify*`
`Diff*`
For example if `signs.add.hl = GitSignsAdd` and `GitSignsAdd` is not
defined but `GitGutterAdd` is defined, then `GitSignsAdd` will be linked
to `GitGutterAdd`.
]],
},

View File

@ -0,0 +1,91 @@
local api = vim.api
local dprint = require("gitsigns/debug").dprint
local M = {}
local GitSignHl = {}
local hls = {
GitSignsAdd = { 'GitGutterAdd', 'SignifySignAdd', 'DiffAdd' },
GitSignsChange = { 'GitGutterChange', 'SignifySignChange', 'DiffChange' },
GitSignsDelete = { 'GitGutterDelete', 'SignifySignDelete', 'DiffDelete' },
}
local function hl_link(to, from, reverse)
local to_exists, _ = pcall(api.nvim_get_hl_by_name, to, false)
if to_exists then
return
end
if not reverse then
vim.cmd(('highlight link %s %s'):format(to, from))
return
end
local exists, hl = pcall(api.nvim_get_hl_by_name, from, true)
if exists then
local bg = hl.background and ('guibg=#%06x'):format(hl.background) or ''
local fg = hl.foreground and ('guifg=#%06x'):format(hl.foreground) or ''
vim.cmd(table.concat({ 'highlight', to, fg, bg, 'gui=reverse' }, ' '))
end
end
local stdHl = {
'DiffAdd',
'DiffChange',
'DiffDelete',
}
local function isStdHl(hl)
return vim.tbl_contains(stdHl, hl)
end
local function isGitSignHl(hl)
return hls[hl] ~= nil
end
function M.setup_highlight(hl_name0)
if not isGitSignHl(hl_name0) then
return
end
local hl_name = hl_name0
local exists, hl = pcall(api.nvim_get_hl_by_name, hl_name, true)
if exists and (hl.foreground or hl.background) then
return
end
for _, d in ipairs(hls[hl_name]) do
local _, dhl = pcall(api.nvim_get_hl_by_name, d, true)
local color = dhl.foreground or dhl.background
if color then
dprint(('Deriving %s from %s'):format(hl_name, d))
if isStdHl(d) then
hl_link(hl_name, d, true)
else
hl_link(hl_name, d)
end
return
end
end
end
function M.setup_other_highlight(hl, from_hl)
local hl_pfx, hl_sfx = hl:sub(1, -3), hl:sub(-2, -1)
if isGitSignHl(hl_pfx) and (hl_sfx == 'Ln' or hl_sfx == 'Nr') then
dprint(('Deriving %s from %s'):format(hl, from_hl))
hl_link(hl, from_hl, hl_sfx == 'Ln')
end
end
return M

View File

@ -10,6 +10,7 @@ local throttle_leading = gs_debounce.throttle_leading
local debounce_trailing = gs_debounce.debounce_trailing
local gs_popup = require('gitsigns/popup')
local gs_hl = require('gitsigns/highlight')
local sign_define = require('gitsigns/signs').sign_define
local process_config = require('gitsigns/config').process
@ -555,14 +556,12 @@ local function setup(cfg: Config)
for t, sign_name in pairs(sign_map) do
local cs = config.signs[t]
local enum HlTy 'numhl' 'linehl' end
gs_hl.setup_highlight(cs.hl)
for _, hl in ipairs({'numhl', 'linehl'} as {HlTy}) do
if config[hl] then
local hl_exists, _ = pcall(api.nvim_get_hl_by_name, cs[hl], false)
if not hl_exists then
vim.cmd(('highlight link %s %s'):format(cs[hl], cs.hl))
end
local enum HlTy 'numhl' 'linehl' end
for _, hlty in ipairs({'numhl', 'linehl'} as {HlTy}) do
if config[hlty] then
gs_hl.setup_other_highlight(cs[hlty], cs.hl)
end
end

View File

@ -12,11 +12,11 @@ local schema: {string:SchemaElem} = {
type = 'table',
deep_extend = true,
default = [[{
add = {hl = 'DiffAdd' , text = '', numhl='GitSignsAddNr' , linehl='GitSignsAddLr' },
change = {hl = 'DiffChange', text = '', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
delete = {hl = 'DiffDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
topdelete = {hl = 'DiffDelete', text = '', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLr' },
changedelete = {hl = 'DiffChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLr' },
add = {hl = 'GitSignsAdd' , text = '', numhl='GitSignsAddNr' , linehl='GitSignsAddLn' },
change = {hl = 'GitSignsChange', text = '', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
delete = {hl = 'GitSignsDelete', text = '_', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
topdelete = {hl = 'GitSignsDelete', text = '', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn' },
changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn' },
}]],
description = [[
Configuration for signs:
@ -28,6 +28,18 @@ local schema: {string:SchemaElem} = {
(see |gitsigns-config.linehl|).
`show_count` to enable showing count of hunk, e.g. number of deleted
lines.
Note if `hl`, `numhl` or `linehl` use a `GitSigns*` highlight and it is
not defined, it will be automatically derived by searching for other
defined highlights in the following order:
`GitGutter*`
`Signify*`
`Diff*`
For example if `signs.add.hl = GitSignsAdd` and `GitSignsAdd` is not
defined but `GitGutterAdd` is defined, then `GitSignsAdd` will be linked
to `GitGutterAdd`.
]]
},

View File

@ -0,0 +1,91 @@
local api = vim.api
local dprint = require("gitsigns/debug").dprint
local M = {}
local enum GitSignHl
'GitSignsAdd'
'GitSignsChange'
'GitSignsDelete'
end
local hls: {GitSignHl:{string}} = {
GitSignsAdd = { 'GitGutterAdd' , 'SignifySignAdd' , 'DiffAdd' },
GitSignsChange = { 'GitGutterChange', 'SignifySignChange', 'DiffChange' },
GitSignsDelete = { 'GitGutterDelete', 'SignifySignDelete', 'DiffDelete' },
}
local function hl_link(to: string, from: string, reverse: boolean)
local to_exists, _ = pcall(api.nvim_get_hl_by_name, to, false)
if to_exists then
return
end
if not reverse then
vim.cmd(('highlight link %s %s'):format(to, from))
return
end
local exists, hl = pcall(api.nvim_get_hl_by_name, from, true)
if exists then
local bg = hl.background and ('guibg=#%06x'):format(hl.background) or ''
local fg = hl.foreground and ('guifg=#%06x'):format(hl.foreground) or ''
vim.cmd(table.concat({'highlight', to, fg, bg, 'gui=reverse'}, ' '))
end
end
local stdHl = {
'DiffAdd',
'DiffChange',
'DiffDelete'
}
local function isStdHl(hl: string): boolean
return vim.tbl_contains(stdHl, hl)
end
local function isGitSignHl(hl: string): boolean
return hls[hl as GitSignHl] ~= nil
end
-- Setup a GitSign* highlight by deriving it from other potentially present
-- highlights.
function M.setup_highlight(hl_name0: string)
if not isGitSignHl(hl_name0) then
return
end
local hl_name = hl_name0 as GitSignHl
local exists, hl = pcall(api.nvim_get_hl_by_name, hl_name, true)
if exists and (hl.foreground or hl.background) then
-- Alread defined
return
end
for _, d in ipairs(hls[hl_name]) do
local _, dhl = pcall(api.nvim_get_hl_by_name, d, true)
local color = dhl.foreground or dhl.background
if color then
dprint(('Deriving %s from %s'):format(hl_name, d))
if isStdHl(d) then
hl_link(hl_name, d, true)
else
hl_link(hl_name, d)
end
return
end
end
end
function M.setup_other_highlight(hl: string, from_hl: string)
local hl_pfx, hl_sfx = hl:sub(1, -3), hl:sub(-2, -1)
if isGitSignHl(hl_pfx) and (hl_sfx == 'Ln' or hl_sfx == 'Nr') then
dprint(('Deriving %s from %s'):format(hl, from_hl))
hl_link(hl, from_hl, hl_sfx == 'Ln')
end
end
return M

View File

@ -18,7 +18,6 @@ local function check_status(status)
eq(status, get_buf_var("gitsigns_status_dict"))
end
local scratch = os.getenv('PJ_ROOT')..'/scratch'
local test_file = scratch..'/dummy.txt'
local newfile = scratch.."/newfile.txt"
@ -80,6 +79,10 @@ local function command_fmt(str, ...)
command(str:format(...))
end
local function edit(path)
command_fmt("edit %s", path)
end
local function buf_var_exists(name)
return pcall(get_buf_var, name)
end
@ -106,24 +109,69 @@ local function match_lines(lines, spec)
end
end
local function match_lines2(lines, spec)
local i = 1
for _, line in ipairs(lines) do
if line ~= '' then
local s = spec[i]
if s then
if s.pattern then
if string.match(line, s.text) then
i = i + 1
end
elseif s.next then
eq(s.text, line)
i = i + 1
else
if s == line then
i = i + 1
end
end
end
end
end
if i < #spec + 1 then
local unmatched = {}
for j = i, #spec do
table.insert(unmatched, spec[j].text or spec[j])
end
print(require'inspect'(unmatched))
error(('Did not match patterns:\n - %s'):format(table.concat(unmatched, '\n - ')))
end
end
local function debug_messages()
return exec_lua("return require'gitsigns'.debug_messages()")
end
local function match_debug_messages(spec)
local res = exec_lua("return require'gitsigns'.debug_messages()")
match_lines(res, spec)
match_lines(debug_messages(), spec)
end
local function match_dag(lines, spec)
for _, s in ipairs(spec) do
match_lines2(lines, {s})
end
end
local function p(str)
return {text=str, pattern=true}
end
local function n(str)
return {text=str, next=true}
end
local function testsuite(variant, advanced_features)
local test_config = {
debug_mode = true,
signs = {
add = {text = '+'},
delete = {text = '_'},
change = {text = '~'},
topdelete = {text = '^'},
changedelete = {text = '%'},
add = {hl = 'DiffAdd' , text = '+'},
delete = {hl = 'DiffDelete', text = '_'},
change = {hl = 'DiffChange', text = '~'},
topdelete = {hl = 'DiffDelete', text = '^'},
changedelete = {hl = 'DiffChange', text = '%'},
},
keymaps = {
noremap = true,
@ -188,9 +236,47 @@ local function testsuite(variant, advanced_features)
})
end)
local function edit(path)
command_fmt("edit %s", path)
end
it('sets up highlights', function()
command("set termguicolors")
local test_config2 = helpers.deepcopy(test_config)
test_config2.signs.add.hl = nil
test_config2.signs.change.hl = nil
test_config2.signs.delete.hl = nil
test_config2.signs.changedelete.hl = nil
test_config2.signs.topdelete.hl = nil
test_config2.numhl = true
test_config2.linehl = true
exec_lua('gs.setup(...)', test_config2)
local d = debug_messages()
match_dag(d, {
p'Deriving GitSignsChangeNr from GitSignsChange',
p'Deriving GitSignsChangeLn from GitSignsChange',
p'Deriving GitSignsDelete from DiffDelete',
p'Deriving GitSignsDeleteNr from GitSignsDelete',
p'Deriving GitSignsDeleteLn from GitSignsDelete',
p'Deriving GitSignsAdd from DiffAdd',
p'Deriving GitSignsAddNr from GitSignsAdd',
p'Deriving GitSignsAddLn from GitSignsAdd',
p'Deriving GitSignsDeleteNr from GitSignsDelete',
p'Deriving GitSignsDeleteLn from GitSignsDelete',
p'Deriving GitSignsChangeNr from GitSignsChange',
p'Deriving GitSignsChangeLn from GitSignsChange'
})
eq('GitSignsChange xxx gui=reverse guibg=#ffbbff',
exec_capture('hi GitSignsChange'))
eq('GitSignsDelete xxx gui=reverse guifg=#0000ff guibg=#e0ffff',
exec_capture('hi GitSignsDelete'))
eq('GitSignsAdd xxx gui=reverse guibg=#add8e6',
exec_capture('hi GitSignsAdd'))
end)
it('basic signs', function()
exec_lua('gs.setup(...)', test_config)

View File

@ -270,6 +270,8 @@ global record vim
tbl_isempty: function(table): boolean
tbl_contains: function(table, any): boolean
record InspectOptions
depth: number
newline: string