Add various helper functions for defining formatters (#166)

This commit is contained in:
Mohsin Kaleem 2023-03-25 14:56:54 +00:00 committed by GitHub
parent 43ffed7890
commit edec1e61c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 43 deletions

View file

@ -2,10 +2,103 @@
;;; Commentary:
;; Formatter definitions for `apheleia'.
;; Formatter helper functions and definitions for `apheleia'.
;;; Code:
(require 'cl-lib)
(require 'subr-x)
(defun apheleia-formatters-indent (tab-flag indent-flag indent-var)
"Set flag for indentation.
Helper function for `apheleia-formatters' which allows you to supply
alternating flags based on the current buffers indent configuration. If the
buffer is indented with tabs then returns TAB-FLAG. Otherwise if INDENT-VAR
is set in the buffer return INDENT-FLAG and the value of INDENT-VAR. Use this
to easily configure the indentation level of a formatter."
(cond
(indent-tabs-mode tab-flag)
(indent-var
(when-let ((indent (and (boundp indent-var)
(symbol-value indent-var))))
(list indent-flag (number-to-string indent))))))
(defun apheleia-formatters-js-indent (tab-flag indent-flag)
"Variant of `apheleia-formatters-indent' for JavaScript like modes.
See `apheleia-formatters-indent' for a description of TAB-FLAG and
INDENT-FLAG."
(apheleia-formatters-indent
tab-flag indent-flag
(cl-case major-mode
(json-mode 'js-indent-level)
(json-ts-mode 'json-ts-mode-indent-offset)
(js-mode 'js-indent-level)
(js-jsx-mode 'js-indent-level)
(js2-mode 'js2-basic-offset)
(js2-jsx-mode 'js2-basic-offset)
(js3-mode 'js3-indent-level))))
(defcustom apheleia-formatters-respect-fill-column nil
"Whether formatters should set `fill-column' related flags."
:type 'boolean
:group 'apheleia)
(defun apheleia-formatters-fill-column (fill-flag)
"Set flag for wrap column.
Helper function to set a flag based on `fill-column'. When `fill-column' is set
and `apheleia-formatters-respect-fill-column' return a list of FILL-FLAG and
`fill-column'."
(when (and apheleia-formatters-respect-fill-column
(bound-and-true-p fill-column))
(list fill-flag (number-to-string fill-column))))
(defun apheleia-formatters-locate-file (file-flag file-name)
"Set a flag based on a dominating-file.
Look for a file up recursively from the current directory until FILE-NAME is
found. If found return a list of FILE-FLAG and the absolute path to the located
FILE-NAME."
(when-let ((file (locate-dominating-file default-directory file-name)))
(list file-flag (concat (expand-file-name file) file-name))))
(defun apheleia-formatters-extension-p (&rest exts)
"Assert whether current buffer has an extension in EXTS."
(when-let ((name buffer-file-name)
(ext (file-name-extension name)))
(cl-find-if (apply-partially #'string-equal ext)
exts)))
(defcustom apheleia-formatters-mode-extension-assoc
'((c-mode . ".c")
(c-ts-mode . ".c")
(c++-mode . ".cpp")
(c++-ts-mode . ".cpp")
(glsl-mode . ".glsl")
(java-mode . ".java")
(java-ts-mode . ".java"))
"Association list between major-modes and common file extensions for them."
:type 'alist
:group 'apheleia)
(defun apheleia-formatters-mode-extension (&optional flag)
"Get a file-extension based on the current `major-mode'.
If FLAG is set this function returns a list of FLAG and then the extension.
Otherwise return the extension only."
(when-let ((ext
(alist-get major-mode apheleia-formatters-mode-extension-assoc)))
(if flag
(list flag ext)
ext)))
(defun apheleia-formatters-local-buffer-file-name ()
"Get variable `buffer-file-name' without any remote components."
(when-let ((name buffer-file-name))
(let ((remote (file-remote-p name)))
(if remote
(substring name (length remote))
name))))
(defcustom apheleia-formatters
'((bean-format . ("bean-format"))
(black . ("black" "-"))

View file

@ -32,19 +32,16 @@ If ALL is non-nil, unconditionally return all formatters."
"Check out given Git REF and return `apheleia-formatters' from there.
Return an Elisp data structure, same as the `apheleia-formatters'
already in memory on the current branch."
(let ((old-apheleia (make-temp-file "apheleia-" nil ".el"))
(let ((old-apheleia (make-temp-file "apheleia-" 'dir))
(stderr-file (make-temp-file "apheleia-ft-stderr-")))
(with-temp-file old-apheleia
(with-temp-buffer
(let ((exit-status
(call-process
"git"
nil (list (current-buffer) stderr-file) nil
"show" (format "%s:apheleia-formatters.el" ref))))
"--work-tree" old-apheleia "checkout" ref "--" "*.el")))
(unless (zerop exit-status)
(with-temp-buffer
(insert-file-contents stderr-file)
(princ (buffer-string)))
(error "Failed to 'git show %s:apheleia.el', got exit status %S"
(error "Failed to 'git checkout %s -- *.el', got exit status %S"
ref exit-status))))
(with-temp-buffer
(call-process
@ -52,7 +49,9 @@ already in memory on the current branch."
(expand-file-name invocation-name invocation-directory)
invocation-name)
nil (current-buffer) nil
"--batch" "-l" old-apheleia "--eval" "(prin1 apheleia-formatters)")
"--batch" "-L" old-apheleia
"--eval" "(require 'apheleia)"
"--eval" "(prin1 apheleia-formatters)")
(goto-char (point-min))
(read (current-buffer)))))
@ -226,8 +225,12 @@ environment variable, defaulting to all formatters."
(let ((load-suffixes '(".el")))
(locate-library "apheleia"))))))
exec-path)))
;; Some formatters use the current file-name or buffer-name to interpret the
;; type of file that is being formatted. Some may not be able to determine
;; this from the contents of the file so we set this to force it.
(rename-buffer in-file)
(setq stdout-buffer (get-buffer-create
(format "*apheleia-ft-stdout-%S" formatter)))
(format "*apheleia-ft-stdout-%S%s" formatter extension)))
(with-current-buffer stdout-buffer
(erase-buffer))
(if (functionp command)
@ -243,41 +246,14 @@ environment variable, defaulting to all formatters."
(copy-to-buffer stdout-buffer (point-min) (point-max))))
(progn
(let ((result (apheleia--format-command command nil nil)))
(setq command (nthcdr 3 result)
in-temp-real-file (nth 0 result)
out-temp-file (nth 1 result)))
(with-current-buffer stdout-buffer
(erase-buffer))
(mapc
(lambda (arg)
(when (memq arg '(file filepath input output inplace))
(cl-pushnew arg syms)))
command)
(when (or (memq 'file syms) (memq 'filepath syms))
(setq in-temp-real-file (apheleia-ft--write-temp-file
in-text extension)))
(when (or (memq 'input syms) (memq 'inplace syms))
(setq in-temp-file (apheleia-ft--write-temp-file
in-text extension))
(when (memq 'inplace syms)
(setq out-temp-file in-temp-file)))
(when (memq 'output syms)
(setq out-temp-file (apheleia-ft--write-temp-file
"" extension)))
(setq command (delq 'npx command))
(setq command
(mapcar
(lambda (arg)
(pcase arg
((or `file `filepath)
in-temp-real-file)
((or `input `inplace)
in-temp-file)
(`output
out-temp-file)
((guard (stringp arg))
arg)
(_ (eval arg))))
command))
(setq stdout-buffer (get-buffer-create
(format "*apheleia-ft-stdout-%S" formatter)))
(setq exit-status
(apply
#'call-process