|
| 1 | +(defun visual-format--cleanup (start end) |
| 2 | + (while (and (< start end) |
| 3 | + (setq start (text-property-any start end 'display visual-format--space))) |
| 4 | + (remove-text-properties |
| 5 | + start |
| 6 | + (setq start (or (text-property-not-all start end 'display visual-format--space) |
| 7 | + end)) |
| 8 | + '(display nil)))) |
| 9 | + |
| 10 | +(require 'treesit) |
| 11 | + |
| 12 | +(defvar-local visual-format--buffer nil) |
| 13 | +(defvar-local visual-format--buffer-formatted nil) |
| 14 | + |
| 15 | +(defmacro visual-format--with-buff (&rest body) |
| 16 | + `(with-current-buffer visual-format--buffer |
| 17 | + ,@body)) |
| 18 | + |
| 19 | +(defmacro visual-format--with-buff-fmt (&rest body) |
| 20 | + `(with-current-buffer visual-format--buffer-formatted |
| 21 | + ,@body)) |
| 22 | + |
| 23 | +(defun visual-format--depth-first-walk (&optional node node-fmt prev-node prev-node-fmt depth) |
| 24 | + (let ((node (or node (visual-format--with-buff (treesit-buffer-root-node)))) |
| 25 | + (node-fmt (or node-fmt (visual-format--with-buff-fmt (treesit-buffer-root-node)))) |
| 26 | + (prev-leaf prev-node) |
| 27 | + (prev-leaf-fmt prev-node-fmt) |
| 28 | + (depth (or depth 0))) |
| 29 | + (cl-assert (= (treesit-node-child-count node) (treesit-node-child-count node-fmt))) |
| 30 | + (dotimes (i (treesit-node-child-count node)) |
| 31 | + (let* ((n (treesit-node-child node i)) |
| 32 | + (n-fmt (treesit-node-child node-fmt i))) |
| 33 | + (if (zerop (treesit-node-child-count n)) ; leaf |
| 34 | + (let* ((pos-beg |
| 35 | + (visual-format--with-buff |
| 36 | + (or (and prev-leaf (max 1 (1- (treesit-node-end prev-leaf)))) |
| 37 | + (point-min)))) |
| 38 | + (pos-end (treesit-node-start n)) |
| 39 | + (pos-beg-fmt |
| 40 | + (visual-format--with-buff-fmt |
| 41 | + (or (and prev-leaf-fmt (max 1 (1- (treesit-node-end prev-leaf-fmt)))) |
| 42 | + (point-min)))) |
| 43 | + (pos-end-fmt (treesit-node-start n-fmt)) |
| 44 | + (fmt-spaces (visual-format--with-buff-fmt (buffer-substring pos-beg-fmt pos-end-fmt)))) |
| 45 | + (visual-format--with-buff |
| 46 | + (message "Token at (%d):\t %s \t\t\t\t Prev: %s" |
| 47 | + depth |
| 48 | + (treesit-node-text n t) |
| 49 | + (treesit-node-text prev-leaf t)) |
| 50 | + (unless (string= (buffer-substring pos-beg pos-end) fmt-spaces) |
| 51 | + (put-text-property pos-beg pos-end 'display fmt-spaces))) |
| 52 | + (setq prev-leaf n |
| 53 | + prev-leaf-fmt n-fmt)) |
| 54 | + (let ((last-nodes (visual-format--depth-first-walk n n-fmt prev-leaf prev-leaf-fmt (1+ depth)))) |
| 55 | + (setq prev-leaf (car last-nodes) |
| 56 | + prev-leaf-fmt (cdr last-nodes)))))) |
| 57 | + (cons prev-leaf prev-leaf-fmt))) |
| 58 | + |
| 59 | +(defvar visual-format-function #'apheleia-format-buffer) |
| 60 | + |
| 61 | +(defun visual-format-buffer () |
| 62 | + "Visually format the buffer without modifing it." |
| 63 | + (interactive) |
| 64 | + (visual-format--cleanup (point-min) (point-max)) |
| 65 | + (let ((visual-format--buffer (current-buffer)) |
| 66 | + (visual-format--buffer-formatted (get-buffer-create (format " *visual-format: %s*" (buffer-name)))) |
| 67 | + (buf-str (buffer-string)) |
| 68 | + (buf-mode major-mode)) |
| 69 | + (visual-format--with-buff-fmt |
| 70 | + (delay-mode-hooks (funcall buf-mode)) |
| 71 | + (delete-region (point-min) (point-max)) |
| 72 | + (insert buf-str) |
| 73 | + (call-interactively visual-format-function)) |
| 74 | + (with-silent-modifications |
| 75 | + (visual-format--depth-first-walk)))) |
| 76 | + |
| 77 | +;;;###autoload |
| 78 | +(define-minor-mode visual-format-mode |
| 79 | + "Format code without modifying the buffer." |
| 80 | + :lighter " VFmt" |
| 81 | + :global nil |
| 82 | + (if visual-format-mode |
| 83 | + (visual-format-buffer) |
| 84 | + (visual-format--cleanup (point-min) (point-max)))) |
0 commit comments