diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index 63b932f4e..a581e2560 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -61,12 +61,8 @@ (defun +format/buffer (&optional arg) "Reformat the current buffer using LSP or `format-all-buffer'." (interactive "P") - (call-interactively - (if (and +format-with-lsp - (bound-and-true-p lsp-mode) - (lsp-feature? "textDocument/formatting")) - #'lsp-format-buffer - #'apheleia-format-buffer))) + (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) @@ -76,11 +72,8 @@ 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") - (if (and +format-with-lsp - (bound-and-true-p lsp-mode) - (lsp-feature? "textDocument/rangeFormatting")) - (call-interactively #'lsp-format-region) - (+format-region beg end))) + (or (run-hook-with-args-until-success '+format-functions beg end 'region) + (+format-region beg end))) ;;;###autoload (defun +format/region-or-buffer () @@ -91,3 +84,55 @@ is selected)." (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-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))))))))) diff --git a/modules/editor/format/config.el b/modules/editor/format/config.el index 77dd06479..397dc92db 100644 --- a/modules/editor/format/config.el +++ b/modules/editor/format/config.el @@ -27,6 +27,17 @@ This has no effect on the +onsave flag, apheleia will always be used there.") (defvaralias '+format-with 'apheleia-formatter "Set this to explicitly use a certain formatter for the current buffer.") +(defvar +format-functions + '(+format-in-org-src-blocks-fn + +format-with-lsp-fn) + "A list of functions to run when formatting a buffer or region. + +Each function is given three arguments: the starting point, end point, and a +symbol indicating the type of operation being requested (as a symbol: either +`region' or `buffer'). + +The first function to return non-nil will abort all functions after it, +including Apheleia itself.") ;; ;;; Bootstrap