feat: Enable `attach()` to work with any buffer when given context data

* feat: Enable `attach()` to work with any buffer when given context data.

* fix(test): Pattern escape file names when matching debug messages.
This commit is contained in:
Sindre T. Strøm 2023-02-03 10:25:22 +01:00 committed by GitHub
parent 0d4fe37ba5
commit ec4742a7ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 67 deletions

View File

@ -102,7 +102,7 @@ setup({cfg}) *gitsigns.setup()*
{cfg} Table object containing configuration for
Gitsigns. See |gitsigns-usage| for more details.
attach({bufnr}) *gitsigns.attach()*
attach({bufnr}, {ctx}) *gitsigns.attach()*
Attach Gitsigns to the buffer.
Attributes: ~
@ -110,6 +110,21 @@ attach({bufnr}) *gitsigns.attach()*
Parameters: ~
{bufnr} (number): Buffer number
{ctx} (table|nil):
Git context data that may optionally be used to attach to any
buffer that represents a real git object.
• {file}: (string)
Path to the file represented by the buffer, relative to the
top-level.
• {toplevel}: (string)
Path to the top-level of the parent git repository.
• {gitdir}: (string)
Path to the git directory of the parent git repository
(typically the ".git/" directory).
• {commit}: (string)
The git revision that the file belongs to.
• {base}: (string|nil)
The git revision that the file should be compared to.
detach({bufnr}) *gitsigns.detach()*
Detach Gitsigns from the buffer {bufnr}. If {bufnr} is not

91
lua/gitsigns.lua generated
View File

