Skip to content

Commit 49d0527

Browse files
authored
Merge pull request #1715 from lucasadelino/margin
2 parents 269a813 + 0c67bb8 commit 49d0527

File tree

12 files changed

+249
-13
lines changed

12 files changed

+249
-13
lines changed

lua/neogit/buffers/status/actions.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ M.v_log_popup = function(_self)
454454
return popups.open("log")
455455
end
456456

457+
---@param self StatusBuffer
458+
M.v_margin_popup = function(self)
459+
return popups.open("margin", function(p)
460+
p { buffer = self }
461+
end)
462+
end
463+
457464
---@param _self StatusBuffer
458465
M.v_worktree_popup = function(_self)
459466
return popups.open("worktree")
@@ -1418,6 +1425,7 @@ M.n_help_popup = function(self)
14181425
bisect = { commits = commits },
14191426
reset = { commit = commit },
14201427
tag = { commit = commit },
1428+
margin = { buffer = self },
14211429
stash = { name = stash and stash:match("^stash@{%d+}") },
14221430
diff = {
14231431
section = { name = section_name },
@@ -1456,6 +1464,13 @@ M.n_log_popup = function(_self)
14561464
return popups.open("log")
14571465
end
14581466

1467+
---@param self StatusBuffer
1468+
M.n_margin_popup = function(self)
1469+
return popups.open("margin", function(p)
1470+
p { buffer = self }
1471+
end)
1472+
end
1473+
14591474
---@param _self StatusBuffer
14601475
M.n_worktree_popup = function(_self)
14611476
return popups.open("worktree")

lua/neogit/buffers/status/init.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ function M:open(kind)
127127
[popups.mapping_for("HelpPopup")] = self:_action("v_help_popup"),
128128
[popups.mapping_for("IgnorePopup")] = self:_action("v_ignore_popup"),
129129
[popups.mapping_for("LogPopup")] = self:_action("v_log_popup"),
130+
[popups.mapping_for("MarginPopup")] = self:_action("v_margin_popup"),
130131
[popups.mapping_for("MergePopup")] = self:_action("v_merge_popup"),
131132
[popups.mapping_for("PullPopup")] = self:_action("v_pull_popup"),
132133
[popups.mapping_for("PushPopup")] = self:_action("v_push_popup"),
@@ -183,6 +184,7 @@ function M:open(kind)
183184
[popups.mapping_for("HelpPopup")] = self:_action("n_help_popup"),
184185
[popups.mapping_for("IgnorePopup")] = self:_action("n_ignore_popup"),
185186
[popups.mapping_for("LogPopup")] = self:_action("n_log_popup"),
187+
[popups.mapping_for("MarginPopup")] = self:_action("n_margin_popup"),
186188
[popups.mapping_for("MergePopup")] = self:_action("n_merge_popup"),
187189
[popups.mapping_for("PullPopup")] = self:_action("n_pull_popup"),
188190
[popups.mapping_for("PushPopup")] = self:_action("n_push_popup"),

lua/neogit/buffers/status/ui.lua

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ local Ui = require("neogit.lib.ui")
22
local Component = require("neogit.lib.ui.component")
33
local util = require("neogit.lib.util")
44
local common = require("neogit.buffers.common")
5+
local config = require("neogit.config")
56
local a = require("plenary.async")
7+
local state = require("neogit.lib.state")
68

79
local col = Ui.col
810
local row = Ui.row
@@ -323,7 +325,7 @@ local SectionItemCommit = Component.new(function(item)
323325
local ref = {}
324326
local ref_last = {}
325327

326-
if item.commit.ref_name ~= "" then
328+
if item.commit.ref_name ~= "" and state.get({ "NeogitMarginPopup", "decorate" }, true) then
327329
-- Render local only branches first
328330
for name, _ in pairs(item.decoration.locals) do
329331
if name:match("^refs/") then
@@ -359,6 +361,79 @@ local SectionItemCommit = Component.new(function(item)
359361
end
360362
end
361363

364+
local virtual_text
365+
366+
-- Render author and date in margin, if visible
367+
if state.get({ "margin", "visibility" }, false) then
368+
local margin_date_style = state.get({ "margin", "date_style" }, 1)
369+
local details = state.get({ "margin", "details" }, false)
370+
371+
local date
372+
local rel_date
373+
local date_width = 10
374+
local clamp_width = 30 -- to avoid having too much space when relative date is short
375+
376+
-- Render date
377+
if item.commit.rel_date:match(" years?,") then
378+
rel_date, _ = item.commit.rel_date:gsub(" years?,", "y")
379+
rel_date = rel_date .. " "
380+
elseif item.commit.rel_date:match("^%d ") then
381+
rel_date = " " .. item.commit.rel_date
382+
else
383+
rel_date = item.commit.rel_date
384+
end
385+
386+
if margin_date_style == 1 then -- relative date (short)
387+
local unpacked = vim.split(rel_date, " ")
388+
389+
-- above, we added a space if the rel_date started with a single number
390+
-- we get the last two elements to deal with that
391+
local date_number = unpacked[#unpacked - 1]
392+
local date_quantifier = unpacked[#unpacked]
393+
if date_quantifier:match("months?") then
394+
date_quantifier = date_quantifier:gsub("m", "M") -- to distinguish from minutes
395+
end
396+
397+
-- add back the space if we have a single number
398+
local left_pad
399+
if #unpacked > 2 then
400+
left_pad = " "
401+
else
402+
left_pad = ""
403+
end
404+
405+
date = left_pad .. date_number .. date_quantifier:sub(1, 1)
406+
date_width = 3
407+
clamp_width = 23
408+
elseif margin_date_style == 2 then -- relative date (long)
409+
date = rel_date
410+
date_width = 10
411+
else -- local iso date
412+
if config.values.log_date_format == nil then
413+
-- we get the unix date to be able to convert the date to the local timezone
414+
date = os.date("%Y-%m-%d %H:%M", item.commit.unix_date)
415+
date_width = 16 -- TODO: what should the width be here?
416+
else
417+
date = item.commit.log_date
418+
date_width = 16
419+
end
420+
end
421+
422+
local author_table = { "" }
423+
if details then
424+
author_table = {
425+
util.str_clamp(item.commit.author_name, clamp_width - (#date > date_width and #date or date_width)),
426+
"NeogitGraphAuthor",
427+
}
428+
end
429+
430+
virtual_text = {
431+
{ " ", "Constant" },
432+
author_table,
433+
{ util.str_min_width(date, date_width), "Special" },
434+
}
435+
end
436+
362437
return row(
363438
util.merge(
364439
{ text.highlight("NeogitObjectId")(item.commit.abbreviated_commit) },
@@ -367,7 +442,12 @@ local SectionItemCommit = Component.new(function(item)
367442
ref_last,
368443
{ text(item.commit.subject) }
369444
),
370-
{ oid = item.commit.oid, yankable = item.commit.oid, item = item }
445+
{
446+
virtual_text = virtual_text,
447+
oid = item.commit.oid,
448+
yankable = item.commit.oid,
449+
item = item,
450+
}
371451
)
372452
end)
373453

lua/neogit/config.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ end
245245
---| "PushPopup"
246246
---| "CommitPopup"
247247
---| "LogPopup"
248+
---| "MarginPopup"
248249
---| "RevertPopup"
249250
---| "StashPopup"
250251
---| "IgnorePopup"
@@ -626,6 +627,7 @@ function M.get_default_values()
626627
["c"] = "CommitPopup",
627628
["f"] = "FetchPopup",
628629
["l"] = "LogPopup",
630+
["L"] = "MarginPopup",
629631
["m"] = "MergePopup",
630632
["p"] = "PullPopup",
631633
["r"] = "RebasePopup",

lua/neogit/lib/git/log.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ local commit_header_pat = "([| ]*)(%*?)([| ]*)commit (%w+)"
3030
---@field verification_flag string?
3131
---@field rel_date string
3232
---@field log_date string
33+
---@field unix_date string
3334

3435
---Parses the provided list of lines into a CommitLogEntry
3536
---@param raw string[]
@@ -340,6 +341,7 @@ local function format(show_signature)
340341
committer_date = "%cD",
341342
rel_date = "%cr",
342343
log_date = "%cd",
344+
unix_date = "%ct",
343345
}
344346

345347
if show_signature then

lua/neogit/lib/popup/builder.lua

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ local M = {}
8080
---@field description string
8181
---@field callback function
8282
---@field heading string?
83+
---@field persist_popup boolean? set to true to prevent closing the popup when invoking
84+
85+
---@class PopupActionOptions
86+
---@field persist_popup boolean Controls if the action should close the popup (false/nil) or keep it open (true)
8387

8488
---@class PopupSwitchOpts
8589
---@field enabled? boolean Controls if the switch should default to 'on' state
@@ -440,8 +444,11 @@ end
440444
---@param keys string|string[] Key or list of keys for the user to press that runs the action
441445
---@param description string Description of action in UI
442446
---@param callback? fun(popup: PopupData) Function that gets run in async context
447+
---@param opts? PopupActionOptions
443448
---@return self
444-
function M:action(keys, description, callback)
449+
function M:action(keys, description, callback, opts)
450+
opts = opts or {}
451+
445452
if type(keys) == "string" then
446453
keys = { keys }
447454
end
@@ -459,6 +466,7 @@ function M:action(keys, description, callback)
459466
keys = keys,
460467
description = description,
461468
callback = callback,
469+
persist_popup = opts.persist_popup or false,
462470
})
463471

464472
return self
@@ -470,10 +478,11 @@ end
470478
---@param keys string|string[] Key or list of keys for the user to press that runs the action
471479
---@param description string Description of action in UI
472480
---@param callback? fun(popup: PopupData) Function that gets run in async context
481+
---@param opts? PopupActionOptions
473482
---@return self
474-
function M:action_if(cond, keys, description, callback)
483+
function M:action_if(cond, keys, description, callback, opts)
475484
if cond then
476-
return self:action(keys, description, callback)
485+
return self:action(keys, description, callback, opts)
477486
end
478487

479488
return self

lua/neogit/lib/popup/init.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,11 @@ function M:mappings()
366366
for _, key in ipairs(action.keys) do
367367
mappings.n[key] = a.void(function()
368368
logger.debug(string.format("[POPUP]: Invoking action %q of %s", key, self.state.name))
369-
self:close()
369+
if not action.persist_popup then
370+
logger.debug("[POPUP]: Closing popup")
371+
self:close()
372+
end
373+
370374
action.callback(self)
371375
Watcher.instance():dispatch_refresh()
372376
end)

lua/neogit/popups/help/actions.lua

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ local function present(commands)
2121
end
2222

2323
if type(keymap) == "table" and next(keymap) then
24+
-- HACK: Remove "za" as listed keymap for toggle action.
25+
table.sort(keymap)
26+
if name == "Toggle" and keymap[2] == "za" then
27+
table.remove(keymap, 2)
28+
end
29+
2430
return { { name = name, keys = keymap, cmp = table.concat(keymap):lower(), fn = fn } }
2531
else
2632
return { { name = name, keys = {}, cmp = "", fn = fn } }
@@ -75,6 +81,9 @@ M.popups = function(env)
7581
{ "LogPopup", "Log", popups.open("log", function(p)
7682
p(env.log)
7783
end) },
84+
{ "MarginPopup", "Margin", popups.open("margin", function(p)
85+
p(env.margin)
86+
end) },
7887
{
7988
"CherryPickPopup",
8089
"Cherry Pick",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
local M = {}
2+
3+
local state = require("neogit.lib.state")
4+
local a = require("plenary.async")
5+
6+
function M.refresh_buffer(buffer)
7+
return a.void(function()
8+
buffer:dispatch_refresh({ update_diffs = { "*:*" } }, "margin_refresh_buffer")
9+
end)
10+
end
11+
12+
function M.toggle_visibility()
13+
local visibility = state.get({ "margin", "visibility" }, false)
14+
local new_visibility = not visibility
15+
state.set({ "margin", "visibility" }, new_visibility)
16+
end
17+
18+
function M.cycle_date_style()
19+
local styles = { "relative_short", "relative_long", "local_datetime" }
20+
local current_index = state.get({ "margin", "date_style" }, #styles)
21+
local next_index = (current_index % #styles) + 1 -- wrap around to the first style
22+
23+
state.set({ "margin", "date_style" }, next_index)
24+
end
25+
26+
function M.toggle_details()
27+
local details = state.get({ "margin", "details" }, false)
28+
local new_details = not details
29+
state.set({ "margin", "details" }, new_details)
30+
end
31+
32+
return M

lua/neogit/popups/margin/init.lua

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
local popup = require("neogit.lib.popup")
2+
-- local config = require("neogit.config")
3+
local actions = require("neogit.popups.margin.actions")
4+
5+
local M = {}
6+
7+
-- TODO: Implement various flags/switches
8+
9+
function M.create(env)
10+
local p = popup
11+
.builder()
12+
:name("NeogitMarginPopup")
13+
-- :option("n", "max-count", "256", "Limit number of commits", { default = "256", key_prefix = "-" })
14+
-- :switch("o", "topo", "Order commits by", {
15+
-- cli_suffix = "-order",
16+
-- options = {
17+
-- { display = "", value = "" },
18+
-- { display = "topo", value = "topo" },
19+
-- { display = "author-date", value = "author-date" },
20+
-- { display = "date", value = "date" },
21+
-- },
22+
-- })
23+
-- :switch("g", "graph", "Show graph", {
24+
-- enabled = true,
25+
-- internal = true,
26+
-- incompatible = { "reverse" },
27+
-- dependent = { "color" },
28+
-- })
29+
-- :switch_if(
30+
-- config.values.graph_style == "ascii" or config.values.graph_style == "kitty",
31+
-- "c",
32+
-- "color",
33+
-- "Show graph in color",
34+
-- { internal = true, incompatible = { "reverse" } }
35+
-- )
36+
:switch(
37+
"d",
38+
"decorate",
39+
"Show refnames",
40+
{ enabled = true, internal = true }
41+
)
42+
:group_heading("Refresh")
43+
:action_if(env.buffer, "g", "buffer", actions.refresh_buffer(env.buffer), { persist_popup = true })
44+
:new_action_group("Margin")
45+
:action("L", "toggle visibility", actions.toggle_visibility, { persist_popup = true })
46+
:action("l", "cycle style", actions.cycle_date_style, { persist_popup = true })
47+
:action("d", "toggle details", actions.toggle_details, { persist_popup = true })
48+
:action("x", "toggle shortstat", actions.log_current, { persist_popup = true })
49+
:build()
50+
51+
p:show()
52+
53+
return p
54+
end
55+
56+
return M

0 commit comments

Comments
 (0)