doomemacs/modules/editor/format/autoload/format.el

156 lines
5.9 KiB
EmacsLisp
Raw Normal View History

;;; editor/format/autoload.el -*- lexical-binding: t; -*-
(defun +format--current-indentation ()
(save-excursion
(goto-char (point-min))
(skip-chars-forward " \t\n")
(current-indentation)))
(defun +format-region (start end &optional callback)
"Format from START to END with `apheleia'."
(when-let* ((command (apheleia--get-formatters
(if current-prefix-arg
'prompt
'interactive)))
(cur-buffer (current-buffer))
(formatted-buffer (get-buffer-create " *apheleia-formatted*"))
(indent 0))
(with-current-buffer formatted-buffer
(erase-buffer)
(unless (featurep :system 'windows)
(setq-local coding-system-for-read 'utf-8)
(setq-local coding-system-for-write 'utf-8))
;; Ensure this temp buffer seems as much like the origin buffer as
;; possible, in case the formatter is an elisp function, like `gofmt'.
(cl-loop for (var . val)
in (cl-remove-if-not #'listp (buffer-local-variables cur-buffer))
;; Making enable-multibyte-characters buffer-local causes an
;; error.
unless (eq var 'enable-multibyte-characters)
;; Using setq-local would quote var.
do (set (make-local-variable var) val))
;;
(insert-buffer-substring-no-properties cur-buffer start end)
;; Since we're piping a region of text to the formatter, remove any
;; leading indentation to make it look like a file.
(setq indent (+format--current-indentation))
(when (> indent 0)
(indent-rigidly (point-min) (point-max) (- indent)))
;;
(apheleia-format-buffer
command
(lambda ()
(with-current-buffer formatted-buffer
(when (> indent 0)
;; restore indentation without affecting new
;; indentation
(indent-rigidly (point-min) (point-max)
(max 0 (- indent (+format--current-indentation)))))
(set-buffer-modified-p nil))
(with-current-buffer cur-buffer
(delete-region start end)
(insert-buffer-substring-no-properties formatted-buffer)
(when callback (funcall callback))
(kill-buffer formatted-buffer)))))))
;;
2020-03-10 01:48:20 -04:00
;;; Commands
;;;###autoload
(defun +format/buffer (&optional arg)
"Reformat the current buffer using LSP or `format-all-buffer'."
(interactive "P")
(or (run-hook-with-args-until-success '+format-functions (point-min) (point-max) 'buffer)
(call-interactively #'apheleia-format-buffer)))
;;;###autoload
(defun +format/region (beg end &optional arg)
"Runs the active formatter on the lines within BEG and END.
WARNING: this may not work everywhere. It will throw errors if the region
contains a syntax error in isolation. It is mostly useful for formatting
snippets or single lines."
(interactive "rP")
(or (run-hook-with-args-until-success '+format-functions beg end 'region)
(+format-region beg end)))
;;;###autoload
(defun +format/region-or-buffer ()
"Runs the active formatter on the selected region (or whole buffer, if nothing
is selected)."
(interactive)
(call-interactively
(if (doom-region-active-p)
#'+format/region
#'+format/buffer)))
;;
;;; Specialized formatters
;;;###autoload
(defun +format-with-lsp-fn (beg end op)
"Format the region/buffer using any available lsp-mode formatter.
Does nothing if `+format-with-lsp' is nil or the active server doesn't support
the requested feature."
(when (and +format-with-lsp
(bound-and-true-p lsp-mode)
(lsp-feature?
(if (eq op 'buffer)
"textDocument/formatting"
"textDocument/rangeFormatting")))
(call-interactively
(if (eq op 'buffer)
#'lsp-format-buffer
#'lsp-format-region))
t))
;;;###autoload
(defun +format-with-eglot-fn (beg end op)
"Format the region/buffer using any available eglot formatter.
Does nothing if `+format-with-lsp' is nil or the active server doesn't support
the requested feature."
(when (and +format-with-lsp
(bound-and-true-p eglot-managed-mode)
(eglot--server-capable
(if (eq op 'buffer)
:documentFormattingProvider
:documentRangeFormattingProvider)))
(if (eq op 'buffer)
(eglot-format-buffer)
(eglot-format beg end))
t))
;;;###autoload
(defun +format-in-org-src-blocks-fn (beg end _op)
"TODO"
(when (derived-mode-p 'org-mode)
(goto-char beg)
(while (re-search-forward org-babel-src-block-regexp end t)
(let* ((element (org-element-at-point))
(block-beg (save-excursion
(goto-char (org-babel-where-is-src-block-head element))
(line-beginning-position 2)))
(block-end (save-excursion
(goto-char (org-element-property :end element))
(skip-chars-backward " \t\n")
(line-beginning-position)))
(beg (if beg (max beg block-beg) block-beg))
(end (if end (min end block-end) block-end))
(lang (org-element-property :language element))
(major-mode (org-src-get-lang-mode lang)))
(save-excursion
(if (eq major-mode 'org-mode)
(user-error "Cannot reformat an org src block in org-mode")
;; Determine formatter based on language and format the region
(let ((formatter (apheleia--get-formatters 'interactive)))
(unless formatter
(setq formatter (apheleia--get-formatters 'prompt))
(unless formatter
(user-error "No formatter configured for language: %s" lang)))
(let ((apheleia-formatter formatter))
(+format-region beg end)))))))))