[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/rust-mode 542f1755d8 5/5: Merge pull request #563 from bem
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/rust-mode 542f1755d8 5/5: Merge pull request #563 from bemoody/indent-doc |
Date: |
Tue, 12 Nov 2024 01:00:47 -0500 (EST) |
branch: elpa/rust-mode
commit 542f1755d8929ca83564322d7030d558f3392fe1
Merge: 3bd0863f28 c0cdf44dfe
Author: Sibi Prabakaran <sibi@psibi.in>
Commit: GitHub <noreply@github.com>
Merge pull request #563 from bemoody/indent-doc
Indent example-code blocks in comments
---
rust-mode-tests.el | 92 +++++++++++++++++++++++++++++++++++++++++
rust-prog-mode.el | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 210 insertions(+), 1 deletion(-)
diff --git a/rust-mode-tests.el b/rust-mode-tests.el
index 62cd261de3..a0ee0bbd44 100644
--- a/rust-mode-tests.el
+++ b/rust-mode-tests.el
@@ -887,6 +887,98 @@ struct A {
"
))
+;; When point is inside an example code block, indent-for-tab-command
+;; should reindent the example code.
+(ert-deftest indent-inside-doc-example ()
+ (rust-test-manip-code
+ "
+/// ```
+/// if 2 + 2 == 4 {
+/// success();
+/// }
+/// ```
+"
+ 34
+ #'indent-for-tab-command
+ "
+/// ```
+/// if 2 + 2 == 4 {
+/// success();
+/// }
+/// ```
+"
+ 38))
+
+;; Inside example code blocks, hidden lines starting with "# " should
+;; be indented as if the "# " wasn't there.
+(ert-deftest indent-inside-doc-example-hidden-code ()
+ (rust-test-manip-code
+ "
+/// ```
+/// # if 2 + 2 == 4 {
+/// # success();
+/// # }
+/// ```
+"
+ 36
+ #'indent-for-tab-command
+ "
+/// ```
+/// # if 2 + 2 == 4 {
+/// # success();
+/// # }
+/// ```
+"
+ 42))
+
+;; Inside example code blocks, hidden lines starting with "# "
+;; shouldn't affect indentation of non-hidden lines.
+(ert-deftest indent-inside-doc-example-with-hidden-block ()
+ (rust-test-manip-code
+ "
+/// ```
+/// # if 2 + 2 == 4 {
+/// success();
+/// # }
+/// ```
+"
+ 40
+ #'indent-for-tab-command
+ "
+/// ```
+/// # if 2 + 2 == 4 {
+/// success();
+/// # }
+/// ```
+"
+ 36))
+
+;; When point is outside the comment, indent-for-tab-command should
+;; reindent the comment line without affecting its contents.
+(ert-deftest indent-outside-doc-example ()
+ (rust-test-manip-code
+ "
+impl Foo {
+ /// ```
+ /// if 2 + 2 == 4 {
+ /// success();
+ /// }
+ /// ```
+}
+"
+ 49
+ #'indent-for-tab-command
+ "
+impl Foo {
+ /// ```
+ /// if 2 + 2 == 4 {
+ /// success();
+ /// }
+ /// ```
+}
+"
+ 53))
+
(defconst rust-test-motion-string
"
fn fn1(arg: i32) -> bool {
diff --git a/rust-prog-mode.el b/rust-prog-mode.el
index 05bc5e0daf..62639499c5 100644
--- a/rust-prog-mode.el
+++ b/rust-prog-mode.el
@@ -567,7 +567,7 @@ buffer."
;; foo.bar
(t (funcall skip-dot-identifier)))))))
-(defun rust-mode-indent-line ()
+(defun rust-mode--indent-line ()
(interactive)
(let ((indent
(save-excursion
@@ -769,6 +769,123 @@ buffer."
(save-excursion (= (progn (goto-char pos1) (line-end-position))
(progn (goto-char pos2) (line-end-position)))))
+(defun rust-mode-indent-line ()
+ "Indent the current line, and indent code examples in comments.
+
+Indent the current line as `rust-mode-indent-line' does. If
+point is inside a block comment containing a Markdown code
+example (delimited by triple backquotes), then also indent the
+current line within the code example."
+ (interactive)
+
+ ;; First, reindent the current line.
+ (rust-mode--indent-line)
+
+ ;; If point is inside a comment:
+ (let ((ppss (syntax-ppss)))
+ (when (nth 4 ppss)
+ (rust-with-comment-fill-prefix
+ (lambda ()
+ (let* ((orig-buf (current-buffer))
+ (orig-point (point))
+ (orig-eol (line-end-position))
+ (orig-bol (line-beginning-position))
+ (orig-mode major-mode)
+ (com-start (nth 8 ppss))
+ (com-prefix (replace-regexp-in-string "\\s-*\\'" ""
+ (or fill-prefix "")))
+ (com-re (regexp-quote com-prefix))
+ (cb-re (concat "^" com-re "\\(\\s-*\\)```"))
+ cb-start cb-pad cb-hidden-marker indented rel-point)
+
+ ;; If point is within the prefix (not inside the comment
+ ;; body), don't do anything fancy.
+ (when (>= orig-point (+ orig-bol (length com-prefix)))
+ (save-excursion
+ ;; If we're in a // comment block, use the fill prefix to
+ ;; find the start of the block. If we're in a /*
+ ;; comment, the start is determined by ppss.
+ (when (string-match "^\\s-*//" com-prefix)
+ (setq com-start orig-bol)
+ (while (and (= (forward-line -1) 0)
+ (looking-at com-re))
+ (setq com-start (point))))
+
+ ;; Search for ``` lines within the comment block, and
+ ;; identify the start of the current code block if any.
+ (goto-char com-start)
+ (while (re-search-forward cb-re orig-bol t)
+ (setq cb-start (unless cb-start (line-end-position))
+ cb-pad (match-string 1))))
+
+ (when cb-start
+ ;; We're inside a code block. Copy preceding contents to
+ ;; a temporary buffer.
+ (with-temp-buffer
+ (insert-buffer-substring orig-buf cb-start orig-eol)
+ (forward-char (- orig-point orig-eol))
+ (save-excursion
+ ;; For each line in the temporary buffer, remove
+ ;; the comment prefix, left padding if present, and
+ ;; hidden-line marker if present. For example, if
+ ;; the code block begins with:
+ ;;
+ ;; ^ /// ```$
+ ;;
+ ;; then trim lines as follows:
+ ;;
+ ;; ^ ///$ becomes ^$
+ ;; ^ /// let x = 2;$ becomes ^ let x = 2;$
+ ;; ^ /// # fn main() {$ becomes ^fn main() {$
+ ;;
+ ;; If the line we're indenting isn't a hidden line,
+ ;; then remove hidden lines completely - non-hidden
+ ;; lines are indented as if the hidden lines don't
+ ;; exist.
+ (let ((trim-re (concat com-re "\\(?:" cb-pad "\\)?"
+ "\\(\\s-*# \\)?")))
+ (beginning-of-line)
+ (if (and (looking-at trim-re) (match-beginning 1))
+ (setq cb-hidden-marker "# ")
+ (setq cb-hidden-marker ""
+ trim-re (concat com-re "\\(?:" cb-pad "\\)?"
+ "\\(\\s-*# .*\\)?")))
+
+ (goto-char (point-min))
+ (while (not (eobp))
+ (when (looking-at trim-re)
+ (delete-region (point) (match-end 0)))
+ (forward-line 1))))
+
+ ;; Reindent the line. Copy local settings from the
+ ;; parent buffer, but disable indent-tabs-mode unless
+ ;; it is enabled in the parent buffer and the code
+ ;; block is already tab-aligned.
+ (funcall orig-mode)
+ (mapc (lambda (v)
+ (when (custom-variable-p (or (car-safe v) v))
+ (if (symbolp v)
+ (makunbound (make-local-variable v))
+ (set (make-local-variable (car v)) (cdr v)))))
+ (buffer-local-variables orig-buf))
+ (or (string-suffix-p "\t" cb-pad)
+ (= 0 (length com-prefix) (length cb-pad))
+ (setq-local indent-tabs-mode nil))
+ (rust-mode--indent-line)
+
+ ;; Extract the indented line and copy back into the
+ ;; original buffer.
+ (setq rel-point (- (point) (point-max)))
+ (beginning-of-line)
+ (setq indented
+ (concat com-prefix cb-pad cb-hidden-marker
+ (buffer-substring (point) (point-max)))))
+ (goto-char orig-eol)
+ (unless (equal indented (buffer-substring orig-bol orig-eol))
+ (delete-region orig-bol orig-eol)
+ (insert indented))
+ (forward-char rel-point)))))))))
+
;;; Font-locking definitions and helpers
(defun rust-next-string-interpolation (limit)