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 :call showIssueLink('','','False')") vim.command("nnoremap s :call showIssueLink('','','True')") vim.command("call 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 q :bdelete") # 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 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('')") 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('')") 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 q :q") 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 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"))