From be39020c64e94c79f08adc8844f06f772343a2e8 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sun, 24 May 2020 00:13:17 +0100 Subject: [PATCH] feat(text-editing): Add reformatter package And override `reformatter-replace-buffer-contents-from-file` with a less intrusive variant based on how go-mode's `gofmt` command updates buffer with formatted content. --- modules/text-editing/siren-reformatter.el | 109 ++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 modules/text-editing/siren-reformatter.el diff --git a/modules/text-editing/siren-reformatter.el b/modules/text-editing/siren-reformatter.el new file mode 100644 index 0000000..4785b5e --- /dev/null +++ b/modules/text-editing/siren-reformatter.el @@ -0,0 +1,109 @@ +;;; siren-reformatter.el --- jimeh's Emacs Siren: reformatter-mode configuration. + +;;; Commentary: + +;; Basic configuration for reformatter-mode. + +;;; Code: + +(use-package reformatter + :defer t + + :config + (with-eval-after-load 'reformatter + ;; Provide a better replace buffer contents implementation based on how + ;; go-mode replaces buffer content after formatting code with gofmt. + (defun reformatter-replace-buffer-contents-from-file (file) + "Replace the accessible portion of the current buffer with +the contents of FILE." + (let* ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (patchbuf (get-buffer-create "*Reformatter Patch*"))) + + (unwind-protect + (save-restriction + (widen) + (with-current-buffer patchbuf (erase-buffer)) + (call-process-region (point-min) (point-max) + "diff" nil patchbuf nil "-n" "-" file) + (if (> (buffer-size patchbuf) 0) + (reformatter--apply-rcs-patch patchbuf))) + + (kill-buffer patchbuf))))) + + (defun reformatter--apply-rcs-patch (patch-buffer) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer." + (let ((target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0) + (column (current-column))) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "Invalid rcs patch or internal error in reformatter--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (cl-decf line-offset len) + (goto-char (point-min)) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (reformatter--goto-line (- from line-offset)) + (cl-incf line-offset len) + (reformatter--delete-whole-line len))) + (t + (error "Invalid rcs patch or internal error in reformatter--apply-rcs-patch"))))))) + (move-to-column column))) + + (defun reformatter--goto-line (line) + (goto-char (point-min)) + (forward-line (1- line))) + + (defun reformatter--delete-whole-line (&optional arg) + "Delete the current line without putting it in the `kill-ring'. +Derived from function `kill-whole-line'. ARG is defined as for that +function." + (setq arg (or arg 1)) + (if (and (> arg 0) + (eobp) + (save-excursion (forward-visible-line 0) (eobp))) + (signal 'end-of-buffer nil)) + (if (and (< arg 0) + (bobp) + (save-excursion (end-of-visible-line) (bobp))) + (signal 'beginning-of-buffer nil)) + (cond ((zerop arg) + (delete-region (progn (forward-visible-line 0) (point)) + (progn (end-of-visible-line) (point)))) + ((< arg 0) + (delete-region (progn (end-of-visible-line) (point)) + (progn (forward-visible-line (1+ arg)) + (unless (bobp) + (backward-char)) + (point)))) + (t + (delete-region (progn (forward-visible-line 0) (point)) + (progn (forward-visible-line arg) (point))))))) + +(provide 'siren-reformatter) +;;; siren-reformatter.el ends here