Skip to content

Commit 0ecc2e1

Browse files
authored
Merge pull request #1406 from ej-shafran/stash-list-support
2 parents ffae3b5 + 708bd23 commit 0ecc2e1

File tree

6 files changed

+266
-3
lines changed

6 files changed

+266
-3
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
local Buffer = require("neogit.lib.buffer")
2+
local config = require("neogit.config")
3+
local CommitViewBuffer = require("neogit.buffers.commit_view")
4+
local popups = require("neogit.popups")
5+
local status_maps = require("neogit.config").get_reversed_status_maps()
6+
local util = require("neogit.lib.util")
7+
8+
local git = require("neogit.lib.git")
9+
local ui = require("neogit.buffers.stash_list_view.ui")
10+
local input = require("neogit.lib.input")
11+
12+
---@class StashListBuffer
13+
---@field stashes StashItem[]
14+
local M = {}
15+
M.__index = M
16+
17+
--- Gets all current stashes
18+
function M.new(stashes)
19+
local instance = {
20+
stashes = stashes,
21+
}
22+
23+
setmetatable(instance, M)
24+
return instance
25+
end
26+
27+
function M:close()
28+
self.buffer:close()
29+
self.buffer = nil
30+
end
31+
32+
--- Creates a buffer populated with output of `git stash list`
33+
--- and supports related operations.
34+
function M:open()
35+
self.buffer = Buffer.create {
36+
name = "NeogitStashView",
37+
filetype = "NeogitStashView",
38+
header = "Stashes (" .. #self.stashes .. ")",
39+
kind = config.values.stash.kind,
40+
context_highlight = true,
41+
mappings = {
42+
v = {
43+
[popups.mapping_for("CherryPickPopup")] = function()
44+
-- local stash = self.buffer.ui:get_commit_under_cursor()[1]
45+
-- if stash then
46+
-- local stash_item = util.find(self.stashes, function(s)
47+
-- return s.idx == tonumber(stash:match("stash@{(%d+)}"))
48+
-- end)
49+
--
50+
-- if stash and input.get_permission("Pop stash " .. stash_item.name) then
51+
-- git.stash.pop(stash)
52+
-- end
53+
-- end
54+
end,
55+
[status_maps["Discard"]] = function()
56+
local stashes = self.buffer.ui:get_commits_in_selection()
57+
if stashes then
58+
if
59+
stashes
60+
and input.get_permission(table.concat(stashes, "\n") .. "\n\nDrop " .. #stashes .. " stashes?")
61+
then
62+
for _, stash in ipairs(stashes) do
63+
git.stash.drop(stash)
64+
end
65+
end
66+
end
67+
end,
68+
[popups.mapping_for("BranchPopup")] = popups.open("branch", function(p)
69+
p { commits = self.buffer.ui:get_commits_in_selection() }
70+
end),
71+
[popups.mapping_for("CommitPopup")] = popups.open("commit", function(p)
72+
p { commit = self.buffer.ui:get_commit_under_cursor() }
73+
end),
74+
[popups.mapping_for("FetchPopup")] = popups.open("fetch"),
75+
[popups.mapping_for("MergePopup")] = popups.open("merge", function(p)
76+
p { commit = self.buffer.ui:get_commit_under_cursor() }
77+
end),
78+
[popups.mapping_for("PushPopup")] = popups.open("push", function(p)
79+
p { commit = self.buffer.ui:get_commit_under_cursor() }
80+
end),
81+
[popups.mapping_for("RebasePopup")] = popups.open("rebase", function(p)
82+
p { commit = self.buffer.ui:get_commit_under_cursor() }
83+
end),
84+
[popups.mapping_for("RevertPopup")] = popups.open("revert", function(p)
85+
p { commits = self.buffer.ui:get_commits_in_selection() }
86+
end),
87+
[popups.mapping_for("ResetPopup")] = popups.open("reset", function(p)
88+
p { commit = self.buffer.ui:get_commit_under_cursor() }
89+
end),
90+
[popups.mapping_for("TagPopup")] = popups.open("tag", function(p)
91+
p { commit = self.buffer.ui:get_commit_under_cursor() }
92+
end),
93+
[popups.mapping_for("PullPopup")] = popups.open("pull"),
94+
[popups.mapping_for("DiffPopup")] = popups.open("diff", function(p)
95+
local items = self.buffer.ui:get_commits_in_selection()
96+
p {
97+
section = { name = "log" },
98+
item = { name = items },
99+
}
100+
end),
101+
[popups.mapping_for("BisectPopup")] = popups.open("bisect", function(p)
102+
p { commits = self.buffer.ui:get_commits_in_selection() }
103+
end),
104+
},
105+
n = {
106+
["V"] = function()
107+
vim.cmd("norm! V")
108+
end,
109+
[popups.mapping_for("CherryPickPopup")] = function()
110+
local stash = self.buffer.ui:get_commit_under_cursor()
111+
if stash then
112+
local stash_item = util.find(self.stashes, function(s)
113+
return s.idx == tonumber(stash:match("stash@{(%d+)}"))
114+
end)
115+
116+
if stash and input.get_permission("Pop stash " .. stash_item.name) then
117+
git.stash.pop(stash)
118+
end
119+
end
120+
end,
121+
[status_maps["Discard"]] = function()
122+
local stash = self.buffer.ui:get_commit_under_cursor()
123+
if stash then
124+
local stash_item = util.find(self.stashes, function(s)
125+
return s.idx == tonumber(stash:match("stash@{(%d+)}"))
126+
end)
127+
128+
if stash and input.get_permission("Drop stash " .. stash_item.name) then
129+
git.stash.drop(stash)
130+
end
131+
end
132+
end,
133+
[popups.mapping_for("BisectPopup")] = popups.open("bisect", function(p)
134+
p { commits = { self.buffer.ui:get_commit_under_cursor() } }
135+
end),
136+
[popups.mapping_for("BranchPopup")] = popups.open("branch", function(p)
137+
p { commits = { self.buffer.ui:get_commit_under_cursor() } }
138+
end),
139+
[popups.mapping_for("CommitPopup")] = popups.open("commit", function(p)
140+
p { commit = self.buffer.ui:get_commit_under_cursor() }
141+
end),
142+
[popups.mapping_for("FetchPopup")] = popups.open("fetch"),
143+
[popups.mapping_for("MergePopup")] = popups.open("merge", function(p)
144+
p { commit = self.buffer.ui:get_commit_under_cursor() }
145+
end),
146+
[popups.mapping_for("PushPopup")] = popups.open("push", function(p)
147+
p { commit = self.buffer.ui:get_commit_under_cursor() }
148+
end),
149+
[popups.mapping_for("RebasePopup")] = popups.open("rebase", function(p)
150+
p { commit = self.buffer.ui:get_commit_under_cursor() }
151+
end),
152+
[popups.mapping_for("RemotePopup")] = popups.open("remote"),
153+
[popups.mapping_for("RevertPopup")] = popups.open("revert", function(p)
154+
p { commits = { self.buffer.ui:get_commit_under_cursor() } }
155+
end),
156+
[popups.mapping_for("ResetPopup")] = popups.open("reset", function(p)
157+
p { commit = self.buffer.ui:get_commit_under_cursor() }
158+
end),
159+
[popups.mapping_for("TagPopup")] = popups.open("tag", function(p)
160+
p { commit = self.buffer.ui:get_commit_under_cursor() }
161+
end),
162+
[popups.mapping_for("PullPopup")] = popups.open("pull"),
163+
[popups.mapping_for("DiffPopup")] = popups.open("diff", function(p)
164+
local item = self.buffer.ui:get_commit_under_cursor()
165+
p {
166+
section = { name = "log" },
167+
item = { name = item },
168+
}
169+
end),
170+
[status_maps["YankSelected"]] = function()
171+
local yank = self.buffer.ui:get_commit_under_cursor()
172+
if yank then
173+
yank = string.format("'%s'", yank)
174+
vim.cmd.let("@+=" .. yank)
175+
vim.cmd.echo(yank)
176+
else
177+
vim.cmd("echo ''")
178+
end
179+
end,
180+
["<esc>"] = require("neogit.lib.ui.helpers").close_topmost(self),
181+
[status_maps["Close"]] = require("neogit.lib.ui.helpers").close_topmost(self),
182+
[status_maps["GoToFile"]] = function()
183+
local commit = self.buffer.ui:get_commit_under_cursor()
184+
if commit then
185+
CommitViewBuffer.new(commit):open()
186+
end
187+
end,
188+
[status_maps["OpenOrScrollDown"]] = function()
189+
local commit = self.buffer.ui:get_commit_under_cursor()
190+
if commit then
191+
CommitViewBuffer.open_or_scroll_down(commit)
192+
end
193+
end,
194+
[status_maps["OpenOrScrollUp"]] = function()
195+
local commit = self.buffer.ui:get_commit_under_cursor()
196+
if commit then
197+
CommitViewBuffer.open_or_scroll_up(commit)
198+
end
199+
end,
200+
},
201+
},
202+
after = function()
203+
vim.cmd([[setlocal nowrap]])
204+
end,
205+
render = function()
206+
return ui.View(self.stashes)
207+
end,
208+
}
209+
end
210+
211+
return M
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
local Ui = require("neogit.lib.ui")
2+
local Component = require("neogit.lib.ui.component")
3+
local util = require("neogit.lib.util")
4+
5+
local text = Ui.text
6+
local col = Ui.col
7+
local row = Ui.row
8+
9+
local M = {}
10+
11+
---Parses output of `git stash list` and splits elements into table
12+
M.Stash = Component.new(function(stash)
13+
local label = table.concat({ "stash@{", stash.idx, "}" }, "")
14+
return col({
15+
row({
16+
text.highlight("Comment")(label),
17+
text(" "),
18+
text(stash.message),
19+
}, {
20+
virtual_text = {
21+
{ " ", "Constant" },
22+
{ stash.rel_date, "Special" },
23+
},
24+
}),
25+
}, { oid = label })
26+
end)
27+
28+
---@param stashes StashItem[]
29+
---@return table
30+
function M.View(stashes)
31+
return util.map(stashes, function(stash)
32+
return M.Stash(stash)
33+
end)
34+
end
35+
36+
return M

lua/neogit/config.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ function M.get_default_values()
405405
popup = {
406406
kind = "split",
407407
},
408+
stash = {
409+
kind = "tab",
410+
},
408411
refs_view = {
409412
kind = "tab",
410413
},

lua/neogit/lib/git/stash.lua

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,23 @@ function M.rename(stash)
8787
end
8888

8989
---@class StashItem
90-
---@field idx number
90+
---@field idx number string the id of the stash i.e. stash@{7}
9191
---@field name string
92-
---@field message string
92+
---@field rel_date string relative timestamp
93+
---@field message string the message associated with each stash.
9394

9495
function M.register(meta)
9596
meta.update_stashes = function(state)
9697
state.stashes.items = util.map(M.list(), function(line)
9798
local idx, message = line:match("stash@{(%d*)}: (.*)")
9899

100+
---@class StashItem
99101
return {
102+
rel_date = git.cli.log
103+
.max_count(1)
104+
.format("%cr")
105+
.args(("stash@{%s}"):format(idx))
106+
.call({ hidden = true }).stdout[1],
100107
idx = tonumber(idx),
101108
name = line,
102109
message = message,

lua/neogit/popups/stash/actions.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local git = require("neogit.lib.git")
22
local input = require("neogit.lib.input")
33

44
local FuzzyFinderBuffer = require("neogit.buffers.fuzzy_finder")
5+
local StashListBuffer = require("neogit.buffers.stash_list_view")
56

67
local M = {}
78

@@ -71,4 +72,9 @@ function M.rename(popup)
7172
use("rename", popup.state.env.stash)
7273
end
7374

75+
--- git stash list
76+
function M.list()
77+
StashListBuffer.new(git.repo.state.stashes.items):open()
78+
end
79+
7480
return M

lua/neogit/popups/stash/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function M.create(stash)
2727
:action("a", "apply", actions.apply)
2828
:action("d", "drop", actions.drop)
2929
:new_action_group("Inspect")
30-
:action("l", "List")
30+
:action("l", "List", actions.list)
3131
:action("v", "Show")
3232
:new_action_group("Transform")
3333
:action("b", "Branch")

0 commit comments

Comments
 (0)