branch: externals/matlab-mode commit a63e0fe398fa0e49ef0ca71eb1371a0b45ce62cd Author: John Ciolfi <john.ciolfi...@gmail.com> Commit: John Ciolfi <john.ciolfi...@gmail.com>
Indent first non-help comment With this commit the help comment is the comment immediately following a declaration. A blank non-comment line demarcates the end of the help comment. For example, function b = foo % this is the help for function foo % which can span multiple lines % this is a non-help comment for the following code b = 1; end Note if there's a blank line after the declaration then it is considered a non-help comment: function b = foo % this is a non-help comment for the following code b = 1; end See: https://github.com/mathworks/Emacs-MATLAB-Mode/issues/40 --- matlab-scan.el | 97 +++++++++++++++++----- tests/indents.m | 11 ++- tests/metest-indent-test2-files/indent1.m | 16 ++++ tests/metest-indent-test2-files/indent1_expected.m | 16 ++++ tests/metest-indent-test2-files/indent2.m | 9 ++ tests/metest-indent-test2-files/indent2_expected.m | 9 ++ tests/metest-indent-test2-files/indent3.m | 7 ++ tests/metest-indent-test2-files/indent3_expected.m | 7 ++ tests/metest-indent-test2.el | 78 +++++++++++++++++ tests/metest.el | 3 + 10 files changed, 227 insertions(+), 26 deletions(-) diff --git a/matlab-scan.el b/matlab-scan.el index 005846c99e..5df173504d 100644 --- a/matlab-scan.el +++ b/matlab-scan.el @@ -1,8 +1,8 @@ ;;; matlab-scan.el --- Tools for contextually scanning a MATLAB buffer -*- lexical-binding: t -*- -;; Copyright (C) 2024 Free Software Foundation, Inc. +;; Copyright (C) 2021-2025 Free Software Foundation, Inc. -;; Author: <elud...@mathworks.com> +;; Author: elud...@mathworks.com, john.ciolfi...@gmail.com ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as @@ -773,36 +773,87 @@ If LVL2 is nil, compute it." ;; ;; Some utilities require some level of buffer scanning to get the answer. ;; Keep those separate so they can depend on the earlier decls. + +(defun matlab--scan-is-really-a-help-comment (start-pt declaration-pt) + "Is the comment at START-PT really a help comment? +It is assumed the comment follows a declaration starting at +DECLARATION-PT and there is no code between START-PT and the +declaration. The comment at START-PT is really a help comment for the +declaration when where are no non-comment blank lines between START-PT +and DECLARATION-PT. Examples: + + function a = foo + % help comment for function foo + + % This code comment, e.g. describing the following code + a = 1; + end + + function a = foo + % help comment for function foo + + % copyright after help comment is not a code comment line + + % not a help comment (code comment) + a = 1; + end" + (let (found-non-comment-blank-line) + (save-excursion + (goto-char start-pt) + (beginning-of-line) + + (when (looking-at "^[[:blank:]]*%[[:blank:]]*copyright\\b") + (beginning-of-line) + (forward-line -1) + (while (looking-at "^[[:blank:]]*$") + (forward-line -1))) + + (while (and (> (point) declaration-pt) + (not found-non-comment-blank-line) + (re-search-backward "^[[:blank:]]*$" declaration-pt t)) + (if (nth 4 (syntax-ppss)) + ;; blank line in a multi-line %{ ... %} comment + (forward-line -1) + (setq found-non-comment-blank-line t))) + ;; If we found a non-comment blank line, then this is not a help comment. + (not found-non-comment-blank-line)))) + (defun matlab-scan-comment-help-p (ctxt &optional pt) "Return declaration column if the current line is part of a help comment. Use the context CTXT as a lvl1 or lvl2 context to compute. Declarations are things like functions and classdefs. -Indentation a help comment depends on the column of the declaration. -Optional PT, if non-nil, means return the point instead of column" - (let ((lvl2 nil) (lvl1 nil)) +Indentation of a help comment depends on the column of the declaration. +Optional PT, if non-nil, means return the declaration point instead of the +indent column." + (let ((lvl2 nil) + (lvl1 nil) + (start-pt (point))) (if (symbolp (car ctxt)) - (setq lvl1 ctxt) + (setq lvl1 ctxt) (setq lvl1 (matlab-get-lvl1-from-lvl2 ctxt) - lvl2 ctxt)) - + lvl2 ctxt)) + (when (matlab-line-comment-p lvl1) ;; Try to get from lvl2 context (let ((c-lvl1 (when lvl2 (matlab-previous-code-line lvl2))) - (boc-lvl1 nil)) - (save-excursion - (unless c-lvl1 - ;; If not, compute it ourselves. - (save-excursion - (beginning-of-line) - (forward-comment -100000) - (setq c-lvl1 (matlab-compute-line-context 1)))) - ;; On previous line, move to beginning of that command. - (matlab-scan-beginning-of-command c-lvl1 'code-only) - (setq boc-lvl1 (matlab-compute-line-context 1)) - ;; On previous code line - was it a declaration? - (when (matlab-line-declaration-p boc-lvl1) - (matlab-with-context-line boc-lvl1 - (if pt (point) (current-indentation))))))))) + (boc-lvl1 nil)) + (save-excursion + (unless c-lvl1 + ;; If not, compute it ourselves. + (save-excursion + (beginning-of-line) + (forward-comment -100000) + (setq c-lvl1 (matlab-compute-line-context 1)))) + ;; On previous line, move to beginning of that command. + (matlab-scan-beginning-of-command c-lvl1 'code-only) + (setq boc-lvl1 (matlab-compute-line-context 1)) + ;; On previous code line - was it a declaration? + (when (and (matlab-line-declaration-p boc-lvl1) + (matlab--scan-is-really-a-help-comment start-pt (point))) + (matlab-with-context-line boc-lvl1 + (if pt + (point) ;; declaration point + (current-indentation))))))))) (defun matlab-scan-previous-line-ellipsis-p () "Return the position of the previous line's continuation if there is one. diff --git a/tests/indents.m b/tests/indents.m index a5e4b9c7e5..0af39fb301 100644 --- a/tests/indents.m +++ b/tests/indents.m @@ -19,8 +19,8 @@ function indents(a,b,stuff,cmddual1fake,cmddual2fake) % of many lines % !!0 -% including a gap -% !!0 +% including a gap - comment for following code +% !!4 arguments (Repeating) % !!4 a (1,1) {mustBeNumeric} % !!8 @@ -282,12 +282,17 @@ function B = continuations_and_block_comments % !!0 % !!0 % !!0 - %{ !!2 { } !!2 %} +%{ + blank line means this block comment is not part of help and is for following code + !!6 { } + !!6 +%} + arg1=1; %{ diff --git a/tests/metest-indent-test2-files/indent1.m b/tests/metest-indent-test2-files/indent1.m new file mode 100644 index 0000000000..44fdcbf8cb --- /dev/null +++ b/tests/metest-indent-test2-files/indent1.m @@ -0,0 +1,16 @@ + function b = indent1 + %{ + sadf + + asdfasd + %} + % foo + % foo + + + + % copyright blah + + % foo + b = 1; + end diff --git a/tests/metest-indent-test2-files/indent1_expected.m b/tests/metest-indent-test2-files/indent1_expected.m new file mode 100644 index 0000000000..fa4d9d24aa --- /dev/null +++ b/tests/metest-indent-test2-files/indent1_expected.m @@ -0,0 +1,16 @@ +function b = indent1 +%{ + sadf + + asdfasd +%} +% foo +% foo + + + +% copyright blah + + % foo + b = 1; +end diff --git a/tests/metest-indent-test2-files/indent2.m b/tests/metest-indent-test2-files/indent2.m new file mode 100644 index 0000000000..388d1f9b1e --- /dev/null +++ b/tests/metest-indent-test2-files/indent2.m @@ -0,0 +1,9 @@ + function b = indent2 + % help for indent2 + % using multiple + % single line comments + + + % comment for following code + b = 1; + end diff --git a/tests/metest-indent-test2-files/indent2_expected.m b/tests/metest-indent-test2-files/indent2_expected.m new file mode 100644 index 0000000000..b195fd5265 --- /dev/null +++ b/tests/metest-indent-test2-files/indent2_expected.m @@ -0,0 +1,9 @@ +function b = indent2 +% help for indent2 +% using multiple +% single line comments + + + % comment for following code + b = 1; +end diff --git a/tests/metest-indent-test2-files/indent3.m b/tests/metest-indent-test2-files/indent3.m new file mode 100644 index 0000000000..2319e25c20 --- /dev/null +++ b/tests/metest-indent-test2-files/indent3.m @@ -0,0 +1,7 @@ + function b = indent3 + + % this is a comment for following code + b = 1; + + % copyright in odd location + end diff --git a/tests/metest-indent-test2-files/indent3_expected.m b/tests/metest-indent-test2-files/indent3_expected.m new file mode 100644 index 0000000000..111ab18499 --- /dev/null +++ b/tests/metest-indent-test2-files/indent3_expected.m @@ -0,0 +1,7 @@ +function b = indent3 + + % this is a comment for following code + b = 1; + + % copyright in odd location +end diff --git a/tests/metest-indent-test2.el b/tests/metest-indent-test2.el new file mode 100644 index 0000000000..82e16db829 --- /dev/null +++ b/tests/metest-indent-test2.el @@ -0,0 +1,78 @@ +;;; metest-indent-test2.el --- Testing suite for MATLAB Emacs -*- lexical-binding: t -*- +;; +;; Copyright 2025 Free Software Foundation, Inc. +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to +;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;; + +;;; Commentary: +;; +;; Tests to exercise font-lock using ./metest-indent-test2-files/* +;; + +;;; Code: + +(require 'cl-seq) + +(defun metest-indent-test2-files () + "Return list of full paths to each metest-indent-test2-files/*.m." + (cl-delete-if (lambda (m-file) + (string-match "_expected\\.m$" m-file)) + (directory-files "metest-indent-test2-files" t "\\.m$"))) + +(defvar metest-indent-test2 (cons "indent-test2" (metest-indent-test2-files))) + +(defun metest-indent-test2 (&optional m-file) + "Test indent using ./metest-indent-test2-files/M-FILE. +Compare indent of ./metest-indent-test2-files/M-FILE against +./metest-indent-test2-files/NAME_expected.m + +If M-FILE is not provided, loop comparing all + ./metest-indent-test2-files/*.m" + (let* ((m-files (if m-file + (progn + (setq m-file (file-truename m-file)) + (when (not (file-exists-p m-file)) + (error "File %s does not exist" m-file)) + (list m-file)) + (metest-indent-test2-files)))) + (dolist (m-file m-files) + (save-excursion + (message "START: metest-indent-test2 %s" m-file) + (find-file m-file) + (indent-region (point-min) (point-max)) + (let* ((got (buffer-substring (point-min) (point-max))) + (expected-file (replace-regexp-in-string "\\.m$" "_expected.m" m-file)) + (got-file (concat expected-file "~")) + (expected (when (file-exists-p expected-file) + (with-temp-buffer + (insert-file-contents-literally expected-file) + (buffer-string))))) + (set-buffer-modified-p nil) + (kill-buffer) + (when (not (string= got expected)) + (let ((coding-system-for-write 'raw-text-unix)) + (write-region got nil got-file)) + (when (not expected) + (error "Baseline for %s does not exists. See %s and if it looks good rename it to %s" + m-file got-file expected-file)) + (error "Baseline for %s does not match, got: %s, expected: %s" + m-file got-file expected-file)))) + (message "PASS: metest-indent-test2 %s" m-file))) + "success") + + +(provide 'metest-indent-test2) +;;; metest-indent-test2.el ends here diff --git a/tests/metest.el b/tests/metest.el index ae9f09564d..f46e4ad3d4 100644 --- a/tests/metest.el +++ b/tests/metest.el @@ -37,6 +37,7 @@ (add-to-list 'load-path ".") (require 'metest-font-lock-test2) +(require 'metest-indent-test2) (defun metest-all-syntax-tests () "Run all the syntax test cases in this file." @@ -55,6 +56,8 @@ (metest-run 'metest-sexp-counting-test) (metest-run 'metest-sexp-traversal-test) + (metest-run 'metest-indent-test2) + ;; Randomize indentation first before indenting ;; to force the indenter to make changes and give ;; the cache and performance a harder problem.