From f06a2787b0678f828edbb85e361c256c1e9be63f Mon Sep 17 00:00:00 2001 From: David Frank Date: Fri, 19 Nov 2021 23:45:48 +0100 Subject: [PATCH] feat: use curl as fallback Previously, curl would only be used if gh or glab were not found. But if the executable was found it was used. Now, if the the executable returns a non zero exit code, a curl request will be send. This is specifically useful, if gh or glab are installed but not setup correctly. --- lua/cmp_git/github.lua | 72 ++++++++++++++++-------------------------- lua/cmp_git/gitlab.lua | 59 +++++++++++++--------------------- lua/cmp_git/utils.lua | 57 ++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 82 deletions(-) diff --git a/lua/cmp_git/github.lua b/lua/cmp_git/github.lua index 13ded43..f986066 100644 --- a/lua/cmp_git/github.lua +++ b/lua/cmp_git/github.lua @@ -1,6 +1,7 @@ local Job = require("plenary.job") local utils = require("cmp_git.utils") local sort = require("cmp_git.sort") +local log = require("cmp_git.log") local GitHub = { cache = { @@ -21,48 +22,33 @@ GitHub.new = function(overrides) return self end -local get_command = function(callback, gh_command, curl_url, handle_item) - local command = nil - - if false and vim.fn.executable("gh") == 1 and gh_command then - command = gh_command - elseif vim.fn.executable("curl") == 1 and curl_url then - command = { - "curl", - "-s", - "-H", - "'Accept: application/vnd.github.v3+json'", - curl_url, - } - - if vim.fn.exists("$GITHUB_API_TOKEN") == 1 then - local token = vim.fn.getenv("GITHUB_API_TOKEN") - local authorization_header = string.format("Authorization: token %s", token) - table.insert(command, "-H") - table.insert(command, authorization_header) - end - else - vim.notify("gh and curl executables not found!") - return +local get_items = function(callback, gh_args, curl_url, handle_item) + local gh_job = utils.build_job("gh", callback, gh_args, handle_item) + + curl_args = { + "curl", + "-s", + "-H", + "'Accept: application/vnd.github.v3+json'", + curl_url, + } + + if vim.fn.exists("$GITHUB_API_TOKEN") == 1 then + local token = vim.fn.getenv("GITHUB_API_TOKEN") + local authorization_header = string.format("Authorization: token %s", token) + table.insert(curl_args, "-H") + table.insert(curl_args, authorization_header) end - command.cwd = utils.get_cwd() - command.on_exit = vim.schedule_wrap(function(job) - local result = table.concat(job:result(), "") - - local items = utils.handle_response(result, handle_item) + local curl_job = utils.build_job("curl", callback, curl_args, handle_item) - callback({ items = items, isIncomplete = false }) - end) - - return command + return utils.chain_fallback(gh_job, curl_job) end local get_pull_requests_job = function(callback, git_info, trigger_char, config) - return Job:new(get_command( + return get_items( callback, { - "gh", "pr", "list", "--repo", @@ -105,14 +91,13 @@ local get_pull_requests_job = function(callback, git_info, trigger_char, config) data = pr, } end - )) + ) end local get_issues_job = function(callback, git_info, trigger_char, config) - return Job:new(get_command( + return get_items( callback, { - "gh", "issue", "list", "--repo", @@ -155,7 +140,7 @@ local get_issues_job = function(callback, git_info, trigger_char, config) }, } end - )) + ) end local _get_issues = function(self, callback, git_info, trigger_char, config) @@ -234,10 +219,10 @@ function GitHub:get_issues_and_prs(callback, git_info, trigger_char, config) callback({ items = merged, isIncomplete = false }) else if git_info.host ~= "github.com" then - vim.notify("Can't fetch Github issues or pull requests, not a github repository") + log.warn("Can't fetch Github issues or pull requests, not a github repository") return false elseif git_info.owner == nil and git_info.repo == nil then - vim.notify("Can't figure out git repository or owner") + log.warn("Can't figure out git repository or owner") return false end @@ -279,8 +264,7 @@ function GitHub:get_mentions(callback, git_info, trigger_char, config) config = vim.tbl_extend("force", self.config.mentions, config or {}) - Job - :new(get_command( + local job = get_items( function(args) callback(args) self.cache.mentions[bufnr] = args.items @@ -301,8 +285,8 @@ function GitHub:get_mentions(callback, git_info, trigger_char, config) data = mention, } end - )) - :start() + ) + job:start() return true end diff --git a/lua/cmp_git/gitlab.lua b/lua/cmp_git/gitlab.lua index 525ea50..34f65d6 100644 --- a/lua/cmp_git/gitlab.lua +++ b/lua/cmp_git/gitlab.lua @@ -1,6 +1,6 @@ -local Job = require("plenary.job") local utils = require("cmp_git.utils") local sort = require("cmp_git.sort") +local log = require("cmp_git.log") local GitLab = { cache = { @@ -25,39 +25,24 @@ local get_project_id = function(git_info) return utils.url_encode(string.format("%s/%s", git_info.owner, git_info.repo)) end -local get_items = function(callback, glab_command, curl_url, handle_item) - local command = nil - - if vim.fn.executable("glab") == 1 and glab_command then - command = glab_command - elseif vim.fn.executable("curl") == 1 and curl_url then - command = { - "curl", - "-s", - curl_url, - } - - if vim.fn.exists("$GITLAB_TOKEN") == 1 then - local token = vim.fn.getenv("GITLAB_TOKEN") - local authorization_header = string.format("Authorization: Bearer %s", token) - table.insert(command, "-H") - table.insert(command, authorization_header) - end - else - vim.notify("glab and curl executables not found!") - return - end +local get_items = function(callback, glab_args, curl_url, handle_item) + local glab_job = utils.build_job("glab", callback, glab_args, handle_item) - command.cwd = utils.get_cwd() - command.on_exit = vim.schedule_wrap(function(job) - local result = table.concat(job:result(), "") + curl_args = { + "-s", + curl_url, + } - local items = utils.handle_response(result, handle_item) + if vim.fn.exists("$GITLAB_TOKEN") == 1 then + local token = vim.fn.getenv("GITLAB_TOKEN") + local authorization_header = string.format("Authorization: Bearer %s", token) + table.insert(curl_args, "-H") + table.insert(curl_args, authorization_header) + end - callback({ items = items, isIncomplete = false }) - end) + local curl_job = utils.build_job("curl", callback, curl_args, handle_item) - Job:new(command):start() + return utils.chain_fallback(glab_job, curl_job) end function GitLab:get_issues(callback, git_info, trigger_char, config) @@ -75,13 +60,12 @@ function GitLab:get_issues(callback, git_info, trigger_char, config) config = vim.tbl_extend("force", self.config.issues, config or {}) local id = get_project_id(git_info) - get_items( + local job = get_items( function(args) callback(args) self.cache.issues[bufnr] = args.items end, { - "glab", "api", string.format("/projects/%s/issues?per_page=%d&state=%s", id, config.limit, config.state), }, @@ -109,6 +93,7 @@ function GitLab:get_issues(callback, git_info, trigger_char, config) } end ) + job:start() return true end @@ -127,13 +112,12 @@ function GitLab:get_mentions(callback, git_info, trigger_char, config) config = vim.tbl_extend("force", self.config.mentions, config or {}) local id = get_project_id(git_info) - get_items( + local job = get_items( function(args) callback(args) self.cache.mentions[bufnr] = args.items end, { - "glab", "api", string.format("/projects/%s/users?per_page=%d", id, config.limit), }, @@ -150,6 +134,7 @@ function GitLab:get_mentions(callback, git_info, trigger_char, config) } end ) + job:start() return true end @@ -165,17 +150,16 @@ function GitLab:get_merge_requests(callback, git_info, trigger_char, config) callback({ items = self.cache.merge_requests[bufnr], isIncomplete = false }) return true end - + config = vim.tbl_extend("force", self.config.merge_requests, config or {}) local id = get_project_id(git_info) - get_items( + local job = get_items( function(args) callback(args) self.cache.merge_requests[bufnr] = args.items end, { - "glab", "api", string.format("/projects/%s/merge_requests?per_page=%d&state=%s", id, config.limit, config.state), }, @@ -200,6 +184,7 @@ function GitLab:get_merge_requests(callback, git_info, trigger_char, config) } end ) + job:start() return true end diff --git a/lua/cmp_git/utils.lua b/lua/cmp_git/utils.lua index 75bc5dc..4ea8b3e 100644 --- a/lua/cmp_git/utils.lua +++ b/lua/cmp_git/utils.lua @@ -1,3 +1,6 @@ +local log = require("cmp_git.log") +local Job = require("plenary.job") + local M = {} local char_to_hex = function(c) @@ -92,12 +95,64 @@ M.get_cwd = function() return vim.fn.getcwd() end +M.build_job = function(exec, callback, args, handle_item) + -- TODO: Find a nicer way, that we can keep chaining jobs at call side + if vim.fn.executable(exec) ~= 1 or not args then + log.fmt_debug("Can't work with %s for this call", exec) + return nil + end + + return Job:new({ + command = exec, + args = args, + cwd = M.get_cwd(), + on_exit = vim.schedule_wrap(function(job, code) + if code ~= 0 then + log.fmt_debug("%s returned with exit code %d", exec, code) + else + log.fmt_debug("%s returned with a result", exec) + local result = table.concat(job:result(), "") + + local items = M.handle_response(result, handle_item) + + callback({ items = items, isIncomplete = false }) + end + end), + }) +end + +--- Start the second job if the first on fails, handle cases if the first or second job is nil. +--- The last job debug prints on failure +M.chain_fallback = function(first, second) + if first and second then + first:and_then_on_failure(second) + second:after_failure(function(_, code, _) + log.fmt_debug("%s failed with exit code %d, couldn't retrieve any completion info", second.command, code) + end) + + return first + elseif first then + first:after_failure(function(_, code, _) + log.fmt_debug("%s failed with exit code %d, couldn't retrieve any completion info", first.command, code) + end) + return first + elseif second then + second:after_failure(function(_, code, _) + log.fmt_debug("%s failed with exit code %d, couldn't retrieve any completion info", second.command, code) + end) + return second + else + log.debug("Neither %s or %s could be found", first.command, second.command) + return nil + end +end + M.handle_response = function(response, handle_item) local items = {} local process_data = function(ok, parsed) if not ok then - vim.notify("Failed to parse api result") + log.warn("Failed to parse api result") return end