Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: commit component #1027

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
894b9b2
chore: commit plugin stub
gzbd Apr 13, 2023
ed69090
chore: compare branch commits in a job
gzbd Apr 13, 2023
76fc414
chore: ensure that git is run in git_dir
gzbd Apr 13, 2023
b57e718
chore: added options for icons and time interval
gzbd Apr 13, 2023
a494eb8
chore: added master_name option
gzbd Apr 14, 2023
db2ffb4
chore: watch on branch change
gzbd Apr 14, 2023
a15ddc3
feat: check icon setting
gzbd Apr 14, 2023
2bf2144
feat: option to diff against master
gzbd Apr 14, 2023
9772047
feat: added findout_master_name setting
gzbd Apr 14, 2023
e918f13
feat: support colors
gzbd Apr 14, 2023
29d1ff5
feat: show only diverged option
gzbd Apr 16, 2023
86a1066
feat: watch for branch tip change
gzbd Apr 18, 2023
4fa2a2e
chore: watch for remote tip change
gzbd Apr 18, 2023
f9eb05e
chore: detect if origin is set
gzbd Apr 19, 2023
8903519
chore: don't display branch state when no origin
gzbd Apr 19, 2023
ac3afaf
feat: do not compare with master when on master
gzbd Apr 19, 2023
10351ce
feat: detect if the branch has upstream
gzbd Apr 19, 2023
623452b
fix: set proper cwd for git commands
gzbd Apr 19, 2023
52d99a6
chore: debounce calls to update diff
gzbd Apr 19, 2023
88f130a
feat: detect conflicting changes
gzbd Apr 21, 2023
c52324b
chore: add color for diverged
gzbd Apr 21, 2023
4276272
chore: move jobs to a separate file
gzbd Apr 21, 2023
176ab51
fix: swap parameters when checking for conflicts
gzbd Apr 21, 2023
fd4a2c3
chore: reduce check_for_conflict output size
gzbd Apr 21, 2023
aeb4017
chore: splitted commits_status into multiple modules
gzbd Apr 23, 2023
dd1f26a
fix: repository might not be ready yet
gzbd Apr 25, 2023
81f9e3b
refactor: cleaned RepoWatcher:new
gzbd Apr 25, 2023
e3bf93c
refactor: removed duplicated code
gzbd Apr 25, 2023
ebb5d3e
refactor: function rename
gzbd Apr 25, 2023
c46af72
refactor: use shared logic in restart_watch
gzbd Apr 25, 2023
9b6e148
refactor: stop throttling timer
gzbd Apr 25, 2023
9ef8566
refactor: use cwd param in jobstart
gzbd Apr 26, 2023
f3e9214
fix: don't stop watching current repo
gzbd Apr 26, 2023
a82da64
refactor: rename file
gzbd Apr 26, 2023
a209210
fix: use method call
gzbd Apr 26, 2023
51d094c
refactor: refined watch_repo logic
gzbd Apr 26, 2023
301a1eb
chore: added a comment for function source
gzbd Apr 26, 2023
a2dbbc5
chore: added comments
gzbd Apr 26, 2023
a6b57cf
fix: icon when diff_against_master is not set
gzbd Apr 26, 2023
2ca0614
refactor: changed module exports
gzbd Apr 27, 2023
29e25c7
refactor: address linter complains
gzbd Apr 27, 2023
291270f
format: formatted code with stylua
gzbd Apr 27, 2023
822e277
docs: added commit component docs
gzbd Apr 27, 2023
e07b7a6
docs: added colored option
gzbd Apr 27, 2023
a532331
fix: fixed typo
gzbd May 4, 2023
0b21656
Merge branch 'master' into feat/commit-component
gzbd Oct 6, 2023
0b06c7a
Merge branch 'master' into feat/commit-component
shadmansaleh Oct 19, 2023
225503b
chore: added component icon to docs
gzbd Oct 22, 2023
23db282
chore: make show only diverged on by default
gzbd Oct 22, 2023
b428a84
refactor: use lua to parse command output
gzbd Oct 22, 2023
310dc3c
refactor: use rev-list instead of log to get commit diff count
gzbd Nov 17, 2023
80b7419
Merge branch 'master' into feat/commit-component
gzbd Nov 29, 2023
5188cb8
Merge branch 'master' into feat/commit-component
gzbd Mar 7, 2024
714009f
Merge branch 'master' of github.com:nvim-lualine/lualine.nvim into fe…
gzbd Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ sections = {lualine_a = {'mode'}}
#### Available components

