77; ; Michael Ivey
88; ; Phil Hagelberg
99; ; Dan McKinley
10- ; ; Version: 1.2.2
11- ; ; Package-Requires: ((eieio "1.4 ") (gh "0.9.2") (tabulated-list "0 "))
10+ ; ; Version: 1.3.0
11+ ; ; Package-Requires: ((emacs "24.1 ") (gh "0.9.2"))
1212; ; Keywords: tools
1313; ; Homepage: https://github.com/defunkt/gist.el
1414
4242(require 'eieio-base )
4343(require 'timezone )
4444
45+ (require 'gh-api )
4546(require 'gh-gist )
4647(require 'gh-profile )
4748
@@ -138,13 +139,25 @@ appropriate modes from fetched gist files (based on filenames)."
138139 :value-type (string :tag " Extension" )))
139140
140141(defvar gist-list-db nil )
142+ (unless (hash-table-p gist-list-db)
143+ (setq gist-list-db (make-hash-table :test 'equal )))
144+
145+ (defvar gist-list-db-by-user nil )
146+ (unless (hash-table-p gist-list-db-by-user)
147+ (setq gist-list-db-by-user (make-hash-table :test 'equal )))
141148
142149(defvar gist-id nil )
143150(make-variable-buffer-local 'gist-id )
144151
145152(defvar gist-filename nil )
146153(make-variable-buffer-local 'gist-filename )
147154
155+ (defvar gist-user-history nil " History list for gist-list-user." )
156+
157+ (defvar gist-list-buffer-user nil " Username for this gist buffer." )
158+ (make-variable-buffer-local 'gist-list-buffer-user )
159+ (put 'gist-list-buffer-user 'permanent-local t )
160+
148161(defun gist-get-api (&optional sync )
149162 (let ((gh-profile-current-profile
150163 (or gh-profile-current-profile (gh-profile-completing-read))))
@@ -202,7 +215,7 @@ With a prefix argument, makes a private paste."
202215
203216(defun gist-created-callback (gist )
204217 (let ((location (oref gist :html-url )))
205- (gist-list-reload t )
218+ (gist-list-reload 'current-user t )
206219 (message gist-created-fmt location)
207220 (when gist-view-gist
208221 (browse-url location))
@@ -256,24 +269,47 @@ Copies the URL into the kill ring."
256269 (mark-inactive (gist-buffer-private))))
257270
258271;;;### autoload
259- (defun gist-list (&optional force-reload background )
260- " Displays a list of all of the current user's gists in a new buffer."
261- (interactive " P" )
272+ (defun gist-list-user (username &optional force-reload background )
273+ " Displays a list of a user's gists in a new buffer. When called from
274+ a program, pass 'current-user as the username to view the user's own
275+ gists, or nil for the username and a non-nil value for force-reload to
276+ reload the gists for the current buffer."
277+ (interactive
278+ (let ((username (read-from-minibuffer " GitHub user: " nil nil nil
279+ 'gist-user-history ))
280+ (force-reload (equal current-prefix-arg '(4 ))))
281+ (list username force-reload)))
262282 ; ; if buffer exists, it contains the current gh profile
263283 (let* ((gh-profile-current-profile (or gh-profile-current-profile
264284 (gh-profile-completing-read)))
265- (bufname (format " *%s :gists* " gh-profile-current-profile))
266- (api (gist-get-api nil )))
285+ (bufname (if (null username)
286+ (if (not (equal major-mode 'gist-list-mode ))
287+ (error " Current buffer isn't a gist-list-mode buffer " )
288+ (buffer-name ))
289+ (format " *%s :%s gists* "
290+ gh-profile-current-profile
291+ (if (or (equal " " username)
292+ (eq 'current-user username))
293+ " "
294+ (format " %s 's-" username)))))
295+ (api (gist-get-api nil ))
296+ (username (or (and (null username) gist-list-buffer-user)
297+ (and (not (or (null username)
298+ (equal " " username)
299+ (eq 'current-user username)))
300+ username)
301+ (gh-api-get-username api))))
267302 (when force-reload
268303 (pcache-clear (oref api :cache ))
269- (or background (message " Retrieving list of your gists... " )))
304+ (or background (message " Retrieving list of gists... " )))
270305 (unless (and background (not (get-buffer bufname)))
271- (let ((resp (gh-gist-list api)))
306+ (let ((resp (gh-gist-list api username )))
272307 (gh-url-add-response-callback
273308 resp
274309 (lexical-let ((buffer bufname))
275310 (lambda (gists )
276311 (with-current-buffer (get-buffer-create buffer)
312+ (setq gist-list-buffer-user username)
277313 (gist-lists-retrieved-callback gists background)))))
278314 (gh-url-add-response-callback
279315 resp
@@ -283,9 +319,15 @@ Copies the URL into the kill ring."
283319 (with-current-buffer buffer
284320 (setq gh-profile-current-profile profile)))))))))
285321
286- (defun gist-list-reload (&optional background )
322+ ;;;### autoload
323+ (defun gist-list (&optional force-reload background )
324+ " Displays a list of all of the current user's gists in a new buffer."
325+ (interactive " P" )
326+ (gist-list-user 'current-user force-reload background))
327+
328+ (defun gist-list-reload (&optional username background )
287329 (interactive )
288- (gist-list t background))
330+ (gist-list-user username t background))
289331
290332(defun gist-tabulated-entry (gist )
291333 (let* ((data (gist-parse-gist gist))
@@ -295,8 +337,13 @@ Copies the URL into the kill ring."
295337(defun gist-lists-retrieved-callback (gists &optional background )
296338 " Called when the list of gists has been retrieved. Displays
297339the list."
298- (setq gist-list-db gists)
299- (gist-list-render background))
340+ (dolist (g (gethash gist-list-buffer-user gist-list-db-by-user))
341+ (remhash (oref g :id ) gist-list-db))
342+ (dolist (g gists)
343+ (puthash (oref g :id ) g gist-list-db))
344+ (puthash gist-list-buffer-user gists gist-list-db-by-user)
345+ (gist-list-render (gethash gist-list-buffer-user gist-list-db-by-user)
346+ background))
300347
301348(defun gist--get-time (gist )
302349 (let* ((date (timezone-parse-date (oref gist :date )))
@@ -337,15 +384,18 @@ for the gist."
337384 (interactive " sGist ID: " )
338385 (let ((gist nil )
339386 (multi nil )
340- (prefix (format " *gist %s * " id))
387+ (prefix (format " *gist- %s * " id))
341388 (result nil )
342389 (profile (gh-profile-current-profile)))
343390 (setq gist (gist-list-db-get-gist id))
344391 (let ((api (gist-get-api t )))
345392 (cond ((null gist)
346393 ; ; fetch it
347394 (setq gist (oref (gh-gist-get api id) :data ))
348- (add-to-list 'gist-list-db gist))
395+ (puthash (oref gist :id ) gist gist-list-db)
396+ (let* ((user (oref gist :user ))
397+ (gists (push gist (gethash user gist-list-db-by-user))))
398+ (puthash user gists gist-list-db-by-user)))
349399 ((not (gh-gist-gist-has-files gist))
350400 (gh-gist-get api gist))))
351401 (let ((files (oref gist :files )))
@@ -393,16 +443,25 @@ for the gist."
393443 (gist-fetch-current)
394444 (select-window win)))
395445
446+ (defun gist--check-perms-and-get-api (gist errormsg apiflg )
447+ (let* ((api (gist-get-api apiflg))
448+ (username (gh-api-get-username api))
449+ (gs (gethash username gist-list-db-by-user)))
450+ (if (not (memq gist gs))
451+ (user-error errormsg)
452+ api)))
453+
396454(defun gist-edit-current-description ()
397455 (interactive )
398456 (let* ((id (tabulated-list-get-id ))
399457 (gist (gist-list-db-get-gist id))
400- (old-descr (oref gist :description ))
401- (new-descr (read-from-minibuffer " Description: " old-descr)))
402- (let* ((g (clone gist
458+ (api (gist--check-perms-and-get-api
459+ gist " Can't edit a gist that doesn't belong to you" t )))
460+ (let* ((old-descr (oref gist :description ))
461+ (new-descr (read-from-minibuffer " Description: " old-descr))
462+ (g (clone gist
403463 :files nil
404464 :description new-descr))
405- (api (gist-get-api t ))
406465 (resp (gh-gist-edit api g)))
407466 (gh-url-add-response-callback resp
408467 (lambda (gist )
@@ -413,18 +472,20 @@ for the gist."
413472 (let* ((buffer (get-buffer buffer))
414473 (id (tabulated-list-get-id ))
415474 (gist (gist-list-db-get-gist id))
416- (fname (file-name-nondirectory (or (buffer-file-name buffer) (buffer-name buffer)))))
417- (let* ((g (clone gist :files
418- (list
419- (gh-gist-gist-file " file"
420- :filename fname
421- :content (with-current-buffer buffer
422- (buffer-string ))))))
423- (api (gist-get-api t ))
424- (resp (gh-gist-edit api g)))
425- (gh-url-add-response-callback resp
426- (lambda (gist )
427- (gist-list-reload))))))
475+ (api (gist--check-perms-and-get-api
476+ gist " Can't modify a gist that doesn't belong to you" t ))
477+ (fname (file-name-nondirectory (or (buffer-file-name buffer)
478+ (buffer-name buffer))))
479+ (g (clone gist :files
480+ (list
481+ (gh-gist-gist-file " file"
482+ :filename fname
483+ :content (with-current-buffer buffer
484+ (buffer-string ))))))
485+ (resp (gh-gist-edit api g)))
486+ (gh-url-add-response-callback resp
487+ (lambda (gist )
488+ (gist-list-reload)))))
428489
429490(defun gist-remove-file (fname )
430491 (interactive (list
@@ -435,24 +496,27 @@ for the gist."
435496 (mapcar #' (lambda (f ) (oref f :filename ))
436497 (oref gist :files ))))))
437498 (let* ((id (tabulated-list-get-id ))
438- (gist (gist-list-db-get-gist id)))
439- (let* ((g (clone gist :files
440- (list
441- (gh-gist-gist-file " file"
442- :filename fname
443- :content nil ))))
444- (api (gist-get-api t ))
445- (resp (gh-gist-edit api g)))
446- (gh-url-add-response-callback resp
447- (lambda (gist )
448- (gist-list-reload))))))
499+ (gist (gist-list-db-get-gist id))
500+ (api (gist--check-perms-and-get-api
501+ gist " Can't modify a gist that doesn't belong to you" t ))
502+ (g (clone gist :files
503+ (list
504+ (gh-gist-gist-file " file"
505+ :filename fname
506+ :content nil ))))
507+ (resp (gh-gist-edit api g)))
508+ (gh-url-add-response-callback resp
509+ (lambda (gist )
510+ (gist-list-reload)))))
449511
450512(defun gist-kill-current ()
451513 (interactive )
452- (let ((id (tabulated-list-get-id )))
514+ (let* ((id (tabulated-list-get-id ))
515+ (gist (gist-list-db-get-gist id))
516+ (api (gist--check-perms-and-get-api
517+ gist " Can't delete a gist that doesn't belong to you" t )))
453518 (when (yes-or-no-p (format " Really delete gist %s ? " id) )
454- (let* ((api (gist-get-api t ))
455- (resp (gh-gist-delete api id)))
519+ (let* ((resp (gh-gist-delete api id)))
456520 (gist-list-reload)))))
457521
458522(defun gist-current-url ()
@@ -472,6 +536,49 @@ put it into `kill-ring'."
472536 (interactive )
473537 (browse-url (gist-current-url)))
474538
539+ (defun gist--do-star (id how msg )
540+ (let* ((api (gist-get-api t ))
541+ (resp (gh-gist-set-star api id how)))
542+ (gh-url-add-response-callback resp
543+ (lambda (gist )
544+ (message msg id)))))
545+
546+ ;;;### autoload
547+ (defun gist-star ()
548+ (interactive )
549+ (let ((id (tabulated-list-get-id )))
550+ (gist--do-star id t " Starred gist %s" )))
551+
552+ ;;;### autoload
553+ (defun gist-unstar ()
554+ (interactive )
555+ (let ((id (tabulated-list-get-id )))
556+ (gist--do-star id nil " Unstarred gist %s" )))
557+
558+ ;;;### autoload
559+ (defun gist-list-starred (&optional background )
560+ " List your starred gists."
561+ (interactive )
562+ (let* ((api (gist-get-api t ))
563+ (resp (gh-gist-list-starred api)))
564+ (gh-url-add-response-callback
565+ resp
566+ (lexical-let ((buffer " *starred-gists*" ))
567+ (lambda (gists )
568+ (with-current-buffer (get-buffer-create buffer)
569+ (gist-list-render gists background)))))))
570+
571+ ;;;### autoload
572+ (defun gist-fork ()
573+ " Fork a gist."
574+ (interactive )
575+ (let* ((id (tabulated-list-get-id ))
576+ (api (gist-get-api))
577+ (resp (gh-gist-fork api id)))
578+ (gh-url-add-response-callback resp
579+ (lambda (gist )
580+ (message " Forked gist %s " id)))))
581+
475582(defvar gist-list-menu-mode-map
476583 (let ((map (make-sparse-keymap )))
477584 (set-keymap-parent map tabulated-list-mode-map)
@@ -484,6 +591,9 @@ put it into `kill-ring'."
484591 (define-key map " -" 'gist-remove-file )
485592 (define-key map " y" 'gist-print-current-url )
486593 (define-key map " b" 'gist-browse-current-url )
594+ (define-key map " *" 'gist-star )
595+ (define-key map " ^" 'gist-unstar )
596+ (define-key map " f" 'gist-fork )
487597 map))
488598
489599(define-derived-mode gist-list-mode tabulated-list-mode " Gist Menu"
@@ -499,20 +609,20 @@ put it into `kill-ring'."
499609 (tabulated-list-init-header )
500610 (use-local-map gist-list-menu-mode-map))
501611
502- (defun gist-list-render (&optional background )
612+ (defun gist-list-render (gists &optional background )
503613 (gist-list-mode)
504- (setq tabulated-list-entries
505- (mapcar 'gist-tabulated-entry gist-list-db))
614+ (setq tabulated-list-entries (mapcar 'gist-tabulated-entry gists))
506615 (tabulated-list-print )
507616 (gist-list-tag-multi-files)
508617 (unless background
509618 (set-window-buffer nil (current-buffer ))))
510619
511620(defun gist-list-tag-multi-files ()
512621 (let ((ids nil ))
513- (dolist (gist gist-list-db)
514- (when (< 1 (length (oref gist :files )))
515- (push (oref gist :id ) ids)))
622+ (maphash (lambda (k v )
623+ (when (< 1 (length (oref v :files )))
624+ (push (oref v :id ) ids)))
625+ gist-list-db)
516626 (save-excursion
517627 (goto-char (point-min ))
518628 (while (not (eobp ))
@@ -521,8 +631,7 @@ put it into `kill-ring'."
521631 (forward-line 1 ))))))
522632
523633(defun gist-list-db-get-gist (id )
524- (loop for gist in gist-list-db if (string= (oref gist :id ) id)
525- return gist))
634+ (gethash id gist-list-db))
526635
527636; ;; Gist minor mode
528637
0 commit comments