commit | author | age
|
5cb5f7
|
1 |
;;; magit-margin.el --- margins in Magit buffers -*- 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: Jonas Bernoulli <jonas@bernoul.li> |
|
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 |
;; This library implements support for showing additional information |
|
27 |
;; in the margins of Magit buffers. Currently this is only used for |
|
28 |
;; commits, for which the committer date or age, and optionally the |
|
29 |
;; author name are shown. |
|
30 |
|
|
31 |
;;; Code: |
|
32 |
|
|
33 |
(require 'dash) |
|
34 |
|
|
35 |
(eval-when-compile |
|
36 |
(require 'subr-x)) |
|
37 |
|
|
38 |
(require 'magit-section) |
|
39 |
(require 'magit-mode) |
|
40 |
|
|
41 |
(defgroup magit-margin nil |
|
42 |
"Information Magit displays in the margin. |
|
43 |
|
|
44 |
You can change the STYLE and AUTHOR-WIDTH of all `magit-*-margin' |
|
45 |
options to the same values by customizing `magit-log-margin' |
|
46 |
*before* `magit' is loaded. If you do that, then the respective |
|
47 |
values for the other options will default to what you have set |
|
48 |
for that variable. Likewise if you set `magit-log-margin's INIT |
|
49 |
to nil, then that is used in the default of all other options. But |
|
50 |
setting it to t, i.e. re-enforcing the default for that option, |
|
51 |
does not carry to other options." |
|
52 |
:link '(info-link "(magit)Log Margin") |
|
53 |
:group 'magit-log) |
|
54 |
|
|
55 |
(defvar-local magit-buffer-margin nil) |
|
56 |
(put 'magit-buffer-margin 'permanent-local t) |
|
57 |
|
|
58 |
(defvar-local magit-set-buffer-margin-refresh nil) |
|
59 |
|
|
60 |
(defvar magit--age-spec) |
|
61 |
|
|
62 |
;;; Commands |
|
63 |
|
|
64 |
(magit-define-popup magit-margin-popup |
|
65 |
"Popup console for changing appearance of the margin." |
|
66 |
:actions '("Margin" |
|
67 |
(?L "Toggle visibility" magit-toggle-margin) |
|
68 |
(?l "Cycle style" magit-cycle-margin-style) |
|
69 |
(?d "Toggle details" magit-toggle-margin-details) |
|
70 |
(lambda () |
|
71 |
(and (with-current-buffer magit-pre-popup-buffer |
|
72 |
(derived-mode-p 'magit-refs-mode)) |
|
73 |
(propertize "Left edge" 'face 'magit-popup-heading))) |
|
74 |
(?v "Change verbosity" magit-refs-set-show-commit-count)) |
|
75 |
:max-action-columns 1) |
|
76 |
|
|
77 |
(defun magit-toggle-margin () |
|
78 |
"Show or hide the Magit margin." |
|
79 |
(interactive) |
|
80 |
(unless (magit-margin-option) |
|
81 |
(user-error "Magit margin isn't supported in this buffer")) |
|
82 |
(setcar magit-buffer-margin (not (magit-buffer-margin-p))) |
|
83 |
(magit-set-buffer-margin)) |
|
84 |
|
|
85 |
(defun magit-cycle-margin-style () |
|
86 |
"Cycle style used for the Magit margin." |
|
87 |
(interactive) |
|
88 |
(unless (magit-margin-option) |
|
89 |
(user-error "Magit margin isn't supported in this buffer")) |
|
90 |
;; This is only suitable for commit margins (there are not others). |
|
91 |
(setf (cadr magit-buffer-margin) |
|
92 |
(pcase (cadr magit-buffer-margin) |
|
93 |
(`age 'age-abbreviated) |
|
94 |
(`age-abbreviated |
|
95 |
(let ((default (cadr (symbol-value (magit-margin-option))))) |
|
96 |
(if (stringp default) default "%Y-%m-%d %H:%M "))) |
|
97 |
(_ 'age))) |
|
98 |
(magit-set-buffer-margin nil t)) |
|
99 |
|
|
100 |
(defun magit-toggle-margin-details () |
|
101 |
"Show or hide details in the Magit margin." |
|
102 |
(interactive) |
|
103 |
(unless (magit-margin-option) |
|
104 |
(user-error "Magit margin isn't supported in this buffer")) |
|
105 |
(setf (nth 3 magit-buffer-margin) |
|
106 |
(not (nth 3 magit-buffer-margin))) |
|
107 |
(magit-set-buffer-margin nil t)) |
|
108 |
|
|
109 |
;;; Core |
|
110 |
|
|
111 |
(defun magit-buffer-margin-p () |
|
112 |
(car magit-buffer-margin)) |
|
113 |
|
|
114 |
(defun magit-margin-option () |
|
115 |
(pcase major-mode |
|
116 |
(`magit-cherry-mode 'magit-cherry-margin) |
|
117 |
(`magit-log-mode 'magit-log-margin) |
|
118 |
(`magit-log-select-mode 'magit-log-select-margin) |
|
119 |
(`magit-reflog-mode 'magit-reflog-margin) |
|
120 |
(`magit-refs-mode 'magit-refs-margin) |
|
121 |
(`magit-stashes-mode 'magit-stashes-margin) |
|
122 |
(`magit-status-mode 'magit-status-margin))) |
|
123 |
|
|
124 |
(defun magit-set-buffer-margin (&optional reset refresh) |
|
125 |
(when-let ((option (magit-margin-option))) |
|
126 |
(let* ((default (symbol-value option)) |
|
127 |
(default-width (nth 2 default))) |
|
128 |
(when (or reset (not magit-buffer-margin)) |
|
129 |
(setq magit-buffer-margin (copy-sequence default))) |
|
130 |
(pcase-let ((`(,enable ,style ,_width ,details ,details-width) |
|
131 |
magit-buffer-margin)) |
|
132 |
(when (functionp default-width) |
|
133 |
(setf (nth 2 magit-buffer-margin) |
|
134 |
(funcall default-width style details details-width))) |
|
135 |
(dolist (window (get-buffer-window-list nil nil 0)) |
|
136 |
(with-selected-window window |
|
137 |
(magit-set-window-margin window) |
|
138 |
(if enable |
|
139 |
(add-hook 'window-configuration-change-hook |
|
140 |
'magit-set-window-margin nil t) |
|
141 |
(remove-hook 'window-configuration-change-hook |
|
142 |
'magit-set-window-margin t)))) |
|
143 |
(when (and enable (or refresh magit-set-buffer-margin-refresh)) |
|
144 |
(magit-refresh-buffer)))))) |
|
145 |
|
|
146 |
(defun magit-set-window-margin (&optional window) |
|
147 |
(when (or window (setq window (get-buffer-window))) |
|
148 |
(with-selected-window window |
|
149 |
(set-window-margins |
|
150 |
nil (car (window-margins)) |
|
151 |
(and (magit-buffer-margin-p) |
|
152 |
(nth 2 magit-buffer-margin)))))) |
|
153 |
|
|
154 |
(defun magit-make-margin-overlay (&optional string previous-line) |
|
155 |
(if previous-line |
|
156 |
(save-excursion |
|
157 |
(forward-line -1) |
|
158 |
(magit-make-margin-overlay string)) |
|
159 |
;; Don't put the overlay on the complete line to work around #1880. |
|
160 |
(let ((o (make-overlay (1+ (line-beginning-position)) |
|
161 |
(line-end-position) |
|
162 |
nil t))) |
|
163 |
(overlay-put o 'evaporate t) |
|
164 |
(overlay-put o 'before-string |
|
165 |
(propertize "o" 'display |
|
166 |
(list (list 'margin 'right-margin) |
|
167 |
(or string " "))))))) |
|
168 |
|
|
169 |
(defun magit-maybe-make-margin-overlay () |
|
170 |
(when (or (magit-section-match |
|
171 |
'(unpulled unpushed recent stashes local cherries) |
|
172 |
magit-insert-section--current) |
|
173 |
(and (eq major-mode 'magit-refs-mode) |
|
174 |
(magit-section-match |
|
175 |
'(remote commit tags) |
|
176 |
magit-insert-section--current))) |
|
177 |
(magit-make-margin-overlay nil t))) |
|
178 |
|
|
179 |
;;; Custom Support |
|
180 |
|
|
181 |
(defun magit-margin-set-variable (mode symbol value) |
|
182 |
(set-default symbol value) |
|
183 |
(message "Updating margins in %s buffers..." mode) |
|
184 |
(dolist (buffer (buffer-list)) |
|
185 |
(with-current-buffer buffer |
|
186 |
(when (eq major-mode mode) |
|
187 |
(magit-set-buffer-margin t) |
|
188 |
(magit-refresh)))) |
|
189 |
(message "Updating margins in %s buffers...done" mode)) |
|
190 |
|
|
191 |
(defconst magit-log-margin--custom-type |
|
192 |
'(list (boolean :tag "Show margin initially") |
|
193 |
(choice :tag "Show committer" |
|
194 |
(string :tag "date using time-format" "%Y-%m-%d %H:%M ") |
|
195 |
(const :tag "date's age" age) |
|
196 |
(const :tag "date's age (abbreviated)" age-abbreviated)) |
|
197 |
(const :tag "Calculate width using magit-log-margin-width" |
|
198 |
magit-log-margin-width) |
|
199 |
(boolean :tag "Show author name by default") |
|
200 |
(integer :tag "Show author name using width"))) |
|
201 |
|
|
202 |
;;; Time Utilities |
|
203 |
|
|
204 |
(defvar magit--age-spec |
|
205 |
`((?Y "year" "years" ,(round (* 60 60 24 365.2425))) |
|
206 |
(?M "month" "months" ,(round (* 60 60 24 30.436875))) |
|
207 |
(?w "week" "weeks" ,(* 60 60 24 7)) |
|
208 |
(?d "day" "days" ,(* 60 60 24)) |
|
209 |
(?h "hour" "hours" ,(* 60 60)) |
|
210 |
(?m "minute" "minutes" 60) |
|
211 |
(?s "second" "seconds" 1)) |
|
212 |
"Time units used when formatting relative commit ages. |
|
213 |
|
|
214 |
The value is a list of time units, beginning with the longest. |
|
215 |
Each element has the form (CHAR UNIT UNITS SECONDS). UNIT is the |
|
216 |
time unit, UNITS is the plural of that unit. CHAR is a character |
|
217 |
abbreviation. And SECONDS is the number of seconds in one UNIT. |
|
218 |
|
|
219 |
This is defined as a variable to make it possible to use time |
|
220 |
units for a language other than English. It is not defined |
|
221 |
as an option, because most other parts of Magit are always in |
|
222 |
English.") |
|
223 |
|
|
224 |
(defun magit--age (date &optional abbreviate) |
|
225 |
(cl-labels ((fn (age spec) |
|
226 |
(pcase-let ((`(,char ,unit ,units ,weight) (car spec))) |
|
227 |
(let ((cnt (round (/ age weight 1.0)))) |
|
228 |
(if (or (not (cdr spec)) |
|
229 |
(>= (/ age weight) 1)) |
|
230 |
(list cnt (cond (abbreviate char) |
|
231 |
((= cnt 1) unit) |
|
232 |
(t units))) |
|
233 |
(fn age (cdr spec))))))) |
|
234 |
(fn (abs (- (float-time) |
|
235 |
(if (stringp date) |
|
236 |
(string-to-number date) |
|
237 |
date))) |
|
238 |
magit--age-spec))) |
|
239 |
|
|
240 |
;;; _ |
|
241 |
(provide 'magit-margin) |
|
242 |
;;; magit-margin.el ends here |