Skip to content

Commit

Permalink
add Milestone commands (#749)
Browse files Browse the repository at this point in the history
* query for milestones

* implement telescope picker

* put not implemented for others

* add helper commands

* expose from Octo

* early stop with blank title

* remove the unused Job

* display desciption if exists
  • Loading branch information
wd60622 authored Dec 30, 2024
1 parent fe5ee9e commit 8b2f23c
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 0 deletions.
47 changes: 47 additions & 0 deletions lua/octo/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ local vim = vim

local M = {}

local get_current_buffer = function()
local bufnr = vim.api.nvim_get_current_buf()
return octo_buffers[bufnr]
end

function M.setup()
vim.api.nvim_create_user_command("Octo", function(opts)
require("octo.commands").octo(unpack(opts.fargs))
Expand Down Expand Up @@ -58,6 +63,48 @@ function M.setup()
picker.discussions(opts)
end,
},
milestone = {
list = function(repo, ...)
local opts = M.process_varargs(repo, ...)
opts.cb = function(item)
utils.info("Picked " .. item.title)
end
picker.milestones(opts)
end,
add = function(repo, ...)
local buffer = get_current_buffer()
if not buffer then
return
end

local opts = M.process_varargs(repo, ...)
opts.cb = function(item)
utils.add_milestone(buffer:isIssue(), buffer.number, item.title)
end
picker.milestones(opts)
end,
remove = function(repo, ...)
local buffer = get_current_buffer()
if not buffer then
return
end

local milestone = buffer.node.milestone
if utils.is_blank(milestone) then
utils.error "No milestone to remove"
return
end

utils.remove_milestone(buffer:isIssue(), buffer.number)
end,
create = function(...)
vim.fn.inputsave()
local title = vim.fn.input "Enter milestone title: "
local description = vim.fn.input "Enter milestone description: "
vim.fn.inputrestore()
utils.create_milestone(title, description)
end,
},
issue = {
create = function(repo)
M.create_issue(repo)
Expand Down
14 changes: 14 additions & 0 deletions lua/octo/gh/graphql.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3548,6 +3548,20 @@ query {
}
]]

M.open_milestones_query = [[
query($name: String!, $owner: String!, $n_milestones: Int!) {
repository(owner: $owner, name: $name) {
milestones(first: $n_milestones, states: [OPEN]) {
nodes {
id
title
description
}
}
}
}
]]

return function(query, ...)
local opts = { escape = true }
for _, v in ipairs { ... } do
Expand Down
1 change: 1 addition & 0 deletions lua/octo/pickers/fzf-lua/provider.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ M.picker = {
review_commits = require "octo.pickers.fzf-lua.pickers.review_commits",
search = require "octo.pickers.fzf-lua.pickers.search",
users = require "octo.pickers.fzf-lua.pickers.users",
milestones = M.not_implemented,
}

return M
44 changes: 44 additions & 0 deletions lua/octo/pickers/telescope/entry_maker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,50 @@ function M.gen_from_project_card()
end
end

function M.gen_from_milestone(title_width, show_description)
title_width = title_width or 10

local make_display = function(entry)
if not entry then
return nil
end

local columns, items
if show_description then
columns = {
{ entry.milestone.title, "OctoDetailsLabel" },
{ entry.milestone.description },
}
items = { { width = title_width }, { remaining = true } }
else
columns = {
{ entry.milestone.title, "OctoDetailsLabel" },
}
items = { { width = title_width } }
end

local displayer = entry_display.create {
separator = "",
items = items,
}

return displayer(columns)
end

return function(milestone)
if not milestone or vim.tbl_isempty(milestone) then
return nil
end

return {
value = milestone.id,
ordinal = milestone.title,
display = make_display,
milestone = milestone,
}
end
end

function M.gen_from_label()
local make_display = function(entry)
if not entry then
Expand Down
73 changes: 73 additions & 0 deletions lua/octo/pickers/telescope/provider.lua
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,10 @@ local function select(opts)
local selection = action_state.get_selected_entry(prompt_bufnr)
table.insert(items, get_item(selection))
cb = single_cb
elseif multiple_cb == nil then
utils.error "Multiple selections are not allowed"
actions.close(prompt_bufnr)
return
else
for _, selection in ipairs(selections) do
table.insert(items, get_item(selection))
Expand Down Expand Up @@ -1268,6 +1272,74 @@ function M.discussions(opts)
}
end

