Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OUTPUT-FILTER option #28

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions reformatter-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,30 @@
(reformatter-tests-shfmt-in-place)
(should (equal "[ foo ] && echo yes\n" (buffer-string)))))

(reformatter-define reformatter-tests-output-filter-reverse
:program "cat"
:output-filter (lambda ()
(reverse-region (point-min) (point-max))
t)
:args nil)

(ert-deftest reformatter-tests-output-filter ()
(with-temp-buffer
(insert "one\ntwo\n")
(reformatter-tests-output-filter-reverse)
(should (equal "two\none\n" (buffer-string)))))

(reformatter-define reformatter-tests-output-filter-no-op
:program "wc"
:output-filter (lambda ()
nil)
:args nil)

(ert-deftest reformatter-tests-output-filter-no-op ()
(with-temp-buffer
(insert "one\ntwo\n")
(reformatter-tests-output-filter-no-op)
(should (equal "one\ntwo\n" (buffer-string)))))

(provide 'reformatter-tests)
;;; reformatter-tests.el ends here
56 changes: 44 additions & 12 deletions reformatter.el
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,28 @@
(require 'cl-lib))
(require 'ansi-color)

(defun reformatter--do-region (name beg end program args stdin stdout input-file exit-code-success-p display-errors)
(defun reformatter--apply-output-filter (program-output-file output-filter)
"Run OUTPUT-FILTER in a buffer containing PROGRAM-OUTPUT-FILE.
The result of OUTPUT-FILTER is returned, and if OUTPUT-FILTER
succeeded, the buffer's contents will be saved back to
PROGRAM-OUTPUT-FILE."
(with-temp-buffer
(insert-file-contents program-output-file)
(goto-char (point-min))
(let ((success (funcall output-filter)))
(when success
(write-region (point-min) (point-max) program-output-file nil :quiet))
success)))

(defun reformatter--do-region (name beg end program args stdin stdout input-file exit-code-success-p output-filter display-errors)
"Do the work of reformatter called NAME.
Reformats the current buffer's region from BEG to END using
PROGRAM and ARGS. For args STDIN, STDOUT, INPUT-FILE,
EXIT-CODE-SUCCESS-P and DISPLAY-ERRORS see the documentation of
the `reformatter-define' macro."
EXIT-CODE-SUCCESS-P, OUTPUT-FILTER and DISPLAY-ERRORS see the
documentation of the `reformatter-define' macro."
(cl-assert input-file)
(cl-assert (functionp exit-code-success-p))
(cl-assert (or (null output-filter) (functionp output-filter)))
(when (and input-file
(buffer-file-name)
(string= (file-truename input-file)
Expand Down Expand Up @@ -115,13 +129,14 @@ the `reformatter-define' macro."
(if (funcall exit-code-success-p retcode)
(progn
(save-restriction
;; This replacement method minimises
;; disruption to marker positions and the
;; undo list
(narrow-to-region beg end)
(reformatter-replace-buffer-contents-from-file (if stdout
stdout-file
input-file)))
(let ((replacement-text-file (if stdout stdout-file input-file)))
(when (or (null output-filter)
(reformatter--apply-output-filter replacement-text-file output-filter))
;; This replacement method minimises
;; disruption to marker positions and the
;; undo list
(narrow-to-region beg end)
(reformatter-replace-buffer-contents-from-file replacement-text-file))))
;; If there are no errors then we hide the error buffer
(delete-windows-on error-buffer))
(if display-errors
Expand All @@ -131,7 +146,7 @@ the `reformatter-define' macro."
(delete-file stdout-file))))

;;;###autoload
(cl-defmacro reformatter-define (name &key program args (mode t) (stdin t) (stdout t) input-file lighter keymap group (exit-code-success-p 'zerop))
(cl-defmacro reformatter-define (name &key program args (mode t) (stdin t) (stdout t) input-file lighter keymap group (exit-code-success-p 'zerop) output-filter)
"Define a reformatter command with NAME.

When called, the reformatter will use PROGRAM and any ARGS to
Expand Down Expand Up @@ -191,6 +206,20 @@ INPUT-FILE
deleted automatically. You might find the function
`reformatter-temp-file-in-current-directory' helpful.

OUTPUT-FILTER

Sometimes a reformatter program might output the reformatted
text inside structured output such as JSON, so that the
structured output would need to be parsed to extract the
reformatted text. If provided, OUTPUT-FILTER is expected to be
a function with no arguments which will be run in the context
of a writeable buffer containing the full output text. The
function may then proceed to change the buffer contents so that
they contain only the replacement text. If the function
returns non-nil, it will be assumed that the output was
processed successfully. If the function returns nil, no
replacement of the original text will be performed.

MODE

Unless nil, also generate a minor mode that will call the
Expand Down Expand Up @@ -226,6 +255,7 @@ EXIT-CODE-SUCCESS-P
(declare (indent defun))
(cl-assert (symbolp name))
(cl-assert (functionp exit-code-success-p))
(cl-assert (or (null output-filter) (functionp output-filter)))
(cl-assert program)
;; Note: we skip using `gensym' here because the macro arguments are only
;; referred to once below, but this may have to change later.
Expand Down Expand Up @@ -271,7 +301,9 @@ DISPLAY-ERRORS, shows a buffer if the formatting fails."
(reformatter--do-region
',name beg end
,program ,args ,stdin ,stdout input-file
#',exit-code-success-p display-errors))
#',exit-code-success-p
#',output-filter
display-errors))
(when (file-exists-p input-file)
(delete-file input-file)))))

Expand Down