[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/tuareg 1a9b3ec438 1/3: Fix indentation and paragraph-fill
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/tuareg 1a9b3ec438 1/3: Fix indentation and paragraph-fill inside comments |
Date: |
Tue, 4 Jan 2022 15:58:25 -0500 (EST) |
branch: elpa/tuareg
commit 1a9b3ec438d2a100cfc98f46fee60242ee4c9dbc
Author: Mattias Engdegård <mattiase@acm.org>
Commit: Mattias Engdegård <mattiase@acm.org>
Fix indentation and paragraph-fill inside comments
Inside comments, explicit indentation as well as the automatic
indentation with electric-indent-mode active (just pressing RET, in
Emacs 28) now use the indentation of the previous nonempty line, even
when that line is on the same line as the initial `(*`.
Filling paragraphs with M-q inside a comment now only fills a single
paragraph instead of the entire comment. On an empty line, the
preceding paragraph is used.
Inside doc comments, lines starting with @-tags (like `@param`) start
a paragraph, and subsequent lines are indented to the text after the
tag.
Fixes #213.
---
indent-test.ml | 13 +++--
tuareg-tests.el | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tuareg.el | 107 +++++++++++++++++++++++++++++++++-------
3 files changed, 247 insertions(+), 23 deletions(-)
diff --git a/indent-test.ml b/indent-test.ml
index 6a63a09255..9e66e855e6 100644
--- a/indent-test.ml
+++ b/indent-test.ml
@@ -44,11 +44,14 @@ let qs1 = {| quoted string |} (* (issue #24) *)
let qs2 = {eof| other quoted string |noteof} |eof}
(* ocp-indent does it as follows:
-let test1 = with_connection (fun conn ->
- do_something conn x;
- ...
- )
- toto
+
+ let test1 = with_connection (fun conn ->
+ ␣␣␣␣do_something conn x;
+ ␣␣␣␣...
+ ␣␣)
+ ␣␣␣␣toto
+
+ (space written as ␣ to avoid reindent smashing this comment)
*)
let test1 = with_connection (fun conn ->
do_something conn x;
diff --git a/tuareg-tests.el b/tuareg-tests.el
index f248320118..4130a8d7db 100644
--- a/tuareg-tests.el
+++ b/tuareg-tests.el
@@ -652,5 +652,155 @@ the original code."
(tuareg-test--comment-uncomment-region
"let f x =\n g x\n y\n"))))
+(defun tuareg-test--do-at (text pos fun)
+ "Call FUN in TEXT at POS and return the resulting text."
+ (with-temp-buffer
+ (tuareg-mode)
+ (electric-indent-mode 1)
+ (insert text)
+ (goto-char pos)
+ (funcall fun)
+ (buffer-substring-no-properties (point-min) (point-max))))
+
+(defun tuareg-test--line-start (text line)
+ "Position of start of LINE (0-based) in TEXT."
+ (let ((ofs 0))
+ (while (and (> line 0)
+ (let ((nl (string-match-p "\n" text ofs)))
+ (setq ofs (1+ nl))
+ (setq line (1- line)))))
+ (1+ ofs)))
+
+(defun tuareg-test--do-at-line (text line fun)
+ "Call FUN in TEXT at start of LINE (0-based) and return the resulting text."
+ (tuareg-test--do-at text (tuareg-test--line-start text line) fun))
+
+(ert-deftest tuareg-indent-comment-text ()
+ ;; Indenting a line should use the indentation of the previous line's text.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (** alpha\n"
+ "beta\n")
+ 1 #'indent-for-tab-command)
+ (concat " (** alpha\n"
+ " beta\n")))
+ ;; Tab should indent even at the end of the line.
+ (should (equal (tuareg-test--do-at
+ (concat " (** alpha\n"
+ "beta")
+ 17 #'indent-for-tab-command)
+ (concat " (** alpha\n"
+ " beta")))
+ ;; An interactive `newline' should indent the new line correctly
+ ;; in Emacs 28 and later.
+ (when (>= emacs-major-version 28)
+ (should (equal (tuareg-test--do-at
+ "(** alpha"
+ 10 (lambda () (call-interactively #'newline)))
+ (concat "(** alpha\n"
+ " "))))
+ ;; The previous line's indentation should be respected and preserved.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha\n"
+ " beta\n"
+ " gamma\n")
+ 2 #'indent-for-tab-command)
+ (concat " (* alpha\n"
+ " beta\n"
+ " gamma\n")))
+ ;; Use the previous nonempty line for indentation.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha\n"
+ " beta\n"
+ " \n"
+ " gamma\n")
+ 3 #'indent-for-tab-command)
+ (concat " (* alpha\n"
+ " beta\n"
+ " \n"
+ " gamma\n")))
+ ;; Indent to text after @-tags in doc comments.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (** alpha\n"
+ " @param beta\n"
+ " gamma\n")
+ 2 #'indent-for-tab-command)
+ (concat " (** alpha\n"
+ " @param beta\n"
+ " gamma\n")))
+ ;; An @-tag starts a new paragraph.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (** alpha\n"
+ " @param beta\n"
+ " @return gamma\n")
+ 2 #'indent-for-tab-command)
+ (concat " (** alpha\n"
+ " @param beta\n"
+ " @return gamma\n")))
+ ;; @-tags are not special in plain comments.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha\n"
+ " @param beta\n"
+ " gamma\n")
+ 2 #'indent-for-tab-command)
+ (concat " (* alpha\n"
+ " @param beta\n"
+ " gamma\n")))
+ ;; Filling one paragraph does not affect other paragraphs.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha beta gamma\n"
+ "delta epsilon\n"
+ "\n"
+ "zeta eta theta iota kappa *)\n")
+ 1 (lambda () (let ((fill-column 17))
+ (funcall (local-key-binding (kbd "M-q"))))))
+ (concat " (* alpha beta\n"
+ " gamma delta\n"
+ " epsilon\n"
+ "\n"
+ "zeta eta theta iota kappa *)\n")))
+ ;; Filling affects the preceding paragraph, not the succeeding.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha beta gamma\n"
+ "delta epsilon\n"
+ "\n"
+ "zeta eta theta iota kappa *)\n")
+ 2 (lambda () (let ((fill-column 17))
+ (funcall (local-key-binding (kbd "M-q"))))))
+ (concat " (* alpha beta\n"
+ " gamma delta\n"
+ " epsilon\n"
+ "\n"
+ "zeta eta theta iota kappa *)\n")))
+
+ ;; A paragraph's indentation is determined by its first line.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (* alpha\n"
+ " beta\n"
+ "\n"
+ " gamma\n"
+ "delta epsilon zeta eta *)\n")
+ 3 (lambda () (let ((fill-column 19))
+ (funcall (local-key-binding (kbd "M-q"))))))
+ (concat " (* alpha\n"
+ " beta\n"
+ "\n"
+ " gamma delta\n"
+ " epsilon zeta\n"
+ " eta *)\n")))
+ ;; @-tags separate paragraphs in doc comments.
+ (should (equal (tuareg-test--do-at-line
+ (concat " (** alpha\n"
+ " beta\n"
+ " @param gamma delta epsilon\n"
+ " @param zeta eta theta iota\n")
+ 4 (lambda () (let ((fill-column 25))
+ (funcall (local-key-binding (kbd "M-q"))))))
+ (concat " (** alpha\n"
+ " beta\n"
+ " @param gamma delta epsilon\n"
+ " @param zeta eta\n"
+ " theta iota\n")))
+ )
+
(provide 'tuareg-tests)
diff --git a/tuareg.el b/tuareg.el
index be2a0c8d13..673a2e998f 100644
--- a/tuareg.el
+++ b/tuareg.el
@@ -2882,24 +2882,52 @@ This function moves the point."
(defun tuareg--fill-comment ()
"Assumes the point is inside a comment and justify it.
This function moves the point."
- (let* ((start (set-marker (make-marker) (nth 8 (syntax-ppss))))
- (end (make-marker))
- fill-prefix
- (use-hard-newlines t))
- (goto-char (marker-position start))
- (indent-according-to-mode)
- (setq fill-prefix (make-string (+ 3 (current-column)) ?\ ))
- (forward-comment 1)
- (set-marker end (point))
- (goto-char (marker-position start))
- (let ((e (marker-position end)))
- (while (re-search-forward "\n\n" e t)
- (put-text-property (match-beginning 0) (match-end 0) 'hard 't)))
- (fill-region start end)
- (remove-text-properties (marker-position start) (marker-position end)
- '(hard))
- (set-marker start nil)
- (set-marker end nil)))
+ (let* ((com-start (nth 8 (syntax-ppss)))
+ content-start
+ com-end
+ in-doc-comment
+ par-start
+ par-end)
+ (save-excursion
+ (goto-char com-start)
+ (setq content-start (and (looking-at comment-start-skip)
+ (match-end 0)))
+ (setq in-doc-comment (looking-at-p (rx "(**" (not (in "*")))))
+ (forward-comment 1)
+ (setq com-end (point)))
+
+ ;; In doc comments, let @tags start a paragraph.
+ (let ((paragraph-start
+ (if in-doc-comment
+ (concat paragraph-start
+ "\\|"
+ (rx (* (in " \t")) "@" (+ (in "a-z")) symbol-end))
+ paragraph-start)))
+ (save-restriction
+ (narrow-to-region content-start com-end)
+ (save-excursion
+ (skip-chars-forward " \t")
+ (backward-paragraph)
+ (skip-chars-forward " \t\n")
+ (setq par-start (point))
+ (forward-paragraph)
+ (setq par-end (point))))
+
+ ;; Set `fill-prefix' to preserve the indentation of the start of the
+ ;; paragraph, assuming that is what the user wants.
+ (let ((fill-prefix
+ (save-excursion
+ (goto-char par-start)
+ (let ((col
+ (if (and in-doc-comment
+ (looking-at (rx "@" (+ (in "a-z")) symbol-end)))
+ (progn
+ ;; Indent after @tag. Is this excessive?
+ (goto-char (match-end 0))
+ (1+ (current-column)))
+ (current-column))))
+ (make-string col ?\s)))))
+ (fill-region-as-paragraph par-start par-end)))))
(defun tuareg-indent-phrase ()
"Depending of the context: justify and indent a comment,
@@ -3118,6 +3146,47 @@ file outside _build? "))
(t (list here-beg here-end here-beg here-end t))))))
(t (funcall orig-fun))))
+(defun tuareg--indent-line-inside-comment ()
+ "Indent the current line if it is inside a comment."
+ (let ((ppss (syntax-ppss)))
+ (and (nth 4 ppss)
+ (let ((indent-col
+ (save-excursion
+ (let* ((com-start (nth 8 ppss))
+ (in-doc-comment
+ (save-excursion
+ (goto-char com-start)
+ (looking-at-p (rx "(**" (not (in "*"))))))
+ tag-starts-line)
+ ;; Use the indentation of the previous nonempty line.
+ ;; If we are in a doc comment and that line
+ ;; starts with an @tag, and the current line
+ ;; doesn't, then indent to after the @tag.
+ (goto-char (max com-start (line-beginning-position)))
+ (setq tag-starts-line
+ (and in-doc-comment
+ (looking-at-p
+ (rx (* (in " \t"))
+ "@" (+ (in "a-z")) symbol-end))))
+ (skip-chars-backward " \t\n" com-start)
+ (goto-char (max com-start (line-beginning-position)))
+ (when (looking-at (rx "(*" (* "*")))
+ (goto-char (match-end 0)))
+ (skip-chars-forward " \t")
+ (when (and in-doc-comment
+ (not tag-starts-line)
+ (looking-at (rx "@" (+ (in "a-z")) " ")))
+ (goto-char (match-end 0))))
+ (current-column))))
+ (indent-line-to indent-col)
+ t))))
+
+(defun tuareg--indent-line (orig-fun)
+ (let ((res (funcall orig-fun)))
+ (if (eq res 'noindent)
+ (tuareg--indent-line-inside-comment)
+ res)))
+
(defun tuareg--common-mode-setup ()
(setq-local syntax-propertize-function #'tuareg-syntax-propertize)
(setq-local parse-sexp-ignore-comments t)
@@ -3130,6 +3199,8 @@ file outside _build? "))
;; hasn't provided any alternative so far :-(
(add-function :before (local 'smie--hanging-eolp-function)
#'tuareg--hanging-eolp-advice))
+ (add-function :around (local 'indent-line-function)
+ #'tuareg--indent-line)
(add-hook 'smie-indent-functions #'tuareg-smie--args nil t)
(add-hook 'smie-indent-functions #'tuareg-smie--inside-string nil t)
(setq-local add-log-current-defun-function #'tuareg-current-fun-name)