dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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