@ -28,7 +28,7 @@ local api = vim.api
local uv = vim.loop
local current_buf = api.nvim_get_current_buf
local M = {}
local M = {GitContext = {}, }
@ -36,6 +36,16 @@ local M = {}
local GitContext = M.GitContext
function M.detach_all()
for k, _ in pairs(cache) do
M.detach(k)
@ -186,7 +196,7 @@ end
local attach_throttled = throttle_by_id(function(cbuf, aucmd)
local attach_throttled = throttle_by_id(function(cbuf, ctx, aucmd)
local __FUNC__ = 'attach'
if vimgrep_running then
dprint('attaching is disabled')
@ -209,33 +219,45 @@ local attach_throttled = throttle_by_id(function(cbuf, aucmd)
return
end
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
dprint('Exceeds max_file_length')
return
end
if vim.bo[cbuf].buftype ~= '' then
dprint('Non-normal buffer')
return
end
local file, commit = get_buf_path(cbuf)
local encoding = vim.bo[cbuf].fileencoding
if encoding == '' then
encoding = 'utf-8'
end
local file
local commit
local gitdir_oap
local toplevel_oap
local file_dir = util.dirname(file)
if ctx then
gitdir_oap = ctx.gitdir
toplevel_oap = ctx.toplevel
file = ctx.toplevel .. util.path_sep .. ctx.file
commit = ctx.commit
else
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
dprint('Exceeds max_file_length')
return
end
if not file_dir or not util.path_exists(file_dir) then
dprint('Not a path')
return
if vim.bo[cbuf].buftype ~= '' then
dprint('Non-normal buffer')
return
end
file, commit = get_buf_path(cbuf)
local file_dir = util.dirname(file)
if not file_dir or not util.path_exists(file_dir) then
dprint('Not a path')
return
end
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
end
local gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
if not git_obj then
if not git_obj and not ctx then
git_obj = try_worktrees(cbuf, file, encoding)
scheduler()
end
@ -258,7 +280,7 @@ local attach_throttled = throttle_by_id(function(cbuf, aucmd)
return
end
if not util.path_exists(file) or uv.fs_stat(file).type == 'directory' then
if not ctx and (not util.path_exists(file) or uv.fs_stat(file).type == 'directory') then
dprint('Not a file')
return
end
@ -283,7 +305,7 @@ local attach_throttled = throttle_by_id(function(cbuf, aucmd)
end
cache[cbuf] = CacheEntry.new({
base = config.base,
base = ctx and ctx.base or config.base,
file = file,
commit = commit,
gitdir_watcher = manager.watch_gitdir(cbuf, repo.gitdir),
@ -318,8 +340,23 @@ end)
M.attach = void(function(bufnr, _trigger)
attach_throttled(bufnr or current_buf(), _trigger)
M.attach = void(function(bufnr, ctx, _trigger)
attach_throttled(bufnr or current_buf(), ctx, _trigger)
end)
local function setup_cli()
@ -414,7 +451,7 @@ M.setup = void(function(cfg)
for _, buf in ipairs(api.nvim_list_bufs()) do
if api.nvim_buf_is_loaded(buf) and
api.nvim_buf_get_name(buf) ~= '' then
M.attach(buf, 'setup')
M.attach(buf, nil, 'setup')
scheduler()
end
end
@ -423,9 +460,9 @@ M.setup = void(function(cfg)
autocmd('VimLeavePre', M.detach_all)
autocmd('ColorScheme', hl.setup_highlights)
autocmd('BufRead', wrap_func(M.attach, nil, 'BufRead'))
autocmd('BufNewFile', wrap_func(M.attach, nil, 'BufNewFile'))
autocmd('BufWritePost', wrap_func(M.attach, nil, 'BufWritePost'))
autocmd('BufRead', wrap_func(M.attach, nil, nil, 'BufRead'))
autocmd('BufNewFile', wrap_func(M.attach, nil, nil, 'BufNewFile'))
autocmd('BufWritePost', wrap_func(M.attach, nil, nil, 'BufWritePost'))
autocmd('OptionSet', {
pattern = 'fileformat',

2
lua/gitsigns/git.lua generated
View File

@ -290,7 +290,7 @@ function M.get_repo_info(path, cmd, gitdir, toplevel)
local results = git_command(args, {
command = cmd or 'git',
suppress_stderr = true,
cwd = path,
cwd = toplevel or path,
})
local ret = {

View File

@ -32,9 +32,19 @@ local record M
setup : function(cfg: Config)
detach : function(bufnr: integer, _keep_signs: boolean)
detach_all : function()
attach : function(cbuf: integer, trigger: string)
attach : function(cbuf: integer, ctx: GitContext, trigger: string)
record GitContext
toplevel: string
gitdir: string
file: string
commit: string
base: string
end
end
local GitContext = M.GitContext
--- Detach Gitsigns from all buffers it is attached to.
function M.detach_all()
for k, _ in pairs(cache as {integer:CacheEntry}) do
@ -186,7 +196,7 @@ end
-- Ensure attaches cannot be interleaved.
-- Since attaches are asynchronous we need to make sure an attach isn't
-- performed whilst another one is in progress.
local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
local attach_throttled = throttle_by_id(function(cbuf: integer, ctx: GitContext, aucmd: string)
local __FUNC__ = 'attach'
if vimgrep_running then
dprint('attaching is disabled')
@ -209,33 +219,45 @@ local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
return
end
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
dprint('Exceeds max_file_length')
return
end
if vim.bo[cbuf].buftype ~= '' then
dprint('Non-normal buffer')
return
end
local file, commit = get_buf_path(cbuf)
local encoding = vim.bo[cbuf].fileencoding
if encoding == '' then
encoding = 'utf-8'
end
local file: string
local commit: string
local gitdir_oap: string
local toplevel_oap: string
local file_dir = util.dirname(file)
if ctx then
gitdir_oap = ctx.gitdir
toplevel_oap = ctx.toplevel
file = ctx.toplevel .. util.path_sep .. ctx.file
commit = ctx.commit
else
if api.nvim_buf_line_count(cbuf) > config.max_file_length then
dprint('Exceeds max_file_length')
return
end
if not file_dir or not util.path_exists(file_dir) then
dprint('Not a path')
return
if vim.bo[cbuf].buftype ~= '' then
dprint('Non-normal buffer')
return
end
file, commit = get_buf_path(cbuf)
local file_dir = util.dirname(file)
if not file_dir or not util.path_exists(file_dir) then
dprint('Not a path')
return
end
gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
end
local gitdir_oap, toplevel_oap = on_attach_pre(cbuf)
local git_obj = git.Obj.new(file, encoding, gitdir_oap, toplevel_oap)
if not git_obj then
if not git_obj and not ctx then
git_obj = try_worktrees(cbuf, file, encoding)
scheduler()
end
@ -258,7 +280,7 @@ local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
return
end
if not util.path_exists(file) or uv.fs_stat(file).type == 'directory' then
if not ctx and (not util.path_exists(file) or uv.fs_stat(file).type == 'directory') then
dprint('Not a file')
return
end
@ -283,7 +305,7 @@ local attach_throttled = throttle_by_id(function(cbuf: integer, aucmd: string)
end
cache[cbuf] = CacheEntry.new {
base = config.base,
base = ctx and ctx.base or config.base,
file = file,
commit = commit,
gitdir_watcher = manager.watch_gitdir(cbuf, repo.gitdir),
@ -318,8 +340,23 @@ end)
---
--- Parameters: ~
--- {bufnr} (number): Buffer number
M.attach = void(function(bufnr: integer, _trigger: string)
attach_throttled(bufnr or current_buf(), _trigger)
--- {ctx} (table|nil):
--- Git context data that may optionally be used to attach to any
--- buffer that represents a real git object.
--- • {file}: (string)
--- Path to the file represented by the buffer, relative to the
--- top-level.
--- • {toplevel}: (string)
--- Path to the top-level of the parent git repository.
--- • {gitdir}: (string)
--- Path to the git directory of the parent git repository
--- (typically the ".git/" directory).
--- • {commit}: (string)
--- The git revision that the file belongs to.
--- • {base}: (string|nil)
--- The git revision that the file should be compared to.
M.attach = void(function(bufnr: integer, ctx: GitContext, _trigger: string)
attach_throttled(bufnr or current_buf(), ctx, _trigger)
end)
local function setup_cli()
@ -414,7 +451,7 @@ M.setup = void(function(cfg: Config)
for _, buf in ipairs(api.nvim_list_bufs()) do
if api.nvim_buf_is_loaded(buf)
and api.nvim_buf_get_name(buf) ~= '' then
M.attach(buf, 'setup')
M.attach(buf, nil, 'setup')
scheduler()
end
end
@ -423,9 +460,9 @@ M.setup = void(function(cfg: Config)
autocmd('VimLeavePre' , M.detach_all)
autocmd('ColorScheme' , hl.setup_highlights)
autocmd('BufRead' , wrap_func(M.attach, nil, 'BufRead'))
autocmd('BufNewFile' , wrap_func(M.attach, nil, 'BufNewFile'))
autocmd('BufWritePost', wrap_func(M.attach, nil, 'BufWritePost'))
autocmd('BufRead' , wrap_func(M.attach, nil, nil, 'BufRead'))
autocmd('BufNewFile' , wrap_func(M.attach, nil, nil, 'BufNewFile'))
autocmd('BufWritePost', wrap_func(M.attach, nil, nil, 'BufWritePost'))
autocmd('OptionSet', {
pattern = 'fileformat',

View File

@ -290,7 +290,7 @@ function M.get_repo_info(path: string, cmd: string, gitdir: string, toplevel: st
local results = git_command(args, {
command = cmd or 'git',
suppress_stderr = true,
cwd = path
cwd = toplevel or path,
})
local ret: M.RepoInfo = {

View File

@ -55,7 +55,7 @@ describe('gitdir_watcher', function()
'attach(1): Attaching (trigger=BufRead)',
p"run_job: git .* config user.name",
p"run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD",
p('run_job: git .* ls%-files .* '..test_file),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
'watch_gitdir(1): Watching git dir',
p'run_job: git .* show :0:dummy.txt',
'update(1): updates: 1, jobs: 6',
@ -70,10 +70,10 @@ describe('gitdir_watcher', function()
match_debug_messages {
'watcher_cb(1): Git dir update',
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
p('run_job: git .* ls%-files .* '..test_file),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
p'run_job: git .* diff %-%-name%-status %-C %-%-cached',
'handle_moved(1): File moved to dummy.txt2',
p('run_job: git .* ls%-files .* '..test_file2),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file2)),
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt to .*/dummy.txt2',
p'run_job: git .* show :0:dummy.txt2',
'update(1): updates: 2, jobs: 11'
@ -88,10 +88,10 @@ describe('gitdir_watcher', function()
match_debug_messages {
'watcher_cb(1): Git dir update',
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
p('run_job: git .* ls%-files .* '..test_file2),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file2)),
p'run_job: git .* diff %-%-name%-status %-C %-%-cached',
'handle_moved(1): File moved to dummy.txt3',
p('run_job: git .* ls%-files .* '..test_file3),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file3)),
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt2 to .*/dummy.txt3',
p'run_job: git .* show :0:dummy.txt3',
'update(1): updates: 3, jobs: 16'
@ -106,11 +106,11 @@ describe('gitdir_watcher', function()
match_debug_messages {
'watcher_cb(1): Git dir update',
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
p('run_job: git .* ls%-files .* '..test_file3),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file3)),
p'run_job: git .* diff %-%-name%-status %-C %-%-cached',
p('run_job: git .* ls%-files .* '..test_file),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
'handle_moved(1): Moved file reset',
p('run_job: git .* ls%-files .* '..test_file),
p('run_job: git .* ls%-files .* '..helpers.pesc(test_file)),
p'handle_moved%(1%): Renamed buffer 1 from .*/dummy.txt3 to .*/dummy.txt',
p'run_job: git .* show :0:dummy.txt',
'update(1): updates: 4, jobs: 22'

View File

@ -91,7 +91,7 @@ describe('gitsigns', function()
'attach(1): Attaching (trigger=BufRead)',
p'run_job: git .* config user.name',
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
p('run_job: git .* ls%-files %-%-stage %-%-others %-%-exclude%-standard %-%-eol '..test_file),
p('run_job: git .* ls%-files %-%-stage %-%-others %-%-exclude%-standard %-%-eol '..helpers.pesc(test_file)),
'watch_gitdir(1): Watching git dir',
p'run_job: git .* show :0:dummy.txt',
'update(1): updates: 1, jobs: 7'
@ -201,7 +201,7 @@ describe('gitsigns', function()
'attach(1): Attaching (trigger=BufNewFile)',
p'run_job: git .* config user.name',
p'run_job: git .* rev%-parse %-%-show%-toplevel %-%-absolute%-git%-dir %-%-abbrev%-ref HEAD',
p('run_job: git .* ls%-files %-%-stage %-%-others %-%-exclude%-standard %-%-eol '..newfile),
p('run_job: git .* ls%-files %-%-stage %-%-others %-%-exclude%-standard %-%-eol '..helpers.pesc(newfile)),
'attach(1): Not a file',
}

View File

@ -47,6 +47,15 @@ local test_file_text = {
'content', 'doesn\'t', 'matter,', 'it', 'just', 'needs', 'to', 'be', 'static.'
}
--- Escapes magic chars in |lua-patterns|.
---
---@see https://github.com/rxi/lume
---@param s string String to escape
---@return string %-escaped pattern string
function M.pesc(s)
return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
end
function M.git(args)
system{"git", "-C", M.scratch, unpack(args)}
end