pkryger / difftastic.el

Wrapper for difftastic
GNU General Public License v3.0
45 stars 4 forks source link

+STARTUP: showeverything

+STARTUP: literallinks

+OPTIONS: toc:nil num:nil author:nil

** Description :PROPERTIES: :CUSTOM_ID: description :END: The =difftastic= Emacs package is designed to integrate [[https://github.com/wilfred/difftastic][difftastic]] - a structural diff tool - into your Emacs workflow, enhancing your code review and comparison experience. This package automatically displays =difftastic='s output within Emacs using faces from your user theme, ensuring consistency with your overall coding environment.

** Features :PROPERTIES: :CUSTOM_ID: features :END:

** Installation :PROPERTIES: :CUSTOM_ID: installation :END: *** Installing from MELPA :PROPERTIES: :CUSTOM_ID: installing-from-melpa :END: The easiest way to install and keep =difftastic= up-to-date is using Emacs' built-in package manager. =difftastic= is available in the MELPA repository. Refer to https://melpa.org/#/getting-started for how to install a package from MELPA.

*** Installing from GitHub :PROPERTIES: :CUSTOM_ID: installing-from-github :END:

  1. Clone this repository to a directory of your choice.
  2. Add the following lines to your Emacs configuration file (usually =~/.emacs= or =~/.emacs.d/init.el=):

+begin_src emacs-lisp

(add-to-list 'load-path "/path/to/difftastic.el") (require 'difftastic)

+end_src

** Configuration :PROPERTIES: :CUSTOM_ID: configuration :END: To configure =difftastic= commands in =magit-diff= prefix, use the following code snippet in your Emacs configuration:

+begin_src emacs-lisp

(require 'difftastic)

;; Add commands to a `magit-difftastic' (eval-after-load 'magit-diff '(transient-append-suffix 'magit-diff '(-1 -1) [("D" "Difftastic diff (dwim)" difftastic-magit-diff) ("S" "Difftastic show" difftastic-magit-show)])) (add-hook 'magit-blame-read-only-mode-hook (lambda () (keymap-set magit-blame-read-only-mode-map "D" #'difftastic-magit-show) (keymap-set magit-blame-read-only-mode-map "S" #'difftastic-magit-show)))

+end_src

Or, if you use =use-package=:

+begin_src emacs-lisp

(use-package difftastic :demand t :bind (:map magit-blame-read-only-mode-map ("D" . difftastic-magit-show) ("S" . difftastic-magit-show)) :config (eval-after-load 'magit-diff '(transient-append-suffix 'magit-diff '(-1 -1) [("D" "Difftastic diff (dwim)" difftastic-magit-diff) ("S" "Difftastic show" difftastic-magit-show)])))

+end_src

** Usage :PROPERTIES: :CUSTOM_ID: usage :END: The following commands are meant to help to interact with =difftastic=. Commands are followed by their default keybindings in =difftastic-mode= (in parenthesis).

** Customization :PROPERTIES: :CUSTOM_ID: customization :END: *** Face Customization :PROPERTIES: :CUSTOM_ID: face-customization :END: You can customize the appearance of =difftastic= output by adjusting the faces used for highlighting. To customize a faces, use the following code snippet in your configuration:

+begin_src emacs-lisp

;; Customize faces used to display difftastic output. (setq difftastic-normal-colors-vector (vector ;; use black face from ansi-color' (aref ansi-color-normal-colors-vector 0) ;; use face for removed marker fromdifftastic' (aref difftastic-normal-colors-vector 1) ;; use face for added marker from difftastic' (aref difftastic-normal-colors-vector 2) 'my-section-face 'my-comment-face 'my-string-face 'my-warning-face ;; use white face fromansi-color' (aref ansi-color-normal-colors-vector 7)))

;; Customize highlight faces (setq difftastic-highlight-alist `((,(aref difftastic-normal-colors-vector 2) . my-added-highlight) (,(aref difftastic-normal-colors-vector 1) . my-removed-highlight)))

;; Disable highlight faces (use difftastic's default) (setq difftastic-highlight-alist nil)

+end_src

*** Window management :PROPERTIES: :CUSTOM_ID: window-management :END: The =difftastic= relies on the =difft= command line tool to produce an output that can be displayed in an Emacs buffer window. In short: it runs the =difft=, converts ANSI codes into user defined colors and displays it in window. The =difft= can be instructed with a hint to help it produce a content that can fit into user output, by specifying a requested width. However, the latter is not always respected.

The =difftastic= provides a few variables to let you customize these aspects of interaction with =difft=:

** Contributing :PROPERTIES: :CUSTOM_ID: contributing :END: Contributions are welcome! Feel free to submit issues and pull requests on the [[https://github.com/pkryger/difftastic.el][GitHub repository]].

*** Testing :PROPERTIES: :CUSTOM_ID: testing :END: When creating a pull request make sure all tests in [[file:test/difftastic.t.el]] are passing. When adding a new functionality, please strive to add tests for it as well.

To run tests:

** README.org and Commentary authoring and exporting :noexport: The [[file:README.org]] file is a source of =Commentary= section in the [[file:difftastic.el]]. That is:

One time setup:

+name: export-commentary-setup

+begin_src emacs-lisp :results none

(defun difftastic-org-export-commentary-remove-top-level (backend) "Remove top level headline from export. BACKEND is the export back-end being used, as a symbol." (org-map-entries (lambda () (when (and (eq backend 'difftastic-commentary) (looking-at "^* ")) (delete-region (point) (save-excursion (outline-next-heading) (point))) (setq org-map-continue-from (point))))))

(add-to-list 'org-export-before-parsing-functions

'difftastic-org-export-commentary-remove-top-level)

(defun difftastic-org-export-commentary-src-block (src-block _contents info) "Transcode a SRC-BLOCK element from Org to Commentary. CONTENTS is nil. INFO is a plist used as a communication channel." (org-element-normalize-string (org-export-format-code-default src-block info)))

(defun difftastic-org-export-commentary-final-output (contents _backend _info) "Transcode CONTENTS element from Org to Commentary." (replace-regexp-in-string "^;;\'" "" (replace-regexp-in-string "^;; $" ";;" (replace-regexp-in-string "^" ";; " contents))))

(org-export-define-derived-backend 'difftastic-commentary 'ascii :translate-alist '((src-block . difftastic-org-export-commentary-src-block)) :filters-alist '((:filter-final-output . difftastic-org-export-commentary-final-output)))

(defmacro with-difftastic-org-export-commentary-defaults (body) "Execute BODY with difftastic org export commentary defaults." `(let ((org-ascii-text-width 75) (org-ascii-global-margin 0) (org-ascii-inner-margin 0)) ,body))

+end_src

To quickly validate generated Commentary content - which may be usefull for developing exporting mechanism - you can use the following snippet:

+begin_src emacs-lisp :results none

(with-difftastic-org-export-commentary-defaults (org-export-to-buffer 'difftastic-commentary "Org DIFFTASTIC-COMMENTARY Export" nil nil nil nil nil #'emacs-lisp-mode))

+end_src

To generate the Commentary section and save it to [[file:difftastic.el]] file, you can use the following snippet:

+begin_src emacs-lisp :results none

(with-difftastic-org-export-commentary-defaults (let ((org-export-show-temporary-export-buffer nil) (export-buffer "Org DIFFTASTIC-COMMENTARY Export")) (org-export-to-buffer 'difftastic-commentary export-buffer) (with-current-buffer (find-file-noselect "difftastic.el") (goto-char (point-min)) (let ((start (progn (re-search-forward "^;;; Commentary:$") (beginning-of-line 3) (point))) (end (progn (re-search-forward "^;;; Code:$") (end-of-line 0) (point)))) (delete-region start end)) (insert (with-current-buffer export-buffer (buffer-string))) (save-buffer))))

+end_src

Note that =emacs-lisp-checkdoc= doesn't run in =org-mode= buffer, so the generated content may have issues that are not highlighted while authoring. Please open the [[file:difftastic.el]] and check it for any new issues.

** Acknowledgments :noexport: :PROPERTIES: :CUSTOM_ID: acknowledgments :END: This package was inspired by the need for an integration of =difftastic= within Emacs, enhancing the code review process for developers.

This work is based on Tassilo Horn's [[https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html][blog entry]].

=magit-diff= keybindings and a concept of updating faces comes from a Shiv Jha-Mathur's [[https://shivjm.blog/better-magit-diffs/][blog entry]].

This all has been strongly influenced by - a class in itself - [[https://github.com/magit/magit][Magit]] and [[https://github.com/magit/transient][Transient]] Emacs packages by Jonas Bernoulli.

** Similar packages :noexport: :PROPERTIES: :CUSTOM_ID: similar-packages :END: *** Diff ANSI :PROPERTIES: :CUSTOM_ID: diff-ansi :END: There's a [[https://codeberg.org/ideasman42/emacs-diff-ansi][diff-ansi]] package available. I haven't spent much time on it, but at a first glance it doesn't seem that it supports =difftastic= out of box. Perhaps it is possible to configure it to support =difftastic= as a custom tool.

** License :noexport: :PROPERTIES: :CUSTOM_ID: license :END: This package is licensed under the [[https://www.gnu.org/licenses/gpl-3.0.en.html][GPLv3 License]].


Happy coding! If you encounter any issues or have suggestions for improvements, please don't hesitate to reach out on the [[https://github.com/pkryger/difftastic.el][GitHub repository]]. Your feedback is highly appreciated.

LocalWords: MELPA DWIM