dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

magit-margin.el (9206B)


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