Skip to content

Commit 2a04538

Browse files
authored
Update mix-format.el
1 parent 5e0a255 commit 2a04538

File tree

1 file changed

+153
-40
lines changed

1 file changed

+153
-40
lines changed

mix-format.el

Lines changed: 153 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,50 +28,163 @@
2828
;; M-x mix-format
2929
;;
3030

31+
(defcustom mixfmt-elixir "elixir"
32+
"Path to the Elixir interpreter."
33+
:type 'string
34+
:group 'mix-format)
35+
36+
(defcustom mixfmt-mix "/usr/bin/mix"
37+
"Path to the 'mix' executable."
38+
:type 'string
39+
:group 'mix-format)
40+
41+
(defcustom mixfmt-args nil
42+
"Additional arguments to 'mix format'"
43+
:type '(repeat string)
44+
:group 'mix-format)
45+
46+
(defcustom mix-format-hook nil
47+
"Hook called by `mix-format'."
48+
:type 'hook
49+
:group 'mix-format)
50+
51+
3152
;;; Code
53+
(defun mix-format-before-save ()
54+
"Add this to .emacs to run mix format on the current buffer when saving:
55+
\(add-hook 'before-save-hook 'mix-format-before-save).
56+
57+
Note that this will cause ‘elixir-mode’ to get loaded the first time
58+
you save any file, kind of defeating the point of autoloading."
59+
60+
(interactive)
61+
(when (eq major-mode 'elixir-mode) (mix-format)))
62+
63+
64+
(defun mixfmt--goto-line (line)
65+
(goto-char (point-min))
66+
(forward-line (1- line)))
67+
68+
(defun mixfmt--delete-whole-line (&optional arg)
69+
"Delete the current line without putting it in the `kill-ring'.
70+
Derived from function `kill-whole-line'. ARG is defined as for that
71+
function.
72+
73+
Shamelessly stolen from go-mode (https://github.com/dominikh/go-mode.el)"
74+
(setq arg (or arg 1))
75+
(if (and (> arg 0)
76+
(eobp)
77+
(save-excursion (forward-visible-line 0) (eobp)))
78+
(signal 'end-of-buffer nil))
79+
(if (and (< arg 0)
80+
(bobp)
81+
(save-excursion (end-of-visible-line) (bobp)))
82+
(signal 'beginning-of-buffer nil))
83+
(cond ((zerop arg)
84+
(delete-region (progn (forward-visible-line 0) (point))
85+
(progn (end-of-visible-line) (point))))
86+
((< arg 0)
87+
(delete-region (progn (end-of-visible-line) (point))
88+
(progn (forward-visible-line (1+ arg))
89+
(unless (bobp)
90+
(backward-char))
91+
(point))))
92+
(t
93+
(delete-region (progn (forward-visible-line 0) (point))
94+
(progn (forward-visible-line arg) (point))))))
95+
96+
(defun mixfmt--apply-rcs-patch (patch-buffer)
97+
"Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer.
98+
Shamelessly stolen from go-mode (https://github.com/dominikh/go-mode.el)"
99+
100+
(let ((target-buffer (current-buffer))
101+
;; Relative offset between buffer line numbers and line numbers
102+
;; in patch.
103+
;;
104+
;; Line numbers in the patch are based on the source file, so
105+
;; we have to keep an offset when making changes to the
106+
;; buffer.
107+
;;
108+
;; Appending lines decrements the offset (possibly making it
109+
;; negative), deleting lines increments it. This order
110+
;; simplifies the forward-line invocations.
111+
(line-offset 0))
112+
(save-excursion
113+
(with-current-buffer patch-buffer
114+
(goto-char (point-min))
115+
(while (not (eobp))
116+
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
117+
(error "Invalid rcs patch or internal error in mixfmt--apply-rcs-patch"))
118+
(forward-line)
119+
(let ((action (match-string 1))
120+
(from (string-to-number (match-string 2)))
121+
(len (string-to-number (match-string 3))))
122+
(cond
123+
((equal action "a")
124+
(let ((start (point)))
125+
(forward-line len)
126+
(let ((text (buffer-substring start (point))))
127+
(with-current-buffer target-buffer
128+
(cl-decf line-offset len)
129+
(goto-char (point-min))
130+
(forward-line (- from len line-offset))
131+
(insert text)))))
132+
((equal action "d")
133+
(with-current-buffer target-buffer
134+
(mixfmt--goto-line (- from line-offset))
135+
(cl-incf line-offset len)
136+
(mixfmt--delete-whole-line len)))
137+
(t
138+
(error "Invalid rcs patch or internal error in mixfmt--apply-rcs-patch"))))))))
139+
)
140+
32141
(defun mix-format (&optional is-interactive)
33142
(interactive "p")
34143

35-
(unwind-protect
36-
(let* (
37-
(in-file (make-temp-file "mix-format"))
38-
(out-file (make-temp-file "mix-format"))
39-
(err-file (make-temp-file "mix-format"))
40-
(contents (buffer-substring-no-properties (point-min) (point-max)))
41-
(_ (with-temp-file in-file (insert contents))))
42-
43-
44-
(let* ((command "mix")
45-
(error-buffer (get-buffer-create "*mix-format errors*"))
46-
(retcode (with-temp-buffer
47-
(call-process command
48-
nil (list out-file err-file)
49-
nil
50-
"format"
51-
"--print"
52-
in-file))))
53-
54-
(with-current-buffer error-buffer
55-
(read-only-mode 0)
56-
(insert-file-contents err-file nil nil nil t)
57-
(ansi-color-apply-on-region (point-min) (point-max))
58-
(special-mode))
59-
60-
(if (zerop retcode )
61-
(let ((p (point)))
62-
(save-excursion
63-
(erase-buffer)
64-
(insert-buffer-substring out-file)
65-
(message "mix format applied"))
66-
(goto-char p))
67-
68-
(if is-interactive
69-
(display-buffer error-buffer)
70-
(message "mix-format failed: see %s" (buffer-name error-buffer)))
71-
))
72-
(delete-file in-file)
73-
(delete-file err-file)
74-
(delete-file out-file))))
144+
(let ((outbuff (get-buffer-create "*mix-format-output*"))
145+
(errbuff (get-buffer-create "*mix-format-errors*"))
146+
(tmpfile (make-temp-file "mix-format" nil ".ex"))
147+
(our-mixfmt-args (list mixfmt-mix "format"))
148+
(output nil))
149+
150+
(unwind-protect
151+
(save-restriction
152+
(with-current-buffer outbuff
153+
(erase-buffer))
154+
155+
(with-current-buffer errbuff
156+
(setq buffer-read-only nil)
157+
(erase-buffer))
158+
159+
(write-region nil nil tmpfile)
160+
161+
(run-hooks 'mix-format-hook)
162+
163+
(when mixfmt-args
164+
(setq our-mixfmt-args (append our-mixfmt-args mixfmt-args)))
165+
(setq our-mixfmt-args (append our-mixfmt-args (list tmpfile)))
166+
167+
(if (zerop (apply #'call-process mixfmt-elixir nil errbuff nil our-mixfmt-args))
168+
(progn
169+
(if (zerop (call-process-region (point-min) (point-max) "diff" nil outbuff nil "-n" "-" tmpfile))
170+
(message "File is already formatted")
171+
(progn
172+
(mixfmt--apply-rcs-patch outbuff)
173+
(message "mix format applied")))
174+
(kill-buffer errbuff))
175+
176+
(progn
177+
(with-current-buffer errbuff
178+
(setq buffer-read-only t)
179+
(ansi-color-apply-on-region (point-min) (point-max))
180+
(special-mode))
181+
182+
(if is-interactive
183+
(display-buffer errbuff)
184+
(message "mix-format failed: see %s" (buffer-name errbuff)))))
185+
186+
(delete-file tmpfile)
187+
(kill-buffer outbuff)))))
75188

76189
(provide 'mix-format)
77190

0 commit comments

Comments
 (0)