function M.milestones(opts)
local owner, name = utils.split_repo(opts.repo)
local query = graphql "open_milestones_query"

gh.graphql {
query = query,
fields = {
owner = owner,
name = name,
n_milestones = 25,
},
opts = {
cb = function(output, stderr)
if stderr and not utils.is_blank(stderr) then
utils.error(stderr)
return
end

local resp = vim.fn.json_decode(output)
local nodes = resp.data.repository.milestones.nodes

if #nodes == 0 then
utils.error(string.format("There are no matching milestones in %s.", opts.repo))
return
end

local title_width = 0
for _, milestone in ipairs(nodes) do
title_width = math.max(title_width, #milestone.title)
end

local non_empty_descriptions = false
for _, milestone in ipairs(nodes) do
if not utils.is_blank(milestone.description) then
non_empty_descriptions = true
break
end
end

pickers
.new(vim.deepcopy(dropdown_opts), {
finder = finders.new_table {
results = nodes,
entry_maker = entry_maker.gen_from_milestone(title_width, non_empty_descriptions),
},
sorter = conf.generic_sorter(opts),
attach_mappings = function(_, map)
actions.select_default:replace(function(prompt_bufnr)
select {
bufnr = prompt_bufnr,
single_cb = function(selected)
opts.cb(selected[1])
end,
multiple_cb = nil,
get_item = function(selected)
return selected.milestone
end,
}
end)
return true
end,
})
:find()
end,
},
}
end

M.picker = {
actions = M.actions,
assigned_labels = M.select_assigned_label,
Expand All @@ -1289,6 +1361,7 @@ M.picker = {
review_commits = M.review_commits,
search = M.search,
users = M.select_user,
milestones = M.milestones,
}

return M
1 change: 1 addition & 0 deletions lua/octo/pickers/vim-clap/provider.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ M.picker = {
review_commits = M.not_implemented,
search = M.not_implemented,
users = M.not_implemented,
milestones = M.not_implemented,
}

return M
70 changes: 70 additions & 0 deletions lua/octo/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,76 @@ function M.commit_exists(commit, cb)
}):start()
end

---Add a milestone to an issue or PR
function M.add_milestone(issue, number, milestone_name)
local command = issue and "issue" or "pr"
local args = { command, "edit", number, "--milestone", milestone_name }

gh.run {
args = args,
cb = function(output, stderr)
if stderr and not M.is_blank(stderr) then
M.error(stderr)
elseif output then
M.info("Added milestone " .. milestone_name)
end
end,
}
end

---Remove a milestone from an issue or PR
function M.remove_milestone(issue, number)
local command = issue and "issue" or "pr"
local args = { command, "edit", number, "--remove-milestone" }

gh.run {
args = args,
cb = function(output, stderr)
if stderr and not M.is_blank(stderr) then
M.error(stderr)
elseif output then
M.info "Removed milestone"
end
end,
}
end

---https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28#create-a-milestone
---Create a new milestone
function M.create_milestone(title, description)
if M.is_blank(title) then
M.error "Title is required to create milestone"
return
end

local owner, name = M.split_repo(M.get_remote_name())
local endpoint = string.format("repos/%s/%s/milestones", owner, name)
local args = { "api", "--method", "POST", endpoint }

local data = {
title = title,
description = description,
state = "open",
}

for key, value in pairs(data) do
table.insert(args, "-f")
table.insert(args, string.format("%s=%s", key, value))
end

gh.run {
args = args,
cb = function(output, stderr)
if stderr and not M.is_blank(stderr) then
M.error(stderr)
elseif output then
local resp = vim.fn.json_decode(output)
M.info("Created milestone " .. resp.title)
end
end,
}
end

function M.develop_issue(issue_repo, issue_number, branch_repo)
if M.is_blank(branch_repo) then
branch_repo = M.get_remote_name()
Expand Down

0 comments on commit 8b2f23c

Please sign in to comment.