diff --git a/lua/vgit/features/buffer/LiveGutter.lua b/lua/vgit/features/buffer/LiveGutter.lua index 0b104c15..d952d84e 100644 --- a/lua/vgit/features/buffer/LiveGutter.lua +++ b/lua/vgit/features/buffer/LiveGutter.lua @@ -8,128 +8,82 @@ local live_gutter_setting = require('vgit.settings.live_gutter') local LiveGutter = Object:extend() function LiveGutter:constructor() - return { - name = 'Live Gutter', - } -end - -LiveGutter.clear = loop.coroutine(function(self, buffer) - loop.free_textlock() - - if buffer:is_rendering() then - return self - end - - buffer:sign_unplace() - - return self -end) - -function LiveGutter:reset() - local buffers = GitBuffer:list() - - for i = 1, #buffers do - local buffer = buffers[i] - - if buffer then - self:clear(buffer) - end - end + return { name = 'Live Gutter' } end LiveGutter.fetch = loop.debounce_coroutine(function(self, buffer) loop.free_textlock() - if not buffer:is_valid() then - return self - end - - loop.free_textlock() - local live_signs = buffer:get_cached_live_signs() + if not buffer:is_valid() then return self end loop.free_textlock() - buffer:clear_cached_live_signs() + local err = buffer:live_hunks() loop.free_textlock() - local err = buffer.git_object:live_hunks(buffer:get_lines()) - if err then - loop.free_textlock() - buffer:set_cached_live_signs(live_signs) console.debug.error(err) - return self end - local hunks = buffer.git_object.hunks - - if not hunks then - loop.free_textlock() - buffer:set_cached_live_signs(live_signs) - - return self - else - local diff_status = buffer.git_object:generate_diff_status() - - loop.free_textlock() - buffer:set_var('vgit_status', diff_status) - end - - loop.free_textlock() - self:clear(buffer) - - for i = 1, #hunks do - loop.free_textlock() - buffer:cache_live_sign(hunks[i]) - end + buffer:sign_unplace() + buffer:generate_status() return self -end, 50) +end, 10) function LiveGutter:render(buffer, top, bot) - if not live_gutter_setting:get('enabled') then - return self - end + if not live_gutter_setting:get('enabled') then return self end local hunks = buffer.git_object.hunks + if not hunks then return self end - if not hunks then - return self + local signs = {} + for i = top, bot do + signs[#signs + 1] = buffer.signs[i] end - local cached_live_signs = buffer:get_cached_live_signs() - local gutter_signs = {} + buffer:sign_placelist(signs) - for i = top, bot do - gutter_signs[#gutter_signs + 1] = cached_live_signs[i] + return self +end + +function LiveGutter:reset() + local buffers = GitBuffer:list() + + for i = 1, #buffers do + local buffer = buffers[i] + + if buffer then + buffer:sign_unplace() + end end - buffer:sign_placelist(gutter_signs) + return self end function LiveGutter:register_events() git_buffer_store - .attach('attach', function(git_buffer) self:fetch(git_buffer) end) - .attach('reload', function(git_buffer) - loop.free_textlock() - self:fetch(git_buffer) + .attach('attach', function(buffer) + self:fetch(buffer) end) - .attach('change', function(git_buffer, p_lnum, n_lnum, byte_count) - if p_lnum == n_lnum and byte_count == 0 then - return - end - loop.free_textlock() - self:fetch(git_buffer) + .attach('reload', function(buffer) + self:fetch(buffer) + end) + .attach('change', function(buffer) + self:fetch(buffer) end) - .attach('watch', function(git_buffer) - git_buffer:sync() - self:fetch(git_buffer) + .attach('watch', function(buffer) + buffer:sync() + self:fetch(buffer) end) - .attach('git_watch', function(git_buffers) - for i = 1, #git_buffers do - self:fetch(git_buffers[i]) + .attach('git_watch', function(buffers) + for i = 1, #buffers do + local buffer = buffers[i] + self:fetch(buffer) end end) - .attach('render', function(git_buffer, top, bot) self:render(git_buffer, top, bot) end) + .attach('render', function(buffer, top, bot) + self:render(buffer, top, bot) + end) return self end diff --git a/lua/vgit/git/GitBuffer.lua b/lua/vgit/git/GitBuffer.lua index b22e0bf1..ffd796d5 100644 --- a/lua/vgit/git/GitBuffer.lua +++ b/lua/vgit/git/GitBuffer.lua @@ -1,4 +1,3 @@ -local loop = require('vgit.core.loop') local Buffer = require('vgit.core.Buffer') local GitObject = require('vgit.git.GitObject') local signs_setting = require('vgit.settings.signs') @@ -8,85 +7,57 @@ local GitBuffer = Buffer:extend() function GitBuffer:sync() Buffer.sync(self) - self.git_object = GitObject(self.filename) - self.live_signs = {} + self.signs = {} self.is_processing = false self.is_showing_lens = false + self.git_object = GitObject(self.filename) return self end -function GitBuffer:set_cached_live_signs(live_signs) - self.state.live_signs = live_signs - - return self -end - -function GitBuffer:get_cached_live_signs() return self.state.live_signs end - -function GitBuffer:clear_cached_live_signs() - self.state.live_signs = {} - - return self +function GitBuffer:is_ignored() + return self.git_object:is_ignored() end -function GitBuffer:cache_live_sign(hunk) - local bufnr = self.bufnr - local live_signs = self:get_cached_live_signs() - local sign_priority = signs_setting:get('priority') - local sign_group = self.namespace:get_sign_ns_id(self) - local sign_types = signs_setting:get('usage').main - - for j = hunk.top, hunk.bot do - local lnum = (hunk.type == 'remove' and j == 0) and 1 or j - - live_signs[lnum] = { - id = lnum, - lnum = lnum, - buffer = bufnr, - group = sign_group, - name = sign_types[hunk.type], - priority = sign_priority, - } - end - - return self +function GitBuffer:is_tracked() + return self.git_object:tracked_filename() ~= '' end function GitBuffer:is_inside_git_dir() - loop.free_textlock() - local is_inside_git_dir = self.git_object:is_inside_git_dir() - loop.free_textlock() - - if not is_inside_git_dir then - return false - end - - return true + return self.git_object:is_inside_git_dir() end -function GitBuffer:is_ignored() - loop.free_textlock() - local is_ignored = self.git_object:is_ignored() - loop.free_textlock() +function GitBuffer:generate_status() + return self:set_var('vgit_status', self.git_object:generate_status()) +end - if is_ignored then - return true - end +function GitBuffer:live_hunks() + local lines = self:get_lines() + local err, hunks = self.git_object:live_hunks(lines) - return false -end + if err then return err end -function GitBuffer:is_tracked() - loop.free_textlock() - local tracked_filename = self.git_object:tracked_filename() - loop.free_textlock() + local sign_types = signs_setting:get('usage').main + local sign_priority = signs_setting:get('priority') + local sign_group = self.namespace:get_sign_ns_id(self) - if tracked_filename == '' then - return false + self.signs = {} + for i = 1, #hunks do + local hunk = hunks[i] + for j = hunk.top, hunk.bot do + local lnum = (hunk.type == 'remove' and j == 0) and 1 or j + self.signs[lnum] = { + id = lnum, + lnum = lnum, + buffer = self.bufnr, + group = sign_group, + name = sign_types[hunk.type], + priority = sign_priority, + } + end end - return true + return nil, hunks end return GitBuffer diff --git a/lua/vgit/git/GitObject.lua b/lua/vgit/git/GitObject.lua index b8d09d1b..329f6963 100644 --- a/lua/vgit/git/GitObject.lua +++ b/lua/vgit/git/GitObject.lua @@ -1,10 +1,10 @@ -local utils = require('vgit.core.utils') local fs = require('vgit.core.fs') local loop = require('vgit.core.loop') -local Hunk = require('vgit.git.cli.models.Hunk') -local Patch = require('vgit.git.cli.models.Patch') local Git = require('vgit.git.cli.Git') +local utils = require('vgit.core.utils') local Object = require('vgit.core.Object') +local Hunk = require('vgit.git.cli.models.Hunk') +local Patch = require('vgit.git.cli.models.Patch') local GitObject = Object:extend() @@ -26,13 +26,33 @@ function GitObject:constructor(filename) } end -function GitObject:get_filename() return self.filename.native end +function GitObject:is_inside_git_dir() + return self.git:is_inside_git_dir() +end -function GitObject:get_filetype() return self.filetype end +function GitObject:is_ignored() + return self.git:is_ignored(self.filename.native) +end -function GitObject:is_tracked() return self:tracked_filename() ~= '' end +function GitObject:get_filename() + return self.filename.native +end -function GitObject:is_in_remote() return self.git:is_in_remote(self:tracked_filename()) end +function GitObject:get_filetype() + return self.filetype +end + +function GitObject:is_tracked() + return self:tracked_filename() ~= '' +end + +function GitObject:is_in_remote() + return self.git:is_in_remote(self:tracked_filename()) +end + +function GitObject:config() + return self.git:config({ is_background = true }) +end function GitObject:tracked_filename() if self.filename.tracked == nil then @@ -44,11 +64,13 @@ function GitObject:tracked_filename() return self.filename.tracked end -function GitObject:patch_hunk(hunk) return Patch(self.git:tracked_full_filename(self.filename.native), hunk) end +function GitObject:patch_hunk(hunk) + return Patch(self.git:tracked_full_filename(self.filename.native), hunk) +end function GitObject:stage_hunk_from_patch(patch) local patch_filename = fs.tmpname() - + loop.free_textlock() fs.write_file(patch_filename, patch) loop.free_textlock() @@ -56,14 +78,13 @@ function GitObject:stage_hunk_from_patch(patch) loop.free_textlock() fs.remove_file(patch_filename) - loop.free_textlock() return err end function GitObject:unstage_hunk_from_patch(patch) local patch_filename = fs.tmpname() - + loop.free_textlock() fs.write_file(patch_filename, patch) loop.free_textlock() @@ -71,14 +92,17 @@ function GitObject:unstage_hunk_from_patch(patch) loop.free_textlock() fs.remove_file(patch_filename) - loop.free_textlock() return err end -function GitObject:stage_hunk(hunk) return self:stage_hunk_from_patch(self:patch_hunk(hunk)) end +function GitObject:stage_hunk(hunk) + return self:stage_hunk_from_patch(self:patch_hunk(hunk)) +end -function GitObject:unstage_hunk(hunk) return self:unstage_hunk_from_patch(self:patch_hunk(hunk)) end +function GitObject:unstage_hunk(hunk) + return self:unstage_hunk_from_patch(self:patch_hunk(hunk)) +end function GitObject:stage() local filename = self:tracked_filename() @@ -90,26 +114,18 @@ function GitObject:stage() return self.git:stage_file(filename) end -function GitObject:unstage() return self.git:unstage_file(self:tracked_filename()) end - -function GitObject:is_inside_git_dir() return self.git:is_inside_git_dir() end +function GitObject:unstage() + return self.git:unstage_file(self:tracked_filename()) +end function GitObject:lines(commit_hash) - commit_hash = commit_hash or '' - - return self.git:show(self:tracked_filename(), commit_hash, { is_background = true }) + return self.git:show(self:tracked_filename(), commit_hash or '', { is_background = true }) end -function GitObject:is_ignored() return self.git:is_ignored(self.filename.native) end - function GitObject:blame_line(lnum) - if self._cache.line_blames[lnum] then - return nil, self._cache.line_blames[lnum] - end + if self._cache.line_blames[lnum] then return nil, self._cache.line_blames[lnum] end - local err, blame = self.git:blame_line(self:tracked_filename(), lnum, { - is_background = true, - }) + local err, blame = self.git:blame_line(self:tracked_filename(), lnum, { is_background = true }) if blame then self._cache.line_blames[lnum] = blame @@ -119,31 +135,23 @@ function GitObject:blame_line(lnum) end function GitObject:blames() - return self.git:blames(self:tracked_filename(), { - is_background = true, - }) + return self.git:blames(self:tracked_filename(), { is_background = true }) end -function GitObject:config() - return self.git:config({ - is_background = true, - }) -end +function GitObject:live_hunks(current_lines) + loop.free_textlock() + local filename = self:tracked_filename() -function GitObject:native_hunks(filename, current_lines) if filename == '' then + loop.free_textlock() local hunks = self.git:untracked_hunks(current_lines) - self.hunks = hunks return nil, hunks end - local original_lines_err, original_lines = self:lines() - loop.free_textlock() - if original_lines_err then - return original_lines_err - end + local original_lines_err, original_lines = self:lines() + if original_lines_err then return original_lines_err end local o_lines_str = '' local c_lines_str = '' @@ -164,6 +172,7 @@ function GitObject:native_hunks(filename, current_lines) self.hunks = {} local hunks = self.hunks + loop.free_textlock() vim.diff(o_lines_str, c_lines_str, { on_hunk = function(start_o, count_o, start_c, count_c) local hunk = Hunk({ { start_o, count_o }, { start_c, count_c } }) @@ -186,86 +195,37 @@ function GitObject:native_hunks(filename, current_lines) end, algorithm = 'myers', }) - return nil, hunks -end - -function GitObject:piped_hunks(filename, current_lines) - if filename == '' then - local hunks = self.git:untracked_hunks(current_lines) - self.hunks = hunks - return nil, hunks - end - - local temp_filename_b = fs.tmpname() - local temp_filename_a = fs.tmpname() - local original_lines_err, original_lines = self:lines() - - loop.free_textlock() - if original_lines_err then - return original_lines_err - end - - fs.write_file(temp_filename_a, original_lines) - loop.free_textlock() - fs.write_file(temp_filename_b, current_lines) - loop.free_textlock() - - local hunks_err, hunks = self.git:file_hunks(temp_filename_a, temp_filename_b, { is_background = true }) - loop.free_textlock() - fs.remove_file(temp_filename_a) - loop.free_textlock() - fs.remove_file(temp_filename_b) - loop.free_textlock() - - if not hunks_err then - self.hunks = hunks - end - - return hunks_err, hunks + return nil, hunks end -function GitObject:live_hunks(current_lines) - loop.free_textlock() - local filename = self:tracked_filename() - local inexpensive_lines_limit = 5000 - - if #current_lines > inexpensive_lines_limit then - return self:piped_hunks(filename, current_lines) - end - - return self:native_hunks(filename, current_lines) +function GitObject:staged_hunks() + return self.git:staged_hunks(self:tracked_filename(), { is_background = true }) end -function GitObject:staged_hunks() return self.git:staged_hunks(self:tracked_filename(), { is_background = true }) end - function GitObject:remote_hunks(parent_hash, commit_hash) return self.git:remote_hunks(self:tracked_filename(), parent_hash, commit_hash, { is_background = true }) end function GitObject:logs() - return self.git:file_logs(self:tracked_filename(), { - is_background = true, - }) + return self.git:file_logs(self:tracked_filename(), { is_background = true }) end -function GitObject:status() return self.git:file_status(self:tracked_filename()) end +function GitObject:status() + return self.git:file_status(self:tracked_filename()) +end -function GitObject:generate_diff_status() +function GitObject:generate_status() local hunks = self.hunks or {} - local stats_dict = { - added = 0, - changed = 0, - removed = 0, - } + local stats_dict = { added = 0, changed = 0, removed = 0 } + for _, h in ipairs(hunks) do - -- hunk stats only contain added/removed, lines that - -- are both added and removed are considered "changed" local changed = math.min(h.stat.added, h.stat.removed) stats_dict.added = stats_dict.added + math.abs(h.stat.added - changed) stats_dict.removed = stats_dict.removed + math.abs(h.stat.removed - changed) stats_dict.changed = stats_dict.changed + changed end + return stats_dict end