Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd0d10f6

Browse files
committed
feat(gh): you can now useSnacks.picker.gh_actions() directly to see actions for the checked out PR
1 parent07c569d commitd0d10f6

File tree

7 files changed

+214
-52
lines changed

7 files changed

+214
-52
lines changed

‎lua/snacks/gh/actions.lua‎

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,11 @@ M.actions.gh_actions = {
8080
returnSnacks.picker.actions.jump(ctx.picker,item,ctx.action)
8181
end
8282
localactions=M.get_actions(item)
83-
actions.gh_actions=nil-- remove self to avoid recursion
84-
Snacks.picker.pick({
85-
source="gh_actions",
83+
actions.gh_actions=nil-- remove this action
84+
actions.gh_perform_action=nil-- remove this action
85+
Snacks.picker.gh_actions({
86+
item=item,
8687
layout= {
87-
preset="select",
88-
layout= {max_width=80 },
8988
config=function(layout)
9089
-- Fit list height to number of items, up to 10
9190
for_,boxinipairs(layout.layout)do
@@ -95,35 +94,6 @@ M.actions.gh_actions = {
9594
end
9695
end,
9796
},
98-
main= {current=true },
99-
title= ("Actions for %s #%d"):format(item.type,item.number),
100-
finder=function()
101-
localitems= {}---@typesnacks.picker.finder.Item[]
102-
forname,actioninpairs(actions)do
103-
---@classsnacks.picker.gh.Action:snacks.picker.finder.Item
104-
items[#items+1]= {
105-
text=Snacks.picker.util.text(action, {"name","desc"}),
106-
file=item.uri,
107-
name=name,
108-
item=item,
109-
desc=action.descorname,
110-
action=action,
111-
}
112-
end
113-
table.sort(items,function(a,b)
114-
localpa=a.action.priorityor0
115-
localpb=b.action.priorityor0
116-
ifpa~=pbthen
117-
returnpa>pb
118-
end
119-
returna.desc<b.desc
120-
end)
121-
fori,itinipairs(items)do
122-
it.text= ("%d. %s"):format(i,it.text)
123-
end
124-
returnitems
125-
end,
126-
format="gh_format_action",
12797
---@paramitsnacks.picker.gh.Action
12898
confirm=function(picker,it,action)
12999
ifnotitthen
@@ -140,6 +110,16 @@ M.actions.gh_actions = {
140110
end,
141111
}
142112

113+
M.actions.gh_perform_action= {
114+
action=function(item,ctx)
115+
ifnotitemthen
116+
return
117+
end
118+
item.action.action(item.item,ctx)
119+
ctx.picker:close()
120+
end,
121+
}
122+
143123
M.actions.gh_browse= {
144124
desc="Open in web browser",
145125
title="Open {type} #{number} in web browser",

‎lua/snacks/gh/api.lua‎

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local M = {}
66

77
---@typetable<string,snacks.picker.gh.Item>
88
localcache=setmetatable({}, {__mode="v"})
9+
localpr_cache= {}---@typetable<string,snacks.picker.gh.Item>
910

1011
---@typetable<string,snacks.gh.api.Config|{}>
1112
localconfig= {
@@ -304,10 +305,10 @@ function M.list(what, cb, opts)
304305
})
305306
end
306307

307-
---@paramitemsnacks.gh.api.View
308308
---@paramcbfun(item?:snacks.picker.gh.Item,updated?:boolean)
309+
---@paramitemsnacks.gh.api.View
309310
---@paramopts?{fields?:string[],force?:boolean}
310-
functionM.view(item,cb,opts)
311+
functionM.view(cb,item,opts)
311312
opts=optsor {}
312313
localapi_opts=get_opts(item.type,"view")
313314
ifopts.fieldsthen
@@ -455,4 +456,83 @@ function M.comments(item, cb)
455456
})
456457
end
457458

459+
functionM.get_branch()
460+
localbranch=vim.fn.system({"git","branch","--show-current"}):gsub("\n","")
461+
462+
-- Get all config in one call
463+
localgit_config=vim.fn
464+
.system({
465+
"git",
466+
"config",
467+
"--get-regexp",
468+
("^(branch\\.%s\\.|remote\\.)"):format(branch),
469+
})
470+
:gsub("\n$","")
471+
472+
localcfg= {}---@typetable<string,string>
473+
for_,lineinipairs(vim.split(git_config,"\n"))do
474+
localkey,value=line:match("^([^%s]+)%s+(.+)$")
475+
ifkeythen
476+
cfg[key]=value
477+
end
478+
end
479+
480+
-- Extract values
481+
localremote=cfg[("branch.%s.remote"):format(branch)]or""
482+
localmerge=cfg[("branch.%s.merge"):format(branch)]or""
483+
localorigin_url=cfg["remote.origin.url"]or""
484+
485+
-- Get fork URL (either named remote or direct URL)
486+
localurl= (remote:match("^https://")orremote:match("^git@"))andremote
487+
orcfg[("remote.%s.url"):format(remote)]
488+
orremote
489+
490+
-- Parse author from fork URL
491+
localauthor---@typestring?
492+
ifurl~=""then
493+
---@typestring?
494+
localowner_repo=url:match("github%.com[:/](.+/.+)%.git")orurl:match("github%.com[:/](.+/.+)$")
495+
author=owner_repoandowner_repo:match("^([^/]+)/")ornil
496+
end
497+
498+
-- Parse repo from origin
499+
localrepo=origin_url:match("github%.com[:/](.+/.+)%.git")ororigin_url:match("github%.com[:/](.+/.+)$")
500+
501+
-- Parse head from merge ref
502+
localhead=merge:match("^refs/heads/(.+)$")orbranch
503+
504+
-- Get base branch (default branch from origin)
505+
localbase=vim.fn.system({"git","symbolic-ref","refs/remotes/origin/HEAD"}):gsub("\n","")
506+
base=base:match("refs/remotes/origin/(.+)")or"main"
507+
508+
---@typesnacks.gh.api.Branch
509+
return {
510+
url=url,
511+
author=author,
512+
repo=repo,
513+
branch=branch,
514+
head=head,
515+
base=base,
516+
}
517+
end
518+
519+
---@paramcbfun(item?:snacks.picker.gh.Item)
520+
functionM.current_pr(cb)
521+
localbranch=M.get_branch()
522+
localkey=string.format("%s:%s/%s->%s",branch.authoror"",branch.repoor"",branch.head,branch.base)
523+
ifpr_cache[key]then
524+
returncb(pr_cache[key])
525+
end
526+
returnM.list("pr",function(items)
527+
pr_cache[key]=itemsanditems[1]ornil
528+
cb(pr_cache[key])
529+
end, {
530+
author=branch.author,
531+
head=branch.head,
532+
base=branch.base,
533+
repo=branch.repo,
534+
limit=1,
535+
})
536+
end
537+
458538
returnM

‎lua/snacks/gh/buf.lua‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ function M:render(opts)
9898
self:wo()
9999

100100
localspinner---@typesnacks.util.Spinner?
101-
localproc=Api.view(self.item,function(it,updated)
101+
localproc=Api.view(function(it,updated)
102102
vim.schedule(function()
103103
ifnotself:valid()then
104104
return
@@ -112,7 +112,7 @@ function M:render(opts)
112112
self:keys()
113113
end
114114
end)
115-
end, {force=opts.force })
115+
end,self.item,{force=opts.force })
116116

117117
-- initial render (is partial if proc is running)
118118
ifItem.is(self.item)then

‎lua/snacks/gh/types.lua‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,11 @@
175175
---@fieldclosed?number
176176
---@fieldmerged?number
177177
---@fielddraft?boolean
178+
179+
---@classsnacks.gh.api.Branch
180+
---@fieldurlstring URL of the remote branch
181+
---@fieldauthorstring owner of the remote branch
182+
---@fieldrepostring owner/name format
183+
---@fieldbranchstring local branch name
184+
---@fieldbasestring branch we want to merge into
185+
---@fieldheadstring branch we want to merge from

‎lua/snacks/picker/config/sources.lua‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,20 @@ M.gh_labels = {
309309
format="gh_format_label",
310310
}
311311

312+
---@classsnacks.picker.gh.actions.Config:snacks.picker.Config
313+
---@fieldnumbernumber issue or PR number
314+
---@fieldrepostring GitHub repository(owner/repo). Defaults to current git repo
315+
---@fieldtype"issue"|"pr"
316+
---@fielditem?snacks.picker.gh.Item
317+
M.gh_actions= {
318+
layout= {preset="select",layout= {max_width=50 } },
319+
title=" Actions",
320+
main= {current=true },
321+
finder="gh_get_actions",
322+
format="gh_format_action",
323+
confirm="gh_perform_action",
324+
}
325+
312326
--- Git arguments are use like this:
313327
--- * git [<cmd_args>] <cmd> [<args>]
314328
--- * cmd may be `status`, `log`, `diff`, etc.

‎lua/snacks/picker/source/gh.lua‎

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
localActions=require("snacks.gh.actions")
12
localApi=require("snacks.gh.api")
2-
localActions=require("snacks.gh.actions").actions
33

44
localM= {}
55

@@ -8,15 +8,15 @@ M.actions = setmetatable({}, {
88
iftype(k)~="string"then
99
return
1010
end
11-
ifnotActions[k]then
11+
ifnotActions.actions[k]then
1212
returnnil
1313
end
1414
---@typesnacks.picker.Action
1515
localaction= {
16-
desc=Actions[k].desc,
16+
desc=Actions.actions[k].desc,
1717
action=function(picker,item,action)
1818
---@diagnosticdisable-next-line:param-type-mismatch
19-
returnActions[k].action(item, {
19+
returnActions.actions[k].action(item, {
2020
picker=picker,
2121
items=picker:selected({fallback=true }),
2222
action=action,
@@ -44,7 +44,7 @@ function M.gh(opts, ctx)
4444
end
4545
end
4646

47-
---@paramoptssnacks.picker.Config
47+
---@paramoptssnacks.picker.gh.issue.Config
4848
---@typesnacks.picker.finder
4949
functionM.issue(opts,ctx)
5050
returnM.gh(
@@ -55,7 +55,7 @@ function M.issue(opts, ctx)
5555
)
5656
end
5757

58-
---@paramoptssnacks.picker.Config
58+
---@paramoptssnacks.picker.gh.pr.Config
5959
---@typesnacks.picker.finder
6060
functionM.pr(opts,ctx)
6161
returnM.gh(
@@ -66,6 +66,81 @@ function M.pr(opts, ctx)
6666
)
6767
end
6868

69+
---@paramoptssnacks.picker.gh.actions.Config
70+
---@typesnacks.picker.finder
71+
functionM.get_actions(opts,ctx)
72+
opts=optsor {}
73+
localproc---@typesnacks.spawn.Proc?
74+
ifnotopts.itemandnotopts.numberthen
75+
proc=Api.current_pr(function(pr)
76+
opts.item=pr
77+
end)
78+
end
79+
---@async
80+
returnfunction(cb)
81+
ifprocthen
82+
proc:wait()
83+
end
84+
localitem=opts.item
85+
86+
ifnotitemthen
87+
localrequired= {"type","repo","number"}
88+
localmissing=vim.tbl_filter(function(field)
89+
returnopts[field]==nil
90+
end,required)---@typestring[]
91+
if#missing>0then
92+
Snacks.notify.error({
93+
"Missing required options for `Snacks.picker.gh_actions()`:",
94+
"- `"..table.concat(missing,",").."`",
95+
"",
96+
"Either provide the fields, or run in a git repo with a **current PR**.",
97+
}, {title="Snacks Picker GH Actions"})
98+
return
99+
end
100+
item=Api.get({type=opts.typeor"pr",repo=opts.repo,number=opts.number })
101+
proc=Api.view(function(it)
102+
item=it
103+
end,item)
104+
105+
ifprocthen
106+
proc:wait()
107+
end
108+
ifnotitemthen
109+
Snacks.notify.error("snacks.picker.gh.get_actions: Failed to get item")
110+
return
111+
end
112+
end
113+
114+
localactions=Actions.get_actions(item)
115+
actions.gh_actions=nil-- remove this action
116+
actions.gh_perform_action=nil-- remove this action
117+
localitems= {}---@typesnacks.picker.finder.Item[]
118+
forname,actioninpairs(actions)do
119+
---@classsnacks.picker.gh.Action:snacks.picker.finder.Item
120+
items[#items+1]= {
121+
text=Snacks.picker.util.text(action, {"name","desc"}),
122+
file=item.uri,
123+
name=name,
124+
item=item,
125+
desc=action.descorname,
126+
action=action,
127+
}
128+
end
129+
table.sort(items,function(a,b)
130+
localpa=a.action.priorityor0
131+
localpb=b.action.priorityor0
132+
ifpa~=pbthen
133+
returnpa>pb
134+
end
135+
returna.desc<b.desc
136+
end)
137+
fori,itinipairs(items)do
138+
it.text= ("%d. %s"):format(i,it.text)
139+
cb(it)
140+
end
141+
end
142+
end
143+
69144
---@paramoptssnacks.picker.gh.diff.Config
70145
---@typesnacks.picker.finder
71146
functionM.diff(opts,ctx)

‎lua/snacks/util/spawn.lua‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,20 @@ function Proc:debug(opts)
105105
returnSnacks.debug.cmd(opts)
106106
end
107107

108+
functionProc:setup_async()
109+
self.async=Async.running()
110+
ifself.asyncthen
111+
self.async:on("abort",function()
112+
ifself:running()then
113+
self:kill()
114+
end
115+
end)
116+
end
117+
end
118+
108119
---@async
109120
functionProc:wait()
121+
self:setup_async()
110122
assert(self.async,"Not in an async context")
111123
assert(self.async==Async.running(),"Not in the current async context")
112124
whilenotself.did_exitorself:running()do
@@ -120,14 +132,7 @@ function Proc:run()
120132
returnself:on_exit()
121133
end
122134

123-
self.async=Async.running()
124-
ifself.asyncthen
125-
self.async:on("abort",function()
126-
ifself:running()then
127-
self:kill()
128-
end
129-
end)
130-
end
135+
self:setup_async()
131136

132137
self.stdout=assert(uv.new_pipe())
133138
self.stderr=assert(uv.new_pipe())

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp