2018-09-10 20:51:14 +02:00
|
|
|
;;; prettier-js.el --- Minor mode to format JS code on file save
|
|
|
|
|
|
|
|
;; Version: 0.1.0
|
2018-10-02 15:54:39 +02:00
|
|
|
;; Package-Version: 20180109.726
|
2018-04-25 19:26:53 +02:00
|
|
|
|
|
|
|
;; Copyright (c) 2014 The go-mode Authors. All rights reserved.
|
|
|
|
;; Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
|
|
|
|
|
|
|
|
;; Redistribution and use in source and binary forms, with or without
|
|
|
|
;; modification, are permitted provided that the following conditions are
|
|
|
|
;; met:
|
|
|
|
|
|
|
|
;; * Redistributions of source code must retain the above copyright
|
|
|
|
;; notice, this list of conditions and the following disclaimer.
|
|
|
|
;; * Redistributions in binary form must reproduce the above
|
|
|
|
;; copyright notice, this list of conditions and the following disclaimer
|
|
|
|
;; in the documentation and/or other materials provided with the
|
|
|
|
;; distribution.
|
|
|
|
;; * Neither the name of the copyright holder nor the names of its
|
|
|
|
;; contributors may be used to endorse or promote products derived from
|
|
|
|
;; this software without specific prior written permission.
|
|
|
|
|
|
|
|
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.)
|
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
;; Author: James Long and contributors
|
|
|
|
;; Created: 10 January 2017
|
|
|
|
;; Url: https://github.com/prettier/prettier-emacs
|
|
|
|
;; Keywords: convenience wp edit js
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
|
|
|
|
2018-04-25 19:26:53 +02:00
|
|
|
;;; Commentary:
|
2018-09-10 20:51:14 +02:00
|
|
|
;; Formats your JavaScript code using 'prettier' on file save.
|
2018-04-25 19:26:53 +02:00
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defgroup prettier-js nil
|
|
|
|
"Minor mode to format JS code on file save"
|
|
|
|
:group 'languages
|
|
|
|
:prefix "prettier-js"
|
|
|
|
:link '(url-link :tag "Repository" "https://github.com/prettier/prettier"))
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defcustom prettier-js-command "prettier"
|
|
|
|
"The 'prettier' command."
|
2018-04-25 19:26:53 +02:00
|
|
|
:type 'string
|
2018-09-10 20:51:14 +02:00
|
|
|
:group 'prettier-js)
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defcustom prettier-js-args '()
|
|
|
|
"List of args to send to prettier command."
|
|
|
|
:type '(repeat string)
|
|
|
|
:group 'prettier-js)
|
|
|
|
|
|
|
|
(defcustom prettier-js-show-errors 'buffer
|
|
|
|
"Where to display prettier error output.
|
2018-04-25 19:26:53 +02:00
|
|
|
It can either be displayed in its own buffer, in the echo area, or not at all.
|
|
|
|
Please note that Emacs outputs to the echo area when writing
|
2018-09-10 20:51:14 +02:00
|
|
|
files and will overwrite prettier's echo output if used from inside
|
2018-04-25 19:26:53 +02:00
|
|
|
a `before-save-hook'."
|
|
|
|
:type '(choice
|
|
|
|
(const :tag "Own buffer" buffer)
|
|
|
|
(const :tag "Echo area" echo)
|
|
|
|
(const :tag "None" nil))
|
2018-09-10 20:51:14 +02:00
|
|
|
:group 'prettier-js)
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defcustom prettier-js-width-mode nil
|
2018-04-25 19:26:53 +02:00
|
|
|
"Specify width when formatting buffer contents."
|
|
|
|
:type '(choice
|
|
|
|
(const :tag "Window width" window)
|
|
|
|
(const :tag "Fill column" fill)
|
|
|
|
(const :tag "None" nil))
|
2018-09-10 20:51:14 +02:00
|
|
|
:group 'prettier-js)
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defun prettier-js--goto-line (line)
|
|
|
|
"Move cursor to line LINE."
|
2018-04-25 19:26:53 +02:00
|
|
|
(goto-char (point-min))
|
|
|
|
(forward-line (1- line)))
|
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defun prettier-js--apply-rcs-patch (patch-buffer)
|
2018-04-25 19:26:53 +02:00
|
|
|
"Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer."
|
2018-09-10 20:51:14 +02:00
|
|
|
(let ((target-buffer (current-buffer))
|
2018-04-25 19:26:53 +02:00
|
|
|
;; Relative offset between buffer line numbers and line numbers
|
|
|
|
;; in patch.
|
|
|
|
;;
|
|
|
|
;; Line numbers in the patch are based on the source file, so
|
|
|
|
;; we have to keep an offset when making changes to the
|
|
|
|
;; buffer.
|
|
|
|
;;
|
|
|
|
;; Appending lines decrements the offset (possibly making it
|
|
|
|
;; negative), deleting lines increments it. This order
|
|
|
|
;; simplifies the forward-line invocations.
|
|
|
|
(line-offset 0))
|
|
|
|
(save-excursion
|
|
|
|
(with-current-buffer patch-buffer
|
|
|
|
(goto-char (point-min))
|
|
|
|
(while (not (eobp))
|
|
|
|
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
|
2018-09-10 20:51:14 +02:00
|
|
|
(error "Invalid rcs patch or internal error in prettier-js--apply-rcs-patch"))
|
2018-04-25 19:26:53 +02:00
|
|
|
(forward-line)
|
|
|
|
(let ((action (match-string 1))
|
|
|
|
(from (string-to-number (match-string 2)))
|
|
|
|
(len (string-to-number (match-string 3))))
|
|
|
|
(cond
|
|
|
|
((equal action "a")
|
|
|
|
(let ((start (point)))
|
|
|
|
(forward-line len)
|
|
|
|
(let ((text (buffer-substring start (point))))
|
|
|
|
(with-current-buffer target-buffer
|
2018-09-10 20:51:14 +02:00
|
|
|
(setq line-offset (- line-offset len))
|
|
|
|
(goto-char (point-min))
|
2018-04-25 19:26:53 +02:00
|
|
|
(forward-line (- from len line-offset))
|
|
|
|
(insert text)))))
|
|
|
|
((equal action "d")
|
|
|
|
(with-current-buffer target-buffer
|
2018-09-10 20:51:14 +02:00
|
|
|
(prettier-js--goto-line (- from line-offset))
|
|
|
|
(setq line-offset (+ line-offset len))
|
|
|
|
(let ((beg (point)))
|
|
|
|
(forward-line len)
|
|
|
|
(delete-region (point) beg))))
|
2018-04-25 19:26:53 +02:00
|
|
|
(t
|
2018-09-10 20:51:14 +02:00
|
|
|
(error "Invalid rcs patch or internal error in prettier-js--apply-rcs-patch")))))))))
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defun prettier-js--process-errors (filename errorfile errbuf)
|
|
|
|
"Process errors for FILENAME, using an ERRORFILE and display the output in ERRBUF."
|
2018-04-25 19:26:53 +02:00
|
|
|
(with-current-buffer errbuf
|
2018-09-10 20:51:14 +02:00
|
|
|
(if (eq prettier-js-show-errors 'echo)
|
2018-04-25 19:26:53 +02:00
|
|
|
(progn
|
|
|
|
(message "%s" (buffer-string))
|
2018-09-10 20:51:14 +02:00
|
|
|
(prettier-js--kill-error-buffer errbuf))
|
2018-04-25 19:26:53 +02:00
|
|
|
(insert-file-contents errorfile nil nil nil)
|
2018-09-10 20:51:14 +02:00
|
|
|
;; Convert the prettier stderr to something understood by the compilation mode.
|
2018-04-25 19:26:53 +02:00
|
|
|
(goto-char (point-min))
|
2018-09-10 20:51:14 +02:00
|
|
|
(insert "prettier errors:\n")
|
|
|
|
(while (search-forward-regexp "^stdin" nil t)
|
2018-04-25 19:26:53 +02:00
|
|
|
(replace-match (file-name-nondirectory filename)))
|
|
|
|
(compilation-mode)
|
|
|
|
(display-buffer errbuf))))
|
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defun prettier-js--kill-error-buffer (errbuf)
|
|
|
|
"Kill buffer ERRBUF."
|
2018-04-25 19:26:53 +02:00
|
|
|
(let ((win (get-buffer-window errbuf)))
|
|
|
|
(if win
|
|
|
|
(quit-window t win)
|
|
|
|
(with-current-buffer errbuf
|
|
|
|
(erase-buffer))
|
|
|
|
(kill-buffer errbuf))))
|
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
(defun prettier-js ()
|
|
|
|
"Format the current buffer according to the prettier tool."
|
|
|
|
(interactive)
|
2018-04-25 19:26:53 +02:00
|
|
|
(let* ((ext (file-name-extension buffer-file-name t))
|
2018-09-10 20:51:14 +02:00
|
|
|
(bufferfile (make-temp-file "prettier" nil ext))
|
|
|
|
(outputfile (make-temp-file "prettier" nil ext))
|
|
|
|
(errorfile (make-temp-file "prettier" nil ext))
|
|
|
|
(errbuf (if prettier-js-show-errors (get-buffer-create "*prettier errors*")))
|
|
|
|
(patchbuf (get-buffer-create "*prettier patch*"))
|
2018-04-25 19:26:53 +02:00
|
|
|
(coding-system-for-read 'utf-8)
|
|
|
|
(coding-system-for-write 'utf-8)
|
|
|
|
(width-args
|
|
|
|
(cond
|
2018-09-10 20:51:14 +02:00
|
|
|
((equal prettier-js-width-mode 'window)
|
2018-04-25 19:26:53 +02:00
|
|
|
(list "--print-width" (number-to-string (window-body-width))))
|
2018-09-10 20:51:14 +02:00
|
|
|
((equal prettier-js-width-mode 'fill)
|
2018-04-25 19:26:53 +02:00
|
|
|
(list "--print-width" (number-to-string fill-column)))
|
|
|
|
(t
|
|
|
|
'()))))
|
|
|
|
(unwind-protect
|
|
|
|
(save-restriction
|
|
|
|
(widen)
|
2018-09-10 20:51:14 +02:00
|
|
|
(write-region nil nil bufferfile)
|
2018-04-25 19:26:53 +02:00
|
|
|
(if errbuf
|
|
|
|
(with-current-buffer errbuf
|
|
|
|
(setq buffer-read-only nil)
|
|
|
|
(erase-buffer)))
|
|
|
|
(with-current-buffer patchbuf
|
|
|
|
(erase-buffer))
|
|
|
|
(if (zerop (apply 'call-process
|
2018-09-10 20:51:14 +02:00
|
|
|
prettier-js-command bufferfile (list (list :file outputfile) errorfile)
|
|
|
|
nil (append prettier-js-args width-args (list "--stdin" "--stdin-filepath" buffer-file-name))))
|
2018-04-25 19:26:53 +02:00
|
|
|
(progn
|
2018-09-10 20:51:14 +02:00
|
|
|
(call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "--strip-trailing-cr" "-"
|
2018-04-25 19:26:53 +02:00
|
|
|
outputfile)
|
2018-09-10 20:51:14 +02:00
|
|
|
(prettier-js--apply-rcs-patch patchbuf)
|
|
|
|
(message "Applied prettier with args `%s'" prettier-js-args)
|
|
|
|
(if errbuf (prettier-js--kill-error-buffer errbuf)))
|
|
|
|
(message "Could not apply prettier")
|
2018-04-25 19:26:53 +02:00
|
|
|
(if errbuf
|
2018-09-10 20:51:14 +02:00
|
|
|
(prettier-js--process-errors (buffer-file-name) errorfile errbuf))
|
|
|
|
))
|
|
|
|
(kill-buffer patchbuf)
|
|
|
|
(delete-file errorfile)
|
|
|
|
(delete-file bufferfile)
|
|
|
|
(delete-file outputfile))))
|
2018-04-25 19:26:53 +02:00
|
|
|
|
2018-09-10 20:51:14 +02:00
|
|
|
;;;###autoload
|
|
|
|
(define-minor-mode prettier-js-mode
|
|
|
|
"Runs prettier on file save when this mode is turned on"
|
|
|
|
:lighter " Prettier"
|
|
|
|
:global nil
|
|
|
|
(if prettier-js-mode
|
|
|
|
(add-hook 'before-save-hook 'prettier-js nil 'local)
|
|
|
|
(remove-hook 'before-save-hook 'prettier-js 'local)))
|
|
|
|
|
|
|
|
(provide 'prettier-js)
|
|
|
|
;;; prettier-js.el ends here
|