Skip to content

Commit 3643082

Browse files
author
Benjamin Moody
committed
rust-doc-indent-line: new function.
This can be used as the indent-line-function, as an alternative to rust-mode-indent-line. In addition to indenting the current line as rust-mode-indent-line does, it also checks to see whether we are editing a Markdown code block inside a comment (either a documentation comment or a normal comment.) If we are inside a Markdown code block, it extracts that code block, treats it as Rust code, and indents the current line accordingly. Note that this behavior is only triggered if point is inside the comment body: placing point at the start of the line and pressing tab will only re-indent the comment line, but placing point at the end of the line and pressing tab will re-indent the both comment line and its contents.
1 parent 3bd0863 commit 3643082

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

rust-prog-mode.el

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,123 @@ buffer."
769769
(save-excursion (= (progn (goto-char pos1) (line-end-position))
770770
(progn (goto-char pos2) (line-end-position)))))
771771

772+
(defun rust-doc-indent-line ()
773+
"Indent the current line, and indent code examples in comments.
774+
775+
Indent the current line as `rust-mode-indent-line' does. If
776+
point is inside a block comment containing a Markdown code
777+
example (delimited by triple backquotes), then also indent the
778+
current line within the code example."
779+
(interactive)
780+
781+
;; First, reindent the current line.
782+
(rust-mode-indent-line)
783+
784+
;; If point is inside a comment:
785+
(let ((ppss (syntax-ppss)))
786+
(when (nth 4 ppss)
787+
(rust-with-comment-fill-prefix
788+
(lambda ()
789+
(let* ((orig-buf (current-buffer))
790+
(orig-point (point))
791+
(orig-eol (line-end-position))
792+
(orig-bol (line-beginning-position))
793+
(orig-mode major-mode)
794+
(com-start (nth 8 ppss))
795+
(com-prefix (replace-regexp-in-string "\\s-*\\'" ""
796+
(or fill-prefix "")))
797+
(com-re (regexp-quote com-prefix))
798+
(cb-re (concat "^" com-re "\\(\\s-*\\)```"))
799+
cb-start cb-pad cb-hidden-marker indented rel-point)
800+
801+
;; If point is within the prefix (not inside the comment
802+
;; body), don't do anything fancy.
803+
(when (>= orig-point (+ orig-bol (length com-prefix)))
804+
(save-excursion
805+
;; If we're in a // comment block, use the fill prefix to
806+
;; find the start of the block. If we're in a /*
807+
;; comment, the start is determined by ppss.
808+
(when (string-match "^\\s-*//" com-prefix)
809+
(setq com-start orig-bol)
810+
(while (and (= (forward-line -1) 0)
811+
(looking-at com-re))
812+
(setq com-start (point))))
813+
814+
;; Search for ``` lines within the comment block, and
815+
;; identify the start of the current code block if any.
816+
(goto-char com-start)
817+
(while (re-search-forward cb-re orig-bol t)
818+
(setq cb-start (unless cb-start (line-end-position))
819+
cb-pad (match-string 1))))
820+
821+
(when cb-start
822+
;; We're inside a code block. Copy preceding contents to
823+
;; a temporary buffer.
824+
(with-temp-buffer
825+
(insert-buffer-substring orig-buf cb-start orig-eol)
826+
(forward-char (- orig-point orig-eol))
827+
(save-excursion
828+
;; For each line in the temporary buffer, remove
829+
;; the comment prefix, left padding if present, and
830+
;; hidden-line marker if present. For example, if
831+
;; the code block begins with:
832+
;;
833+
;; ^ /// ```$
834+
;;
835+
;; then trim lines as follows:
836+
;;
837+
;; ^ ///$ becomes ^$
838+
;; ^ /// let x = 2;$ becomes ^ let x = 2;$
839+
;; ^ /// # fn main() {$ becomes ^fn main() {$
840+
;;
841+
;; If the line we're indenting isn't a hidden line,
842+
;; then remove hidden lines completely - non-hidden
843+
;; lines are indented as if the hidden lines don't
844+
;; exist.
845+
(let ((trim-re (concat com-re "\\(?:" cb-pad "\\)?"
846+
"\\(\\s-*# \\)?")))
847+
(beginning-of-line)
848+
(if (and (looking-at trim-re) (match-beginning 1))
849+
(setq cb-hidden-marker "# ")
850+
(setq cb-hidden-marker ""
851+
trim-re (concat com-re "\\(?:" cb-pad "\\)?"
852+
"\\(\\s-*# .*\\)?")))
853+
854+
(goto-char (point-min))
855+
(while (not (eobp))
856+
(when (looking-at trim-re)
857+
(delete-region (point) (match-end 0)))
858+
(forward-line 1))))
859+
860+
;; Reindent the line. Copy local settings from the
861+
;; parent buffer, but disable indent-tabs-mode unless
862+
;; it is enabled in the parent buffer and the code
863+
;; block is already tab-aligned.
864+
(funcall orig-mode)
865+
(mapc (lambda (v)
866+
(when (custom-variable-p (or (car-safe v) v))
867+
(if (symbolp v)
868+
(makunbound (make-local-variable v))
869+
(set (make-local-variable (car v)) (cdr v)))))
870+
(buffer-local-variables orig-buf))
871+
(or (string-suffix-p "\t" cb-pad)
872+
(= 0 (length com-prefix) (length cb-pad))
873+
(setq-local indent-tabs-mode nil))
874+
(rust-mode-indent-line)
875+
876+
;; Extract the indented line and copy back into the
877+
;; original buffer.
878+
(setq rel-point (- (point) (point-max)))
879+
(beginning-of-line)
880+
(setq indented
881+
(concat com-prefix cb-pad cb-hidden-marker
882+
(buffer-substring (point) (point-max)))))
883+
(goto-char orig-eol)
884+
(unless (equal indented (buffer-substring orig-bol orig-eol))
885+
(delete-region orig-bol orig-eol)
886+
(insert indented))
887+
(forward-char rel-point)))))))))
888+
772889
;;; Font-locking definitions and helpers
773890

774891
(defun rust-next-string-interpolation (limit)

0 commit comments

Comments
 (0)