about summary refs log tree commit diff stats
path: root/.emacs.d/lisp/init-git.el
diff options
context:
space:
mode:
authorDavid Morgan <djm_uk@protonmail.com>2022-10-12 16:26:42 +0100
committerDavid Morgan <djm_uk@protonmail.com>2022-10-12 16:26:42 +0100
commit58ae54c06e3adc2a44f984a8d340e091ad3a326e (patch)
treee66d52692a88d9a086fe39f12ea2b67e07147661 /.emacs.d/lisp/init-git.el
parent901800109697f906d7eb2cf433dbcd3c5ed256f4 (diff)
downloaddotfiles-58ae54c06e3adc2a44f984a8d340e091ad3a326e.tar.gz
Add difftastic + magit code
Diffstat (limited to '.emacs.d/lisp/init-git.el')
-rw-r--r--.emacs.d/lisp/init-git.el98
1 files changed, 97 insertions, 1 deletions
diff --git a/.emacs.d/lisp/init-git.el b/.emacs.d/lisp/init-git.el
index 43e88c7..bfcfc22 100644
--- a/.emacs.d/lisp/init-git.el
+++ b/.emacs.d/lisp/init-git.el
@@ -113,13 +113,109 @@
             (magit-shell-command-topdir command)))
       (message "Not a git repository")))
 
+  ;; difftastic code copied from https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html
+  (defun my/magit--with-difftastic (buffer command)
+    "Run COMMAND with GIT_EXTERNAL_DIFF=difft then show result in BUFFER."
+    (let ((process-environment
+           (cons (concat "GIT_EXTERNAL_DIFF=difft --width="
+                         (number-to-string (frame-width)))
+                 process-environment)))
+      ;; Clear the result buffer (we might regenerate a diff, e.g., for
+      ;; the current changes in our working directory).
+      (with-current-buffer buffer
+        (setq buffer-read-only nil)
+        (erase-buffer))
+      ;; Now spawn a process calling the git COMMAND.
+      (make-process
+       :name (buffer-name buffer)
+       :buffer buffer
+       :command command
+       ;; Don't query for running processes when emacs is quit.
+       :noquery t
+       ;; Show the result buffer once the process has finished.
+       :sentinel (lambda (proc event)
+                   (when (eq (process-status proc) 'exit)
+                     (with-current-buffer (process-buffer proc)
+                       (goto-char (point-min))
+                       (ansi-color-apply-on-region (point-min) (point-max))
+                       (setq buffer-read-only t)
+                       (view-mode)
+                       (end-of-line)
+                       ;; difftastic diffs are usually 2-column side-by-side,
+                       ;; so ensure our window is wide enough.
+                       (let ((width (current-column)))
+                         (while (zerop (forward-line 1))
+                           (end-of-line)
+                           (setq width (max (current-column) width)))
+                         ;; Add column size of fringes
+                         (setq width (+ width
+                                        (fringe-columns 'left)
+                                        (fringe-columns 'right)))
+                         (goto-char (point-min))
+                         (pop-to-buffer
+                          (current-buffer)
+                          `(;; If the buffer is that wide that splitting the frame in
+                            ;; two side-by-side windows would result in less than
+                            ;; 80 columns left, ensure it's shown at the bottom.
+                            ,(when (> 80 (- (frame-width) width))
+                               #'display-buffer-at-bottom)
+                            (window-width
+                             . ,(min width (frame-width))))))))))))
+
+  (defun my/magit-show-with-difftastic (rev)
+    "Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft."
+    (interactive
+     (list (or
+            ;; If REV is given, just use it.
+            (when (boundp 'rev) rev)
+            ;; If not invoked with prefix arg, try to guess the REV from
+            ;; point's position.
+            (and (not current-prefix-arg)
+                 (or (magit-thing-at-point 'git-revision t)
+                     (magit-branch-or-commit-at-point)))
+            ;; Otherwise, query the user.
+            (magit-read-branch-or-commit "Revision"))))
+    (if (not rev)
+        (error "No revision specified")
+      (my/magit--with-difftastic
+       (get-buffer-create (concat "*git show difftastic " rev "*"))
+       (list "git" "--no-pager" "show" "--ext-diff" rev))))
+
+  (defun my/magit-diff-with-difftastic (arg)
+    "Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft."
+    (interactive
+     (list (or
+            ;; If RANGE is given, just use it.
+            (when (boundp 'range) range)
+            ;; If prefix arg is given, query the user.
+            (and current-prefix-arg
+                 (magit-diff-read-range-or-commit "Range"))
+            ;; Otherwise, auto-guess based on position of point, e.g., based on
+            ;; if we are in the Staged or Unstaged section.
+            (pcase (magit-diff--dwim)
+              ('unmerged (error "unmerged is not yet implemented"))
+              ('unstaged nil)
+              ('staged "--cached")
+              (`(stash . ,value) (error "stash is not yet implemented"))
+              (`(commit . ,value) (format "%s^..%s" value value))
+              ((and range (pred stringp)) range)
+              (_ (magit-diff-read-range-or-commit "Range/Commit"))))))
+    (let ((name (concat "*git diff difftastic"
+                        (if arg (concat " " arg) "")
+                        "*")))
+      (my/magit--with-difftastic
+       (get-buffer-create name)
+       `("git" "--no-pager" "diff" "--ext-diff" ,@(when arg (list arg))))))
+
   ;; Based on https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html
   (transient-define-prefix my/magit-extra-commands ()
     "Extra magit commands."
     ["Extra commands"
      ("u" "Set upstream" my/magit-set-upstream)
      ("r" "Refresh state (update modeline)" my/magit-refresh-state)
-     ("m" "Update master/main" my/magit-update-master)])
+     ("m" "Update master/main" my/magit-update-master)
+     ("d" "Difftastic Diff (dwim)" my/magit-diff-with-difftastic)
+     ("s" "Difftastic Show" my/magit-show-with-difftastic)])
   (transient-append-suffix 'magit-dispatch "!"
     '("#" "Extra Magit Cmds" my/magit-extra-commands))
   (define-key magit-status-mode-map (kbd "#") #'my/magit-extra-commands)