-
Notifications
You must be signed in to change notification settings - Fork 190
feat(fzf): respect users
option
#1106
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
base: master
Are you sure you want to change the base?
Changes from all commits
6047087
e9098d2
5718e31
c474730
52431d7
a8dbac0
f764a11
127acb0
1ef2c22
4880c16
3efbffc
a11bdd0
82996ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1070,6 +1070,7 @@ query($endCursor: String) { | |
... on User { | ||
id | ||
login | ||
name | ||
} | ||
... on Organization { | ||
id | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,119 +1,200 @@ | ||
---@diagnostic disable | ||
local entry_maker = require "octo.pickers.fzf-lua.entry_maker" | ||
local octo_config = require "octo.config" | ||
local queries = require "octo.gh.queries" | ||
local fzf = require "fzf-lua" | ||
local gh = require "octo.gh" | ||
local graphql = require "octo.gh.graphql" | ||
local picker_utils = require "octo.pickers.fzf-lua.pickers.utils" | ||
local utils = require "octo.utils" | ||
|
||
return function(cb) | ||
local formatted_users = {} | ||
local orgs = {} | ||
|
||
local function contents(prompt) | ||
-- skip empty queries | ||
if not prompt or prompt == "" or utils.is_blank(prompt) then | ||
return {} | ||
end | ||
local query = graphql("users_query", prompt) | ||
local output = gh.run { | ||
args = { "api", "graphql", "--paginate", "-f", string.format("query=%s", query) }, | ||
mode = "sync", | ||
} | ||
if output then | ||
local users = {} | ||
local orgs = {} | ||
local responses = utils.get_pages(output) | ||
for _, resp in ipairs(responses) do | ||
for _, user in ipairs(resp.data.search.nodes) do | ||
if not user.teams then | ||
-- regular user | ||
if not vim.tbl_contains(vim.tbl_keys(users), user.login) then | ||
users[user.login] = { | ||
id = user.id, | ||
login = user.login, | ||
} | ||
end | ||
elseif user.teams and user.teams.totalCount > 0 then | ||
-- organization, collect all teams | ||
if not vim.tbl_contains(vim.tbl_keys(orgs), user.login) then | ||
orgs[user.login] = { | ||
id = user.id, | ||
login = user.login, | ||
teams = user.teams.nodes, | ||
} | ||
else | ||
vim.list_extend(orgs[user.login].teams, user.teams.nodes) | ||
local delimiter = "\t" | ||
|
||
local fzf_opts = { | ||
["--delimiter"] = delimiter, | ||
["--with-nth"] = "3..", | ||
} | ||
|
||
local function format_display(thing, entity_type) | ||
local str = thing.id .. delimiter .. entity_type .. delimiter | ||
local display_login = entity_type == "org" and require("fzf-lua").utils.ansi_codes.magenta(thing.login) or thing.login | ||
str = str .. display_login | ||
|
||
if thing.name and thing.name ~= vim.NIL then | ||
str = string.format("%s (%s)", str, thing.name) | ||
end | ||
|
||
return str | ||
end | ||
|
||
local function get_user_requester(prompt) | ||
-- skip empty queries | ||
if utils.is_blank(prompt) then | ||
return {} | ||
end | ||
local query = graphql("users_query", prompt) | ||
local output = gh.run { | ||
args = { "api", "graphql", "--paginate", "-f", string.format("query=%s", query) }, | ||
mode = "sync", | ||
} | ||
if not output then | ||
return {} | ||
end | ||
local users = {} | ||
local end_idx = output:find "}{" | ||
-- add a newline after }{ if it exists | ||
if end_idx then | ||
output = output:sub(1, end_idx) .. "\n" .. output:sub(end_idx + 1) | ||
end | ||
local jsons = vim.split(output, "\n", { plain = true }) | ||
-- parse each JSON object | ||
for _, json_raw in ipairs(jsons) do | ||
local responses = utils.get_pages(json_raw) | ||
for _, resp in ipairs(responses) do | ||
for _, user in ipairs(resp.data.search.nodes) do | ||
if not user.teams then | ||
-- regular user | ||
if not vim.tbl_contains(vim.tbl_keys(users), user.login) then | ||
users[user.login] = { | ||
id = user.id, | ||
login = user.login, | ||
} | ||
if user.name then | ||
users[user.login].name = user.name | ||
end | ||
end | ||
elseif user.teams and user.teams.totalCount > 0 then | ||
Comment on lines
+46
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yes good question! the json is not separated, you'll see the response being on telescope:
on fzf-lua (without my PR):
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There might be another function in utils.lua that does this processing. It shouldn't exist only in this function |
||
-- organization, collect orgs | ||
if not vim.tbl_contains(vim.tbl_keys(orgs), user.login) then | ||
orgs[user.id] = { | ||
id = user.id, | ||
login = user.login, | ||
teams = user.teams.nodes, | ||
} | ||
else | ||
vim.list_extend(orgs[user.login].teams, user.teams.nodes) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
-- TODO highlight orgs? | ||
local function format_display(thing) | ||
return thing.id .. " " .. thing.login | ||
end | ||
local results = {} | ||
-- process orgs with teams | ||
for _, user in pairs(users) do | ||
user.ordinal = format_display(user, "user") | ||
table.insert(results, user.ordinal) | ||
end | ||
for _, org in pairs(orgs) do | ||
org.login = string.format("%s (%d)", org.login, #org.teams) | ||
org.ordinal = format_display(org, "org") | ||
table.insert(results, org.ordinal) | ||
end | ||
return results | ||
end | ||
|
||
local results = {} | ||
-- process orgs with teams | ||
for _, user in pairs(users) do | ||
user.ordinal = format_display(user) | ||
formatted_users[user.ordinal] = user | ||
table.insert(results, user.ordinal) | ||
end | ||
for _, org in pairs(orgs) do | ||
org.login = string.format("%s (%d)", org.login, #org.teams) | ||
org.ordinal = format_display(org) | ||
formatted_users[org.ordinal] = org | ||
table.insert(results, org.ordinal) | ||
end | ||
return results | ||
else | ||
return {} | ||
end | ||
local function get_users(query_name, node_name) | ||
local repo = utils.get_remote_name() | ||
local owner, name = utils.split_repo(repo) | ||
local output = gh.api.graphql { | ||
query = queries[query_name], | ||
f = { owner = owner, name = name }, | ||
paginate = true, | ||
jq = ".data.repository." .. node_name .. ".nodes", | ||
opts = { mode = "sync" }, | ||
} | ||
if utils.is_blank(output) then | ||
return {} | ||
end | ||
|
||
fzf.fzf_live( | ||
contents, | ||
vim.tbl_deep_extend("force", picker_utils.dropdown_opts, { | ||
fzf_opts = { | ||
["--delimiter"] = "' '", | ||
["--with-nth"] = "2..", | ||
}, | ||
actions = { | ||
["default"] = { | ||
function(user_selected) | ||
local user_entry = formatted_users[user_selected[1]] | ||
if not user_entry.teams then | ||
-- user | ||
cb(user_entry.id) | ||
else | ||
local formatted_teams = {} | ||
local team_titles = {} | ||
local results = {} | ||
local flattened = utils.get_flatten_pages(output) | ||
for _, user in ipairs(flattened) do | ||
user.ordinal = format_display(user, "user") | ||
table.insert(results, user.ordinal) | ||
end | ||
return results | ||
end | ||
|
||
for _, team in ipairs(user_entry.teams) do | ||
local team_entry = entry_maker.gen_from_team(team) | ||
local function get_user_id_type(selection) | ||
local spl = vim.split(selection[1], delimiter) | ||
return spl[1], spl[2] | ||
end | ||
|
||
return function(cb) | ||
local cfg = octo_config.values | ||
if cfg.users == "search" then | ||
return fzf.fzf_live( | ||
get_user_requester, | ||
vim.tbl_deep_extend("force", picker_utils.dropdown_opts, { | ||
fzf_opts = fzf_opts, | ||
actions = { | ||
["default"] = { | ||
function(user_selected) | ||
local user_id, user_type = get_user_id_type(user_selected) | ||
if user_type == "user" then | ||
cb(user_id) | ||
else | ||
-- handle org | ||
local formatted_teams = {} | ||
local team_titles = {} | ||
|
||
if team_entry ~= nil then | ||
formatted_teams[team_entry.ordinal] = team_entry | ||
table.insert(team_titles, team_entry.ordinal) | ||
for _, team in ipairs(orgs[user_id].teams) do | ||
local team_entry = entry_maker.gen_from_team(team) | ||
|
||
if team_entry ~= nil then | ||
formatted_teams[team_entry.ordinal] = team_entry | ||
table.insert(team_titles, team_entry.ordinal) | ||
end | ||
end | ||
end | ||
|
||
fzf.fzf_exec( | ||
team_titles, | ||
vim.tbl_deep_extend("force", picker_utils.dropdown_opts, { | ||
actions = { | ||
["default"] = function(team_selected) | ||
local team_entry = formatted_teams[team_selected[1]] | ||
cb(team_entry.team.id) | ||
end, | ||
}, | ||
}) | ||
) | ||
end | ||
fzf.fzf_exec( | ||
team_titles, | ||
vim.tbl_deep_extend("force", picker_utils.dropdown_opts, { | ||
actions = { | ||
["default"] = function(team_selected) | ||
local team_entry = formatted_teams[team_selected[1]] | ||
if false then | ||
cb(team_entry.team.id) | ||
end | ||
utils.error "Not implemented yet" | ||
end, | ||
}, | ||
}) | ||
) | ||
end | ||
end, | ||
}, | ||
}, | ||
}) | ||
) | ||
else | ||
local users = {} | ||
if cfg.users == "assignable" then | ||
users = get_users("assignable_users", "assignableUsers") | ||
elseif cfg.users == "mentionable" then | ||
users = get_users("mentionable_users", "mentionableUsers") | ||
else | ||
utils.error("Invalid user selection mode: " .. cfg.users) | ||
return | ||
end | ||
if #users == 0 then | ||
utils.error(string.format("No %s users found.", cfg.users)) | ||
return | ||
end | ||
fzf.fzf_exec( | ||
users, | ||
vim.tbl_deep_extend("force", picker_utils.dropdown_opts, { | ||
fzf_opts = fzf_opts, | ||
actions = { | ||
["default"] = function(user_selected) | ||
local user_id, _ = get_user_id_type(user_selected) | ||
cb(user_id) | ||
end, | ||
}, | ||
}, | ||
}) | ||
) | ||
}) | ||
) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be scoped here or defined within function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use if for the inception picker of orgs.
if its an org, I need to open another picker with the org teams, so I keep the orgs on the module level so I can access it another time.
Basically this is the same thing that was before my PR, but now just for orgs and not for users.
for users I can keep everything I need in the results table and use
with-nth
option of fzf to not show the irrelevant data.