feat(diffthis): various improvements
- When running diffthis (or vim-fugitive's Gdiffsplit), hunk operations to the index will now update the diff buffer. - Similar to vim-fugitive's Gdiffsplit, buffers of a file in the git index can now be edited. When the buffer is written, it updates the git index accordingly. Likewise the index buffer can also be reloaded (via 'edit'). - Moved all diffthis code to a separate module. Fixes #501
This commit is contained in:
parent
83ab3ca26f
commit
acdd74bc6c
|
@ -182,6 +182,9 @@ diffthis({base}) *gitsigns.diffthis()*
|
|||
Perform a |vimdiff| on the given file with {base} if it is
|
||||
given, or with the currently set base (index by default).
|
||||
|
||||
If {base} is the index, then the opened diff buffer is editable
|
||||
and any written changes will update the index accordingly.
|
||||
|
||||
Examples: >
|
||||
" Diff against the index
|
||||
:Gitsigns diffthis
|
||||
|
|
|
@ -40,6 +40,7 @@ build = {
|
|||
['gitsigns.current_line_blame'] = 'lua/gitsigns/current_line_blame.lua',
|
||||
['gitsigns.debounce'] = 'lua/gitsigns/debounce.lua',
|
||||
['gitsigns.debug'] = 'lua/gitsigns/debug.lua',
|
||||
['gitsigns.diffthis'] = 'lua/gitsigns/diffthis.lua',
|
||||
['gitsigns.diff_ext'] = 'lua/gitsigns/diff_ext.lua',
|
||||
['gitsigns.diff_int'] = 'lua/gitsigns/diff_int.lua',
|
||||
['gitsigns.diff_int.xdl_diff_ffi'] = 'lua/gitsigns/diff_int/xdl_diff_ffi.lua',
|
||||
|
|
|
@ -115,6 +115,13 @@ local function get_cursor_hunk(bufnr, hunks)
|
|||
return gs_hunks.find_hunk(lnum, hunks)
|
||||
end
|
||||
|
||||
local function update(bufnr)
|
||||
manager.update(bufnr)
|
||||
if vim.wo.diff then
|
||||
require('gitsigns.diffthis').update(bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -165,7 +172,7 @@ M.stage_hunk = mk_repeatable(void(function(range)
|
|||
table.insert(bcache.staged_diffs, hunk)
|
||||
|
||||
bcache.compare_text = nil
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end))
|
||||
|
||||
|
||||
|
@ -249,7 +256,7 @@ M.undo_stage_hunk = void(function()
|
|||
|
||||
bcache.git_obj:stage_hunks({ hunk }, true)
|
||||
bcache.compare_text = nil
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
|
||||
|
@ -283,7 +290,7 @@ M.stage_buffer = void(function()
|
|||
end
|
||||
bcache.compare_text = nil
|
||||
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
|
||||
|
@ -311,7 +318,7 @@ M.reset_buffer_index = void(function()
|
|||
bcache.compare_text = nil
|
||||
|
||||
scheduler()
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
local function process_nav_opts(opts)
|
||||
|
@ -452,22 +459,6 @@ local function noautocmd(f)
|
|||
end
|
||||
|
||||
|
||||
local function strip_cr(xs0)
|
||||
for i = 1, #xs0 do
|
||||
if xs0[i]:sub(-1) ~= '\r' then
|
||||
|
||||
return xs0
|
||||
end
|
||||
end
|
||||
|
||||
local xs = vim.deepcopy(xs0)
|
||||
for i = 1, #xs do
|
||||
xs[i] = xs[i]:sub(1, -2)
|
||||
end
|
||||
return xs
|
||||
end
|
||||
|
||||
|
||||
|
||||
M.preview_hunk = noautocmd(function()
|
||||
|
||||
|
@ -479,7 +470,7 @@ M.preview_hunk = noautocmd(function()
|
|||
|
||||
local hlines = gs_hunks.patch_lines(hunk)
|
||||
if vim.bo[cbuf].fileformat == 'dos' then
|
||||
hlines = strip_cr(hlines)
|
||||
hlines = util.strip_cr(hlines)
|
||||
end
|
||||
|
||||
local lines = {
|
||||
|
@ -677,17 +668,10 @@ M.blame_line = void(function(opts)
|
|||
end
|
||||
end)
|
||||
|
||||
local function calc_base(base)
|
||||
if base and base:sub(1, 1):match('[~\\^]') then
|
||||
base = 'HEAD' .. base
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
local function update_buf_base(buf, bcache, base)
|
||||
bcache.base = base
|
||||
bcache.compare_text = nil
|
||||
manager.update(buf, bcache)
|
||||
update(buf)
|
||||
end
|
||||
|
||||
|
||||
|
@ -724,7 +708,7 @@ end
|
|||
|
||||
|
||||
M.change_base = void(function(base, global)
|
||||
base = calc_base(base)
|
||||
base = util.calc_base(base)
|
||||
|
||||
if global then
|
||||
config.base = base
|
||||
|
@ -765,61 +749,13 @@ end
|
|||
|
||||
|
||||
|
||||
M.diffthis = void(function(base)
|
||||
local bufnr = current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then return end
|
||||
|
||||
if api.nvim_win_get_option(0, 'diff') then return end
|
||||
|
||||
local ff = vim.bo[bufnr].fileformat
|
||||
|
||||
local text
|
||||
local err
|
||||
local comp_rev = bcache:get_compare_rev(calc_base(base))
|
||||
|
||||
if base then
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if ff == 'dos' then
|
||||
text = strip_cr(text)
|
||||
end
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
scheduler()
|
||||
else
|
||||
text = bcache:get_compare_text()
|
||||
end
|
||||
|
||||
local ft = api.nvim_buf_get_option(bufnr, 'filetype')
|
||||
|
||||
local bufname = string.format(
|
||||
'gitsigns://%s/%s',
|
||||
bcache.git_obj.repo.gitdir,
|
||||
comp_rev .. ':' .. bcache.git_obj.relpath)
|
||||
|
||||
|
||||
vim.cmd('diffthis')
|
||||
|
||||
vim.cmd(table.concat({
|
||||
'keepalt', 'aboveleft',
|
||||
config.diff_opts.vertical and 'vertical' or '',
|
||||
'split', bufname,
|
||||
}, ' '))
|
||||
|
||||
local dbuf = current_buf()
|
||||
|
||||
api.nvim_buf_set_option(dbuf, 'modifiable', true)
|
||||
util.set_lines(dbuf, 0, -1, text)
|
||||
api.nvim_buf_set_option(dbuf, 'modifiable', false)
|
||||
|
||||
api.nvim_buf_set_option(dbuf, 'filetype', ft)
|
||||
api.nvim_buf_set_option(dbuf, 'buftype', 'nowrite')
|
||||
api.nvim_buf_set_option(dbuf, 'bufhidden', 'wipe')
|
||||
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
M.diffthis = function(base)
|
||||
local diffthis = require('gitsigns.diffthis')
|
||||
diffthis.run(base, config.diff_opts.vertical)
|
||||
end
|
||||
|
||||
local function hunks_to_qflist(buf_or_filename, hunks, qflist)
|
||||
for i, hunk in ipairs(hunks) do
|
||||
|
|
|
@ -5,6 +5,7 @@ local fun1 = {}
|
|||
local fun2 = {}
|
||||
local fun2_2 = {}
|
||||
local fun3 = {}
|
||||
local fun4 = {}
|
||||
|
||||
local Async = {}
|
||||
|
||||
|
@ -18,6 +19,7 @@ local Async = {}
|
|||
|
||||
|
||||
|
||||
|
||||
local async_thread = {
|
||||
threads = {},
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ local M = {CacheEntry = {}, CacheObj = {}, }
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local CacheEntry = M.CacheEntry
|
||||
|
@ -48,6 +49,15 @@ CacheEntry.get_compare_rev = function(self, base)
|
|||
return string.format(':%d', stage)
|
||||
end
|
||||
|
||||
CacheEntry.get_diffthis_bufname = function(self, rev)
|
||||
rev = rev or self:get_compare_rev()
|
||||
return string.format(
|
||||
'gitsigns://%s/%s',
|
||||
self.git_obj.repo.gitdir,
|
||||
rev .. ':' .. self.git_obj.relpath)
|
||||
|
||||
end
|
||||
|
||||
CacheEntry.get_compare_text = function(self)
|
||||
if self.compare_text then
|
||||
return self.compare_text
|
||||
|
|
|
@ -51,7 +51,7 @@ end
|
|||
|
||||
|
||||
|
||||
function M.throttle_by_id(fn)
|
||||
function M.throttle_by_id(fn, schedule)
|
||||
local scheduled = {}
|
||||
local running = {}
|
||||
return function(id, ...)
|
||||
|
@ -59,7 +59,9 @@ function M.throttle_by_id(fn)
|
|||
|
||||
return
|
||||
end
|
||||
scheduled[id] = true
|
||||
if not running[id] or schedule then
|
||||
scheduled[id] = true
|
||||
end
|
||||
if running[id] then
|
||||
return
|
||||
end
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
local api = vim.api
|
||||
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
local awrap = require('gitsigns.async').wrap
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local CacheEntry = gs_cache.CacheEntry
|
||||
|
||||
local nvim = require('gitsigns.nvim')
|
||||
local util = require('gitsigns.util')
|
||||
local manager = require('gitsigns.manager')
|
||||
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local input = awrap(vim.ui.input, 2)
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
|
||||
|
||||
local bufread = void(function(bufnr, dbufnr, base, bcache)
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local text
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
text = bcache:get_compare_text()
|
||||
else
|
||||
local err
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
text = util.strip_cr(text)
|
||||
end
|
||||
scheduler()
|
||||
end
|
||||
|
||||
local modifiable = vim.bo[dbufnr].modifiable
|
||||
vim.bo[dbufnr].modifiable = true
|
||||
util.set_lines(dbufnr, 0, -1, text)
|
||||
|
||||
vim.bo[dbufnr].modifiable = modifiable
|
||||
vim.bo[dbufnr].modified = false
|
||||
vim.bo[dbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[dbufnr].bufhidden = 'wipe'
|
||||
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
|
||||
local bufwrite = void(function(bufnr, dbufnr, base, bcache)
|
||||
local buftext = util.buf_lines(dbufnr)
|
||||
bcache.git_obj:stage_lines(buftext)
|
||||
vim.bo[dbufnr].modified = false
|
||||
|
||||
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
bcache.compare_text = buftext
|
||||
manager.update(bufnr, bcache)
|
||||
end
|
||||
end)
|
||||
|
||||
M.run = void(function(base, vertical)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
|
||||
if vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local bufname = bcache:get_diffthis_bufname(comp_rev)
|
||||
|
||||
vim.cmd('diffthis')
|
||||
|
||||
vim.cmd(table.concat({
|
||||
'keepalt', 'aboveleft',
|
||||
vertical and 'vertical' or '',
|
||||
'split', bufname,
|
||||
}, ' '))
|
||||
|
||||
local dbuf = vim.api.nvim_get_current_buf()
|
||||
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
|
||||
if comp_rev == ':0' then
|
||||
vim.bo[dbuf].buftype = 'acwrite'
|
||||
|
||||
nvim.autocmd('BufReadCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
end,
|
||||
})
|
||||
|
||||
nvim.autocmd('BufWriteCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufwrite(bufnr, dbuf, base, bcache)
|
||||
end,
|
||||
})
|
||||
else
|
||||
vim.bo[dbuf].buftype = 'nowrite'
|
||||
vim.bo[dbuf].modifiable = false
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local function should_reload(bufnr)
|
||||
if not vim.bo[bufnr].modified then
|
||||
return true
|
||||
end
|
||||
local response
|
||||
while not vim.tbl_contains({ 'O', 'L' }, response) do
|
||||
response = input({
|
||||
prompt = 'Warning: The git index has changed and the buffer was changed as well. [O]K, (L)oad File:',
|
||||
})
|
||||
end
|
||||
return response == 'L'
|
||||
end
|
||||
|
||||
|
||||
M.update = throttle_by_id(void(function(bufnr)
|
||||
if not vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
|
||||
|
||||
local bufname = bcache:get_diffthis_bufname()
|
||||
|
||||
for _, w in ipairs(api.nvim_list_wins()) do
|
||||
if api.nvim_win_is_valid(w) then
|
||||
local b = api.nvim_win_get_buf(w)
|
||||
local bname = api.nvim_buf_get_name(b)
|
||||
if bname == bufname or vim.startswith(bname, 'fugitive://') then
|
||||
if should_reload(b) then
|
||||
api.nvim_buf_call(b, function()
|
||||
|
||||
|
||||
|
||||
|
||||
vim.cmd('doautocmd BufReadCmd')
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end))
|
||||
|
||||
return M
|
|
@ -107,6 +107,7 @@ local M = {BlameInfo = {}, Version = {}, Repo = {}, FileProps = {}, Obj = {}, }
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
local in_git_dir = function(file)
|
||||
|
@ -451,7 +452,7 @@ Obj.ensure_file_in_index = function(self)
|
|||
else
|
||||
|
||||
|
||||
local info = table.concat({ self.mode_bits, self.object_name, self.relpath }, ',')
|
||||
local info = string.format('%s,%s,%s', self.mode_bits, self.object_name, self.relpath)
|
||||
self:command({ 'update-index', '--add', '--cacheinfo', info })
|
||||
end
|
||||
|
||||
|
@ -459,6 +460,18 @@ Obj.ensure_file_in_index = function(self)
|
|||
end
|
||||
end
|
||||
|
||||
Obj.stage_lines = function(self, lines)
|
||||
local stdout = self:command({
|
||||
'hash-object', '-w', '--path', self.relpath, '--stdin',
|
||||
}, { writer = lines })
|
||||
|
||||
local new_object = stdout[1]
|
||||
|
||||
self:command({
|
||||
'update-index', '--cacheinfo', string.format('%s,%s,%s', self.mode_bits, new_object, self.relpath),
|
||||
})
|
||||
end
|
||||
|
||||
Obj.stage_hunks = function(self, hunks, invert)
|
||||
self:ensure_file_in_index()
|
||||
self:command({
|
||||
|
|
|
@ -328,7 +328,7 @@ end
|
|||
|
||||
|
||||
|
||||
M.update = throttle_by_id(update0)
|
||||
M.update = throttle_by_id(update0, true)
|
||||
|
||||
M.setup = function()
|
||||
M.update_debounced = debounce_trailing(config.update_debounce, void(M.update))
|
||||
|
|
|
@ -107,4 +107,28 @@ function M.copy_array(x)
|
|||
return r
|
||||
end
|
||||
|
||||
|
||||
function M.strip_cr(xs0)
|
||||
for i = 1, #xs0 do
|
||||
if xs0[i]:sub(-1) ~= '\r' then
|
||||
|
||||
return xs0
|
||||
end
|
||||
end
|
||||
|
||||
local xs = vim.deepcopy(xs0)
|
||||
for i = 1, #xs do
|
||||
xs[i] = xs[i]:sub(1, -2)
|
||||
end
|
||||
return xs
|
||||
end
|
||||
|
||||
function M.calc_base(base)
|
||||
if base and base:sub(1, 1):match('[~\\^]') then
|
||||
base = 'HEAD' .. base
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
|
|
@ -47,7 +47,7 @@ local record M
|
|||
blame_line : function -- function()
|
||||
change_base : function(base: string, global: boolean)
|
||||
reset_base : function(global: boolean)
|
||||
diffthis : function -- function(base: string)
|
||||
diffthis : function(base: string)
|
||||
|
||||
record QFListOpts
|
||||
use_location_list: boolean
|
||||
|
@ -115,6 +115,13 @@ local function get_cursor_hunk(bufnr: integer, hunks: {Hunk}): Hunk, integer
|
|||
return gs_hunks.find_hunk(lnum, hunks)
|
||||
end
|
||||
|
||||
local function update(bufnr: integer)
|
||||
manager.update(bufnr)
|
||||
if vim.wo.diff then
|
||||
require('gitsigns.diffthis').update(bufnr)
|
||||
end
|
||||
end
|
||||
|
||||
--- Stage the hunk at the cursor position, or all lines in the
|
||||
--- given range. If {range} is provided, all lines in the given
|
||||
--- range are staged. This supports partial-hunks, meaning if a
|
||||
|
@ -165,7 +172,7 @@ M.stage_hunk = mk_repeatable(void(function(range: {integer, integer})
|
|||
table.insert(bcache.staged_diffs, hunk)
|
||||
|
||||
bcache.compare_text = nil -- Invalidate
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end))
|
||||
|
||||
--- Reset the lines of the hunk at the cursor position, or all
|
||||
|
@ -249,7 +256,7 @@ M.undo_stage_hunk = void(function()
|
|||
|
||||
bcache.git_obj:stage_hunks({hunk}, true)
|
||||
bcache.compare_text = nil -- Invalidate
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
--- Stage all hunks in current buffer.
|
||||
|
@ -283,7 +290,7 @@ M.stage_buffer = void(function()
|
|||
end
|
||||
bcache.compare_text = nil -- Invalidate
|
||||
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
--- Unstage all hunks for current buffer in the index. Note:
|
||||
|
@ -311,7 +318,7 @@ M.reset_buffer_index = void(function()
|
|||
bcache.compare_text = nil -- Invalidate
|
||||
|
||||
scheduler()
|
||||
manager.update(bufnr)
|
||||
update(bufnr)
|
||||
end)
|
||||
|
||||
local function process_nav_opts(opts: NavHunkOpts)
|
||||
|
@ -451,22 +458,6 @@ local function noautocmd(f: function()): function()
|
|||
end
|
||||
end
|
||||
|
||||
-- Strip '\r' from the EOL of each line only if all lines end with '\r'
|
||||
local function strip_cr(xs0: {string}): {string}
|
||||
for i = 1, #xs0 do
|
||||
if xs0[i]:sub(-1) ~= '\r' then
|
||||
-- don't strip, return early
|
||||
return xs0
|
||||
end
|
||||
end
|
||||
-- all lines end with '\r', need to strip
|
||||
local xs = vim.deepcopy(xs0)
|
||||
for i = 1, #xs do
|
||||
xs[i] = xs[i]:sub(1, -2)
|
||||
end
|
||||
return xs
|
||||
end
|
||||
|
||||
--- Preview the hunk at the cursor position in a floating
|
||||
--- window.
|
||||
M.preview_hunk = noautocmd(function()
|
||||
|
@ -479,7 +470,7 @@ M.preview_hunk = noautocmd(function()
|
|||
|
||||
local hlines = gs_hunks.patch_lines(hunk)
|
||||
if vim.bo[cbuf].fileformat == 'dos' then
|
||||
hlines = strip_cr(hlines)
|
||||
hlines = util.strip_cr(hlines)
|
||||
end
|
||||
|
||||
local lines = {
|
||||
|
@ -677,17 +668,10 @@ M.blame_line = void(function(opts: boolean | BlameOpts)
|
|||
end
|
||||
end)
|
||||
|
||||
local function calc_base(base: string): string
|
||||
if base and base:sub(1, 1):match('[~\\^]') then
|
||||
base = 'HEAD'..base
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
local function update_buf_base(buf: integer, bcache: CacheEntry, base: string)
|
||||
bcache.base = base
|
||||
bcache.compare_text = nil
|
||||
manager.update(buf, bcache)
|
||||
update(buf)
|
||||
end
|
||||
|
||||
--- Change the base revision to diff against. If {base} is not
|
||||
|
@ -724,7 +708,7 @@ end
|
|||
--- For a more complete list of ways to specify bases, see
|
||||
--- |gitsigns-revision|.
|
||||
M.change_base = void(function(base: string, global: boolean)
|
||||
base = calc_base(base)
|
||||
base = util.calc_base(base)
|
||||
|
||||
if global then
|
||||
config.base = base
|
||||
|
@ -752,6 +736,9 @@ end
|
|||
--- Perform a |vimdiff| on the given file with {base} if it is
|
||||
--- given, or with the currently set base (index by default).
|
||||
---
|
||||
--- If {base} is the index, then the opened diff buffer is editable
|
||||
--- and any written changes will update the index accordingly.
|
||||
---
|
||||
--- Examples: >
|
||||
--- " Diff against the index
|
||||
--- :Gitsigns diffthis
|
||||
|
@ -765,61 +752,10 @@ end
|
|||
---
|
||||
--- Attributes: ~
|
||||
--- {async}
|
||||
M.diffthis = void(function(base: string)
|
||||
local bufnr = current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then return end
|
||||
|
||||
if api.nvim_win_get_option(0, 'diff') then return end
|
||||
|
||||
local ff = vim.bo[bufnr].fileformat
|
||||
|
||||
local text: {string}
|
||||
local err: string
|
||||
local comp_rev = bcache:get_compare_rev(calc_base(base))
|
||||
|
||||
if base then
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if ff == 'dos' then
|
||||
text = strip_cr(text)
|
||||
end
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
scheduler()
|
||||
else
|
||||
text = bcache:get_compare_text()
|
||||
end
|
||||
|
||||
local ft = api.nvim_buf_get_option(bufnr, 'filetype')
|
||||
|
||||
local bufname = string.format(
|
||||
'gitsigns://%s/%s',
|
||||
bcache.git_obj.repo.gitdir,
|
||||
comp_rev..':'..bcache.git_obj.relpath
|
||||
)
|
||||
|
||||
vim.cmd'diffthis'
|
||||
|
||||
vim.cmd(table.concat({
|
||||
'keepalt', 'aboveleft',
|
||||
config.diff_opts.vertical and 'vertical' or '',
|
||||
'split', bufname
|
||||
}, ' '))
|
||||
|
||||
local dbuf = current_buf()
|
||||
|
||||
api.nvim_buf_set_option(dbuf, 'modifiable', true)
|
||||
util.set_lines(dbuf, 0, -1, text)
|
||||
api.nvim_buf_set_option(dbuf, 'modifiable', false)
|
||||
|
||||
api.nvim_buf_set_option(dbuf, 'filetype', ft)
|
||||
api.nvim_buf_set_option(dbuf, 'buftype', 'nowrite')
|
||||
api.nvim_buf_set_option(dbuf, 'bufhidden', 'wipe')
|
||||
|
||||
vim.cmd'diffthis'
|
||||
end)
|
||||
M.diffthis = function(base: string)
|
||||
local diffthis = require('gitsigns.diffthis')
|
||||
diffthis.run(base, config.diff_opts.vertical)
|
||||
end
|
||||
|
||||
local function hunks_to_qflist(buf_or_filename: number|string, hunks: {Hunk}, qflist: {vim.fn.QFItem})
|
||||
for i, hunk in ipairs(hunks) do
|
||||
|
|
|
@ -5,11 +5,13 @@ local type fun1 = function <A1> (A1)
|
|||
local type fun2 = function <A1,A2> (A1,A2)
|
||||
local type fun2_2 = function <A1,A2,R1,R2> (A1,A2) : R1,R2
|
||||
local type fun3 = function <A1,A2,A3> (A1,A2,A3)
|
||||
local type fun4 = function <A1,A2,A3,A4> (A1,A2,A3,A4)
|
||||
|
||||
local record Async
|
||||
void: function (fun0 ): fun0
|
||||
void: function<A1> (fun1<A1> ): fun1<A1>
|
||||
void: function<A1,A2>(fun2<A1,A2>): fun2<A1,A2>
|
||||
void: function<A1,A2,A3,A4>(fun4<A1,A2,A3,A4>): fun4<A1,A2,A3,A4>
|
||||
|
||||
wrap: function<A1,A2,R1,R2>(function(A1,A2, function(R1,R2)), integer): fun2_2 <A1,A2,R1,R2>
|
||||
wrap: function<A1,A2,A3> (function(A1,A2,A3,function()) , integer): fun3 <A1,A2,A3>
|
||||
|
|
|
@ -18,6 +18,7 @@ local record M
|
|||
|
||||
get_compare_rev: function(CacheEntry, base: string): string
|
||||
get_compare_text: function(self: CacheEntry): {string}
|
||||
get_diffthis_bufname: function(self: CacheEntry, rev: string): string
|
||||
new: function(CacheEntry): CacheEntry
|
||||
destroy: function(CacheEntry)
|
||||
end
|
||||
|
@ -48,6 +49,15 @@ CacheEntry.get_compare_rev = function(self: CacheEntry, base: string): string
|
|||
return string.format(':%d', stage)
|
||||
end
|
||||
|
||||
CacheEntry.get_diffthis_bufname = function(self: CacheEntry, rev: string): string
|
||||
rev = rev or self:get_compare_rev()
|
||||
return string.format(
|
||||
'gitsigns://%s/%s',
|
||||
self.git_obj.repo.gitdir,
|
||||
rev..':'..self.git_obj.relpath
|
||||
)
|
||||
end
|
||||
|
||||
CacheEntry.get_compare_text = function(self: CacheEntry): {string}
|
||||
if self.compare_text then
|
||||
return self.compare_text
|
||||
|
|
|
@ -51,7 +51,7 @@ end
|
|||
--
|
||||
--@param fn (function) Function to throttle
|
||||
--@returns (function) throttled function.
|
||||
function M.throttle_by_id(fn: function): function
|
||||
function M.throttle_by_id(fn: function, schedule: boolean): function
|
||||
local scheduled: {any:boolean} = {}
|
||||
local running: {any:boolean} = {}
|
||||
return function(id: any, ...)
|
||||
|
@ -59,7 +59,9 @@ function M.throttle_by_id(fn: function): function
|
|||
-- If fn is already scheduled, then drop
|
||||
return
|
||||
end
|
||||
scheduled[id] = true
|
||||
if not running[id] or schedule then
|
||||
scheduled[id] = true
|
||||
end
|
||||
if running[id] then
|
||||
return
|
||||
end
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
local api = vim.api
|
||||
|
||||
local void = require('gitsigns.async').void
|
||||
local scheduler = require('gitsigns.async').scheduler
|
||||
local awrap = require('gitsigns.async').wrap
|
||||
|
||||
local gs_cache = require('gitsigns.cache')
|
||||
local cache = gs_cache.cache
|
||||
local CacheEntry = gs_cache.CacheEntry
|
||||
|
||||
local nvim = require('gitsigns.nvim')
|
||||
local util = require('gitsigns.util')
|
||||
local manager = require('gitsigns.manager')
|
||||
|
||||
local throttle_by_id = require('gitsigns.debounce').throttle_by_id
|
||||
|
||||
local input = awrap(vim.ui.input, 2)
|
||||
|
||||
local record M
|
||||
run: function
|
||||
update: function
|
||||
end
|
||||
|
||||
local bufread = void(function(bufnr: integer, dbufnr: integer, base: string, bcache: CacheEntry)
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local text: {string}
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
text = bcache:get_compare_text()
|
||||
else
|
||||
local err: string
|
||||
text, err = bcache.git_obj:get_show_text(comp_rev)
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
if vim.bo[bufnr].fileformat == 'dos' then
|
||||
text = util.strip_cr(text)
|
||||
end
|
||||
scheduler()
|
||||
end
|
||||
|
||||
local modifiable = vim.bo[dbufnr].modifiable
|
||||
vim.bo[dbufnr].modifiable = true
|
||||
util.set_lines(dbufnr, 0, -1, text)
|
||||
|
||||
vim.bo[dbufnr].modifiable = modifiable
|
||||
vim.bo[dbufnr].modified = false
|
||||
vim.bo[dbufnr].filetype = vim.bo[bufnr].filetype
|
||||
vim.bo[dbufnr].bufhidden = 'wipe'
|
||||
|
||||
vim.cmd'diffthis'
|
||||
end)
|
||||
|
||||
local bufwrite = void(function(bufnr: integer, dbufnr: integer, base: string, bcache: CacheEntry)
|
||||
local buftext = util.buf_lines(dbufnr)
|
||||
bcache.git_obj:stage_lines(buftext)
|
||||
vim.bo[dbufnr].modified = false
|
||||
-- If diff buffer base matches the bcache base then also update the
|
||||
-- signs.
|
||||
if util.calc_base(base) == util.calc_base(bcache.base) then
|
||||
bcache.compare_text = buftext
|
||||
manager.update(bufnr, bcache)
|
||||
end
|
||||
end)
|
||||
|
||||
M.run = void(function(base: string, vertical: boolean)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local bcache = cache[bufnr]
|
||||
if not bcache then
|
||||
return
|
||||
end
|
||||
|
||||
if vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local comp_rev = bcache:get_compare_rev(util.calc_base(base))
|
||||
local bufname = bcache:get_diffthis_bufname(comp_rev)
|
||||
|
||||
vim.cmd'diffthis'
|
||||
|
||||
vim.cmd(table.concat({
|
||||
'keepalt', 'aboveleft',
|
||||
vertical and 'vertical' or '',
|
||||
'split', bufname
|
||||
}, ' '))
|
||||
|
||||
local dbuf = vim.api.nvim_get_current_buf()
|
||||
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
|
||||
if comp_rev == ':0' then
|
||||
vim.bo[dbuf].buftype = 'acwrite'
|
||||
|
||||
nvim.autocmd('BufReadCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufread(bufnr, dbuf, base, bcache)
|
||||
end
|
||||
})
|
||||
|
||||
nvim.autocmd('BufWriteCmd', {
|
||||
group = 'gitsigns',
|
||||
buffer = dbuf,
|
||||
callback = function()
|
||||
bufwrite(bufnr, dbuf, base, bcache)
|
||||
end
|
||||
})
|
||||
else
|
||||
vim.bo[dbuf].buftype = 'nowrite'
|
||||
vim.bo[dbuf].modifiable = false
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local function should_reload(bufnr: integer): boolean
|
||||
if not vim.bo[bufnr].modified then
|
||||
return true
|
||||
end
|
||||
local response: string
|
||||
while not vim.tbl_contains({'O', 'L'}, response) do
|
||||
response = input{
|
||||
prompt = 'Warning: The git index has changed and the buffer was changed as well. [O]K, (L)oad File:'
|
||||
}
|
||||
end
|
||||
return response == 'L'
|
||||
end
|
||||
|
||||
-- This function needs to be throttled as there is a call to vim.ui.input
|
||||
M.update = throttle_by_id(void(function(bufnr: integer)
|
||||
if not vim.wo.diff then
|
||||
return
|
||||
end
|
||||
|
||||
local bcache = cache[bufnr]
|
||||
|
||||
-- Note this will be the bufname for the currently set base
|
||||
-- which are the only ones we want to update
|
||||
local bufname = bcache:get_diffthis_bufname()
|
||||
|
||||
for _, w in ipairs(api.nvim_list_wins()) do
|
||||
if api.nvim_win_is_valid(w) then
|
||||
local b = api.nvim_win_get_buf(w)
|
||||
local bname = api.nvim_buf_get_name(b)
|
||||
if bname == bufname or vim.startswith(bname, 'fugitive://') then
|
||||
if should_reload(b) then
|
||||
api.nvim_buf_call(b, function(): nil
|
||||
|
||||
|
||||
|
||||
|
||||
vim.cmd('doautocmd BufReadCmd')
|
||||
vim.cmd('diffthis')
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end))
|
||||
|
||||
return M
|
|
@ -103,6 +103,7 @@ local record M
|
|||
get_show_text : function(Obj, string): {string}, string
|
||||
ensure_file_in_index : function(Obj)
|
||||
stage_hunks : function(Obj, {Hunk}, boolean)
|
||||
stage_lines : function(Obj, {string})
|
||||
has_moved : function(Obj): string
|
||||
new : function(string): Obj
|
||||
end
|
||||
|
@ -451,7 +452,7 @@ Obj.ensure_file_in_index = function(self: Obj)
|
|||
else
|
||||
-- Update the index with the common ancestor (stage 1) which is what bcache
|
||||
-- stores
|
||||
local info = table.concat({self.mode_bits, self.object_name, self.relpath}, ',')
|
||||
local info = string.format('%s,%s,%s', self.mode_bits, self.object_name, self.relpath)
|
||||
self:command{'update-index', '--add', '--cacheinfo', info}
|
||||
end
|
||||
|
||||
|
@ -459,6 +460,18 @@ Obj.ensure_file_in_index = function(self: Obj)
|
|||
end
|
||||
end
|
||||
|
||||
Obj.stage_lines = function(self: Obj, lines: {string})
|
||||
local stdout = self:command({
|
||||
'hash-object', '-w', '--path', self.relpath, '--stdin'
|
||||
}, { writer = lines })
|
||||
|
||||
local new_object = stdout[1]
|
||||
|
||||
self:command{
|
||||
'update-index', '--cacheinfo', string.format('%s,%s,%s', self.mode_bits, new_object, self.relpath)
|
||||
}
|
||||
end
|
||||
|
||||
Obj.stage_hunks = function(self: Obj, hunks: {Hunk}, invert: boolean)
|
||||
self:ensure_file_in_index()
|
||||
self:command({
|
||||
|
|
|
@ -328,7 +328,7 @@ end
|
|||
-- Since updates are asynchronous we need to make sure an update isn't performed
|
||||
-- whilst another one is in progress. If this happens then schedule another
|
||||
-- update after the current one has completed.
|
||||
M.update = throttle_by_id(update0) as function(integer, CacheEntry)
|
||||
M.update = throttle_by_id(update0, true) as function(integer, CacheEntry)
|
||||
|
||||
M.setup = function()
|
||||
M.update_debounced = debounce_trailing(config.update_debounce, void(M.update)) as function(integer)
|
||||
|
|
|
@ -107,4 +107,28 @@ function M.copy_array<T>(x: {T}): {T}
|
|||
return r
|
||||
end
|
||||
|
||||
-- Strip '\r' from the EOL of each line only if all lines end with '\r'
|
||||
function M.strip_cr(xs0: {string}): {string}
|
||||
for i = 1, #xs0 do
|
||||
if xs0[i]:sub(-1) ~= '\r' then
|
||||
-- don't strip, return early
|
||||
return xs0
|
||||
end
|
||||
end
|
||||
-- all lines end with '\r', need to strip
|
||||
local xs = vim.deepcopy(xs0)
|
||||
for i = 1, #xs do
|
||||
xs[i] = xs[i]:sub(1, -2)
|
||||
end
|
||||
return xs
|
||||
end
|
||||
|
||||
function M.calc_base(base: string): string
|
||||
if base and base:sub(1, 1):match('[~\\^]') then
|
||||
base = 'HEAD'..base
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
|
|
@ -137,7 +137,7 @@ local record api
|
|||
nvim_list_runtime_paths: function(): {string}
|
||||
nvim_list_tabpages: function(): {any}
|
||||
nvim_list_uis: function(): any
|
||||
nvim_list_wins: function(): {number}
|
||||
nvim_list_wins: function(): {integer}
|
||||
nvim_load_context: function({string:any}): any
|
||||
nvim_open_win: function(number, boolean, {string:any}): integer
|
||||
nvim_out_write: function(string)
|
||||
|
@ -178,7 +178,7 @@ local record api
|
|||
nvim_win_call: function<T>(number, (function(): T)): T
|
||||
nvim_win_close: function(number, boolean)
|
||||
nvim_win_del_var: function(number, string)
|
||||
nvim_win_get_buf: function(number): number
|
||||
nvim_win_get_buf: function(integer): integer
|
||||
nvim_win_get_config: function(number): {string:any}
|
||||
nvim_win_get_cursor: function(number): {integer}
|
||||
nvim_win_get_height: function(integer): integer
|
||||
|
@ -209,6 +209,7 @@ global record vim
|
|||
executable: function(string): integer
|
||||
expand: function(string): string
|
||||
getcwd: function(): string
|
||||
input: function(string, string): string
|
||||
|
||||
record QFItem
|
||||
bufnr: integer
|
||||
|
@ -290,6 +291,20 @@ global record vim
|
|||
record BufOption
|
||||
{BufOption}
|
||||
fileformat: string
|
||||
filetype: string
|
||||
modifiable: boolean
|
||||
modified: boolean
|
||||
|
||||
enum BufHidden
|
||||
'' 'hide' 'unload' 'delete' 'wipe'
|
||||
end
|
||||
|
||||
bufhidden: BufHidden
|
||||
|
||||
enum BufType
|
||||
'' 'acwrite' 'help' 'nofile' 'nowrite' 'quickfix' 'terminal' 'prompt'
|
||||
end
|
||||
buftype: BufType
|
||||
end
|
||||
|
||||
bo: BufOption
|
||||
|
@ -489,6 +504,10 @@ global record vim
|
|||
|
||||
wait: function(number, function, number, boolean)
|
||||
|
||||
record ui
|
||||
input: function({string:any}, function(string))
|
||||
end
|
||||
|
||||
record VersionDetails
|
||||
api_compatible: integer
|
||||
api_level: integer
|
||||
|
|
Loading…
Reference in New Issue