Skip to content
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

add Milestone commands #749

Merged
merged 9 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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