7
7
; ; Michael Ivey
8
8
; ; Phil Hagelberg
9
9
; ; 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"))
12
12
; ; Keywords: tools
13
13
; ; Homepage: https://github.com/defunkt/gist.el
14
14
42
42
(require 'eieio-base )
43
43
(require 'timezone )
44
44
45
+ (require 'gh-api )
45
46
(require 'gh-gist )
46
47
(require 'gh-profile )
47
48
@@ -138,13 +139,25 @@ appropriate modes from fetched gist files (based on filenames)."
138
139
:value-type (string :tag " Extension" )))
139
140
140
141
(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 )))
141
148
142
149
(defvar gist-id nil )
143
150
(make-variable-buffer-local 'gist-id )
144
151
145
152
(defvar gist-filename nil )
146
153
(make-variable-buffer-local 'gist-filename )
147
154
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
+
148
161
(defun gist-get-api (&optional sync )
149
162
(let ((gh-profile-current-profile
150
163
(or gh-profile-current-profile (gh-profile-completing-read))))
@@ -202,7 +215,7 @@ With a prefix argument, makes a private paste."
202
215
203
216
(defun gist-created-callback (gist )
204
217
(let ((location (oref gist :html-url )))
205
- (gist-list-reload t )
218
+ (gist-list-reload 'current-user t )
206
219
(message gist-created-fmt location)
207
220
(when gist-view-gist
208
221
(browse-url location))
@@ -256,24 +269,47 @@ Copies the URL into the kill ring."
256
269
(mark-inactive (gist-buffer-private))))
257
270
258
271
;;;### 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)))
262
282
; ; if buffer exists, it contains the current gh profile
263
283
(let* ((gh-profile-current-profile (or gh-profile-current-profile
264
284
(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))))
267
302
(when force-reload
268
303
(pcache-clear (oref api :cache ))
269
- (or background (message " Retrieving list of your gists... " )))
304
+ (or background (message " Retrieving list of gists... " )))
270
305
(unless (and background (not (get-buffer bufname)))
271
- (let ((resp (gh-gist-list api)))
306
+ (let ((resp (gh-gist-list api username )))
272
307
(gh-url-add-response-callback
273
308
resp
274
309
(lexical-let ((buffer bufname))
275
310
(lambda (gists )
276
311
(with-current-buffer (get-buffer-create buffer)
312
+ (setq gist-list-buffer-user username)
277
313
(gist-lists-retrieved-callback gists background)))))
278
314
(gh-url-add-response-callback
279
315
resp
@@ -283,9 +319,15 @@ Copies the URL into the kill ring."
283
319
(with-current-buffer buffer
284
320
(setq gh-profile-current-profile profile)))))))))
285
321
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 )
287
329
(interactive )
288
- (gist-list t background))
330
+ (gist-list-user username t background))
289
331
290
332
(defun gist-tabulated-entry (gist )
291
333
(let* ((data (gist-parse-gist gist))
@@ -295,8 +337,13 @@ Copies the URL into the kill ring."
295
337
(defun gist-lists-retrieved-callback (gists &optional background )
296
338
" Called when the list of gists has been retrieved. Displays
297
339
the 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))
300
347
301
348
(defun gist--get-time (gist )
302
349
(let* ((date (timezone-parse-date (oref gist :date )))
@@ -337,15 +384,18 @@ for the gist."
337
384
(interactive " sGist ID: " )
338
385
(let ((gist nil )
339
386
(multi nil )
340
- (prefix (format " *gist %s * " id))
387
+ (prefix (format " *gist- %s * " id))
341
388
(result nil )
342
389
(profile (gh-profile-current-profile)))
343
390
(setq gist (gist-list-db-get-gist id))
344
391
(let ((api (gist-get-api t )))
345
392
(cond ((null gist)
346
393
; ; fetch it
347
394
(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)))
349
399
((not (gh-gist-gist-has-files gist))
350
400
(gh-gist-get api gist))))
351
401
(let ((files (oref gist :files )))
@@ -393,16 +443,25 @@ for the gist."
393
443
(gist-fetch-current)
394
444
(select-window win)))
395
445
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
+
396
454
(defun gist-edit-current-description ()
397
455
(interactive )
398
456
(let* ((id (tabulated-list-get-id ))
399
457
(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
403
463
:files nil
404
464
:description new-descr))
405
- (api (gist-get-api t ))
406
465
(resp (gh-gist-edit api g)))
407
466
(gh-url-add-response-callback resp
408
467
(lambda (gist )
@@ -413,18 +472,20 @@ for the gist."
413
472
(let* ((buffer (get-buffer buffer))
414
473
(id (tabulated-list-get-id ))
415
474
(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)))))
428
489
429
490
(defun gist-remove-file (fname )
430
491
(interactive (list
@@ -435,24 +496,27 @@ for the gist."
435
496
(mapcar #' (lambda (f ) (oref f :filename ))
436
497
(oref gist :files ))))))
437
498
(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)))))
449
511
450
512
(defun gist-kill-current ()
451
513
(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 )))
453
518
(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)))
456
520
(gist-list-reload)))))
457
521
458
522
(defun gist-current-url ()
@@ -472,6 +536,49 @@ put it into `kill-ring'."
472
536
(interactive )
473
537
(browse-url (gist-current-url)))
474
538
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
+
475
582
(defvar gist-list-menu-mode-map
476
583
(let ((map (make-sparse-keymap )))
477
584
(set-keymap-parent map tabulated-list-mode-map)
@@ -484,6 +591,9 @@ put it into `kill-ring'."
484
591
(define-key map " -" 'gist-remove-file )
485
592
(define-key map " y" 'gist-print-current-url )
486
593
(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 )
487
597
map))
488
598
489
599
(define-derived-mode gist-list-mode tabulated-list-mode " Gist Menu"
@@ -499,20 +609,20 @@ put it into `kill-ring'."
499
609
(tabulated-list-init-header )
500
610
(use-local-map gist-list-menu-mode-map))
501
611
502
- (defun gist-list-render (&optional background )
612
+ (defun gist-list-render (gists &optional background )
503
613
(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))
506
615
(tabulated-list-print )
507
616
(gist-list-tag-multi-files)
508
617
(unless background
509
618
(set-window-buffer nil (current-buffer ))))
510
619
511
620
(defun gist-list-tag-multi-files ()
512
621
(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)
516
626
(save-excursion
517
627
(goto-char (point-min ))
518
628
(while (not (eobp ))
@@ -521,8 +631,7 @@ put it into `kill-ring'."
521
631
(forward-line 1 ))))))
522
632
523
633
(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))
526
635
527
636
; ;; Gist minor mode
528
637
0 commit comments