cider-format.el (5285B)
1 ;;; cider-format.el --- Code and EDN formatting functionality -*- lexical-binding: t -*- 2 3 ;; Copyright © 2013-2023 Bozhidar Batsov, Artur Malabarba and CIDER contributors 4 ;; 5 ;; Author: Bozhidar Batsov <bozhidar@batsov.dev> 6 ;; Artur Malabarba <bruce.connor.am@gmail.com> 7 8 ;; This program is free software: you can redistribute it and/or modify 9 ;; it under the terms of the GNU General Public License as published by 10 ;; the Free Software Foundation, either version 3 of the License, or 11 ;; (at your option) any later version. 12 13 ;; This program is distributed in the hope that it will be useful, 14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 ;; GNU General Public License for more details. 17 18 ;; You should have received a copy of the GNU General Public License 19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 20 21 ;; This file is not part of GNU Emacs. 22 23 ;;; Commentary: 24 25 ;; Middleware-powered code and EDN formatting functionality. 26 27 ;;; Code: 28 29 (require 'map) 30 (require 'seq) 31 (require 'subr-x) 32 33 (require 'cider-client) 34 (require 'cider-util) 35 36 37 ;; Format 38 39 (defun cider--format-reindent (formatted start) 40 "Reindent FORMATTED to align with buffer position START." 41 (let* ((start-column (save-excursion (goto-char start) (current-column))) 42 (indent-line (concat "\n" (make-string start-column ? )))) 43 (replace-regexp-in-string "\n" indent-line formatted))) 44 45 46 ;;; Format region 47 48 (defun cider--format-region (start end formatter) 49 "Format the contents of the given region. 50 51 START and END represent the region's boundaries. 52 53 FORMATTER is a function of one argument which is used to convert 54 the string contents of the region into a formatted string. 55 56 Uses the following heuristic to try to maintain point position: 57 58 - Take a snippet of text starting at current position, up to 64 chars. 59 - Search for the snippet, with lax whitespace, in the formatted text. 60 - If snippet is less than 64 chars (point was near end of buffer), search 61 from end instead of beginning. 62 - Place point at match beginning, or `point-min' if no match." 63 (let* ((original (buffer-substring-no-properties start end)) 64 (formatted (funcall formatter original)) 65 (indented (cider--format-reindent formatted start))) 66 (unless (equal original indented) 67 (let* ((pos (point)) 68 (pos-max (1+ (buffer-size))) 69 (l 64) 70 (endp (> (+ pos l) pos-max)) 71 (snippet (thread-last 72 (buffer-substring-no-properties 73 pos (min (+ pos l) pos-max)) 74 (regexp-quote) 75 (replace-regexp-in-string "[[:space:]\t\n\r]+" "[[:space:]\t\n\r]*")))) 76 (delete-region start end) 77 (insert indented) 78 (goto-char (if endp (point-max) (point-min))) 79 (funcall (if endp #'re-search-backward #'re-search-forward) snippet nil t) 80 (goto-char (or (match-beginning 0) start)) 81 (when (looking-at-p "\n") (forward-char)))))) 82 83 ;;;###autoload 84 (defun cider-format-region (start end) 85 "Format the Clojure code in the current region. 86 START and END represent the region's boundaries." 87 (interactive "r") 88 (cider-ensure-connected) 89 (cider--format-region start end 90 (lambda (buf) 91 (cider-sync-request:format-code buf cider-format-code-options)))) 92 93 94 ;;; Format defun 95 96 ;;;###autoload 97 (defun cider-format-defun () 98 "Format the code in the current defun." 99 (interactive) 100 (cider-ensure-connected) 101 (let ((defun-bounds (cider-defun-at-point 't))) 102 (cider-format-region (car defun-bounds) (cadr defun-bounds)))) 103 104 105 ;;; Format buffer 106 107 (defun cider--format-buffer (formatter) 108 "Format the contents of the current buffer. 109 110 Uses FORMATTER, a function of one argument, to convert the string contents 111 of the buffer into a formatted string." 112 (cider--format-region 1 (1+ (buffer-size)) formatter)) 113 114 ;;;###autoload 115 (defun cider-format-buffer () 116 "Format the Clojure code in the current buffer." 117 (interactive) 118 (check-parens) 119 (cider-ensure-connected) 120 (cider--format-buffer (lambda (buf) 121 (cider-sync-request:format-code buf cider-format-code-options)))) 122 123 124 ;;; Format EDN 125 126 ;;;###autoload 127 (defun cider-format-edn-buffer () 128 "Format the EDN data in the current buffer." 129 (interactive) 130 (check-parens) 131 (cider-ensure-connected) 132 (cider--format-buffer (lambda (edn) 133 (cider-sync-request:format-edn edn fill-column)))) 134 135 ;;;###autoload 136 (defun cider-format-edn-region (start end) 137 "Format the EDN data in the current region. 138 START and END represent the region's boundaries." 139 (interactive "r") 140 (cider-ensure-connected) 141 (let* ((start-column (save-excursion (goto-char start) (current-column))) 142 (right-margin (- fill-column start-column))) 143 (cider--format-region start end 144 (lambda (edn) 145 (cider-sync-request:format-edn edn right-margin))))) 146 147 ;;;###autoload 148 (defun cider-format-edn-last-sexp () 149 "Format the EDN data of the last sexp." 150 (interactive) 151 (apply #'cider-format-edn-region (cider-sexp-at-point 'bounds))) 152 153 (provide 'cider-format) 154 ;;; cider-format.el ends here