From 6b9e9352bea4d12bdf3728dbc006a6fb5c8e461b Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Mon, 11 Oct 2021 18:37:09 +0600 Subject: [PATCH] refactor: split diff component (#81) --- lua/lualine/components/diff.lua | 217 ----------------------- lua/lualine/components/diff/git_diff.lua | 135 ++++++++++++++ lua/lualine/components/diff/init.lua | 88 +++++++++ 3 files changed, 223 insertions(+), 217 deletions(-) delete mode 100644 lua/lualine/components/diff.lua create mode 100644 lua/lualine/components/diff/git_diff.lua create mode 100644 lua/lualine/components/diff/init.lua diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua deleted file mode 100644 index 246c983..0000000 --- a/lua/lualine/components/diff.lua +++ /dev/null @@ -1,217 +0,0 @@ --- Copyright (c) 2020-2021 shadmansaleh --- MIT license, see LICENSE for more details. -local lualine_require = require 'lualine_require' -local modules = lualine_require.lazy_require { - utils = 'lualine.utils.utils', - utils_notices = 'lualine.utils.notices', - highlight = 'lualine.highlight', - Job = 'lualine.utils.job', -} -local M = lualine_require.require('lualine.component'):extend() - --- Vars --- variable to store git diff stats -M.git_diff = nil --- accumulates output from diff process -M.diff_output_cache = {} --- variable to store git_diff job -M.diff_job = nil -M.active_bufnr = '0' - -local diff_cache = {} -- Stores last known value of diff of a buffer - -local default_options = { - colored = true, - symbols = { added = '+', modified = '~', removed = '-' }, - diff_color = { - added = { - fg = modules.utils.extract_highlight_colors('DiffAdd', 'fg') or '#f0e130', - }, - modified = { - fg = modules.utils.extract_highlight_colors('DiffChange', 'fg') or '#ff0038', - }, - removed = { - fg = modules.utils.extract_highlight_colors('DiffDelete', 'fg') or '#ff0038', - }, - }, -} - --- Initializer -function M:init(options) - M.super.init(self, options) - self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - -- create highlights and save highlight_name in highlights table - if self.options.colored then - self.highlights = { - added = modules.highlight.create_component_highlight_group( - self.options.diff_color.added, - 'diff_added', - self.options - ), - modified = modules.highlight.create_component_highlight_group( - self.options.diff_color.modified, - 'diff_modified', - self.options - ), - removed = modules.highlight.create_component_highlight_group( - self.options.diff_color.removed, - 'diff_removed', - self.options - ), - } - end - - M.diff_checker_enabled = type(self.options.source) ~= 'function' - - if M.diff_checker_enabled then - -- setup internal source - modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff'.update_diff_args()") - modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff'.update_git_diff()") - M.update_diff_args() - end -end - --- Function that runs everytime statusline is updated -function M:update_status(is_focused) - local git_diff - if M.diff_checker_enabled then - if M.active_bufnr ~= vim.g.actual_curbuf then - -- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286 - -- See upstream issue https://github.com/neovim/neovim/issues/15300 - -- Diff is out of sync re sync it. - M.update_diff_args() - end - git_diff = M.git_diff - else - git_diff = self.options.source() - end - - if not is_focused then - git_diff = diff_cache[vim.fn.bufnr()] or {} - end - if git_diff == nil then - return '' - end - - local colors = {} - if self.options.colored then - -- load the highlights and store them in colors table - for name, highlight_name in pairs(self.highlights) do - colors[name] = modules.highlight.component_format_highlight(highlight_name) - end - end - - local result = {} - -- loop though data and load available sections in result table - for _, name in ipairs { 'added', 'modified', 'removed' } do - if git_diff[name] and git_diff[name] > 0 then - if self.options.colored then - table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name]) - else - table.insert(result, self.options.symbols[name] .. git_diff[name]) - end - end - end - if #result > 0 then - return table.concat(result, ' ') - else - return '' - end -end - --- Api to get git sign count --- scheme : --- { --- added = added_count, --- modified = modified_count, --- removed = removed_count, --- } --- error_code = { added = -1, modified = -1, removed = -1 } -function M.get_sign_count() - if M.diff_checker_enabled then - M.update_diff_args() - end - return M.git_diff or { added = -1, modified = -1, removed = -1 } -end - --- process diff data and update git_diff{ added, removed, modified } -function M.process_diff(data) - -- Adapted from https://github.com/wbthomason/nvim-vcs.lua - local added, removed, modified = 0, 0, 0 - for _, line in ipairs(data) do - if string.find(line, [[^@@ ]]) then - local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]]) - local line_stats = { - mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]), - new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]), - } - - if line_stats.mod_count == 0 and line_stats.new_count > 0 then - added = added + line_stats.new_count - elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then - removed = removed + line_stats.mod_count - else - local min = math.min(line_stats.mod_count, line_stats.new_count) - modified = modified + min - added = added + line_stats.new_count - min - removed = removed + line_stats.mod_count - min - end - end - end - M.git_diff = { added = added, modified = modified, removed = removed } -end - --- Updates the job args -function M.update_diff_args() - -- Donn't show git diff when current buffer doesn't have a filename - M.active_bufnr = tostring(vim.fn.bufnr()) - if #vim.fn.expand '%' == 0 then - M.diff_args = nil - M.git_diff = nil - return - end - M.diff_args = { - cmd = string.format( - [[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]], - vim.fn.expand '%:h', - vim.fn.expand '%:t' - ), - on_stdout = function(_, data) - if next(data) then - M.diff_output_cache = vim.list_extend(M.diff_output_cache, data) - end - end, - on_stderr = function(_, data) - data = table.concat(data, '\n') - if #data > 1 or (#data == 1 and #data[1] > 0) then - M.git_diff = nil - M.diff_output_cache = {} - end - end, - on_exit = function() - if #M.diff_output_cache > 0 then - M.process_diff(M.diff_output_cache) - else - M.git_diff = { added = 0, modified = 0, removed = 0 } - end - diff_cache[vim.fn.bufnr()] = M.git_diff - end, - } - M.update_git_diff() -end - --- Update git_diff veriable -function M.update_git_diff() - if M.diff_args then - M.diff_output_cache = {} - if M.diff_job then - M.diff_job:stop() - end - M.diff_job = modules.Job(M.diff_args) - if M.diff_job then - M.diff_job:start() - end - end -end - -return M diff --git a/lua/lualine/components/diff/git_diff.lua b/lua/lualine/components/diff/git_diff.lua new file mode 100644 index 0000000..8507aa0 --- /dev/null +++ b/lua/lualine/components/diff/git_diff.lua @@ -0,0 +1,135 @@ +local lualine_require = require 'lualine_require' +local modules = lualine_require.lazy_require { + utils = 'lualine.utils.utils', + Job = 'lualine.utils.job', +} + +local M = {} + +-- Vars +-- variable to store git diff stats +local git_diff = nil +-- accumulates output from diff process +local diff_output_cache = {} +-- variable to store git_diff job +local diff_job = nil + +local active_bufnr = '0' +local diff_cache = {} -- Stores last known value of diff of a buffer + +-- initialize the module +function M.init(opts) + if type(opts.source) == 'function' then + M.src = opts.source + else + modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff.git_diff'.update_diff_args()") + modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff.git_diff'.update_git_diff()") + M.update_diff_args() + end +end + +-- Api to get git sign count +-- scheme : +-- { +-- added = added_count, +-- modified = modified_count, +-- removed = removed_count, +-- } +-- error_code = { added = -1, modified = -1, removed = -1 } +function M.get_sign_count(bufnr) + if bufnr then + return diff_cache[bufnr] + end + if M.src then + git_diff = M.src() + diff_cache[vim.fn.bufnr()] = git_diff + elseif vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then + -- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286 + -- See upstream issue https://github.com/neovim/neovim/issues/15300 + -- Diff is out of sync re sync it. + M.update_diff_args() + end + return git_diff +end + +-- process diff data and update git_diff{ added, removed, modified } +local function process_diff(data) + -- Adapted from https://github.com/wbthomason/nvim-vcs.lua + local added, removed, modified = 0, 0, 0 + for _, line in ipairs(data) do + if string.find(line, [[^@@ ]]) then + local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]]) + local line_stats = { + mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]), + new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]), + } + + if line_stats.mod_count == 0 and line_stats.new_count > 0 then + added = added + line_stats.new_count + elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then + removed = removed + line_stats.mod_count + else + local min = math.min(line_stats.mod_count, line_stats.new_count) + modified = modified + min + added = added + line_stats.new_count - min + removed = removed + line_stats.mod_count - min + end + end + end + git_diff = { added = added, modified = modified, removed = removed } +end + +-- Updates the job args +function M.update_diff_args() + -- Donn't show git diff when current buffer doesn't have a filename + active_bufnr = tostring(vim.fn.bufnr()) + if #vim.fn.expand '%' == 0 then + M.diff_args = nil + git_diff = nil + return + end + M.diff_args = { + cmd = string.format( + [[git -C %s --no-pager diff --no-color --no-ext-diff -U0 -- %s]], + vim.fn.expand '%:h', + vim.fn.expand '%:t' + ), + on_stdout = function(_, data) + if next(data) then + diff_output_cache = vim.list_extend(diff_output_cache, data) + end + end, + on_stderr = function(_, data) + data = table.concat(data, '\n') + if #data > 1 or (#data == 1 and #data[1] > 0) then + git_diff = nil + diff_output_cache = {} + end + end, + on_exit = function() + if #diff_output_cache > 0 then + process_diff(diff_output_cache) + else + git_diff = { added = 0, modified = 0, removed = 0 } + end + diff_cache[vim.fn.bufnr()] = git_diff + end, + } + M.update_git_diff() +end + +-- Update git_diff veriable +function M.update_git_diff() + if M.diff_args then + diff_output_cache = {} + if diff_job then + diff_job:stop() + end + diff_job = modules.Job(M.diff_args) + if diff_job then + diff_job:start() + end + end +end + +return M diff --git a/lua/lualine/components/diff/init.lua b/lua/lualine/components/diff/init.lua new file mode 100644 index 0000000..0e584d1 --- /dev/null +++ b/lua/lualine/components/diff/init.lua @@ -0,0 +1,88 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. +local lualine_require = require 'lualine_require' +local modules = lualine_require.lazy_require { + git_diff = 'lualine.components.diff.git_diff', + utils = 'lualine.utils.utils', + utils_notices = 'lualine.utils.notices', + highlight = 'lualine.highlight', +} +local M = lualine_require.require('lualine.component'):extend() + +local default_options = { + colored = true, + symbols = { added = '+', modified = '~', removed = '-' }, + diff_color = { + added = { + fg = modules.utils.extract_highlight_colors('DiffAdd', 'fg') or '#f0e130', + }, + modified = { + fg = modules.utils.extract_highlight_colors('DiffChange', 'fg') or '#ff0038', + }, + removed = { + fg = modules.utils.extract_highlight_colors('DiffDelete', 'fg') or '#ff0038', + }, + }, +} + +-- Initializer +function M:init(options) + M.super.init(self, options) + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + -- create highlights and save highlight_name in highlights table + if self.options.colored then + self.highlights = { + added = modules.highlight.create_component_highlight_group( + self.options.diff_color.added, + 'diff_added', + self.options + ), + modified = modules.highlight.create_component_highlight_group( + self.options.diff_color.modified, + 'diff_modified', + self.options + ), + removed = modules.highlight.create_component_highlight_group( + self.options.diff_color.removed, + 'diff_removed', + self.options + ), + } + end + modules.git_diff.init(self.options) +end + +-- Function that runs everytime statusline is updated +function M:update_status(is_focused) + local git_diff = modules.git_diff.get_sign_count((not is_focused and vim.fn.bufnr())) + if git_diff == nil then + return '' + end + + local colors = {} + if self.options.colored then + -- load the highlights and store them in colors table + for name, highlight_name in pairs(self.highlights) do + colors[name] = modules.highlight.component_format_highlight(highlight_name) + end + end + + local result = {} + -- loop though data and load available sections in result table + for _, name in ipairs { 'added', 'modified', 'removed' } do + if git_diff[name] and git_diff[name] > 0 then + if self.options.colored then + table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name]) + else + table.insert(result, self.options.symbols[name] .. git_diff[name]) + end + end + end + if #result > 0 then + return table.concat(result, ' ') + else + return '' + end +end + +return M