diff --git a/README.md b/README.md index abb670c..6fbdcae 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Here is a preview of how lualine can look like. ![normal_cropped](https://user-images.githubusercontent.com/41551030/108650373-bb025580-74bf-11eb-8682-2c09321dd18e.png) ![powerline_cropped](https://user-images.githubusercontent.com/41551030/108650377-bd64af80-74bf-11eb-9c55-fbfc51b39fe8.png) -![signify_cropped](https://user-images.githubusercontent.com/41551030/108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png) +![diff_croped](https://user-images.githubusercontent.com/41551030/108650378-be95dc80-74bf-11eb-9718-82b242ecdd54.png) ![diagnostics_cropped](https://user-images.githubusercontent.com/41551030/108650381-bfc70980-74bf-11eb-9245-85c48f0f154a.png) ![replace](https://user-images.githubusercontent.com/41551030/103467925-32372b00-4d54-11eb-88d6-6d39c46854d8.png) @@ -141,8 +141,7 @@ lualine.inactive_sections = { * location (location in file in line:column format) * mode (vim mode) * progress (%progress in file) -* plugin - * signify (signify status) + * diff (git diff status) @@ -239,14 +238,14 @@ file_status | true | Displays file status (readonly status, modified status) full_path | false | Displays relative path if set to `true`, absolute path if set to `false` shorten | true | if `full_path` is true and `shorten` is `false` it shortens absolute path `aaa/bbb/ccc/file` to `a/b/c/file` -* `signify` component options +* `diff` component options Option | Default | Behaviour | Format :------: | :------: | :----: | :---: -colored | true | displays signify status in color if set to `true` | -color_added | `DiffAdd` foreground color | changes signify's added section foreground color | color in `#rrggbb` format -color_modified | `DiffChange` foreground color | changes signify's changed section foreground color | color in `#rrggbb` format -color_removed | `DiffDelete` foreground color | changes signify's removed section foreground color | color in `#rrggbb` format +colored | true | displays diff status in color if set to `true` | +color_added | `DiffAdd` foreground color | changes diff's added section foreground color | color in `#rrggbb` format +color_modified | `DiffChange` foreground color | changes diff's changed section foreground color | color in `#rrggbb` format +color_removed | `DiffDelete` foreground color | changes diff's removed section foreground color | color in `#rrggbb` format ##### Component options example diff --git a/doc/lualine.txt b/doc/lualine.txt index 9fe8aae..0d17a01 100644 --- a/doc/lualine.txt +++ b/doc/lualine.txt @@ -166,8 +166,7 @@ Available components~ * location (location in file in line:column format) * mode (vim mode) * progress (%progress in file) - plugin~ - * signify (signify status) + * diff (git diff status) -------------------------------------------------------------------------------- @@ -327,7 +326,7 @@ List of options are given below. are automaticaly extracted from colorscheme . If you want to change any of those you can use options given below. - • signify~ + • diff~ • colored (true) Whether to show colors diff --git a/lua/lualine/async.lua b/lua/lualine/async.lua new file mode 100644 index 0000000..dc18002 --- /dev/null +++ b/lua/lualine/async.lua @@ -0,0 +1,75 @@ +local M = {} + +function M:new(args) + args = args or {} + for index, arg in pairs(args) do + self[index] = arg + end + setmetatable(args, self) + self.__index = self + return args +end + +local function close_pipe(pipe) + if pipe ~= nil and not pipe:is_closing() then + pipe:close() + end +end + +function M.close_all() + close_pipe(M.stdin) + close_pipe(M.stderr) + close_pipe(M.stdout) + close_pipe(M.handle) +end + +function M.init_options() + local options = {} + local args = vim.fn.split(M.cmd, ' ') + M.stdin = vim.loop.new_pipe(false) + M.stdout = vim.loop.new_pipe(false) + M.stderr = vim.loop.new_pipe(false) + options.command = table.remove(args, 1) + options.args = args + options.stdio = { + M.stdin, + M.stdout, + M.stderr + } + if M.cwd then + options.cwd = M.cwd + end + if M.env then + options.env = M.env + end + if M.detach then + options.detach = M.detach + end + return options +end + +function M.start() + local options = M.init_options() + M.handle = vim.loop.spawn(options.command, options, vim.schedule_wrap(M.stop)) + if M.on_stdout then + M.stdout:read_start(vim.schedule_wrap(M.on_stdout)) + end + if M.on_stderr then + M.stderr:read_start(vim.schedule_wrap(M.on_stderr)) + end +end + +function M.stop(code, signal) + if M.on_exit then + M.on_exit(code, signal) + end + if M.on_stdout and M.stdout then + M.stdout:read_stop() + end + if M.on_stderr and M.stderr then + M.stderr:read_stop() + end + M.close_all() +end + +return M diff --git a/lua/lualine/components/diff.lua b/lua/lualine/components/diff.lua new file mode 100644 index 0000000..ee5b9fa --- /dev/null +++ b/lua/lualine/components/diff.lua @@ -0,0 +1,183 @@ +-- Copyright (c) 2020-2021 shadmansaleh +-- MIT license, see LICENSE for more details. + +local async = require'lualine.async' +local utils = require'lualine.utils.utils' +local highlight = require"lualine.highlight" + +-- variable to store git diff stats +local git_diff = nil + +-- 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 vim.gsplit(data, '\n') do + if string.find(line, [[^@@ ]]) then + local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]]) + local line_stats = { + mod_count = tokens[3] == '' and 1 or tonumber(tokens[3]), + new_count = 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, modified, removed } +end + +-- variable to store git_diff getter async function +local get_git_diff = nil +-- flag to see if async job exited before updating git_diff +local updated = false + +-- Updates the async function for current file +local function update_git_diff_getter() + -- stop older function properly before overwritting it + if get_git_diff then get_git_diff:stop() end + -- Donn't show git diff when current buffer doesn't have a filename + if #vim.fn.expand('%') == 0 then get_git_diff = nil; git_diff=nil; return end + get_git_diff = async:new({ + 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 data then + process_diff(data) + updated = true + end + end, + on_stderr = function (_, data) + if data then + git_diff = nil + updated = true + end + end, + on_exit = function() + if not updated then + -- updated not set means git exited without emmiting anything on stdout + -- or stderr means file is unchanged + git_diff = {0, 0, 0} + updated = true + end + end + }) +end + +-- Update git_diff veriable +local function update_git_diff() + vim.schedule_wrap(function() + if get_git_diff then + updated = false + get_git_diff:start() + end + end)() +end + +local default_color_added = "#f0e130" +local default_color_removed = "#90ee90" +local default_color_modified = "#ff0038" + +local function diff(options) + if options.colored == nil then options.colored = true end + -- apply colors + if not options.color_added then + options.color_added = utils.extract_highlight_colors('DiffAdd', 'guifg') or default_color_added + end + if not options.color_modified then + options.color_modified = utils.extract_highlight_colors('DiffChange', 'guifg') or default_color_modified + end + if not options.color_removed then + options.color_removed = utils.extract_highlight_colors('DiffDelete', 'guifg') or default_color_removed + end + + local highlights = {} + + -- create highlights and save highlight_name in highlights table + local function create_highlights() + highlights = { + highlight.create_component_highlight_group({fg = options.color_added}, 'diff_added', options), + highlight.create_component_highlight_group({fg = options.color_modified}, 'diff_modified', options), + highlight.create_component_highlight_group({fg = options.color_removed}, 'diff_removed', options), + } + end + + vim.api.nvim_exec([[ + autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff_getter() + autocmd lualine BufEnter * lua require'lualine.components.diff'.update_git_diff() + autocmd lualine BufWritePost * lua require'lualine.components.diff'.update_git_diff() + ]], false) + + -- create highlights + if options.colored then + create_highlights() + utils.expand_set_theme(create_highlights) + end + + -- Function that runs everytime statusline is updated + return function() + if git_diff == nil then return '' end + + local symbols = {'+', '~', '-'} + local colors = {} + if options.colored then + -- load the highlights and store them in colors table + for _, highlight_name in ipairs(highlights) do + table.insert(colors, highlight.component_format_highlight(highlight_name)) + end + end + + local result = {} + -- loop though data and load available sections in result table + for range=1,3 do + if git_diff[range] ~= nil and git_diff[range] > 0 then + if options.colored then + table.insert(result,colors[range]..symbols[range]..git_diff[range]) + else + table.insert(result,symbols[range]..git_diff[range]) + end + end + end + if result[1] ~= nil then + return table.concat(result, ' ') + else + return '' + end + end +end + +-- Api to get git sign count +-- scheme : +-- { +-- added = added_count, +-- modified = modified_count, +-- removed = removed_count, +-- } +-- error_code = { -1, -1, -1 } +local function get_sign_count() + update_git_diff_getter() + update_git_diff() + if git_diff then + return{ + added = git_diff[1], + modified = git_diff[2], + removed = git_diff[3] + } + end + return {-1, -1, -1} +end + +return { + init = function(options) return diff(options) end, + update_git_diff = update_git_diff, + update_git_diff_getter = update_git_diff_getter, + get_sign_count = get_sign_count, +} diff --git a/lua/lualine/components/signify.lua b/lua/lualine/components/signify.lua index 102fc7a..3d62787 100644 --- a/lua/lualine/components/signify.lua +++ b/lua/lualine/components/signify.lua @@ -1,76 +1,8 @@ --- Copyright (c) 2020-2021 shadmansaleh --- MIT license, see LICENSE for more details. - -local utils = require'lualine.utils.utils' -local highlight = require"lualine.highlight" - -local default_color_added = "#f0e130" -local default_color_removed = "#90ee90" -local default_color_modified = "#ff0038" - -local function signify(options) - if options.colored == nil then options.colored = true end - -- apply colors - if not options.color_added then - options.color_added = utils.extract_highlight_colors('DiffAdd', 'guifg') or default_color_added - end - if not options.color_modified then - options.color_modified = utils.extract_highlight_colors('DiffChange', 'guifg') or default_color_modified - end - if not options.color_removed then - options.color_removed = utils.extract_highlight_colors('DiffDelete', 'guifg') or default_color_removed - end - - local highlights = {} - - -- create highlights and save highlight_name in highlights table - local function create_highlights() - highlights = { - highlight.create_component_highlight_group({fg = options.color_added}, 'signify_added', options), - highlight.create_component_highlight_group({fg = options.color_modified}, 'signify_modified', options), - highlight.create_component_highlight_group({fg = options.color_removed}, 'signify_removed', options), - } - end - - -- create highlights - if options.colored then - create_highlights() - utils.expand_set_theme(create_highlights) - end - - -- Function that runs everytime statusline is updated - return function() - -- check if signify is available - if vim.fn.exists('*sy#repo#get_stats') == 0 then return '' end - local data = vim.fn['sy#repo#get_stats']() - if data[1] == -1 then return '' end - - local symbols = {'+', '~', '-'} - local colors = {} - if options.colored then - -- load the highlights and store them in colors table - for _, highlight_name in ipairs(highlights) do - table.insert(colors, highlight.component_format_highlight(highlight_name)) - end - end - - local result = {} - -- loop though data and load available sections in result table - for range=1,3 do - if data[range] ~= nil and data[range] > 0 then - if options.colored then - table.insert(result,colors[range]..symbols[range]..data[range]) - else - table.insert(result,symbols[range]..data[range]) - end - end - end - if result[1] ~= nil then - return table.concat(result, ' ') - else - return '' - end - end +vim.fn.timer_start(3000, function() +if vim.api.nvim_echo then + vim.api.nvim_echo({{'lualine.nvim: Signify component has been renamed to diff please change it in your configuration.', 'WarningMsg'}}, true, {}) +else + print('lualine.nvim: Signify component has been renamed to diff please change it in your configuration.', 'ErrorMsg') end - -return { init = function(options) return signify(options) end } +end) +return require'lualine.components.diff'