Support non-file buffers (#52) (#59)

* Support non-file buffers (#52)

Closes #52

* Apply suggestions from code review

* Oops, my bad

Co-authored-by: Radon Rosborough <radon.neon@gmail.com>
This commit is contained in:
Mohsin Kaleem 2021-11-21 18:45:50 +00:00 committed by GitHub
parent 8e0605cc29
commit 2cf903e9a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 14 deletions

View file

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog].
be run in sequence. be run in sequence.
* Support evaluating items in `apheleia-formatters` to make formatter * Support evaluating items in `apheleia-formatters` to make formatter
commands more dynamic ([#50], [#55]). commands more dynamic ([#50], [#55]).
* Allow apheleia to format buffers without an underlying file ([#52]).
### Formatters ### Formatters
* [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) for * [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) for
@ -46,6 +47,7 @@ The format is based on [Keep a Changelog].
[#48]: https://github.com/raxod502/apheleia/pull/48 [#48]: https://github.com/raxod502/apheleia/pull/48
[#49]: https://github.com/raxod502/apheleia/pull/49 [#49]: https://github.com/raxod502/apheleia/pull/49
[#50]: https://github.com/raxod502/apheleia/pull/50 [#50]: https://github.com/raxod502/apheleia/pull/50
[#52]: https://github.com/raxod502/apheleia/issues/52
[#54]: https://github.com/raxod502/apheleia/pull/54 [#54]: https://github.com/raxod502/apheleia/pull/54
[#55]: https://github.com/raxod502/apheleia/issues/55 [#55]: https://github.com/raxod502/apheleia/issues/55

View file

@ -107,6 +107,18 @@ variables:
only when it is bound. Observe that one of these evaluations only when it is bound. Observe that one of these evaluations
returns a list of flags whereas the other returns a single returns a list of flags whereas the other returns a single
string. These are substituted into the command as you'd expect. string. These are substituted into the command as you'd expect.
* You can also use Apheleia to format buffers that have no underlying
files. In this case the value of `file` and `filepath` will be
the name of the current buffer with any special characters for
the file-system (such as `*` on windows) being stripped out.
This is also how the extension for any temporary files apheleia
might create will be determined. If you're using a formatter
that determines the file-type from the extension you should name
such buffers such that their suffixed with the extension. For
example a buffer called `*foo-bar.c*` that has no associated
file will have an implicit file-name of `foo-bar.c` and any
temporary files will be suffixed with a `.c` extension.
* `apheleia-mode-alist`: Alist mapping major modes and filename * `apheleia-mode-alist`: Alist mapping major modes and filename
regexps to names of formatters to use in those modes and files. See regexps to names of formatters to use in those modes and files. See
the docstring for more information. the docstring for more information.

View file

@ -27,6 +27,9 @@
(require 'map) (require 'map)
(require 'subr-x) (require 'subr-x)
(eval-when-compile
(require 'rx))
(defgroup apheleia nil (defgroup apheleia nil
"Reformat buffer without moving point." "Reformat buffer without moving point."
:group 'external :group 'external
@ -394,6 +397,15 @@ as its sole argument."
;; changes, and 2 if error. ;; changes, and 2 if error.
(memq status '(0 1))))))) (memq status '(0 1)))))))
(defun apheleia--safe-buffer-name ()
"Return `buffer-name' without special file-system characters."
;; See https://stackoverflow.com/q/1976007 for a list of supported
;; characters on all systems.
(replace-regexp-in-string
(rx (or "/" "<" ">" ":" "\"" "\\" "|" "?" "*"))
""
(buffer-name)))
(defun apheleia--format-command (command &optional stdin-buffer) (defun apheleia--format-command (command &optional stdin-buffer)
"Format COMMAND into a shell command and list of file paths. "Format COMMAND into a shell command and list of file paths.
Returns a list with the car being the optional input file-name, the Returns a list with the car being the optional input file-name, the
@ -435,9 +447,10 @@ cmd is to be run."
(when (memq 'input command) (when (memq 'input command)
(let ((input-fname (make-temp-file (let ((input-fname (make-temp-file
"apheleia" nil "apheleia" nil
(and buffer-file-name (when-let ((file-name
(file-name-extension (or buffer-file-name
buffer-file-name 'period))))) (apheleia--safe-buffer-name))))
(file-name-extension file-name 'period)))))
(with-current-buffer stdin (with-current-buffer stdin
(apheleia--write-region-silently nil nil input-fname)) (apheleia--write-region-silently nil nil input-fname))
(setq command (mapcar (lambda (arg) (setq command (mapcar (lambda (arg)
@ -458,15 +471,18 @@ cmd is to be run."
(when stdin-buffer (when stdin-buffer
(error "Cannot run formatter using `file' or `filepath' in a \ (error "Cannot run formatter using `file' or `filepath' in a \
sequence unless it's first in the sequence")) sequence unless it's first in the sequence"))
(setq command (mapcar (lambda (arg) (let ((file-name (or buffer-file-name
(when (eq arg 'file) (concat default-directory
(setq stdin nil)) (apheleia--safe-buffer-name)))))
(if (memq arg '(file filepath)) (setq command (mapcar (lambda (arg)
(prog1 buffer-file-name (when (eq arg 'file)
(when (buffer-modified-p) (setq stdin nil))
(cl-return))) (if (memq arg '(file filepath))
arg)) (prog1 file-name
command))) (when (buffer-modified-p)
(cl-return)))
arg))
command))))
;; Evaluate each element of arg that isn't a string and replace ;; Evaluate each element of arg that isn't a string and replace
;; it with the evaluated value. The result of an evaluation should ;; it with the evaluated value. The result of an evaluation should
;; be a string or a list of strings. If the former its replaced as ;; be a string or a list of strings. If the former its replaced as
@ -772,8 +788,9 @@ operating, to prevent an infinite loop.")
commands commands
(lambda () (lambda ()
(with-demoted-errors "Apheleia: %s" (with-demoted-errors "Apheleia: %s"
(let ((apheleia--format-after-save-in-progress t)) (when buffer-file-name
(apheleia--write-file-silently buffer-file-name)) (let ((apheleia--format-after-save-in-progress t))
(apheleia--write-file-silently buffer-file-name)))
(run-hooks 'apheleia-post-format-hook)))))))) (run-hooks 'apheleia-post-format-hook))))))))
;; Use `progn' to force the entire minor mode definition to be copied ;; Use `progn' to force the entire minor mode definition to be copied