fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 22h ago
276 lines · 10.4 KB Raw Blame History
1 ;;; magit-margin.el --- Margins in Magit buffers -*- lexical-binding:t -*-
2
3 ;; Copyright (C) 2008-2026 The Magit Project Contributors
4
5 ;; Author: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
6 ;; Maintainer: Jonas Bernoulli <emacs.magit@jonas.bernoulli.dev>
7
8 ;; SPDX-License-Identifier: GPL-3.0-or-later
9
10 ;; Magit is free software: you can redistribute it and/or modify it
11 ;; under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14 ;;
15 ;; Magit is distributed in the hope that it will be useful, but WITHOUT
16 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
18 ;; License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with Magit. If not, see <https://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; This library implements support for showing additional information
26 ;; in the margins of Magit buffers. Currently this is only used for
27 ;; commits, for which the committer date or age, and optionally the
28 ;; author name are shown.
29
30 ;;; Code:
31
32 (require 'magit-base)
33 (require 'magit-transient)
34 (require 'magit-mode)
35
36 ;;; Options
37
38 (defgroup magit-margin nil
39 "Information Magit displays in the margin.
40
41 You can change the STYLE and AUTHOR-WIDTH of all `magit-*-margin'
42 options to the same values by customizing `magit-log-margin'
43 *before* `magit' is loaded. If you do that, then the respective
44 values for the other options will default to what you have set
45 for that variable. Likewise if you set `magit-log-margin's INIT
46 to nil, then that is used in the default of all other options. But
47 setting it to t, i.e., re-enforcing the default for that option,
48 does not carry to other options."
49 :link '(info-link "(magit)Log Margin")
50 :group 'magit-log)
51
52 ;;; Settings
53
54 (defvar-local magit--right-margin-delayed nil)
55
56 (defvar-local magit--right-margin-config nil)
57 (put 'magit--right-margin-config 'permanent-local t)
58
59 (defun magit--right-margin-active ()
60 (car magit--right-margin-config))
61
62 (defun magit--right-margin-option ()
63 (pcase major-mode
64 ('magit-cherry-mode 'magit-cherry-margin)
65 ('magit-log-mode 'magit-log-margin)
66 ('magit-log-select-mode 'magit-log-select-margin)
67 ('magit-reflog-mode 'magit-reflog-margin)
68 ('magit-refs-mode 'magit-refs-margin)
69 ('magit-stashes-mode 'magit-stashes-margin)
70 ('magit-status-mode 'magit-status-margin)
71 ('forge-notifications-mode 'magit-status-margin)
72 ('forge-topics-mode 'magit-status-margin)))
73
74 ;;; Commands
75
76 (transient-define-prefix magit-margin-settings ()
77 "Change what information is displayed in the right margin."
78 :info-manual "(magit) Log Margin"
79 ["Margin"
80 (magit-toggle-margin)
81 (magit-cycle-margin-style)
82 (magit-toggle-margin-details)
83 (magit-refs-set-show-commit-count)])
84
85 (transient-define-suffix magit-toggle-margin ()
86 "Show or hide the right margin."
87 :description "Toggle visibility"
88 :key "L"
89 :transient t
90 (interactive)
91 (unless (magit--right-margin-option)
92 (user-error "Magit margin isn't supported in this buffer"))
93 (setcar magit--right-margin-config (not (magit--right-margin-active)))
94 (magit-set-buffer-margins))
95
96 (defvar magit-margin-default-time-format nil
97 "See https://github.com/magit/magit/pull/4605.")
98
99 (transient-define-suffix magit-cycle-margin-style ()
100 "Cycle style used for the right margin."
101 :description "Cycle style"
102 :key "l"
103 :transient t
104 (interactive)
105 (unless (magit--right-margin-option)
106 (user-error "Magit margin isn't supported in this buffer"))
107 ;; This is only suitable for commit margins (there are not others).
108 (setf (cadr magit--right-margin-config)
109 (pcase (cadr magit--right-margin-config)
110 ('age 'age-abbreviated)
111 ('age-abbreviated
112 (let ((default (or magit-margin-default-time-format
113 (cadr (symbol-value (magit--right-margin-option))))))
114 (if (stringp default) default "%Y-%m-%d %H:%M ")))
115 (_ 'age)))
116 (magit-set-buffer-margins nil t))
117
118 (transient-define-suffix magit-toggle-margin-details ()
119 "Show or hide details in the right margin."
120 :description "Toggle details"
121 :key "d"
122 :transient t
123 (interactive)
124 (unless (magit--right-margin-option)
125 (user-error "Magit margin isn't supported in this buffer"))
126 (setf (nth 3 magit--right-margin-config)
127 (not (nth 3 magit--right-margin-config)))
128 (magit-set-buffer-margins nil t))
129
130 ;;; Core
131
132 (defun magit-set-buffer-margins (&optional reset-right refresh-right)
133 (let ((lmargin nil)
134 (rmargin nil)
135 (roption (magit--right-margin-option)))
136 (when (or lmargin roption)
137 (when roption
138 (let* ((default (symbol-value roption))
139 (default-width (nth 2 default)))
140 (when (or reset-right (not magit--right-margin-config))
141 (setq magit--right-margin-config (copy-sequence default)))
142 (pcase-let ((`(,enable ,style ,_width ,details ,details-width)
143 magit--right-margin-config))
144 (setq rmargin enable)
145 (when (functionp default-width)
146 (setf (nth 2 magit--right-margin-config)
147 (funcall default-width style details details-width))))))
148 (dolist (window (get-buffer-window-list nil nil 0))
149 (with-selected-window window
150 (magit-set-window-margins window)
151 (if (or lmargin rmargin)
152 (add-hook 'window-configuration-change-hook
153 #'magit-set-window-margins nil t)
154 (remove-hook 'window-configuration-change-hook
155 #'magit-set-window-margins t))))
156 (when (and rmargin (or refresh-right magit--right-margin-delayed))
157 (magit-refresh-buffer)))))
158
159 (defun magit-set-window-margins (&optional window)
160 (when (or window (setq window (get-buffer-window)))
161 (with-selected-window window
162 (set-window-margins
163 nil
164 (if (characterp (car (magit-section-visibility-indicator)))
165 1
166 (car (window-margins)))
167 (and (magit--right-margin-active)
168 (nth 2 magit--right-margin-config))))))
169
170 (cl-defun magit-make-margin-overlay (&optional string (previous-line nil sline))
171 "Display STRING in the margin of the previous (or current) line.
172 If point is at the beginning of a line, set the margin string for
173 the previous line, otherwise for the current line. Semi-obsolete
174 optional PREVIOUS-LINE can be used to explicitly specify which
175 line is affected."
176 (save-excursion
177 (forward-line (if (if sline previous-line (bolp)) -1 0))
178 ;; Don't put the overlay on the complete line to work around #1880.
179 (let ((o (make-overlay (1+ (point)) (line-end-position) nil t)))
180 (overlay-put o 'evaporate t)
181 (overlay-put o 'before-string
182 (propertize "o" 'display
183 (list (list 'margin 'right-margin)
184 (or string " ")))))))
185
186 (defvar magit-margin-overlay-conditions
187 '( unpulled unpushed recent stashes local cherries
188 [remote branchbuf]
189 [shelved branchbuf]
190 [tags branchbuf]
191 topics discussions issues pullreqs))
192
193 (defun magit-maybe-make-margin-overlay ()
194 (when (magit-section-match magit-margin-overlay-conditions
195 magit-insert-section--current)
196 (magit-make-margin-overlay)))
197
198 ;;; Custom Support
199
200 (defun magit-margin-set-variable (mode symbol value)
201 (set-default symbol value)
202 (message "Updating margins in %s buffers..." mode)
203 (dolist (buffer (buffer-list))
204 (with-current-buffer buffer
205 (when (eq major-mode mode)
206 (magit-set-buffer-margins t)
207 (magit-refresh))))
208 (message "Updating margins in %s buffers...done" mode))
209
210 (defconst magit-log-margin--custom-type
211 '(list (boolean :tag "Show margin initially")
212 (choice :tag "Show committer"
213 (string :tag "date using time-format" "%Y-%m-%d %H:%M ")
214 (const :tag "date's age" age)
215 (const :tag "date's age (abbreviated)" age-abbreviated))
216 (const :tag "Calculate width using magit-log-margin-width"
217 magit-log-margin-width)
218 (boolean :tag "Show author name by default")
219 (integer :tag "Show author name using width")))
220
221 ;;; Time Utilities
222
223 (defvar magit--age-spec
224 `((?Y "year" "years" ,(round (* 60 60 24 365.2425)))
225 (?M "month" "months" ,(round (* 60 60 24 30.436875)))
226 (?w "week" "weeks" ,(* 60 60 24 7))
227 (?d "day" "days" ,(* 60 60 24))
228 (?h "hour" "hours" ,(* 60 60))
229 (?m "minute" "minutes" 60)
230 (?s "second" "seconds" 1))
231 "Time units used when formatting relative commit ages.
232
233 The value is a list of time units, beginning with the longest.
234 Each element has the form (CHAR UNIT UNITS SECONDS). UNIT is the
235 time unit, UNITS is the plural of that unit. CHAR is a character
236 abbreviation. And SECONDS is the number of seconds in one UNIT.
237
238 This is defined as a variable to make it possible to use time
239 units for a language other than English. It is not defined
240 as an option, because most other parts of Magit are always in
241 English.")
242
243 (defun magit--age (date &optional abbreviate)
244 (named-let calc ((age (abs (- (float-time)
245 (if (stringp date)
246 (string-to-number date)
247 date))))
248 (spec magit--age-spec))
249 (pcase-let* ((`((,char ,unit ,units ,weight) . ,spec) spec)
250 (cnt (round (/ age weight 1.0))))
251 (if (or (not spec)
252 (>= (/ age weight) 1))
253 (list cnt (cond (abbreviate char)
254 ((= cnt 1) unit)
255 (units)))
256 (calc age spec)))))
257
258 ;;; _
259 (provide 'magit-margin)
260 ;; Local Variables:
261 ;; read-symbol-shorthands: (
262 ;; ("and$" . "cond-let--and$")
263 ;; ("thread$" . "cond-let--thread$")
264 ;; ("when$" . "cond-let--when$")
265 ;; ("and-let*" . "cond-let--and-let*")
266 ;; ("and-let" . "cond-let--and-let")
267 ;; ("if-let*" . "cond-let--if-let*")
268 ;; ("if-let" . "cond-let--if-let")
269 ;; ("when-let*" . "cond-let--when-let*")
270 ;; ("when-let" . "cond-let--when-let")
271 ;; ("while-let*" . "cond-let--while-let*")
272 ;; ("while-let" . "cond-let--while-let")
273 ;; ("match-string" . "match-string")
274 ;; ("match-str" . "match-string-no-properties"))
275 ;; End:
276 ;;; magit-margin.el ends here