1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-24 02:10:05 +08:00
SpaceVim/bundle/github-issues.vim/rplugin/python/ghissues.py

1114 lines
33 KiB
Python
Vendored

import hashlib
import json
import os
import shlex
import string
import subprocess
import threading
import time
import urllib
from past.builtins import basestring # Works in python 2 and 3
from past.builtins import map, filter # Python 2 map compatibility
try:
# Python 2
# import basestring
import urllib2
except ImportError:
print("Using Python3")
# Python 3 re-organizes urllib
from urllib.parse import urlencode
import urllib.request as urllib2
urllib.urlencode = urlencode
import vim
SHOW_ALL = "[Show all issues]"
SHOW_ASSIGNED_ME = "[Only show assigned to me]"
SHOW_COMMITS = "## [Commits]"
SHOW_FILES_CHANGED = "## [Files Changed]"
# dictionaries for caching
# repo urls on github by filepath
github_repos = {}
# issues by repourl
github_datacache = {}
# api data by repourl + / + endpoint
api_cache = {}
# process handles by file hash
proc_handle = {}
# whether or not a async request is pending by file hash
proc_pending = {}
# reset web cache after this value grows too large
cache_count = 0
# whether or not SSL is known to be enabled
ssl_enabled = False
# keep a list of issues
globissues = {}
# returns the Github url (i.e. jaxbot/vimfiles) for the current file
def getRepoURI():
global github_repos
if "gissues" in vim.current.buffer.name:
parens = getFilenameParens()
return parens[0] + "/" + parens[1]
# get the directory the current file is in
filepath = vim.eval("expand('%:p:h')")
# Remove trailing ".git" segment from path.
# While `git remote -v` appears to work from here in general, it fails when
# invoked for COMMIT_EDITMSG: `fatal: Not a git repository: '.git'`.
filepath = filepath.split(os.path.sep + ".git")[0]
# cache the github repo for performance
if github_repos.get(filepath, None) is not None:
return github_repos[filepath]
# Get info for all remotes.
# Do this first: if it fails, we're not in a Git repo.
try:
all_remotes = subprocess.check_output(
['git', 'remote', '-v'], cwd=filepath)
except subprocess.CalledProcessError:
github_repos[filepath] = ""
return github_repos[filepath]
except OSError:
all_remotes = subprocess.check_output(['git', 'remote', '-v'])
# Try to get the remote for the current branch/HEAD.
try:
remote_ref = subprocess.check_output(
'git rev-parse --abbrev-ref --verify --symbolic-full-name @{upstream}'.split(" "),
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
# Use the first one we find instead
remote = None
#print("github-issues: using default remote: %s" % remote)
else:
try:
branch = subprocess.check_output(
["git", "symbolic-ref", "--short", "HEAD"])
except subprocess.CalledProcessError:
# Branch could not be determined, do not filter by remote.
remote = None
else:
# Remove "/branch" from the end of remote_ref to get the remote.
remote = remote_ref[:-(len(branch) + 1)]
# possible URLs
possible_urls = vim.eval("g:github_issues_urls")
for line in all_remotes.decode().split("\n"):
try:
cur_remote, url = line.split("\t")
except ValueError:
continue
# Filter out non-matching remotes.
if remote and remote.decode() != cur_remote:
continue
# Remove " (fetch)"/" (pull)" and ".git" suffixes.
url = url.split(" ", 1)[0]
if url.endswith(".git"):
url = url[:-4]
# Skip any unwanted urls.
for possible_url in possible_urls:
split_url = url.split(possible_url)
if len(split_url) > 1:
github_repos[filepath] = split_url[1]
#print("github-issues: using repo: %s" % s[1])
break
else:
continue
break
else:
github_repos[filepath] = ""
return github_repos[filepath]
# returns the repo uri, taking into account forks
def getUpstreamRepoURI():
repourl = getRepoURI()
if repourl == "":
return ""
upstream_issues = int(vim.eval("g:github_upstream_issues"))
if upstream_issues == 1:
# try to get from what repo forked
repoinfo = ghApi("", repourl)
if repoinfo and repoinfo["fork"]:
repourl = repoinfo["source"]["full_name"]
return repourl
def showCommits(split=False):
number = vim.eval("b:ghissue_number")
repourl = vim.eval("b:ghissue_repourl")
url = ghUrl("/pulls/" + number + "/commits", repourl)
response = urllib2.urlopen(url, timeout=2)
commits = json.loads(response.read())
buffer_name = "commits/" + repourl + "/" + number
if split:
newSplit(buffer_name)
else:
newTab(buffer_name)
vim.command("setlocal modifiable")
current_buffer = vim.current.buffer
for commit in commits:
current_buffer.append(
(commit['sha'] + " " + commit['commit']['message']).split("\n"))
vim.command("normal ggdd")
vim.command(
"nnoremap <buffer> <CR> :call <SID>showIssueLink('','','False')<cr>")
vim.command("nnoremap <buffer> s :call <SID>showIssueLink('','','True')<cr>")
vim.command("call <SID>commitHighlighting()")
vim.command("let b:ghissue_repourl=\"" + repourl + "\"")
vim.command("setlocal nomodifiable")
def showCommit(sha, split=False):
repourl = vim.eval("b:ghissue_repourl")
url = ghUrl("/commits/" + sha, repourl)
headers = {"Accept": "application/vnd.github.patch"}
req = urllib2.Request(url, None, headers)
diff = urllib2.urlopen(req, timeout=2)
buffer_name = "commit/" + repourl + "/" + sha
if split:
newSplit(buffer_name)
else:
newTab(buffer_name)
vim.command("set syn=diff")
vim.command("setlocal modifiable")
current_buffer = vim.current.buffer
current_buffer.append("".join(diff).split("\n"))
vim.command("normal ggdd")
vim.command("setlocal nomodifiable")
def showFilesChanged(split=False):
number = vim.eval("b:ghissue_number")
repourl = vim.eval("b:ghissue_repourl")
url = ghUrl("/pulls/" + number, repourl)
headers = {"Accept": "application/vnd.github.diff"}
req = urllib2.Request(url, None, headers)
diff = urllib2.urlopen(req, timeout=2)
buffer_name = "files_Changed/" + repourl + "/" + number
if split:
newSplit(buffer_name)
else:
newTab(buffer_name)
vim.command("set syn=diff")
vim.command("setlocal modifiable")
current_buffer = vim.current.buffer
current_buffer.append("".join(diff).split("\n"))
vim.command("normal ggdd")
vim.command("setlocal nomodifiable")
def newTab(name):
vim.command("noswapfile silent tabe +set\\ buftype=nofile %s" % name)
mapQuit()
def newSplit(name):
vim.command(
"noswapfile silent belowright vsplit +set\\ buftype=nofile %s" %
name)
mapQuit()
def mapQuit():
vim.command("nnoremap <buffer> <silent> q :bdelete<CR>")
# displays the issues in a vim buffer
def showIssueList(labels, ignore_cache=False, only_me=False):
repourl = getUpstreamRepoURI()
if repourl == "":
print("github-issues.vim: Failed to find a suitable Github repository URL, sorry!")
vim.command("let github_failed = 1")
return
issues = getIssueList(repourl, '/issues', labels, ignore_cache, only_me)
printIssueList(issues, repourl, labels, only_me)
def printIssueList(issues, repourl='search', labels=False, only_me=False):
global globissues
globissues = issues
# Setup split
if not vim.eval("g:github_same_window") == "1":
cmd = 'silent '
if not vim.eval("g:gissues_list_vsplit") == "1":
spl = 'new +set\ buftype=nofile'
if vim.eval("g:gissues_split_height") != "0":
spl = ' ' + vim.eval("g:gissues_split_height") + spl
cmd = cmd + spl
else:
spl = 'vnew +set\ buftype=nofile'
if vim.eval("g:gissues_vsplit_width") != "0":
spl = ' ' + vim.eval("g:gissues_vsplit_width") + spl
cmd = cmd + spl
if vim.eval("g:gissues_split_expand") == "1":
cmd = 'botright ' + cmd
vim.command(cmd)
if 'labels' not in locals():
labels = ""
# Some Vim versions don't allow noswapfile as a verb
try:
vim.command("noswapfile edit " + "gissues/" + repourl + "/issues")
except BaseException:
vim.command("edit " + "gissues/" + repourl + "/issues")
vim.command("normal ggdG")
current_buffer = vim.current.buffer
cur_milestone = str(vim.eval("g:github_current_milestone"))
# its an array, so dump these into the current (issues) buffer
for issue in issues:
if cur_milestone != "" and (
not issue["milestone"] or issue["milestone"]["title"] != cur_milestone):
continue
issuestr = str(issue["number"]) + " " + issue["title"]
if 'labels' in issue:
for label in issue["labels"]:
issuestr += " [" + label["name"] + "]"
current_buffer.append(issuestr.encode(vim.eval("&encoding")))
if len(current_buffer) < 2:
current_buffer.append("No results found in " + repourl)
if cur_milestone:
current_buffer.append("Filtering by milestone: " + cur_milestone)
if labels:
current_buffer.append("Filtering by labels: " + labels)
if cur_milestone or labels or only_me:
current_buffer.append(SHOW_ALL)
if not only_me:
current_buffer.append(SHOW_ASSIGNED_ME)
highlightColoredLabels(getLabels(), True)
# append leaves an unwanted beginning line. delete it.
vim.command("1delete _")
def showSearchList():
if not vim.eval("g:github_same_window") == "1":
vim.command("silent new")
# Some Vim versions don't allow noswapfile as a verb
try:
vim.command("noswapfile edit " + "gissues")
except BaseException:
vim.command("edit " + "gissues")
vim.command("normal ggdG")
params = {"q": vim.eval("g:gh_issues_query")}
issues = getGHList(1, "custom", 'search/issues', params)
printIssueList(issues)
def showMilestoneList(labels, ignore_cache=False):
repourl = getUpstreamRepoURI()
vim.command("silent new")
# Some Vim versions don't allow noswapfile as a verb
try:
vim.command("noswapfile edit " + "gissues/" + repourl + "/milestones")
except BaseException:
vim.command("edit " + "gissues/" + repourl + "/milestones")
vim.command("normal ggdG")
current_buffer = vim.current.buffer
current_buffer.append("[None]")
milestones = getMilestoneList(repourl, labels, ignore_cache)
for mstone in milestones:
mstonestr = mstone["title"]
current_buffer.append(mstonestr.encode(vim.eval("&encoding")))
vim.command("1delete _")
# pulls the issue array from the server
def getIssueList(repourl, endpoint, query, ignore_cache=False, only_me=False):
global github_datacache
# non-string args correspond to vanilla issues request
# strings default to label unless they correspond to a state
params = {}
if isinstance(query, basestring):
params = {"labels": query}
if query in ["open", "closed", "all"]:
params = {"state": query}
if only_me:
params["assignees"] = getCurrentUser()
return getGHList(ignore_cache, repourl, endpoint, params)
def getCurrentUser():
return ghApi("", "user", True, False)["login"]
# pulls the milestone list from the server
def getMilestoneList(repourl, query="", ignore_cache=False):
global github_datacache
# TODO Add support for 'state', 'sort', 'direction'
params = {}
return getGHList(ignore_cache, repourl, "/milestones", params)
def getGHList(ignore_cache, repourl, endpoint, params):
global cache_count, github_datacache
# Maybe initialise
if github_datacache.get(
repourl, '') == '' or len(
github_datacache[repourl]) < 1:
github_datacache[repourl] = {}
if (ignore_cache or
github_datacache[repourl].get(endpoint, '') == '' or
len(github_datacache[repourl].get(endpoint, '')) < 1 or
time.time() - github_datacache[repourl][endpoint][0]["cachetime"] > 60):
# load the github API. github_repo looks like
# "jaxbot/github-issues.vim", for ex.
try:
github_datacache[repourl][endpoint] = []
more_to_load = True
page = 1
while more_to_load and page <= int(
vim.eval("g:github_issues_max_pages")):
params['page'] = str(page)
# TODO This should be in ghUrl() I think
qs = urllib.urlencode(params)
if repourl != "custom":
url = ghUrl(endpoint + '?' + qs, repourl)
else:
url = ghUrl(endpoint + '?' + qs, repourl, False)
response = urllib2.urlopen(url, timeout=2)
issuearray = json.loads(response.read())
if 'items' in issuearray:
issuearray = issuearray["items"]
# JSON parse the API response, add page to previous pages if
# any
github_datacache[repourl][endpoint] += issuearray
more_to_load = len(issuearray) == 30
page += 1
except urllib2.URLError as e:
github_datacache[repourl][endpoint] = []
if "code" in e and e.code == 404:
print(
"github-issues.vim: Error: Do you have a github_access_token defined?")
if len(github_datacache[repourl][endpoint]) > 0:
github_datacache[repourl][endpoint][0]["cachetime"] = time.time()
return github_datacache[repourl][endpoint]
# populate the omnicomplete synchronously or asynchronously, depending on mode
def populateOmniComplete():
if vim.eval("g:gissues_async_omni") == "1":
if vim.eval("g:gissues_offline_cache") == "1":
populateOmniCompleteFromDisk()
else:
populateOmniCompleteAsync()
else:
doPopulateOmniComplete()
def populateOmniCompleteFromDisk():
url = getUpstreamRepoURI()
if url == "":
return
issues = grabCacheData("/issues")
for issue in issues:
addToOmni(str(issue["number"]) + " " + issue["title"], 'Issue')
labels = grabCacheData("/labels")
if labels is not None:
for label in labels:
addToOmni(unicode(label["name"]), 'Label')
contributors = grabCacheData("/stats/contributors")
if contributors is not None:
for contributor in contributors:
addToOmni(str(contributor["author"]["login"]), 'user')
milestones = getMilestoneList(url)
if milestones is not None:
for milestone in milestones:
addToOmni(str(milestone["title"].encode('utf-8')), 'Milestone')
def apiToDiskAsync(endpoint):
repourl = getUpstreamRepoURI()
url = ghUrl(endpoint, repourl)
filepath = getFilePathForURL(url)
proc_handle[filehash] = subprocess.Popen(
shlex.split("curl " + url),
stdout=open(filepath, "w"),
stderr=open(os.devnull, 'wb')
)
proc_pending[filehash] = True
def cacheFromDisk(endpoint):
repourl = getUpstreamRepoURI()
url = ghUrl(endpoint, repourl)
filepath = getFilePathForURL(url)
try:
jsonfile = open(filepath)
data = json.load(jsonfile)
api_cache[repourl + "/" + endpoint] = data
return data
except Exception as e:
return None
def grabCacheData(endpoint):
repourl = getUpstreamRepoURI()
url = ghUrl(endpoint, repourl)
filepath = getFilePathForURL(url)
# If something was pending and we have fresh data,
# replace what is in memory with the disk data
if proc_pending.get(filepath):
if proc_handle[filepath].poll() is not None:
proc_pending[filepath] = False
return cacheFromDisk(filepath)
if api_cache.get(repourl + "/" + endpoint):
return api_cache[repourl + "/" + endpoint]
apiToDiskAsync(endpoint)
return cacheFromDisk(endpoint)
def getContributors():
return ghApi("/stats/contributors")
# adds issues, labels, and contributors to omni dictionary
def doPopulateOmniComplete():
url = getUpstreamRepoURI()
if url == "":
return
issues = getIssueList(url, "/issues", 0)
for issue in issues:
addToOmni(str(issue["number"]) + " " + issue["title"], 'Issue')
labels = getLabels()
if labels is not None:
for label in labels:
addToOmni(unicode(label["name"]), 'Label')
contributors = getContributors()
if contributors is not None:
for contributor in contributors:
addToOmni(str(contributor["author"]["login"]), 'user')
milestones = getMilestoneList(url)
if milestones is not None:
for milestone in milestones:
addToOmni(str(milestone["title"].encode('utf-8')), 'Milestone')
# calls populateOmniComplete asynchronously
def populateOmniCompleteAsync():
thread = AsyncOmni()
thread.start()
class AsyncOmni(threading.Thread):
def run(self):
# Download and cache the omnicomplete data
url = getUpstreamRepoURI()
if url == "":
return
issues = getIssueList(url, '/issues', 0)
labels = getLabels()
contributors = getContributors()
milestones = getMilestoneList(url)
# adds <keyword> to omni dictionary. used by populateOmniComplete
def addToOmni(keyword, typ):
vim.command("call add(b:omni_options, " +
json.dumps({'word': keyword, 'menu': '[' + typ + ']'}) + ")")
def showIssueLink(number, url="", split="False"):
if url != "":
repourl = url
else:
repourl = getUpstreamRepoURI()
# convert string to boolean
split = split == "True"
word = vim.eval("expand('<cword>')")
line = vim.eval("getline(\".\")")
if line == SHOW_COMMITS:
showCommits(split)
elif line == SHOW_FILES_CHANGED:
showFilesChanged(split)
elif len(word) == 40:
showCommit(word, split)
return
# handle user pressing enter on the gissue list
# possible actions: view issue, filter by label, filter by assignees,
# remove filters
def showIssueBuffer(number, url=False):
if url:
repourl = url
elif 'search/issues' in vim.current.buffer.name:
line_number = vim.eval("line('.')-1")
html_url = globissues[int(line_number)]['html_url']
html_url_split = html_url.split("/")
repourl = html_url_split[3] + "/" + html_url_split[4]
else:
repourl = getUpstreamRepoURI()
line = vim.eval("getline(\".\")")
if line == SHOW_ALL:
showIssueList(0, "True")
return
if line == SHOW_ASSIGNED_ME:
showIssueList(0, "True", "True")
return
labels = getLabels()
if labels is not None:
for label in labels:
if str(label["name"]) == number:
showIssueList(number, "True")
return
if number != "new":
vim.command("normal! 0")
number = vim.eval("expand('<cword>')")
if not vim.eval("g:github_same_window") == "1":
cmd = 'silent '
if not vim.eval("g:gissues_issue_vsplit") == "1":
spl = 'new +set\ buftype=nofile'
if vim.eval("g:gissues_split_height") != "0":
spl = ' ' + vim.eval("g:gissues_split_height") + spl
cmd = cmd + spl
else:
spl = 'vnew +set\ buftype=nofile'
if vim.eval("g:gissues_vsplit_width") != "0":
spl = ' ' + vim.eval("g:gissues_vsplit_width") + spl
cmd = cmd + spl
if vim.eval("g:gissues_split_expand") == "1":
cmd = 'botright ' + cmd
vim.command(cmd)
vim.command("edit gissues/" + repourl + "/" + number)
# show an issue buffer in detail
def showIssue(number=False, repourl=False):
if repourl is False:
repourl = getUpstreamRepoURI()
if number is False:
parens = getFilenameParens()
number = parens[2]
b = vim.current.buffer
vim.command("normal ggdG")
if number == "new":
# new issue
issue = {'title': '',
'body': '',
'number': 'new',
'user': {
'login': ''
},
'assignees': '',
'state': 'open',
'labels': []
}
else:
url = ghUrl("/issues/" + number, repourl)
issue = json.loads(urllib2.urlopen(url, timeout=2).read())
if "pull_request" in issue:
pull_request_url = ghUrl("/pulls/" + number, repourl)
pull_request = json.loads(
urllib2.urlopen(
pull_request_url,
timeout=2).read())
vim.command("let b:ghissue_url=\"" + issue["html_url"] + "\"")
vim.command("let b:ghissue_number=" + number)
vim.command("let b:ghissue_repourl=\"" + repourl + "\"")
encoding = vim.eval("&encoding")
b.append("## Title: " +
issue["title"].encode(encoding).decode(encoding) +
" (" +
str(issue["number"]) +
")")
if issue["user"]["login"]:
b.append(
"## Reported By: " +
issue["user"]["login"].encode(encoding).decode(encoding))
b.append("## State: " + issue["state"])
if issue['assignees']:
b.append(
"## Assignees: " +
' '.join(
i["login"].encode(encoding).decode(encoding) for i in issue["assignees"]))
else:
b.append("## Assignees: ")
if number == "new":
b.append("## Milestone: ")
elif issue['milestone']:
b.append("## Milestone: " + str(issue["milestone"]["title"]))
labelstr = ""
if issue["labels"]:
for label in issue["labels"]:
labelstr += label["name"] + ", "
b.append("## Labels: " + labelstr[:-2])
if number != "new" and "pull_request" in issue:
b.append("## Branch Name: " + str(pull_request["head"]["ref"]))
b.append(SHOW_COMMITS)
b.append(SHOW_FILES_CHANGED)
# This part breaks Python 3
if issue["body"]:
lines = issue["body"].encode(encoding).decode(encoding).split("\n")
b.append(map(lambda line: line.rstrip(), lines))
if number != "new":
b.append("## Comments")
url = ghUrl("/issues/" + number + "/comments", repourl)
data = urllib2.urlopen(url, timeout=2).read()
comments = json.loads(data)
url = ghUrl("/issues/" + number + "/events", repourl)
data = urllib2.urlopen(url, timeout=2).read()
events = json.loads(data)
events = comments + events
if len(events) > 0:
for event in events:
b.append("")
if "user" in event:
user = event["user"]["login"]
else:
user = event["actor"]["login"]
b.append(user.encode(encoding).decode(
encoding) + "(" + event["created_at"] + ")")
if "body" in event:
lines = event["body"].encode(
encoding).decode(encoding).split("\n")
b.append(map(lambda line: line.rstrip(), lines))
else:
eventstr = event["event"].encode(encoding).decode(encoding)
if "commit_id" in event and event["commit_id"]:
eventstr += " from " + event["commit_id"]
b.append(eventstr)
else:
b.append("")
b.append("No comments.")
b.append("")
b.append("## Add a comment")
b.append("")
vim.command("nnoremap <buffer> <silent> q :q<CR>")
vim.command("set ft=gfimarkdown")
vim.command("normal ggdd")
highlightColoredLabels(getLabels())
# mark it as "saved"
vim.command("setlocal nomodified")
# saves an issue and pushes it to the server
def saveGissue():
token = vim.eval("g:github_access_token")
if not token:
print("github-issues.vim: In order to save an issue or add a comment, you need to define a GitHub token. See: https://github.com/jaxbot/github-issues.vim#configuration")
return
parens = getFilenameParens()
number = parens[2]
encoding = "utf-8" # TODO: Get this from vim
issue = {
'title': '',
'body': '',
'assignees': '',
'labels': '',
'milestone': ''
}
commentmode = 0
comment = ""
for line in vim.current.buffer:
if commentmode == 1:
if line == "## Add a comment":
commentmode = 2
continue
if commentmode == 2:
if line != "":
commentmode = 3
comment += line + "\n"
continue
if commentmode == 3:
comment += line + "\n"
continue
if line == "## Comments":
commentmode = 1
continue
if len(line.split("## Reported By:")) > 1:
continue
title = line.split("## Title:")
if len(title) > 1:
issue['title'] = title[1].strip().split(" (" + number + ")")[0]
continue
state = line.split("## State:")
if len(state) > 1:
if state[1].strip().lower() == "closed":
issue['state'] = "closed"
else:
issue['state'] = "open"
continue
milestone = line.split("## Milestone:")
if len(milestone) > 1:
milestones = getMilestoneList(parens[0] + "/" + parens[1], "")
milestone = milestone[1].strip()
for mstone in milestones:
if mstone["title"] == milestone:
issue['milestone'] = str(mstone["number"])
break
continue
labels = line.split("## Labels:")
if len(labels) > 1:
issue['labels'] = labels[1].lstrip().split(', ')
continue
assignees = line.split("## Assignees:")
if len(assignees) > 1:
issue['assignees'] = assignees[1].lstrip().split(' ')
continue
if line == SHOW_COMMITS or line == SHOW_FILES_CHANGED or "## Branch Name:" in line:
continue
if issue['body'] != '':
issue['body'] += '\n'
issue['body'] += line
# remove blank entries
issue['labels'] = filter(bool, issue['labels'])
if number == "new":
if issue['assignees'] == '':
del issue['assignees']
if issue['milestone'] == '':
del issue['milestone']
if issue['body'] == '':
del issue['body']
data = ""
try:
repourl = getUpstreamRepoURI()
url = ghUrl("/issues", repourl)
data = json.dumps(issue).encode(encoding)
request = urllib2.Request(url, data)
data = json.loads(urllib2.urlopen(request, timeout=2).read())
parens[2] = str(data['number'])
vim.current.buffer.name = "gissues/" + \
parens[0] + "/" + parens[1] + "/" + parens[2]
except urllib2.HTTPError as e:
if "code" in e and e.code == 410 or e.code == 404:
print(
"github-issues.vim: Error creating issue. Do you have a github_access_token defined?")
else:
print("github-issues.vim: Unknown HTTP error:")
print(e)
print(data)
print(url)
print(issue)
else:
repourl = vim.eval("b:ghissue_repourl")
url = ghUrl("/issues/" + number, repourl)
data = json.dumps(issue).encode(encoding)
# TODO: Fix POST data should be bytes.
request = urllib2.Request(url, data)
request.get_method = lambda: 'PATCH'
try:
urllib2.urlopen(request, timeout=2)
except urllib2.HTTPError as e:
if "code" in e and e.code == 410 or e.code == 404:
print("Could not update the issue as it does not belong to you!")
if commentmode == 3:
try:
url = ghUrl("/issues/" + parens[2] + "/comments", repourl)
data = json.dumps({'body': comment}).encode(encoding)
request = urllib2.Request(url, data)
urllib2.urlopen(request, timeout=2)
except urllib2.HTTPError as e:
if "code" in e and e.code == 410 or e.code == 404:
print(
"Could not post comment. Do you have a github_access_token defined?")
if commentmode == 3 or number == "new":
showIssue()
# mark it as "saved"
vim.command("setlocal nomodified")
# updates an issues data, such as opening/closing
def setIssueData(issue):
parens = getFilenameParens()
repourl = getUpstreamRepoURI()
url = ghUrl("/issues/" + parens[2], repourl)
request = urllib2.Request(url, json.dumps(issue))
request.get_method = lambda: 'PATCH'
urllib2.urlopen(request, timeout=2)
showIssue()
def getLabels():
return ghApi("/labels")
def getContributors():
return ghApi("/stats/contributors")
# adds labels to the match system
def highlightColoredLabels(labels, decorate=False):
if not labels:
labels = []
labels.append({'name': 'closed', 'color': 'ff0000'})
labels.append({'name': 'open', 'color': '00aa00'})
for label in labels:
# Dummy failsafe value
xtermcolor = "242"
# If we've loaded xterm colors, calculate one
if vim.eval("g:gissues_xterm_colors") != "0":
xtermcolor = rgb_to_xterm(label["color"])[1:]
vim.command(
"hi issueColor" +
label["color"] +
" ctermbg=" +
xtermcolor +
" guifg=#ffffff guibg=#" +
label["color"])
name = label["name"]
if decorate:
name = "\\\\[" + name + "\\\\]"
else:
name = "\\\\<" + name + "\\\\>"
vim.command(
"let m = matchadd(\"issueColor" +
label["color"] +
"\", \"" +
name +
"\")")
vim.command("hi issueButton guifg=#ffffff guibg=#333333 ctermbg=DarkGray")
vim.command("let m = matchadd(\"issueButton\", \"\\\\[.*how.*\\\\]\")")
# queries the ghApi for <endpoint>
def ghApi(endpoint, repourl=False, cache=True, repo=True):
global ssl_enabled
data = None
if not repourl:
repourl = getUpstreamRepoURI()
if cache and api_cache.get(repourl + "/" + endpoint):
return api_cache[repourl + "/" + endpoint]
if not ssl_enabled:
try:
import ssl
ssl_enabled = True
except BaseException:
print("SSL appears to be disabled or not installed on this machine. Please reinstall Python and/or Vim.")
url = ghUrl(endpoint, repourl, repo)
filepath = getFilePathForURL(url)
if vim.eval("g:gissues_offline_cache") and cache:
try:
jsonfile = open(filepath)
data = json.load(jsonfile)
return data
except Exception as e:
# fallback to downloading
pass
try:
req = urllib2.urlopen(url, timeout=5)
request_data = req.read()
data = json.loads(request_data)
cache_file = open(filepath, "w")
cache_file.write(request_data)
cache_file.close()
api_cache[repourl + "/" + endpoint] = data
return data
except Exception as e:
if vim.eval("g:gissues_offline_cache") == "1":
try:
jsonfile = open(filepath)
data = json.load(jsonfile)
print("Github-issues.vim is in OFFLINE mode")
return data
except Exception as e:
pass
if vim.eval("g:gissues_show_errors") != "0":
print(
"github-issues.vim: An error occurred. If this is a private repo, make sure you have a github_access_token defined. Call: " +
endpoint +
" on " +
repourl)
print(e)
return None
def getFilePathForURL(url):
return os.path.expanduser("~/.vim/.gissue-cache/") + \
hashlib.sha224(url.encode('utf-8')).hexdigest()
# generates a github URL, including access token
def ghUrl(endpoint, repourl=False, repo=True):
params = ""
token = vim.eval("g:github_access_token")
if token:
if "?" in endpoint:
params = "&"
else:
params = "?"
params += "access_token=" + token
if repo:
repourl = "repos/" + repourl
if repourl == "custom":
return vim.eval("g:github_api_url") + endpoint + params
else:
return vim.eval("g:github_api_url") + \
urllib2.quote(repourl) + endpoint + params
# returns an array of parens after gissues in filename
def getFilenameParens():
return vim.current.buffer.name.replace(
"\\", "/").split("gissues/")[1].split("/")
def createDirectory(path):
try:
os.makedirs(path)
except OSError:
if not os.path.isdir(path):
raise
if vim.eval("g:gissues_offline_cache") == "1":
createDirectory(os.path.expanduser("~/.vim"))
createDirectory(os.path.expanduser("~/.vim/.gissue-cache"))