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.

Reply via email to