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
@@ -130,14 +131,21 @@ appropriate modes from fetched gist files (based on filenames)."
130
131
:type '(alist :key-type (symbol :tag " Mode" )
131
132
:value-type (string :tag " Extension" )))
132
133
133
- (defvar gist-list-db nil )
134
+ (defvar gist-list-db (make-hash-table :test 'equal ))
135
+ (defvar gist-list-db-by-user (make-hash-table :test 'equal ))
134
136
135
137
(defvar gist-id nil )
136
138
(make-variable-buffer-local 'gist-id )
137
139
138
140
(defvar gist-filename nil )
139
141
(make-variable-buffer-local 'gist-filename )
140
142
143
+ (defvar gist-user-history nil " History list for gist-list-user." )
144
+
145
+ (defvar gist-list-buffer-user nil " Username for this gist buffer." )
146
+ (make-variable-buffer-local 'gist-list-buffer-user )
147
+ (put 'gist-list-buffer-user 'permanent-local t )
148
+
141
149
(defun gist-get-api (&optional sync )
142
150
(let ((gh-profile-current-profile
143
151
(or gh-profile-current-profile (gh-profile-completing-read))))
@@ -195,7 +203,7 @@ With a prefix argument, makes a private paste."
195
203
196
204
(defun gist-created-callback (gist )
197
205
(let ((location (oref gist :html-url )))
198
- (gist-list-reload t )
206
+ (gist-list-reload " " t )
199
207
(message " Paste created: %s " location)
200
208
(when gist-view-gist
201
209
(browse-url location))
@@ -249,25 +257,46 @@ Copies the URL into the kill ring."
249
257
(mark-inactive (gist-buffer-private))))
250
258
251
259
;;;### autoload
252
- (defun gist-list (&optional force-reload background )
253
- " Displays a list of all of the current user's gists in a new buffer."
254
- (interactive " P" )
260
+ (defun gist-list-user (username &optional force-reload background )
261
+ " Displays a list of a user's gists in a new buffer. When called from
262
+ a program, pass a blank string as the username to view the user's own
263
+ gists, or nil for the username and a non-nil value for force-reload to
264
+ reload the gists for the current buffer."
265
+ (interactive
266
+ (let ((username (read-from-minibuffer " GitHub user: " nil nil nil
267
+ 'gist-user-history ))
268
+ (force-reload (equal current-prefix-arg '(4 ))))
269
+ (list username force-reload)))
255
270
; ; if buffer exists, it contains the current gh profile
256
271
(let* ((gh-profile-current-profile (or gh-profile-current-profile
257
272
(gh-profile-completing-read)))
258
- (bufname (format " *%s :gists* " gh-profile-current-profile))
259
- (api (gist-get-api nil )))
273
+ (bufname (if (null username)
274
+ (if (not (equal major-mode 'gist-list-mode ))
275
+ (error " Current buffer isn't a gist-list-mode buffer " )
276
+ (buffer-name ))
277
+ (format " *%s :%s gists* "
278
+ gh-profile-current-profile
279
+ (if (string= " " username)
280
+ " "
281
+ (format " %s 's-" username)))))
282
+ (api (gist-get-api nil ))
283
+ (username (or (and (null username) gist-list-buffer-user)
284
+ (and (not (or (null username)
285
+ (string= " " username)))
286
+ username)
287
+ (gh-api-get-username api))))
260
288
(when force-reload
261
289
(pcache-clear (oref api :cache ))
262
- (or background (message " Retrieving list of your gists... " )))
290
+ (or background (message " Retrieving list of gists... " )))
263
291
(unless (and background (not (get-buffer bufname)))
264
- (let ((resp (gh-gist-list api)))
292
+ (let ((resp (gh-gist-list api username )))
265
293
(gh-url-add-response-callback
266
294
resp
267
295
(lexical-let ((buffer bufname))
268
296
(lambda (gists )
269
297
(with-current-buffer (get-buffer-create buffer)
270
- (gist-lists-retrieved-callback gists background)))))
298
+ (setq gist-list-buffer-user username)
299
+ (gist-lists-retrieved-callback gists username background)))))
271
300
(gh-url-add-response-callback
272
301
resp
273
302
(lexical-let ((profile (oref api :profile ))
@@ -276,20 +305,30 @@ Copies the URL into the kill ring."
276
305
(with-current-buffer buffer
277
306
(setq gh-profile-current-profile profile)))))))))
278
307
279
- (defun gist-list-reload (&optional background )
308
+ ;;;### autoload
309
+ (defun gist-list (&optional force-reload background )
310
+ " Displays a list of all of the current user's gists in a new buffer."
311
+ (interactive " P" )
312
+ (gist-list-user " " force-reload background))
313
+
314
+ (defun gist-list-reload (&optional username background )
280
315
(interactive )
281
- (gist-list t background))
316
+ (gist-list-user username t background))
282
317
283
318
(defun gist-tabulated-entry (gist )
284
319
(let* ((data (gist-parse-gist gist))
285
320
(repo (oref gist :id )))
286
321
(list repo (apply 'vector data))))
287
322
288
- (defun gist-lists-retrieved-callback (gists &optional background )
323
+ (defun gist-lists-retrieved-callback (gists username &optional background )
289
324
" Called when the list of gists has been retrieved. Displays
290
325
the list."
291
- (setq gist-list-db gists)
292
- (gist-list-render background))
326
+ (dolist (g (gethash username gist-list-db-by-user))
327
+ (remhash (oref g :id ) gist-list-db))
328
+ (dolist (g gists)
329
+ (puthash (oref g :id ) g gist-list-db))
330
+ (puthash username gists gist-list-db-by-user)
331
+ (gist-list-render (gethash username gist-list-db-by-user) background))
293
332
294
333
(defun gist--get-time (gist )
295
334
(let* ((date (timezone-parse-date (oref gist :date )))
@@ -330,15 +369,18 @@ for the gist."
330
369
(interactive " sGist ID: " )
331
370
(let ((gist nil )
332
371
(multi nil )
333
- (prefix (format " *gist %s * " id))
372
+ (prefix (format " *gist- %s * " id))
334
373
(result nil )
335
374
(profile (gh-profile-current-profile)))
336
375
(setq gist (gist-list-db-get-gist id))
337
376
(let ((api (gist-get-api t )))
338
377
(cond ((null gist)
339
378
; ; fetch it
340
379
(setq gist (oref (gh-gist-get api id) :data ))
341
- (add-to-list 'gist-list-db gist))
380
+ (puthash (oref gist :id ) gist gist-list-db)
381
+ (let* ((user (oref gist :user ))
382
+ (gists (push gist (gethash user gist-list-db-by-user))))
383
+ (puthash user gists gist-list-db-by-user)))
342
384
((not (gh-gist-gist-has-files gist))
343
385
(gh-gist-get api gist))))
344
386
(let ((files (oref gist :files )))
@@ -386,16 +428,25 @@ for the gist."
386
428
(gist-fetch-current)
387
429
(select-window win)))
388
430
431
+ (defun gist--check-perms-and-get-api (gist errormsg apiflg )
432
+ (let* ((api (gist-get-api t ))
433
+ (username (gh-api-get-username api))
434
+ (gs (gethash username gist-list-db-by-user)))
435
+ (if (not (memq gist gs))
436
+ (user-error errormsg)
437
+ api)))
438
+
389
439
(defun gist-edit-current-description ()
390
440
(interactive )
391
441
(let* ((id (tabulated-list-get-id ))
392
442
(gist (gist-list-db-get-gist id))
393
- (old-descr (oref gist :description ))
394
- (new-descr (read-from-minibuffer " Description: " old-descr)))
395
- (let* ((g (clone gist
443
+ (api (gist--check-perms-and-get-api
444
+ gist " Can't edit a gist that doesn't belong to you" t )))
445
+ (let* ((old-descr (oref gist :description ))
446
+ (new-descr (read-from-minibuffer " Description: " old-descr))
447
+ (g (clone gist
396
448
:files nil
397
449
:description new-descr))
398
- (api (gist-get-api t ))
399
450
(resp (gh-gist-edit api g)))
400
451
(gh-url-add-response-callback resp
401
452
(lambda (gist )
@@ -406,18 +457,20 @@ for the gist."
406
457
(let* ((buffer (get-buffer buffer))
407
458
(id (tabulated-list-get-id ))
408
459
(gist (gist-list-db-get-gist id))
409
- (fname (file-name-nondirectory (or (buffer-file-name buffer) (buffer-name buffer)))))
410
- (let* ((g (clone gist :files
411
- (list
412
- (gh-gist-gist-file " file"
413
- :filename fname
414
- :content (with-current-buffer buffer
415
- (buffer-string ))))))
416
- (api (gist-get-api t ))
417
- (resp (gh-gist-edit api g)))
418
- (gh-url-add-response-callback resp
419
- (lambda (gist )
420
- (gist-list-reload))))))
460
+ (api (gist--check-perms-and-get-api
461
+ gist " Can't modify a gist that doesn't belong to you" t ))
462
+ (fname (file-name-nondirectory (or (buffer-file-name buffer)
463
+ (buffer-name buffer))))
464
+ (g (clone gist :files
465
+ (list
466
+ (gh-gist-gist-file " file"
467
+ :filename fname
468
+ :content (with-current-buffer buffer
469
+ (buffer-string ))))))
470
+ (resp (gh-gist-edit api g)))
471
+ (gh-url-add-response-callback resp
472
+ (lambda (gist )
473
+ (gist-list-reload)))))
421
474
422
475
(defun gist-remove-file (fname )
423
476
(interactive (list
@@ -428,24 +481,26 @@ for the gist."
428
481
(mapcar #' (lambda (f ) (oref f :filename ))
429
482
(oref gist :files ))))))
430
483
(let* ((id (tabulated-list-get-id ))
431
- (gist (gist-list-db-get-gist id)))
432
- (let* ((g (clone gist :files
433
- (list
434
- (gh-gist-gist-file " file"
435
- :filename fname
436
- :content nil ))))
437
- (api (gist-get-api t ))
438
- (resp (gh-gist-edit api g)))
439
- (gh-url-add-response-callback resp
440
- (lambda (gist )
441
- (gist-list-reload))))))
484
+ (gist (gist-list-db-get-gist id))
485
+ (api (gist--check-perms-and-get-api
486
+ gist " Can't modify a gist that doesn't belong to you" t ))
487
+ (g (clone gist :files
488
+ (list
489
+ (gh-gist-gist-file " file"
490
+ :filename fname
491
+ :content nil ))))
492
+ (resp (gh-gist-edit api g)))
493
+ (gh-url-add-response-callback resp
494
+ (lambda (gist )
495
+ (gist-list-reload)))))
442
496
443
497
(defun gist-kill-current ()
444
- (interactive )
445
- (let ((id (tabulated-list-get-id )))
498
+ (let* ((id (tabulated-list-get-id ))
499
+ (gist (gist-list-db-get-gist id))
500
+ (api (gist--check-perms-and-get-api
501
+ gist " Can't delete a gist that doesn't belong to you" t )))
446
502
(when (yes-or-no-p (format " Really delete gist %s ? " id) )
447
- (let* ((api (gist-get-api t ))
448
- (resp (gh-gist-delete api id)))
503
+ (let* ((resp (gh-gist-delete api id)))
449
504
(gist-list-reload)))))
450
505
451
506
(defun gist-current-url ()
@@ -465,6 +520,49 @@ put it into `kill-ring'."
465
520
(interactive )
466
521
(browse-url (gist-current-url)))
467
522
523
+ (defun gist--do-star (id how msg )
524
+ (let* ((api (gist-get-api t ))
525
+ (resp (gh-gist-set-star api id how)))
526
+ (gh-url-add-response-callback resp
527
+ (lambda (gist )
528
+ (message msg id)))))
529
+
530
+ ;;;### autoload
531
+ (defun gist-star ()
532
+ (interactive )
533
+ (let ((id (tabulated-list-get-id )))
534
+ (gist--do-star id t " Starred gist %s" )))
535
+
536
+ ;;;### autoload
537
+ (defun gist-unstar ()
538
+ (interactive )
539
+ (let ((id (tabulated-list-get-id )))
540
+ (gist--do-star id nil " Unstarred gist %s" )))
541
+
542
+ ;;;### autoload
543
+ (defun gist-list-starred (&optional background )
544
+ " List your starred gists."
545
+ (interactive )
546
+ (let* ((api (gist-get-api t ))
547
+ (resp (gh-gist-list-starred api)))
548
+ (gh-url-add-response-callback
549
+ resp
550
+ (lexical-let ((buffer " *starred-gists*" ))
551
+ (lambda (gists )
552
+ (with-current-buffer (get-buffer-create buffer)
553
+ (gist-list-render gists background)))))))
554
+
555
+ ;;;### autoload
556
+ (defun gist-fork ()
557
+ " Fork a gist."
558
+ (interactive )
559
+ (let* ((id (tabulated-list-get-id ))
560
+ (api (gist-get-api))
561
+ (resp (gh-gist-fork api id)))
562
+ (gh-url-add-response-callback resp
563
+ (lambda (gist )
564
+ (message " Forked gist %s " id)))))
565
+
468
566
(defvar gist-list-menu-mode-map
469
567
(let ((map (make-sparse-keymap )))
470
568
(set-keymap-parent map tabulated-list-mode-map)
@@ -477,6 +575,9 @@ put it into `kill-ring'."
477
575
(define-key map " -" 'gist-remove-file )
478
576
(define-key map " y" 'gist-print-current-url )
479
577
(define-key map " b" 'gist-browse-current-url )
578
+ (define-key map " *" 'gist-star )
579
+ (define-key map " ^" 'gist-unstar )
580
+ (define-key map " f" 'gist-fork )
480
581
map))
481
582
482
583
(define-derived-mode gist-list-mode tabulated-list-mode " Gist Menu"
@@ -492,20 +593,20 @@ put it into `kill-ring'."
492
593
(tabulated-list-init-header )
493
594
(use-local-map gist-list-menu-mode-map))
494
595
495
- (defun gist-list-render (&optional background )
596
+ (defun gist-list-render (gists &optional background )
496
597
(gist-list-mode)
497
- (setq tabulated-list-entries
498
- (mapcar 'gist-tabulated-entry gist-list-db))
598
+ (setq tabulated-list-entries (mapcar 'gist-tabulated-entry gists))
499
599
(tabulated-list-print )
500
600
(gist-list-tag-multi-files)
501
601
(unless background
502
602
(set-window-buffer nil (current-buffer ))))
503
603
504
604
(defun gist-list-tag-multi-files ()
505
605
(let ((ids nil ))
506
- (dolist (gist gist-list-db)
507
- (when (< 1 (length (oref gist :files )))
508
- (push (oref gist :id ) ids)))
606
+ (maphash (lambda (k v )
607
+ (when (< 1 (length (oref v :files )))
608
+ (push (oref v :id ) ids)))
609
+ gist-list-db)
509
610
(save-excursion
510
611
(goto-char (point-min ))
511
612
(while (not (eobp ))
@@ -514,8 +615,7 @@ put it into `kill-ring'."
514
615
(forward-line 1 ))))))
515
616
516
617
(defun gist-list-db-get-gist (id )
517
- (loop for gist in gist-list-db if (string= (oref gist :id ) id)
518
- return gist))
618
+ (gethash id gist-list-db))
519
619
520
620
; ;; Gist minor mode
521
621
0 commit comments