├── .gitignore ├── Cask ├── Changes ├── README.md ├── helm-open-github.el └── image └── helm-open-github-from-commit.png /.gitignore: -------------------------------------------------------------------------------- 1 | /.cask/ 2 | *.elc 3 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source gnu) 2 | (source melpa) 3 | 4 | (package-file "helm-open-github.el") 5 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Revision history for helm-open-github.el 2 | 3 | Revision 0.15 2016/12/03 syohex 4 | - Require Emacs 24.4 or higher versions 5 | - Use newer helm interfaces 6 | 7 | Revision 0.14 2015/10/10 syohex 8 | - Fix too many candidates issue(#12 Thanks thierryvolpiatto) 9 | - Update minimum helm version 10 | - Fix buffer name by helm way(#15 Thanks xuchunyang) 11 | 12 | Revision 0.13 2015/03/03 syohex 13 | - Fix wrong 'gh' version #9(Reported by nlamirault) 14 | 15 | Revision 0.12 2015/01/17 syohex 16 | - Improve issue command(Thanks thierryvolpiatto) 17 | We can see all issues now. 18 | 19 | Revision 0.11 2014/07/02 syohex 20 | - Re-factoring 21 | 22 | Revision 0.10 2014/07/02 syohex 23 | - Re-factoring 24 | 25 | Revision 0.09 2014/06/21 syohex 26 | - Fix for cl-lib 27 | - Use Cask now 28 | 29 | Revision 0.08 2014/03/08 syohex 30 | - Enable lexical binding 31 | - switch cl-lib 32 | 33 | Revision 0.07 2013/11/14 syohex 34 | - Fix missing requirements(Thanks tdksk) 35 | 36 | Revision 0.06 2013/11/14 syohex 37 | - Implement pull request interface 38 | 39 | Revision 0.05 2013/10/26 syohex 40 | - Fix repository regexp 41 | - Support multiple candidates 42 | 43 | Revision 0.04 2013/05/01 syohex 44 | - Implement persistent action to helm-open-github-from-commit 45 | 46 | Revision 0.03 2013/04/30 syohex 47 | - Fix name attribute of helm-open-github--from-file-source 48 | (Thanks shibayu36) 49 | 50 | Revision 0.02 2013/04/30 syohex 51 | - Implement show commit detail 52 | 53 | Revision 0.01 2013/04/27 syohex 54 | - Initial release 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # helm-open-github.el [![melpa badge][melpa-badge]][melpa-link] [![melpa stable badge][melpa-stable-badge]][melpa-stable-link] 2 | helm-open-github.el is utilities for opening github url. 3 | This is inspired by URL below. 4 | 5 | - http://shibayu36.hatenablog.com/entry/2013/01/18/211428 6 | 7 | 8 | ## Screenshot 9 | 10 | ![open-github-from-commit](image/helm-open-github-from-commit.png) 11 | 12 | 13 | ## Installation 14 | 15 | You can install `helm-open-github` from [MELPA](http://melpa.org/) with package.el. 16 | 17 | ``` 18 | M-x package-install helm-open-github 19 | ``` 20 | 21 | 22 | ## Dependency 23 | 24 | * Emacs 24.3 or higher 25 | * [helm](https://github.com/emacs-helm/helm) 26 | * [gh](https://github.com/sigma/gh.el) 27 | 28 | 29 | ## Basic Usage 30 | 31 | ### `helm-open-github-from-commit` 32 | 33 | Open commit page from **Commit ID** 34 | 35 | ### `helm-open-github-from-file` 36 | 37 | Open file page from **File Name** 38 | 39 | ### `helm-open-github-from-issues` 40 | 41 | Open issue page from **Issue ID** 42 | 43 | ### `helm-open-github-from-pull-requests` 44 | 45 | Open pull request page from **Pull Request ID** 46 | 47 | 48 | ## Customize 49 | 50 | ### `helm-open-github-commit-limit` 51 | 52 | Issue number shown by `helm-open-github-from-commit`.(Default: `100`) 53 | 54 | ### `helm-open-github-requires-pattern` 55 | 56 | Minimal length to search. If this value is non-nil, delayed search is enabled(Default: `nil`). 57 | This parameter must be set before loading `helm-open-github.el`. 58 | 59 | 60 | ## Sample Configuration 61 | 62 | ```lisp 63 | (global-set-key (kbd "C-c o f") 'helm-open-github-from-file) 64 | (global-set-key (kbd "C-c o c") 'helm-open-github-from-commit) 65 | (global-set-key (kbd "C-c o i") 'helm-open-github-from-issues) 66 | (global-set-key (kbd "C-c o p") 'helm-open-github-from-pull-requests) 67 | ``` 68 | 69 | [melpa-link]: https://melpa.org/#/helm-open-github 70 | [melpa-stable-link]: https://stable.melpa.org/#/helm-open-github 71 | [melpa-badge]: https://melpa.org/packages/helm-open-github-badge.svg 72 | [melpa-stable-badge]: https://stable.melpa.org/packages/helm-open-github-badge.svg 73 | -------------------------------------------------------------------------------- /helm-open-github.el: -------------------------------------------------------------------------------- 1 | ;;; helm-open-github.el --- Utilities of Opening Github Page -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016 by Syohei YOSHIDA 4 | 5 | ;; Author: Syohei YOSHIDA 6 | ;; URL: https://github.com/syohex/emacs-helm-open-github 7 | ;; Version: 0.15 8 | ;; Package-Requires: ((emacs "24.4") (helm-core "3.6.0") (gh "0.8.2")) 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; Open github URL utilities. This package is inspired by URL below. 26 | ;; - http://shibayu36.hatenablog.com/entry/2013/01/18/211428 27 | 28 | ;;; Code: 29 | 30 | (require 'cl-lib) 31 | 32 | (require 'helm-core) 33 | (require 'gh-issues) 34 | (require 'gh-pulls) 35 | 36 | (defgroup helm-open-github nil 37 | "Utilities of opeg " 38 | :prefix "helm-open-github-" 39 | :group 'http) 40 | 41 | (defcustom helm-open-github-commit-limit 100 42 | "Limit of commit id collected" 43 | :type 'integer) 44 | 45 | (defcustom helm-open-github-issues-api 46 | (gh-issues-api "api" :sync t :cache nil :num-retries 1) 47 | "Github API instance. This is-a `gh-issues'" 48 | :type 'gh-issues-api) 49 | 50 | (defcustom helm-open-github-pulls-api 51 | (gh-pulls-api "api" :sync t :cache nil :num-retries 1) 52 | "Github API instance. This is-a `gh-pulls'" 53 | :type 'gh-pulls-api) 54 | 55 | (defcustom helm-open-github-closed-issue-since 19 56 | "Only issues updated this number of days ago are returned." 57 | :type 'integer) 58 | 59 | (defcustom helm-open-github-closed-issue-sort-direction "asc" 60 | "Direction of the sort for closed issues. 61 | Either \"asc\" or \"desc\"." 62 | :type '(radio :tag "Preferred direction of the sort" 63 | (const :tag "Ascendent" "asc") 64 | (const :tag "Descendent" "desc"))) 65 | 66 | (defcustom helm-open-github-requires-pattern nil 67 | "Minimal length to search. As fetching data is an expensive 68 | operation with potentially many results, higher number is 69 | recomended for bigger projects or slower connections. 70 | If this value is non-nil, delayed search is disabled." 71 | :type '(choice (integer :tag "Minimal length") 72 | (boolean :tag "Disable delayed search" nil))) 73 | 74 | (defun helm-open-github--collect-commit-id () 75 | (with-current-buffer (helm-candidate-buffer 'global) 76 | (let ((ret (call-process "git" nil t nil 77 | "--no-pager" "log" 78 | "-n" (number-to-string helm-open-github-commit-limit) 79 | "--pretty=oneline" "--abbrev-commit"))) 80 | (unless (zerop ret) 81 | (error "Failed: get commit ID"))))) 82 | 83 | (defun helm-open-github--command-one-line (cmd args) 84 | (with-temp-buffer 85 | (when (zerop (apply 'call-process cmd nil t nil args)) 86 | (goto-char (point-min)) 87 | (buffer-substring-no-properties 88 | (line-beginning-position) (line-end-position))))) 89 | 90 | (defun helm-open-github--full-commit-id (abbrev-id) 91 | (or (helm-open-github--command-one-line "git" `("rev-parse" ,abbrev-id)) 92 | (error "Failed: 'git rev-parse %s'" abbrev-id))) 93 | 94 | (defun helm-open-github--root-directory () 95 | (let ((root (helm-open-github--command-one-line "git" '("rev-parse" "--show-toplevel")))) 96 | (if (not root) 97 | (error "Error: here is not Git repository") 98 | (file-name-as-directory root)))) 99 | 100 | (defun helm-open-github--host () 101 | (or (helm-open-github--command-one-line "git" '("config" "--get" "hub.host")) 102 | "github.com")) 103 | 104 | (defun helm-open-github--remote-url () 105 | (or (helm-open-github--command-one-line "git" '("config" "--get" "remote.origin.url")) 106 | (error "Failed: Can't get remote.origin URL"))) 107 | 108 | (defun helm-open-github--extract-user-host (remote-url) 109 | (if (string-match "[:/]\\([^/]+\\)/\\([^/]+?\\)\\(?:\\.git\\)?\\'" remote-url) 110 | (cl-values (match-string 1 remote-url) (match-string 2 remote-url)) 111 | (error "Failed: match %s" remote-url))) 112 | 113 | (defun helm-open-github--commit-url (host remote-url commit-id) 114 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 115 | (format "https://%s/%s/%s/commit/%s" 116 | host user repo commit-id))) 117 | 118 | (defun helm-open-github--from-commit-open-url-common (commit-id) 119 | (let* ((host (helm-open-github--host)) 120 | (remote-url (helm-open-github--remote-url))) 121 | (browse-url 122 | (helm-open-github--commit-url host remote-url commit-id)))) 123 | 124 | (defun helm-open-github--full-commit-id-from-candidate (line) 125 | (let ((commit-line (split-string line " "))) 126 | (helm-open-github--full-commit-id (car commit-line)))) 127 | 128 | (defun helm-open-github--from-commit-id-persistent-action (line) 129 | (let* ((commit-id (helm-open-github--full-commit-id-from-candidate line)) 130 | (str (shell-command-to-string 131 | (format "git show --stat --oneline %s" commit-id)))) 132 | (with-help-window (help-buffer) 133 | (princ str)))) 134 | 135 | (defun helm-open-github--from-commit-open-url (_candidate) 136 | (dolist (commit-line (helm-marked-candidates)) 137 | (let ((commit-id (helm-open-github--full-commit-id-from-candidate commit-line))) 138 | (helm-open-github--from-commit-open-url-common commit-id)))) 139 | 140 | (defun helm-open-github--from-commit-open-url-with-input (_candidate) 141 | (let ((commit-id (read-string "Input Commit ID: "))) 142 | (helm-open-github--from-commit-open-url-common 143 | (helm-open-github--full-commit-id commit-id)))) 144 | 145 | (defun helm-open-github--show-commit-id-common (commit-id) 146 | (with-current-buffer (get-buffer-create "*open-github-issues*") 147 | (unless (call-process "git" nil t nil "show" "--stat" "-p" commit-id) 148 | (error "Error: 'git show --stat -p %s'" commit-id)) 149 | (goto-char (point-min)) 150 | (pop-to-buffer (current-buffer)))) 151 | 152 | (defun helm-open-github--show-commit-id (line) 153 | (let* ((commit-line (split-string line " ")) 154 | (commit-id (helm-open-github--full-commit-id (car commit-line)))) 155 | (helm-open-github--show-commit-id-common commit-id))) 156 | 157 | (defun helm-open-github--show-commit-id-with-input (_candidate) 158 | (let ((commit-id (read-string "Input Commit ID: "))) 159 | (helm-open-github--show-commit-id-common 160 | (helm-open-github--full-commit-id commit-id)))) 161 | 162 | (defvar helm-open-github--from-commit-source 163 | (helm-build-in-buffer-source "Open Github From Commit" 164 | :init #'helm-open-github--collect-commit-id 165 | :persistent-action #'helm-open-github--from-commit-id-persistent-action 166 | :action (helm-make-actions 167 | "Open Commit Page" #'helm-open-github--from-commit-open-url 168 | "Show Detail" #'helm-open-github--show-commit-id))) 169 | 170 | (defvar helm-open-github--from-commit-direct-input-source 171 | (helm-build-sync-source "Open Github From Commit Direct Input" 172 | :candidates '("Input Commit ID") 173 | :action (helm-make-actions 174 | "Open Commit Page" #'helm-open-github--from-commit-open-url-with-input 175 | "Show Detail" #'helm-open-github--show-commit-id-with-input))) 176 | 177 | ;;;###autoload 178 | (defun helm-open-github-from-commit () 179 | (interactive) 180 | (helm :sources '(helm-open-github--from-commit-source 181 | helm-open-github--from-commit-direct-input-source) 182 | :buffer "*helm open github*")) 183 | 184 | (defun helm-open-github--collect-files () 185 | (let ((root (helm-open-github--root-directory))) 186 | (with-current-buffer (helm-candidate-buffer 'global) 187 | (let ((default-directory root)) 188 | (unless (zerop (call-process "git" nil t nil "ls-files")) 189 | (error "Failed: 'git ls-files' at %s" default-directory)))))) 190 | 191 | (defun helm-open-github--branch () 192 | (let ((branch (helm-open-github--command-one-line "git" '("symbolic-ref" "HEAD")))) 193 | (if (not branch) 194 | (error "Failed: 'git symbolic-ref HEAD'") 195 | (replace-regexp-in-string "\\`refs/heads/" "" branch)))) 196 | 197 | (defun helm-open-github--file-url (host remote-url branch file marker) 198 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 199 | (format "https://%s/%s/%s/blob/%s/%s%s" 200 | host user repo branch file marker))) 201 | 202 | (defun helm-open-github--highlight-marker (start end) 203 | (cond ((and start end) 204 | (format "#L%s..L%s" start end)) 205 | (start 206 | (format "#L%s" start)) 207 | (t ""))) 208 | 209 | (defun helm-open-github--from-file-action (file &optional start end) 210 | (let ((host (helm-open-github--host)) 211 | (remote-url (helm-open-github--remote-url)) 212 | (branch (helm-open-github--branch)) 213 | (marker (helm-open-github--highlight-marker start end))) 214 | (browse-url 215 | (helm-open-github--file-url host remote-url branch file marker)))) 216 | 217 | (defun helm-open-github--from-file-highlight-region-action (file) 218 | (let ((start-line (read-number "Start Line: ")) 219 | (end-line (read-number "End Line: "))) 220 | (helm-open-github--from-file-action file start-line end-line))) 221 | 222 | (defun helm-open-github--from-file-highlight-line-action (file) 223 | (let ((start-line (read-number "Start Line: "))) 224 | (helm-open-github--from-file-action file start-line))) 225 | 226 | (defvar helm-open-github--from-file-source 227 | (helm-build-in-buffer-source "Open Github From File" 228 | :init #'helm-open-github--collect-files 229 | :action (helm-make-actions 230 | "Open File" (lambda (_cand) 231 | (dolist (file (helm-marked-candidates)) 232 | (helm-open-github--from-file-action file))) 233 | "Open File and Highlight Line" #'helm-open-github--from-file-highlight-line-action 234 | "Open File and Highlight Region" #'helm-open-github--from-file-highlight-region-action))) 235 | 236 | (defun helm-open-github--from-file-direct (file start end) 237 | (let* ((root (helm-open-github--root-directory)) 238 | (repo-path (file-relative-name file root)) 239 | (start-line (line-number-at-pos start)) 240 | (end-line (line-number-at-pos end))) 241 | (helm-open-github--from-file-action repo-path start-line end-line))) 242 | 243 | ;;;###autoload 244 | (defun helm-open-github-from-file () 245 | (interactive) 246 | (if mark-active 247 | (helm-open-github--from-file-direct (buffer-file-name) (region-beginning) (region-end)) 248 | (helm :sources '(helm-open-github--from-file-source) 249 | :buffer "*helm open github*"))) 250 | 251 | (defun helm-open-github--collect-issues () 252 | (let ((remote-url (helm-open-github--remote-url))) 253 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 254 | (let ((issues (gh-issues-issue-list helm-open-github-issues-api user repo))) 255 | (if (null issues) 256 | (error "This repository has no issues!!") 257 | (sort (oref issues data) 258 | (lambda (a b) (> (oref a number) (oref b number))))))))) 259 | 260 | (defmethod gh-issues-issue-list-closed ((api gh-issues-api) user repo) 261 | (gh-api-authenticated-request 262 | api (gh-object-list-reader (oref api issue-cls)) "GET" 263 | (format "/repos/%s/%s/issues" user repo) 264 | nil `(("state" . "closed") 265 | ("since" . ,(format-time-string 266 | "%y-%m-%dT%H:%M:%SZ" 267 | (time-subtract 268 | (current-time) 269 | (seconds-to-time 270 | (* (* (* helm-open-github-closed-issue-since 24) 271 | 60) 60))))) 272 | ("direction" . ,helm-open-github-closed-issue-sort-direction)))) 273 | 274 | (defun helm-open-github--collect-closed-issues () 275 | (let ((remote-url (helm-open-github--remote-url))) 276 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 277 | (let ((issues (gh-issues-issue-list-closed helm-open-github-issues-api user repo))) 278 | (if (null issues) 279 | (error "This repository has no issues!!") 280 | (sort (oref issues data) 281 | (lambda (a b) (> (oref a number) (oref b number))))))))) 282 | 283 | (defun helm-open-github--convert-issue-api-url (url) 284 | (replace-regexp-in-string 285 | "api\\." "" 286 | (replace-regexp-in-string "/repos" "" url))) 287 | 288 | (defun helm-open-github--from-issues-format-candidate (issue) 289 | (with-slots (number title state) issue 290 | (propertize (format "#%-4d [%s] %s" number state title) 291 | 'helm-realvalue issue))) 292 | 293 | (defun helm-open-github--open-issue-url (_candidate) 294 | (dolist (issue (helm-marked-candidates)) 295 | (browse-url (oref issue html-url) 296 | (helm-open-github--convert-issue-api-url (oref issue url))))) 297 | 298 | (defvar helm-open-github--issues-cache (make-hash-table :test 'equal)) 299 | (defvar helm-open-github--from-issues-source 300 | (helm-build-in-buffer-source "Open Github From Open Issues" 301 | :init (lambda () 302 | (let* ((key (helm-open-github--remote-url)) 303 | (issues (gethash key helm-open-github--issues-cache))) 304 | (unless issues 305 | (setq issues 306 | (puthash key (helm-open-github--collect-issues) 307 | helm-open-github--issues-cache))) 308 | (helm-init-candidates-in-buffer 'global 309 | (cl-loop for c in issues 310 | collect (helm-open-github--from-issues-format-candidate c))))) 311 | :delayed (not (null helm-open-github-requires-pattern)) 312 | :requires-pattern helm-open-github-requires-pattern 313 | :get-line 'buffer-substring 314 | :action (helm-make-actions 315 | "Open issue page with browser" #'helm-open-github--open-issue-url))) 316 | 317 | (defvar helm-open-github--closed-issues-cache (make-hash-table :test 'equal)) 318 | (defvar helm-open-github--from-closed-issues-source 319 | (helm-build-in-buffer-source "Open Github From closed Issues" 320 | :init (lambda () 321 | (let* ((key (helm-open-github--remote-url)) 322 | (issues (gethash key helm-open-github--closed-issues-cache))) 323 | (unless issues 324 | (setq issues 325 | (puthash key (helm-open-github--collect-closed-issues) 326 | helm-open-github--closed-issues-cache))) 327 | (helm-init-candidates-in-buffer 'global 328 | (cl-loop for c in issues 329 | collect (helm-open-github--from-issues-format-candidate c))))) 330 | :delayed (not (null helm-open-github-requires-pattern)) 331 | :requires-pattern helm-open-github-requires-pattern 332 | :get-line 'buffer-substring 333 | :action (helm-make-actions 334 | "Open issue page with browser" #'helm-open-github--open-issue-url))) 335 | 336 | (defun helm-open-github--construct-issue-url (host remote-url issue-id) 337 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 338 | (format "https://%s/%s/%s/issues/%s" 339 | host user repo issue-id))) 340 | 341 | (defun helm-open-github--from-issues-direct (host) 342 | (let ((remote-url (helm-open-github--remote-url)) 343 | (issue-id (read-number "Issue ID: "))) 344 | (browse-url 345 | (helm-open-github--construct-issue-url host remote-url issue-id)))) 346 | 347 | ;;;###autoload 348 | (defun helm-open-github-from-issues (arg) 349 | (interactive "P") 350 | (let ((host (helm-open-github--host)) 351 | (url (helm-open-github--remote-url))) 352 | (when arg 353 | (remhash url helm-open-github--closed-issues-cache) 354 | (remhash url helm-open-github--issues-cache)) 355 | (if (not (string= host "github.com")) 356 | (helm-open-github--from-issues-direct host) 357 | (helm :sources '(helm-open-github--from-issues-source 358 | helm-open-github--from-closed-issues-source) 359 | :buffer "*helm open github*")))) 360 | 361 | (defvar helm-open-github--pull-requests nil) 362 | (defun helm-open-github--collect-pullreqs () 363 | (let ((remote-url (helm-open-github--remote-url))) 364 | (cl-multiple-value-bind (user repo) (helm-open-github--extract-user-host remote-url) 365 | (let ((pullreqs (gh-pulls-list helm-open-github-pulls-api user repo))) 366 | (if (null pullreqs) 367 | (error "This repository has no pull requests!!") 368 | (setq helm-open-github--pull-requests 369 | (sort (oref pullreqs data) 370 | (lambda (a b) (< (oref a number) (oref b number)))))))))) 371 | 372 | (defun helm-open-github--pulls-view-common (url) 373 | (with-current-buffer (get-buffer-create "*open-github-diff*") 374 | (view-mode -1) 375 | (erase-buffer) 376 | (unless (zerop (call-process "curl" nil t nil "-s" url)) 377 | (error "Can't download %s" url)) 378 | (goto-char (point-min)) 379 | (diff-mode) 380 | (view-mode +1) 381 | (pop-to-buffer (current-buffer)))) 382 | 383 | (defun helm-open-github--pulls-view-diff (candidate) 384 | (helm-open-github--pulls-view-common (oref candidate diff-url))) 385 | 386 | (defun helm-open-github--pulls-view-patch (candidate) 387 | (helm-open-github--pulls-view-common (oref candidate patch-url))) 388 | 389 | (defvar helm-open-github--from-pulls-source 390 | (helm-build-sync-source "Open Github From Issues" 391 | :init #'helm-open-github--collect-pullreqs 392 | :candidates 'helm-open-github--pull-requests 393 | :volatile t 394 | :delayed (not (null helm-open-github-requires-pattern)) 395 | :requires-pattern helm-open-github-requires-pattern 396 | :real-to-display 'helm-open-github--from-issues-format-candidate 397 | :action (helm-make-actions 398 | "Open issue page with browser" #'helm-open-github--open-issue-url 399 | "View Diff" #'helm-open-github--pulls-view-diff 400 | "View Patch" #'helm-open-github--pulls-view-patch))) 401 | 402 | ;;;###autoload 403 | (defun helm-open-github-from-pull-requests () 404 | (interactive) 405 | (let ((host (helm-open-github--host))) 406 | (if (not (string= host "github.com")) 407 | (helm-open-github--from-issues-direct host) 408 | (helm :sources '(helm-open-github--from-pulls-source) 409 | :buffer "*helm open github*")))) 410 | 411 | (provide 'helm-open-github) 412 | 413 | ;;; helm-open-github.el ends here 414 | -------------------------------------------------------------------------------- /image/helm-open-github-from-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacsorphanage/helm-open-github/5e6d700d1b484bd6cd44bc30674e96d157870c3f/image/helm-open-github-from-commit.png --------------------------------------------------------------------------------