magit-transient.el (9163B)
1 ;;; magit-transient.el --- Support for transients -*- 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 Magit-specific prefix and suffix classes, 26 ;; and their methods. 27 28 ;;; Code: 29 30 (require 'magit-git) 31 (require 'magit-mode) 32 (require 'magit-process) 33 34 (require 'transient) 35 36 ;;; Classes 37 38 (defclass magit--git-variable (transient-variable) 39 ((scope :initarg :scope) 40 (global :initarg :global :initform nil) 41 (default :initarg :default :initform nil))) 42 43 (defclass magit--git-variable:choices (magit--git-variable) 44 ((choices :initarg :choices) 45 (fallback :initarg :fallback :initform nil))) 46 47 (defclass magit--git-variable:boolean (magit--git-variable:choices) 48 ((choices :initarg :choices :initform '("true" "false")))) 49 50 (defclass magit--git-variable:urls (magit--git-variable) 51 ((seturl-arg :initarg :seturl-arg :initform nil))) 52 53 ;;; Methods 54 ;;;; Init 55 56 (cl-defmethod transient-init-scope ((obj magit--git-variable)) 57 (oset obj scope 58 (cond (transient--prefix 59 (oref transient--prefix scope)) 60 ((slot-boundp obj 'scope) 61 (funcall (oref obj scope) obj))))) 62 63 (cl-defmethod transient-init-value ((obj magit--git-variable)) 64 (let ((variable (format (oref obj variable) 65 (oref obj scope))) 66 (arg (if (oref obj global) "--global" "--local"))) 67 (oset obj variable variable) 68 (oset obj value 69 (cond ((oref obj multi-value) 70 (magit-get-all arg variable)) 71 (t 72 (magit-get arg variable)))))) 73 74 (cl-defmethod transient-init-value ((obj magit--git-variable:boolean)) 75 (let ((variable (format (oref obj variable) 76 (oref obj scope))) 77 (arg (if (oref obj global) "--global" "--local"))) 78 (oset obj variable variable) 79 (oset obj value (if (magit-get-boolean arg variable) "true" "false")))) 80 81 ;;;; Read 82 83 (cl-defmethod transient-infix-read :around ((obj magit--git-variable:urls)) 84 (transient--with-emergency-exit 85 (transient--with-suspended-override 86 (mapcar (lambda (url) 87 (if (string-prefix-p "~" url) 88 (expand-file-name url) 89 url)) 90 (cl-call-next-method obj))))) 91 92 (cl-defmethod transient-infix-read ((obj magit--git-variable:choices)) 93 (let ((choices (oref obj choices))) 94 (when (functionp choices) 95 (setq choices (funcall choices))) 96 (if-let ((value (oref obj value))) 97 (cadr (member value choices)) 98 (car choices)))) 99 100 ;;;; Readers 101 102 (defun magit-transient-read-person (prompt initial-input history) 103 (magit-completing-read 104 prompt 105 (mapcar (lambda (line) 106 (save-excursion 107 (and (string-match "\\`[\s\t]+[0-9]+\t" line) 108 (list (substring line (match-end 0)))))) 109 (magit-git-lines "shortlog" "-n" "-s" "-e" "HEAD")) 110 nil nil initial-input history)) 111 112 (defun magit-transient-read-revision (prompt initial-input history) 113 (or (magit-completing-read prompt (cons "HEAD" (magit-list-refnames)) 114 nil nil initial-input history 115 (or (magit-branch-or-commit-at-point) 116 (magit-get-current-branch))) 117 (user-error "Nothing selected"))) 118 119 ;;;; Set 120 121 (cl-defmethod transient-infix-set ((obj magit--git-variable) value) 122 (let ((variable (oref obj variable)) 123 (arg (if (oref obj global) "--global" "--local"))) 124 (oset obj value value) 125 (if (oref obj multi-value) 126 (magit-set-all value arg variable) 127 (magit-set value arg variable)) 128 (magit-refresh) 129 (unless (or value transient--prefix) 130 (message "Unset %s" variable)))) 131 132 (cl-defmethod transient-infix-set ((obj magit--git-variable:urls) values) 133 (let ((previous (oref obj value)) 134 (seturl (oref obj seturl-arg)) 135 (remote (oref transient--prefix scope))) 136 (oset obj value values) 137 (dolist (v (cl-set-difference values previous :test #'equal)) 138 (magit-call-git "remote" "set-url" seturl "--add" remote v)) 139 (dolist (v (cl-set-difference previous values :test #'equal)) 140 (magit-call-git "remote" "set-url" seturl "--delete" remote 141 (concat "^" (regexp-quote v) "$"))) 142 (magit-refresh))) 143 144 ;;;; Draw 145 146 (cl-defmethod transient-format-description ((obj magit--git-variable)) 147 (or (oref obj description) 148 (oref obj variable))) 149 150 (cl-defmethod transient-format-value ((obj magit--git-variable)) 151 (if-let ((value (oref obj value))) 152 (if (oref obj multi-value) 153 (if (cdr value) 154 (mapconcat (lambda (v) 155 (concat "\n " 156 (propertize v 'face 'transient-value))) 157 value "") 158 (propertize (car value) 'face 'transient-value)) 159 (propertize (car (split-string value "\n")) 160 'face 'transient-value)) 161 (if-let* ((default (oref obj default)) 162 (default (if (functionp default) (funcall default) default))) 163 (concat (propertize "default:" 'face 'transient-inactive-value) 164 (propertize default 'face 'transient-value)) 165 (propertize "unset" 'face 'transient-inactive-value)))) 166 167 (cl-defmethod transient-format-value ((obj magit--git-variable:choices)) 168 (let* ((variable (oref obj variable)) 169 (choices (oref obj choices)) 170 (globalp (oref obj global)) 171 (value nil) 172 (global (magit-git-string "config" "--global" variable)) 173 (defaultp (oref obj default)) 174 (default (if (functionp defaultp) (funcall defaultp obj) defaultp)) 175 (fallback (oref obj fallback)) 176 (fallback (and fallback 177 (and-let* ((val (magit-get fallback))) 178 (concat fallback ":" val))))) 179 (if (not globalp) 180 (setq value (magit-git-string "config" "--local" variable)) 181 (setq value global) 182 (setq global nil)) 183 (when (functionp choices) 184 (setq choices (funcall choices))) 185 (concat 186 (propertize "[" 'face 'transient-inactive-value) 187 (mapconcat (lambda (choice) 188 (propertize choice 'face (if (equal choice value) 189 (if (member choice choices) 190 'transient-value 191 'font-lock-warning-face) 192 'transient-inactive-value))) 193 (if (and value (not (member value choices))) 194 (cons value choices) 195 choices) 196 (propertize "|" 'face 'transient-inactive-value)) 197 (and (or global fallback default) 198 (concat 199 (propertize "|" 'face 'transient-inactive-value) 200 (cond (global 201 (propertize (concat "global:" global) 202 'face (cond (value 203 'transient-inactive-value) 204 ((member global choices) 205 'transient-value) 206 (t 207 'font-lock-warning-face)))) 208 (fallback 209 (propertize fallback 210 'face (if value 211 'transient-inactive-value 212 'transient-value))) 213 (default 214 (propertize (if (functionp defaultp) 215 (concat "dwim:" default) 216 (concat "default:" default)) 217 'face (if value 218 'transient-inactive-value 219 'transient-value)))))) 220 (propertize "]" 'face 'transient-inactive-value)))) 221 222 ;;; Utilities 223 224 (defun magit--transient-args-and-files () 225 "Return (args files) for use by log and diff functions. 226 The value derives from that returned by `transient-get-value'." 227 (let ((args (transient-get-value))) 228 (list (seq-filter #'atom args) 229 (cdr (assoc "--" args))))) 230 231 ;;; _ 232 (provide 'magit-transient) 233 ;;; magit-transient.el ends here