Skip to content

Commit 377b2c7

Browse files
committed
Add paragraph fill and auto-fill for multi-line comments
1 parent 1ac7c5c commit 377b2c7

File tree

1 file changed

+123
-1
lines changed

1 file changed

+123
-1
lines changed

src/etc/emacs/rust-mode.el

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,114 @@
209209

210210
collect `(,(rust-re-item-def item) 1 ,face))))
211211

212+
(defun rust-fill-prefix-for-comment-start (line-start)
213+
"Determine what to use for `fill-prefix' based on what is at the beginning of a line."
214+
(let ((result
215+
;; Replace /* with same number of spaces
216+
(replace-regexp-in-string
217+
"\\(?:/\\*+\\)[!*]"
218+
(lambda (s)
219+
;; We want the * to line up with the first * of the comment start
220+
(concat (make-string (- (length s) 2) ?\x20) "*"))
221+
line-start)))
222+
;; Make sure we've got at least one space at the end
223+
(if (not (= (aref result (- (length result) 1)) ?\x20))
224+
(setq result (concat result " ")))
225+
result))
226+
227+
(defun rust-in-comment-paragraph (body)
228+
;; We might move the point to fill the next comment, but we don't want it
229+
;; seeming to jump around on the user
230+
(save-excursion
231+
;; If we're outside of a comment, with only whitespace and then a comment
232+
;; in front, jump to the comment and prepare to fill it.
233+
(when (not (nth 4 (syntax-ppss)))
234+
(beginning-of-line)
235+
(when (looking-at (concat "[[:space:]\n]*" comment-start-skip))
236+
(goto-char (match-end 0))))
237+
238+
;; We need this when we're moving the point around and then checking syntax
239+
;; while doing paragraph fills, because the cache it uses isn't always
240+
;; invalidated during this.
241+
(syntax-ppss-flush-cache 1)
242+
;; If we're at the beginning of a comment paragraph with nothing but
243+
;; whitespace til the next line, jump to the next line so that we use the
244+
;; existing prefix to figure out what the new prefix should be, rather than
245+
;; inferring it from the comment start.
246+
(let ((next-bol (line-beginning-position 2)))
247+
(while (save-excursion
248+
(end-of-line)
249+
(syntax-ppss-flush-cache 1)
250+
(and (nth 4 (syntax-ppss))
251+
(save-excursion
252+
(beginning-of-line)
253+
(looking-at paragraph-start))
254+
(looking-at "[[:space:]]*$")
255+
(nth 4 (syntax-ppss next-bol))))
256+
(goto-char next-bol)))
257+
258+
(syntax-ppss-flush-cache 1)
259+
;; If we're on the last line of a multiline-style comment that started
260+
;; above, back up one line so we don't mistake the * of the */ that ends
261+
;; the comment for a prefix.
262+
(when (save-excursion
263+
(and (nth 4 (syntax-ppss (line-beginning-position 1)))
264+
(looking-at "[[:space:]]*\\*/")))
265+
(goto-char (line-end-position 0)))
266+
(funcall body)))
267+
268+
(defun rust-with-comment-fill-prefix (body)
269+
(let*
270+
((line-string (buffer-substring-no-properties
271+
(line-beginning-position) (line-end-position)))
272+
(line-comment-start
273+
(when (nth 4 (syntax-ppss))
274+
(cond
275+
;; If we're inside the comment and see a * prefix, use it
276+
((string-match "^\\([[:space:]]*\\*+[[:space:]]*\\)"
277+
line-string)
278+
(match-string 1 line-string))
279+
;; If we're at the start of a comment, figure out what prefix
280+
;; to use for the subsequent lines after it
281+
((string-match (concat "[[:space:]]*" comment-start-skip) line-string)
282+
(rust-fill-prefix-for-comment-start
283+
(match-string 0 line-string))))))
284+
(fill-prefix
285+
(or line-comment-start
286+
fill-prefix)))
287+
(funcall body)))
288+
289+
(defun rust-find-fill-prefix ()
290+
(rust-with-comment-fill-prefix (lambda () fill-prefix)))
291+
292+
(defun rust-fill-paragraph (&rest args)
293+
"Special wrapping for `fill-paragraph' to handle multi-line comments with a * prefix on each line."
294+
(rust-in-comment-paragraph
295+
(lambda ()
296+
(rust-with-comment-fill-prefix
297+
(lambda ()
298+
(let
299+
((fill-paragraph-function
300+
(if (not (eq fill-paragraph-function 'rust-fill-paragraph))
301+
fill-paragraph-function)))
302+
(apply 'fill-paragraph args)
303+
t))))))
304+
305+
(defun rust-do-auto-fill (&rest args)
306+
"Special wrapping for `do-auto-fill' to handle multi-line comments with a * prefix on each line."
307+
(rust-with-comment-fill-prefix
308+
(lambda ()
309+
(apply 'do-auto-fill args)
310+
t)))
311+
312+
(defun rust-fill-forward-paragraph (arg)
313+
;; This is to work around some funny behavior when a paragraph separator is
314+
;; at the very top of the file and there is a fill prefix.
315+
(let ((fill-prefix nil)) (forward-paragraph arg)))
316+
317+
(defun rust-comment-indent-new-line (&optional arg)
318+
(rust-with-comment-fill-prefix
319+
(lambda () (comment-indent-new-line arg))))
212320

213321
;; For compatibility with Emacs < 24, derive conditionally
214322
(defalias 'rust-parent-mode
@@ -234,7 +342,21 @@
234342
;; Misc
235343
(set (make-local-variable 'comment-start) "// ")
236344
(set (make-local-variable 'comment-end) "")
237-
(set (make-local-variable 'indent-tabs-mode) nil))
345+
(set (make-local-variable 'indent-tabs-mode) nil)
346+
347+
;; Allow paragraph fills for comments
348+
(set (make-local-variable 'comment-start-skip)
349+
"\\(?://[/!]*\\|/\\*[*!]?\\)[[:space:]]*")
350+
(set (make-local-variable 'paragraph-start)
351+
(concat "[[:space:]]*\\(?:" comment-start-skip "\\|\\*/?[[:space:]]*\\|\\)$"))
352+
(set (make-local-variable 'paragraph-separate) paragraph-start)
353+
(set (make-local-variable 'normal-auto-fill-function) 'rust-do-auto-fill)
354+
(set (make-local-variable 'fill-paragraph-function) 'rust-fill-paragraph)
355+
(set (make-local-variable 'fill-forward-paragraph-function) 'rust-fill-forward-paragraph)
356+
(set (make-local-variable 'adaptive-fill-function) 'rust-find-fill-prefix)
357+
(set (make-local-variable 'comment-multi-line) t)
358+
(set (make-local-variable 'comment-line-break-function) 'rust-comment-indent-new-line)
359+
)
238360

239361

240362
;;;###autoload

0 commit comments

Comments
 (0)