diff --git a/apheleia-formatters.el b/apheleia-formatters.el index 4c324e1..01b79f2 100644 --- a/apheleia-formatters.el +++ b/apheleia-formatters.el @@ -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" "-")) diff --git a/test/formatters/apheleia-ft.el b/test/formatters/apheleia-ft.el index 4c33d34..65a6f23 100755 --- a/test/formatters/apheleia-ft.el +++ b/test/formatters/apheleia-ft.el @@ -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