config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

magit-margin.el (9404B)


      1 ;;; magit-margin.el --- Margins in Magit buffers  -*- lexical-binding:t -*-
      2 
      3 ;; Copyright (C) 2008-2024 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 (defvar-local magit-buffer-margin nil)
     53 (put 'magit-buffer-margin 'permanent-local t)
     54 
     55 (defvar-local magit-set-buffer-margin-refresh nil)
     56 
     57 (defvar magit--age-spec)
     58 
     59 ;;; Commands
     60 
     61 (transient-define-prefix magit-margin-settings ()
     62   "Change what information is displayed in the margin."
     63   :info-manual "(magit) Log Margin"
     64   ["Margin"
     65    (magit-toggle-margin)
     66    (magit-cycle-margin-style)
     67    (magit-toggle-margin-details)
     68    (magit-refs-set-show-commit-count)])
     69 
     70 (transient-define-suffix magit-toggle-margin ()
     71   "Show or hide the Magit margin."
     72   :description "Toggle visibility"
     73   :key "L"
     74   :transient t
     75   (interactive)
     76   (unless (magit-margin-option)
     77     (user-error "Magit margin isn't supported in this buffer"))
     78   (setcar magit-buffer-margin (not (magit-buffer-margin-p)))
     79   (magit-set-buffer-margin))
     80 
     81 (defvar magit-margin-default-time-format nil
     82   "See https://github.com/magit/magit/pull/4605.")
     83 
     84 (transient-define-suffix magit-cycle-margin-style ()
     85   "Cycle style used for the Magit margin."
     86   :description "Cycle style"
     87   :key "l"
     88   :transient t
     89   (interactive)
     90   (unless (magit-margin-option)
     91     (user-error "Magit margin isn't supported in this buffer"))
     92   ;; This is only suitable for commit margins (there are not others).
     93   (setf (cadr magit-buffer-margin)
     94         (pcase (cadr magit-buffer-margin)
     95           ('age 'age-abbreviated)
     96           ('age-abbreviated
     97            (let ((default (or magit-margin-default-time-format
     98                               (cadr (symbol-value (magit-margin-option))))))
     99              (if (stringp default) default "%Y-%m-%d %H:%M ")))
    100           (_ 'age)))
    101   (magit-set-buffer-margin nil t))
    102 
    103 (transient-define-suffix magit-toggle-margin-details ()
    104   "Show or hide details in the Magit margin."
    105   :description "Toggle details"
    106   :key "d"
    107   :transient t
    108   (interactive)
    109   (unless (magit-margin-option)
    110     (user-error "Magit margin isn't supported in this buffer"))
    111   (setf (nth 3 magit-buffer-margin)
    112         (not (nth 3 magit-buffer-margin)))
    113   (magit-set-buffer-margin nil t))
    114 
    115 ;;; Core
    116 
    117 (defun magit-buffer-margin-p ()
    118   (car magit-buffer-margin))
    119 
    120 (defun magit-margin-option ()
    121   (pcase major-mode
    122     ('magit-cherry-mode     'magit-cherry-margin)
    123     ('magit-log-mode        'magit-log-margin)
    124     ('magit-log-select-mode 'magit-log-select-margin)
    125     ('magit-reflog-mode     'magit-reflog-margin)
    126     ('magit-refs-mode       'magit-refs-margin)
    127     ('magit-stashes-mode    'magit-stashes-margin)
    128     ('magit-status-mode     'magit-status-margin)
    129     ('forge-notifications-mode 'magit-status-margin)
    130     ('forge-topics-mode     'magit-status-margin)))
    131 
    132 (defun magit-set-buffer-margin (&optional reset refresh)
    133   (when-let ((option (magit-margin-option)))
    134     (let* ((default (symbol-value option))
    135            (default-width (nth 2 default)))
    136       (when (or reset (not magit-buffer-margin))
    137         (setq magit-buffer-margin (copy-sequence default)))
    138       (pcase-let ((`(,enable ,style ,_width ,details ,details-width)
    139                    magit-buffer-margin))
    140         (when (functionp default-width)
    141           (setf (nth 2 magit-buffer-margin)
    142                 (funcall default-width style details details-width)))
    143         (dolist (window (get-buffer-window-list nil nil 0))
    144           (with-selected-window window
    145             (magit-set-window-margin window)
    146             (if enable
    147                 (add-hook  'window-configuration-change-hook
    148                            #'magit-set-window-margin nil t)
    149               (remove-hook 'window-configuration-change-hook
    150                            #'magit-set-window-margin t))))
    151         (when (and enable (or refresh magit-set-buffer-margin-refresh))
    152           (magit-refresh-buffer))))))
    153 
    154 (defun magit-set-window-margin (&optional window)
    155   (when (or window (setq window (get-buffer-window)))
    156     (with-selected-window window
    157       (set-window-margins
    158        nil (car (window-margins))
    159        (and (magit-buffer-margin-p)
    160             (nth 2 magit-buffer-margin))))))
    161 
    162 (defun magit-make-margin-overlay (&optional string previous-line)
    163   (if previous-line
    164       (save-excursion
    165         (forward-line -1)
    166         (magit-make-margin-overlay string))
    167     ;; Don't put the overlay on the complete line to work around #1880.
    168     (let ((o (make-overlay (1+ (line-beginning-position))
    169                            (line-end-position)
    170                            nil t)))
    171       (overlay-put o 'evaporate t)
    172       (overlay-put o 'before-string
    173                    (propertize "o" 'display
    174                                (list (list 'margin 'right-margin)
    175                                      (or string " ")))))))
    176 
    177 (defvar magit-margin-overlay-conditions
    178   '( unpulled unpushed recent stashes local cherries
    179      [remote branchbuf]
    180      [tags branchbuf]
    181      topics issues pullreqs))
    182 
    183 (defun magit-maybe-make-margin-overlay ()
    184   (when (magit-section-match magit-margin-overlay-conditions
    185                              magit-insert-section--current)
    186     (magit-make-margin-overlay nil t)))
    187 
    188 ;;; Custom Support
    189 
    190 (defun magit-margin-set-variable (mode symbol value)
    191   (set-default symbol value)
    192   (message "Updating margins in %s buffers..." mode)
    193   (dolist (buffer (buffer-list))
    194     (with-current-buffer buffer
    195       (when (eq major-mode mode)
    196         (magit-set-buffer-margin t)
    197         (magit-refresh))))
    198   (message "Updating margins in %s buffers...done" mode))
    199 
    200 (defconst magit-log-margin--custom-type
    201   '(list (boolean :tag "Show margin initially")
    202          (choice  :tag "Show committer"
    203                   (string :tag "date using time-format" "%Y-%m-%d %H:%M ")
    204                   (const  :tag "date's age" age)
    205                   (const  :tag "date's age (abbreviated)" age-abbreviated))
    206          (const   :tag "Calculate width using magit-log-margin-width"
    207                   magit-log-margin-width)
    208          (boolean :tag "Show author name by default")
    209          (integer :tag "Show author name using width")))
    210 
    211 ;;; Time Utilities
    212 
    213 (defvar magit--age-spec
    214   `((?Y "year"   "years"   ,(round (* 60 60 24 365.2425)))
    215     (?M "month"  "months"  ,(round (* 60 60 24 30.436875)))
    216     (?w "week"   "weeks"   ,(* 60 60 24 7))
    217     (?d "day"    "days"    ,(* 60 60 24))
    218     (?h "hour"   "hours"   ,(* 60 60))
    219     (?m "minute" "minutes" 60)
    220     (?s "second" "seconds" 1))
    221   "Time units used when formatting relative commit ages.
    222 
    223 The value is a list of time units, beginning with the longest.
    224 Each element has the form (CHAR UNIT UNITS SECONDS).  UNIT is the
    225 time unit, UNITS is the plural of that unit.  CHAR is a character
    226 abbreviation.  And SECONDS is the number of seconds in one UNIT.
    227 
    228 This is defined as a variable to make it possible to use time
    229 units for a language other than English.  It is not defined
    230 as an option, because most other parts of Magit are always in
    231 English.")
    232 
    233 (defun magit--age (date &optional abbreviate)
    234   (cl-labels ((fn (age spec)
    235                 (pcase-let ((`(,char ,unit ,units ,weight) (car spec)))
    236                   (let ((cnt (round (/ age weight 1.0))))
    237                     (if (or (not (cdr spec))
    238                             (>= (/ age weight) 1))
    239                         (list cnt (cond (abbreviate char)
    240                                         ((= cnt 1) unit)
    241                                         (t units)))
    242                       (fn age (cdr spec)))))))
    243     (fn (abs (- (float-time)
    244                 (if (stringp date)
    245                     (string-to-number date)
    246                   date)))
    247         magit--age-spec)))
    248 
    249 ;;; _
    250 (provide 'magit-margin)
    251 ;;; magit-margin.el ends here