-
Notifications
You must be signed in to change notification settings - Fork 451
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
base: master
Are you sure you want to change the base?
feat: commit component #1027
Changes from 47 commits
894b9b2
ed69090
76fc414
b57e718
a494eb8
db2ffb4
a15ddc3
2bf2144
9772047
e918f13
29d1ff5
86a1066
4fa2a2e
f9eb05e
8903519
ac3afaf
10351ce
623452b
52d99a6
88f130a
c52324b
4276272
176ab51
fd4a2c3
aeb4017
dd1f26a
81f9e3b
e3bf93c
ebb5d3e
c46af72
9b6e148
9ef8566
f3e9214
a82da64
a209210
51d094c
301a1eb
a2dbbc5
a6b57cf
2ca0614
29e25c7
291270f
822e277
e07b7a6
a532331
0b21656
0b06c7a
225503b
23db282
b428a84
310dc3c
80b7419
5188cb8
714009f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -242,6 +242,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) | ||
|
@@ -764,6 +765,29 @@ sections = { | |
} | ||
``` | ||
|
||
#### commit component options | ||
|
||
```lua | ||
sections = { | ||
lualine_a = { | ||
{ | ||
'commit', | ||
master_name = 'master', -- Default master branch name, some repositories use `main`. | ||
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 = false, -- Don't show `0` or check mark if up to date. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should be default true |
||
} | ||
} | ||
} | ||
``` | ||
|
||
--- | ||
|
||
### Tabline | ||
|
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 |
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 = '', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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 = false, | ||
} | ||
|
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
local job = require('lualine.utils.job') | ||
|
||
local M = {} | ||
|
||
function M._run_job(cmd, cwd, callback) | ||
local output = '' | ||
local err = '' | ||
|
||
local j = job { | ||
cmd = { | ||
'sh', | ||
'-c', | ||
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 | grep 'HEAD branch' | cut -d' ' -f5", cwd, function(exit_code, output, _) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not take the output of the command than process it in lua instead of running grep and cut. that way we can drop the pipes and sh -c |
||
output = output:gsub('\n', '') | ||
if exit_code ~= 0 then | ||
callback(false) | ||
return | ||
end | ||
callback(true, output) | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it feels like this might get expensive There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | grep '<<<<<<<']], source, target, target, source) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here pipes can be removed I think |
||
M._run_job(cmd, cwd, function(exit_code, _, _) | ||
if exit_code ~= 0 then | ||
-- grep that finds nothing returns exit code 1 | ||
callback(true, false) | ||
return | ||
end | ||
callback(true, true) | ||
end) | ||
end | ||
|
||
function M.commit_diff(cwd, source, target, callback) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this do same as There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 log --oneline %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 = output:gsub('\n', '\n') | ||
callback(true, commit_count) | ||
end) | ||
end | ||
|
||
return M |
There was a problem hiding this comment.
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