mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 21067e7cbe6d7a0f65ff5c317a96b5c337b0b3d8
commit | author | age
5cb5f7 1 ;;; magit-bookmark.el --- bookmark support for Magit  -*- lexical-binding: t -*-
C 2
3 ;; Copyright (C) 2010-2018  The Magit Project Contributors
4 ;;
5 ;; You should have received a copy of the AUTHORS.md file which
6 ;; lists all contributors.  If not, see http://magit.vc/authors.
7
8 ;; Author: Yuri Khan <yuri.v.khan@gmail.com>
9 ;; Maintainer: Jonas Bernoulli <jonas@bernoul.li>
10
11 ;; Magit is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
15 ;;
16 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
17 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 ;; License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with Magit.  If not, see http://www.gnu.org/licenses.
23
24 ;;; Commentary:
25
26 ;; Support for bookmarks for most Magit buffers.
27
28 ;;; Code:
29
30 (eval-when-compile
31   (require 'subr-x))
32
33 (require 'magit)
34 (require 'bookmark)
35
36 ;;; Supporting primitives
37
38 (defun magit-bookmark--jump (bookmark fn &rest args)
39   "Handle a Magit BOOKMARK.
40
41 This function will:
42
43 1. Bind `default-directory' to the repository root directory
44    stored in the `filename' bookmark property.
45 2. Invoke the function FN with ARGS as arguments.  This needs to
46    restore the buffer.
47 3. Restore the expanded/collapsed status of top level sections
48    and the point position."
49   (declare (indent 2))
50   (let* ((default-directory (bookmark-get-filename bookmark)))
51     (if default-directory
52         (apply fn args)
53       (signal 'bookmark-error-no-filename (list 'stringp default-directory)))
54     (when (derived-mode-p 'magit-mode)
55       (when-let ((hidden-sections (bookmark-prop-get bookmark
56                                                      'magit-hidden-sections)))
57         (dolist (child (oref magit-root-section children))
58           (if (member (cons (oref child type)
59                             (oref child value))
60                       hidden-sections)
61               (magit-section-hide child)
62             (magit-section-show child)))))
63     (--when-let (bookmark-get-position bookmark)
64       (goto-char it))
65     (--when-let (bookmark-get-front-context-string bookmark)
66       (when (search-forward it (point-max) t)
67         (goto-char (match-beginning 0))))
68     (--when-let (bookmark-get-rear-context-string bookmark)
69       (when (search-backward it (point-min) t)
70         (goto-char (match-end 0))))
71     nil))
72
73 (defun magit-bookmark--make-record (mode handler &optional make-props)
74   "Create a Magit bookmark.
75
76 MODE specifies the expected major mode of current buffer.
77
78 HANDLER should be a function that will be used to restore this
79 buffer.
80
81 MAKE-PROPS should be either nil or a function that will be called
82 with `magit-refresh-args' as the argument list, and may return an
83 alist whose every element has the form (PROP . VALUE) and
84 specifies additional properties to store in the bookmark."
85   (declare (indent 1))
86   (unless (eq major-mode mode)
87     (user-error "Not in a %s buffer" mode))
88   (let ((bookmark (bookmark-make-record-default 'no-file)))
89     (bookmark-prop-set bookmark 'handler handler)
90     (bookmark-set-filename bookmark (magit-toplevel))
91     (when (derived-mode-p 'magit-mode)
92       (bookmark-prop-set
93        bookmark 'magit-hidden-sections
94        (--map (cons (oref it type)
95                     (oref it value))
96               (--filter (oref it hidden)
97                         (oref magit-root-section children)))))
98     (when make-props
99       (pcase-dolist (`(,prop . ,value) (apply make-props magit-refresh-args))
100         (bookmark-prop-set bookmark prop value)))
101     bookmark))
102
103 ;;; Status
104
105 ;;;###autoload
106 (defun magit-bookmark--status-jump (bookmark)
107   "Handle a Magit status BOOKMARK."
108   (magit-bookmark--jump bookmark
109       (lambda () (magit-status-internal default-directory))))
110
111 ;;;###autoload
112 (defun magit-bookmark--status-make-record ()
113   "Create a Magit status bookmark."
114   (magit-bookmark--make-record 'magit-status-mode
115     #'magit-bookmark--status-jump))
116
117 ;;; Refs
118
119 ;;;###autoload
120 (defun magit-bookmark--refs-jump (bookmark)
121   "Handle a Magit refs BOOKMARK."
122   (magit-bookmark--jump bookmark #'magit-show-refs
123     (bookmark-prop-get bookmark 'magit-refs)
124     (bookmark-prop-get bookmark 'magit-args)))
125
126 ;;;###autoload
127 (defun magit-bookmark--refs-make-record ()
128   "Create a Magit refs bookmark."
129   (magit-bookmark--make-record 'magit-refs-mode
130     #'magit-bookmark--refs-jump
131     (lambda (refs args)
132       `((magit-refs . ,refs)
133         (magit-args . ,args)))))
134
135 ;;; Log
136
137 ;;;###autoload
138 (defun magit-bookmark--log-jump (bookmark)
139   "Handle a Magit log BOOKMARK."
140   (magit-bookmark--jump bookmark #'magit-log-other
141     (bookmark-prop-get bookmark 'magit-revs)
142     (bookmark-prop-get bookmark 'magit-args)
143     (bookmark-prop-get bookmark 'magit-files)))
144
145 (defun magit-bookmark--log-make-name (buffer-name revs _args files)
146   "Generate the default name for a log bookmark."
147   (concat
148    buffer-name " " (mapconcat #'identity revs " ")
149    (and files
150         (concat " touching " (mapconcat #'identity files " ")))))
151
152 ;;;###autoload
153 (defun magit-bookmark--log-make-record ()
154   "Create a Magit log bookmark."
155   (magit-bookmark--make-record 'magit-log-mode
156     #'magit-bookmark--log-jump
157     (lambda (revs args files)
158       `((defaults    . (,(magit-bookmark--log-make-name
159                           (buffer-name) revs args files)))
160         (magit-revs  . ,revs)
161         (magit-args  . ,args)
162         (magit-files . ,files)))))
163
164 ;;; Reflog
165
166 ;;;###autoload
167 (defun magit-bookmark--reflog-jump (bookmark)
168   "Handle a Magit reflog BOOKMARK."
169   (magit-bookmark--jump bookmark
170       (lambda ()
171         (let ((magit-reflog-arguments (bookmark-prop-get bookmark 'magit-args)))
172           (magit-git-reflog (bookmark-prop-get bookmark 'magit-ref)
173                             magit-reflog-arguments)))))
174
175 (defun magit-bookmark--reflog-make-name (buffer-name ref)
176   "Generate the default name for a reflog bookmark."
177   (concat buffer-name " " ref))
178
179 ;;;###autoload
180 (defun magit-bookmark--reflog-make-record ()
181   "Create a Magit reflog bookmark."
182   (magit-bookmark--make-record 'magit-reflog-mode
183     #'magit-bookmark--reflog-jump
184     (lambda (ref args)
185       `((defaults   . (,(magit-bookmark--reflog-make-name (buffer-name) ref)))
186         (magit-ref  . ,ref)
187         (magit-args . ,args)))))
188
189 ;;; Stashes
190
191 ;;;###autoload
192 (defun magit-bookmark--stashes-jump (bookmark)
193   "Handle a Magit stash list BOOKMARK."
194   (magit-bookmark--jump bookmark #'magit-stash-list))
195
196 ;;;###autoload
197 (defun magit-bookmark--stashes-make-record ()
198   "Create a Magit stash list bookmark."
199   (magit-bookmark--make-record 'magit-stashes-mode
200     #'magit-bookmark--stashes-jump))
201
202 ;;; Cherry
203
204 ;;;###autoload
205 (defun magit-bookmark--cherry-jump (bookmark)
206   "Handle a Magit cherry BOOKMARK."
207   (magit-bookmark--jump bookmark #'magit-cherry
208     (bookmark-prop-get bookmark 'magit-head)
209     (bookmark-prop-get bookmark 'magit-upstream)))
210
211 (defun magit-bookmark--cherry-make-name (buffer-name head upstream)
212   "Generate the default name for a cherry bookmark."
213   (concat buffer-name " " head " upstream " upstream))
214
215 ;;;###autoload
216 (defun magit-bookmark--cherry-make-record ()
217   "Create a Magit cherry bookmark."
218   (magit-bookmark--make-record 'magit-cherry-mode
219     #'magit-bookmark--cherry-jump
220     (lambda (upstream head)
221       `((defaults       . (,(magit-bookmark--cherry-make-name
222                              (buffer-name) head upstream)))
223         (magit-head     . ,head)
224         (magit-upstream . ,upstream)))))
225
226 ;;; Diff
227
228 ;;;###autoload
229 (defun magit-bookmark--diff-jump (bookmark)
230   "Handle a Magit diff BOOKMARK."
231   (magit-bookmark--jump bookmark #'magit-diff-setup
232     (bookmark-prop-get bookmark 'magit-rev-or-range)
233     (bookmark-prop-get bookmark 'magit-const)
234     (bookmark-prop-get bookmark 'magit-args)
235     (bookmark-prop-get bookmark 'magit-files)))
236
237 (defun magit-bookmark--resolve (rev-or-range)
238   "Return REV-OR-RANGE with ref names resolved to commit hashes."
239   (pcase (magit-git-lines "rev-parse" rev-or-range)
240     (`(,rev)
241      (magit-rev-abbrev rev))
242     ((and `(,rev1 ,rev2)
243           (guard (/= ?^ (aref rev1 0)))
244           (guard (=  ?^ (aref rev2 0))))
245      (concat (magit-rev-abbrev (substring rev2 1))
246              ".."
247              (magit-rev-abbrev rev1)))
248     ((and `(,rev1 ,rev2 ,rev3)
249           (guard (/= ?^ (aref rev1 0)))
250           (guard (/= ?^ (aref rev2 0)))
251           (guard (=  ?^ (aref rev3 0))))
252      (ignore rev3)
253      (concat (magit-rev-abbrev rev1)
254              "..."
255              (magit-rev-abbrev rev2)))
256     (_
257      rev-or-range)))
258
259 (defun magit-bookmark--diff-make-name
260     (buffer-name rev-or-range const _args files)
261   "Generate a default name for a diff bookmark."
262   (if (member "--no-index" const)
263       (apply #'format "*magit-diff %s %s" files)
264     (concat buffer-name " "
265             (cond (rev-or-range)
266                   ((member "--cached" const) "staged")
267                   (t                       "unstaged"))
268             (when files
269               (concat " in " (mapconcat #'identity files ", "))))))
270
271 ;;;###autoload
272 (defun magit-bookmark--diff-make-record ()
273   "Create a Magit diff bookmark."
274   (magit-bookmark--make-record 'magit-diff-mode
275     #'magit-bookmark--diff-jump
276     (lambda (rev-or-range const args files)
277       (let ((resolved (magit-bookmark--resolve rev-or-range)))
278         `((defaults           . (,(magit-bookmark--diff-make-name
279                                    (buffer-name) resolved const args files)))
280           (magit-rev-or-range . ,resolved)
281           (magit-const        . ,const)
282           (magit-args         . ,args)
283           (magit-files        . ,files))))))
284
285 ;;; Revision
286
287 ;;;###autoload
288 (defun magit-bookmark--revision-jump (bookmark)
289   "Handle a Magit revision BOOKMARK."
290   (magit-bookmark--jump bookmark #'magit-show-commit
291     (bookmark-prop-get bookmark 'magit-rev)
292     (bookmark-prop-get bookmark 'args)
293     (bookmark-prop-get bookmark 'files)))
294
295 (defun magit-bookmark--revision-make-name (buffer-name rev _args files)
296   "Generate a default name for a revision bookmark."
297   (let ((subject (magit-rev-format "%s" rev)))
298     (concat buffer-name " "
299             (magit-rev-abbrev rev)
300             (cond (files   (concat " " (mapconcat #'identity files " ")))
301                   (subject (concat " " subject))))))
302
303 ;;;###autoload
304 (defun magit-bookmark--revision-make-record ()
305   "Create a Magit revision bookmark."
306   ;; magit-refresh-args stores the revision in relative form.
307   ;; For bookmarks, the exact hash is more appropriate.
308   (magit-bookmark--make-record 'magit-revision-mode
309     #'magit-bookmark--revision-jump
310     (lambda (_rev _ args files)
311       `((defaults    . (,(magit-bookmark--revision-make-name
312                           (buffer-name) magit-buffer-revision-hash
313                           args files)))
314         (magit-rev   . ,magit-buffer-revision-hash)
315         (magit-args  . ,args)
316         (magit-files . ,files)))))
317
318 ;;; Stash
319
320 ;;;###autoload
321 (defun magit-bookmark--stash-jump (bookmark)
322   "Handle a Magit stash BOOKMARK."
323   (magit-bookmark--jump bookmark #'magit-stash-show
324     (bookmark-prop-get bookmark 'magit-stash)
325     (bookmark-prop-get bookmark 'magit-args)
326     (bookmark-prop-get bookmark 'magit-files)))
327
328 (defun magit-bookmark--stash-make-name (buffer-name stash _args files)
329   "Generate the default name for a stash bookmark."
330   (concat buffer-name " " stash " "
331           (if files
332               (mapconcat #'identity files " ")
333             (magit-rev-format "%s" stash))))
334
335 ;;;###autoload
336 (defun magit-bookmark--stash-make-record ()
337   "Create a Magit stash bookmark."
338   (magit-bookmark--make-record 'magit-stash-mode
339     #'magit-bookmark--stash-jump
340     (lambda (stash _ args files)
341       `((defaults    . (,(magit-bookmark--stash-make-name
342                           (buffer-name)
343                           (magit-rev-abbrev magit-buffer-revision-hash)
344                           args files)))
345         (magit-stash . ,magit-buffer-revision-hash)
346         (magit-args  . ,args)
347         (magit-files . ,files)
348         (magit-hidden-sections
349          . ,(--map `(,(oref it type)
350                      . ,(replace-regexp-in-string (regexp-quote stash)
351                                                   magit-buffer-revision-hash
352                                                   (oref it value)))
353                    (--filter (oref it hidden)
354                              (oref magit-root-section children))))))))
355
356 ;;; Submodules
357
358 ;;;###autoload
359 (defun magit-bookmark--submodules-jump (bookmark)
360   "Handle a Magit submodule list BOOKMARK."
361   (magit-bookmark--jump bookmark #'magit-list-submodules))
362
363 ;;;###autoload
364 (defun magit-bookmark--submodules-make-record ()
365   "Create a Magit submodule list bookmark."
366   (magit-bookmark--make-record 'magit-submodule-list-mode
367     #'magit-bookmark--submodules-jump))
368
369 ;;; _
370 (provide 'magit-bookmark)
371 ;;; magit-bookmark.el ends here