|
|
- ;;; cider-format.el --- Code and EDN formatting functionality -*- lexical-binding: t -*-
-
- ;; Copyright © 2013-2019 Bozhidar Batsov, Artur Malabarba and CIDER contributors
- ;;
- ;; Author: Bozhidar Batsov <bozhidar@batsov.com>
- ;; Artur Malabarba <bruce.connor.am@gmail.com>
-
- ;; 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
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
-
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
-
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
- ;; This file is not part of GNU Emacs.
-
- ;;; Commentary:
-
- ;; Middleware-powered code and EDN formatting functionality.
-
- ;;; Code:
-
- (require 'map)
- (require 'seq)
- (require 'subr-x)
-
- (require 'cider-client)
- (require 'cider-util)
-
- ;; Format
-
- (defun cider--format-reindent (formatted start)
- "Reindent FORMATTED to align with buffer position START."
- (let* ((start-column (save-excursion (goto-char start) (current-column)))
- (indent-line (concat "\n" (make-string start-column ? ))))
- (replace-regexp-in-string "\n" indent-line formatted)))
-
- ;;; Format region
-
- (defun cider--format-region (start end formatter)
- "Format the contents of the given region.
-
- START and END represent the region's boundaries.
-
- FORMATTER is a function of one argument which is used to convert
- the string contents of the region into a formatted string.
-
- Uses the following heuristic to try to maintain point position:
-
- - Take a snippet of text starting at current position, up to 64 chars.
- - Search for the snippet, with lax whitespace, in the formatted text.
- - If snippet is less than 64 chars (point was near end of buffer), search
- from end instead of beginning.
- - Place point at match beginning, or `point-min' if no match."
- (let* ((original (buffer-substring-no-properties start end))
- (formatted (funcall formatter original))
- (indented (cider--format-reindent formatted start)))
- (unless (equal original indented)
- (let* ((pos (point))
- (pos-max (1+ (buffer-size)))
- (l 64)
- (endp (> (+ pos l) pos-max))
- (snippet (thread-last (buffer-substring-no-properties
- pos (min (+ pos l) pos-max))
- (regexp-quote)
- (replace-regexp-in-string "[[:space:]\t\n\r]+" "[[:space:]\t\n\r]*"))))
- (delete-region start end)
- (insert indented)
- (goto-char (if endp (point-max) (point-min)))
- (funcall (if endp #'re-search-backward #'re-search-forward) snippet nil t)
- (goto-char (or (match-beginning 0) start))
- (when (looking-at-p "\n") (forward-char))))))
-
- ;;;###autoload
- (defun cider-format-region (start end)
- "Format the Clojure code in the current region.
- START and END represent the region's boundaries."
- (interactive "r")
- (cider-ensure-connected)
- (cider--format-region start end #'cider-sync-request:format-code))
-
- ;;; Format defun
-
- ;;;###autoload
- (defun cider-format-defun ()
- "Format the code in the current defun."
- (interactive)
- (cider-ensure-connected)
- (let ((defun-bounds (cider-defun-at-point 't)))
- (cider-format-region (car defun-bounds) (cadr defun-bounds))))
-
- ;;; Format buffer
-
- (defun cider--format-buffer (formatter)
- "Format the contents of the current buffer.
-
- Uses FORMATTER, a function of one argument, to convert the string contents
- of the buffer into a formatted string."
- (cider--format-region 1 (1+ (buffer-size)) formatter))
-
- ;;;###autoload
- (defun cider-format-buffer ()
- "Format the Clojure code in the current buffer."
- (interactive)
- (check-parens)
- (cider-ensure-connected)
- (cider--format-buffer #'cider-sync-request:format-code))
-
- ;;; Format EDN
-
- ;;;###autoload
- (defun cider-format-edn-buffer ()
- "Format the EDN data in the current buffer."
- (interactive)
- (check-parens)
- (cider-ensure-connected)
- (cider--format-buffer (lambda (edn)
- (cider-sync-request:format-edn edn fill-column))))
-
- ;;;###autoload
- (defun cider-format-edn-region (start end)
- "Format the EDN data in the current region.
- START and END represent the region's boundaries."
- (interactive "r")
- (cider-ensure-connected)
- (let* ((start-column (save-excursion (goto-char start) (current-column)))
- (right-margin (- fill-column start-column)))
- (cider--format-region start end
- (lambda (edn)
- (cider-sync-request:format-edn edn right-margin)))))
-
- ;;;###autoload
- (defun cider-format-edn-last-sexp ()
- "Format the EDN data of the last sexp."
- (interactive)
- (apply 'cider-format-edn-region (cider-sexp-at-point 'bounds)))
-
- (provide 'cider-format)
- ;;; cider-format.el ends here
|