branch: elpa/eldoc-diffstat commit a15690a8a79ca05d64aefb263b396844a2c0fb60 Author: Johann Klähn <joh...@jklaehn.de> Commit: Johann Klähn <joh...@jklaehn.de>
Minor refactoring --- eldoc-diffstat.el | 155 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 62 deletions(-) diff --git a/eldoc-diffstat.el b/eldoc-diffstat.el index 0041a51aae..83b90b5c76 100644 --- a/eldoc-diffstat.el +++ b/eldoc-diffstat.el @@ -4,6 +4,8 @@ ;; Author: Johann Klähn <joh...@jklaehn.de> ;; Keywords: vc, docs +;; Version: 0.1 +;; Package-Requires: ((emacs "29.1")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -20,66 +22,89 @@ ;;; Commentary: -;; Adapted from https://www.tsdh.org/posts/2022-07-20-using-eldoc-with-magit-async.html +;; This package provides a way to display VCS diffstat information via eldoc. +;; It supports Git and Mercurial repositories. +;; +;; To use, call `eldoc-diffstat-setup' in the desired buffer or mode hook. +;; You might also want to add the following to your config: +;; +;; (eldoc-add-command +;; 'magit-next-line 'magit-previous-line +;; 'magit-section-forward 'magit-section-backward +;; 'magit-section-forward-sibling 'magit-section-backward-sibling) +;; +;; Adapted from Tassilo Horn's blog post: +;; https://www.tsdh.org/posts/2022-07-20-using-eldoc-with-magit-async.html ;;; Code: (require 'ansi-color) -(defvar eldoc-diffstat--process nil) +(defvar eldoc-diffstat--process nil + "The latest async process used for fetching diffstat information. +Only one active process at a time; new requests terminate previous ones. +After completion, a cached version of the diffstat output is attached as +a property to the process object.") + (defconst eldoc-diffstat--commands '((Git "git" "--no-pager" "show" "--color=always" "--format=format:%an <%ae>, %aD:%n%s" "--stat=80") (Hg "hg" "--pager=never" "log" "--color=always" "--template" "{author}, {date|rfc822date}:\n{desc|firstline}\n" - "--stat" "--rev"))) + "--stat" "--rev")) + "Alist mapping VCS backend to the command to use for computing the diffstat.") ;;;###autoload (defun eldoc-diffstat-setup () "Configure eldoc buffer-locally to display diffstat for revision at point." (interactive) - (unless (bound-and-true-p eldoc-mode) - (eldoc-mode)) (add-hook 'eldoc-documentation-functions - #'eldoc-diffstat--docstring nil 'local)) + #'eldoc-diffstat--docstring nil 'local) + (unless (bound-and-true-p eldoc-mode) + (eldoc-mode))) (defun eldoc-diffstat--docstring (callback &rest _ignored) "Display diffstat for revision at point by calling CALLBACK. Intended for `eldoc-documentation-functions'." - (when-let* ((info (or (when-let (((fboundp 'magit-commit-at-point)) - (revision (magit-commit-at-point))) - (cons 'Git revision)) - (and (derived-mode-p 'vc-annotate-mode) - (boundp 'vc-annotate-backend) - (fboundp 'vc-annotate-extract-revision-at-line) - (cons vc-annotate-backend - (car (vc-annotate-extract-revision-at-line)))) - (and (derived-mode-p 'log-view-mode) - (boundp 'log-view-vc-backend) - (cons log-view-vc-backend - (log-view-current-tag))))) + (when-let* ((info (eldoc-diffstat--get-revision-info)) (backend (car info)) (revision (cdr info)) (command (alist-get backend eldoc-diffstat--commands))) - (if-let ((result (eldoc--diffstat--get-cache info))) + (if-let ((result (eldoc-diffstat--get-cache info))) (funcall callback result) (eldoc-diffstat--docstring-1 (append command (list revision)) callback info)))) -(defun eldoc--diffstat--get-cache (cache-tag) - "Retrieve cached diffstat result for CACHE-TAG if available. -CACHE-TAG is a cons cell of the form (BACKEND . REVISION) where BACKEND is -a symbol representing the version control system and REVISION is a string -identifying the specific revision. Returns the cached result if available, -nil otherwise." - (when-let* (((processp eldoc-diffstat--process)) - (cached-result (process-get eldoc-diffstat--process :cached-result)) - ((equal cache-tag (car cached-result)))) +(defun eldoc-diffstat--get-revision-info () + "Get revision info for the current buffer context. +The returned record should be a cons cell of the form (BACKEND . REVISION) where +BACKEND is a symbol representing the version control system and REVISION is +a string identifying the specific revision." + (cond + ((when-let (((fboundp 'magit-commit-at-point)) + (revision (magit-commit-at-point))) + (cons 'Git revision))) + ((and (derived-mode-p 'vc-annotate-mode) + (boundp 'vc-annotate-backend) + (fboundp 'vc-annotate-extract-revision-at-line)) + (cons vc-annotate-backend + (car (vc-annotate-extract-revision-at-line)))) + ((and (derived-mode-p 'log-view-mode) + (boundp 'log-view-vc-backend)) + (cons log-view-vc-backend + (log-view-current-tag))))) + +(defun eldoc-diffstat--get-cache (revision-info) + "Retrieve cached diffstat result for REVISION-INFO if available." + (when-let* ((proc eldoc-diffstat--process) + ((processp proc)) + (cached-result (process-get proc :cached-result)) + ((equal revision-info (car cached-result)))) (cdr cached-result))) -(defun eldoc-diffstat--docstring-1 (command callback cache-tag) +(defun eldoc-diffstat--docstring-1 (command callback revision-info) "Asynchronously compute diffstat using COMMAND and pass it to CALLBACK. This function sets up a new asynchronous process to compute the diffstat, -killing any existing process. CACHE-TAG is a unique identifier used for +killing any existing process. REVISION-INFO is a unique identifier used for caching the result, see `eldoc-diffstat--get-cache' for details." ;; Clean up old process and its buffer. (when (processp eldoc-diffstat--process) @@ -96,48 +121,54 @@ caching the result, see `eldoc-diffstat--get-cache' for details." :noquery t :command command :sentinel - (apply-partially #'eldoc-diffstat--sentinel callback))) - (process-put eldoc-diffstat--process :cache-tag cache-tag) + (lambda (&rest args) + (apply #'eldoc-diffstat--sentinel callback args)))) + (process-put eldoc-diffstat--process :revision-info revision-info) ;; Signal that the doc string is computed asynchronously. t) -(defun eldoc-diffstat--sentinel (callback proc event) +(defun eldoc-diffstat--sentinel (callback proc _event) "Display output of PROC by calling CALLBACK if EVENT indicates success." (when (eq (process-status proc) 'exit) (with-current-buffer (process-buffer proc) - (let ((ansi-color-apply-face-function - (lambda (beg end face) - (put-text-property beg end 'face face)))) - (ansi-color-apply-on-region (point-min) (point-max))) - - ;; Delete trailing blank lines. - (goto-char (point-max)) - (delete-blank-lines) - - ;; Make first line bold. - (goto-char (point-min)) - (put-text-property (point) - (line-end-position) - 'face 'bold) - - ;; Join second line. - (forward-line) - (join-line) - - ;; Move summary to the top and make it italic. - (forward-line) - (reverse-region (point) (point-max)) - (put-text-property (point) - (line-end-position) - 'face 'italic) - (forward-line) - (reverse-region (point) (point-max)) + (eldoc-diffstat--format-output-buffer) (let ((result (buffer-string)) - (cache-tag (process-get eldoc-diffstat--process :cache-tag))) + (revision-info (process-get eldoc-diffstat--process :revision-info))) (process-put eldoc-diffstat--process :cached-result - (cons cache-tag result)) + (cons revision-info result)) (funcall callback result))))) +(defun eldoc-diffstat--format-output-buffer () + "Format the diffstat output." + ;; Translate color control sequences into text properties. + (let ((ansi-color-apply-face-function + (lambda (beg end face) + (put-text-property beg end 'face face)))) + (ansi-color-apply-on-region (point-min) (point-max))) + + ;; Delete trailing blank lines. + (goto-char (point-max)) + (delete-blank-lines) + + ;; Make first line bold. + (goto-char (point-min)) + (put-text-property (point) + (line-end-position) + 'face 'bold) + + ;; Join second line. + (forward-line) + (join-line) + + ;; Move summary to the top and make it italic. + (forward-line) + (reverse-region (point) (point-max)) + (put-text-property (point) + (line-end-position) + 'face 'italic) + (forward-line) + (reverse-region (point) (point-max))) + (provide 'eldoc-diffstat) ;;; eldoc-diffstat.el ends here