- `branch` (git branch)
- `commit` (git commit difference between current branch and remote or master)
- `buffers` (shows currently available buffers)
- `diagnostics` (diagnostics count from your preferred source)
- `diff` (git diff status)
Expand Down Expand Up @@ -773,6 +774,30 @@ sections = {
}
```

#### commit component options

```lua
sections = {
lualine_a = {
{
'commit',
icon = '', -- Component icon
master_name = 'master', -- Default master branch name, some repositories use `main`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should mention you're setting a default value for icon option

colored = true, -- Set to true displays safe to merge commits and conflicts in color.
findout_master_name = false, -- Let's the component get the master branch name from origin HEAD branch.
diff_against_master = false, -- Compare current branch to remote and master branch.
fetch_interval = 60000, -- How ofter `git fetch` is done, in ms.
unpulled_master_icon = '⇢ ', -- Icon shown for unpulled changes from master branch.
unpulled_icon = '⇣ ', -- Icon shown for unpulled changes on current branch.
unpushed_icon = '⇡ ', -- Icon shown for unpushed changes on current branch.
use_check_icon = true, -- Use checkmark icon, instead of `0`.
check_icon = '󰸞', -- Icon to display check mark instead of `0`.
show_only_diverged = true, -- Don't show `0` or check mark if up to date.
}
}
}
```

---

### Tabline
Expand Down
54 changes: 54 additions & 0 deletions lua/lualine/components/commit/find_git_dir.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
-- os specific path separator
local sep = package.config:sub(1, 1)

local git_dir_cache = {} -- Stores git paths that we already know of, map file dir to git_dir

-- function taken from ../branch/git_branch.lua module. Code was adjusted to
-- remove setting current_git_dir global variable and remove update_branch()
-- call (component specific).
local function find_git_dir(dir_path)
-- get file dir so we can search from that dir
local file_dir = dir_path or vim.fn.expand('%:p:h')
local root_dir = file_dir
local git_dir
-- Search upward for .git file or folder
while root_dir do
if git_dir_cache[root_dir] then
git_dir = git_dir_cache[root_dir]
break
end
local git_path = root_dir .. sep .. '.git'
local git_file_stat = vim.loop.fs_stat(git_path)
if git_file_stat then
if git_file_stat.type == 'directory' then
git_dir = git_path
elseif git_file_stat.type == 'file' then
-- separate git-dir or submodule is used
local file = io.open(git_path)
if file then
git_dir = file:read()
git_dir = git_dir and git_dir:match('gitdir: (.+)$')
file:close()
end
-- submodule / relative file path
if git_dir and git_dir:sub(1, 1) ~= sep and not git_dir:match('^%a:.*$') then
git_dir = git_path:match('(.*).git') .. git_dir
end
end
if git_dir then
local head_file_stat = vim.loop.fs_stat(git_dir .. sep .. 'HEAD')
if head_file_stat and head_file_stat.type == 'file' then
break
else
git_dir = nil
end
end
end
root_dir = root_dir:match('(.*)' .. sep .. '.-')
end

git_dir_cache[file_dir] = git_dir
return git_dir
end

return find_git_dir
130 changes: 130 additions & 0 deletions lua/lualine/components/commit/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
local M = require('lualine.component'):extend()
local modules = require('lualine_require').lazy_require {
status = 'lualine.components.commit.status',
utils = 'lualine.utils.utils',
}

local default_options = {
icon = '',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if user sets icon option to empty string icon would go away

@chrisgrieser

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, you are right. Thanks!

In that case, this is just a case of the icon not being documented in the component options.

master_name = 'master',
colored = true,
findout_master_name = false,
diff_against_master = false,
fetch_interval = 60000,
unpulled_master_icon = '⇢ ',
unpulled_icon = '⇣ ',
unpushed_icon = '⇡ ',
use_check_icon = true,
check_icon = '󰸞',
show_only_diverged = true,
}

local function apply_default_colors(opts)
local default_color = {
insync = {
fg = modules.utils.extract_color_from_hllist('fg', { 'lualine_a_inactive' }, '#90ee90'),
},
diverged = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'GitSignsChange', 'GitGutterChange', 'DiffChanged', 'DiffChange' },
'#f0e130'
),
},
conflict = {
fg = modules.utils.extract_color_from_hllist(
'fg',
{ 'GitSignsDelete', 'GitGutterDelete', 'DiffRemoved', 'DiffDelete' },
'#ff0038'
),
},
}
opts.color = vim.tbl_deep_extend('keep', opts.color or {}, default_color)
end

-- Initializer
M.init = function(self, options)
M.super.init(self, options)
apply_default_colors(self.options)
self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options)

if self.options.colored then
self.highlights = {
insync = self:create_hl(self.options.color.insync, 'insync'),
diverged = self:create_hl(self.options.color.diverged, 'diverged'),
conflict = self:create_hl(self.options.color.conflict, 'conflict'),
}
end

modules.status.init {
master_name = self.options.master_name,
findout_master_name = self.options.findout_master_name,
diff_against_master = self.options.diff_against_master,
fetch_interval = self.options.fetch_interval,
}
end

function M:update_status(_, is_focused)
local buf = (not is_focused and vim.api.nvim_get_current_buf())

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] = self:format_hl(highlight_name)
end
end

local status = modules.status.status(buf)
local result = {}
local icons = {
self.options.unpulled_master_icon,
self.options.unpulled_icon,
self.options.unpushed_icon,
}

if not self.options.diff_against_master then
table.remove(status, 1)
end

for k, v in ipairs(status) do
local has_conflict = table.remove(v)
for k2, d in ipairs(v) do
if not (self.options.show_only_diverged and d == 0) and d ~= -1 then
local count = tostring(d)
if self.options.use_check_icon then
if d == 0 then
count = self.options.check_icon
end
end

local icon_pos
if not self.options.diff_against_master then
icon_pos = k2 + 1
else
if k == 1 then
icon_pos = 1
else
icon_pos = k2 + 1
end
end

local icon = icons[icon_pos]
if self.options.colored then
local color = (d > 0) and colors['diverged'] or colors['insync']
if has_conflict then
color = colors['conflict']
end

table.insert(result, color .. icon .. count .. (has_conflict and '!' or ''))
else
table.insert(result, icon .. count .. (has_conflict and '!' or ''))
end
end
end
end

return table.concat(result, ' ')
end

return M
91 changes: 91 additions & 0 deletions lua/lualine/components/commit/jobs.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
local job = require('lualine.utils.job')

local M = {}

function M._run_job(cmd, cwd, callback)
local output = ''
local err = ''

local j = job {
cmd = cmd,
cwd = cwd,
stdout_buffered = true,
stderr_buffered = true,
on_stdout = function(_, data)
output = table.concat(data, '\n')
end,
on_stderr = function(_, data)
err = table.concat(data, '\n')
end,
on_exit = function(_, exit_code)
callback(exit_code, output, err)
end,
}

if j then
j:start()
end
end

function M.check_origin(cwd, callback)
M._run_job([[git remote show]], cwd, function(exit_code, output, _)
output = output:gsub('\n', '')
if exit_code ~= 0 or output ~= 'origin' then
callback(false)
return
end

callback(true)
end)
end

function M.get_master_name(cwd, callback)
M._run_job([[git remote show origin]], cwd, function(exit_code, output, _)
if exit_code ~= 0 then
callback(false)
return
end
local _, _, name = string.find(output, "HEAD branch: (%S+)")
callback(true, name)
end)
end

function M.fetch_branch(cwd, name, callback)
local cmd = string.format([[git fetch origin %s]], name)
M._run_job(cmd, cwd, function(exit_code, _, _)
if exit_code ~= 0 then
callback(false)
return
end
callback(true)
end)
end

function M.check_for_conflict(cwd, source, target, callback)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels like this might get expensive

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might get expensive when comparing large diffs involving hundreds of commits. The diff operation is done by git itself and it's done in the memory (no disk writes), so I cannot speed this up. One thing that comes to my mind is to add a setting disabling this feature by the users if unwanted.

local cmd = string.format(
[[git merge-tree `git merge-base %s %s` %s %s]],
source, target, target, source)
M._run_job(cmd, cwd, function(exit_code, output, _)
if exit_code ~= 0 then
callback(true, false)
return
end

local found = string.find(output, "<<<<<<<")
callback(true, found ~= nil)
end)
end

function M.commit_diff(cwd, source, target, callback)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do same as git rev-list --count --left-right HEAD...@{upstream} ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll make a change to use this command as it produces less output.

local cmd = string.format([[git rev-list --count --left-right %s %s]], source, target)
M._run_job(cmd, cwd, function(exit_code, output, err)
if exit_code ~= 0 then
callback(false, -1, err)
return
end
local commit_count = string.match(output, "%d+ (%d+)")
callback(true, tonumber(commit_count))
end)
end

return M