branch: externals/matlab-mode
commit 5cbe5042286f02c8890c27f2790be084f395ff26
Author: John Ciolfi <[email protected]>
Commit: John Ciolfi <[email protected]>

    matlab-ts-mode: indent fixes for incomplete matrix/cell/switch
---
 contributing/treesit-mode-how-to.org               |  84 ++++++-
 matlab-ts-mode.el                                  | 260 +++++++++++++++-----
 tests/t-utils.el                                   |  11 +-
 .../indent_xr_cell1.m                              |   2 +
 .../indent_xr_cell1_expected.org                   | 272 +++++++++++++++++++++
 .../indent_xr_fun.m                                |   2 +
 .../indent_xr_fun_expected.org                     | 139 +++++++++++
 .../indent_xr_mat0.m                               |   6 +
 .../indent_xr_mat0_expected.org                    |  81 ++++++
 .../indent_xr_mat1.m                               |   8 +
 .../indent_xr_mat1_expected.org                    | 119 +++++++++
 .../indent_xr_mat2.m                               |   7 +
 .../indent_xr_mat2_expected.org                    |  65 +++++
 .../indent_xr_mat3.m                               |   6 +
 .../indent_xr_mat3_expected.org                    |  67 +++++
 .../indent_xr_mat4.m                               |  14 ++
 .../indent_xr_mat4_expected.org                    | 167 +++++++++++++
 .../indent_xr_switch.m                             |  16 ++
 .../indent_xr_switch_expected.org                  | 162 ++++++++++++
 tests/test-matlab-ts-mode-indent-xr.el             |  67 +++++
 tests/test-matlab-ts-mode-indent.el                |  10 +-
 21 files changed, 1497 insertions(+), 68 deletions(-)

diff --git a/contributing/treesit-mode-how-to.org 
b/contributing/treesit-mode-how-to.org
index 6d2f42e4ca..caf44f8ac8 100644
--- a/contributing/treesit-mode-how-to.org
+++ b/contributing/treesit-mode-how-to.org
@@ -711,6 +711,9 @@ We use a looping pattern similar to the font-lock test for 
our indent tests:
   ./tests/test-LANGUAGE-ts-mode-indent.el
   ./tests/test-LANGUAGE-ts-mode-indent-files/indent_test1.lang
   ./tests/test-LANGUAGE-ts-mode-indent-files/indent_test1_expected.lang  // 
generated for you
+  ./tests/test-LANGUAGE-ts-mode-indent-files/indent_test2.lang
+  ./tests/test-LANGUAGE-ts-mode-indent-files/indent_test2_expected.lang  // 
generated for you
+  ....
 #+end_example
 
 where test-LANGUAGE-ts-mode-indent.el contains:
@@ -752,6 +755,85 @@ where test-LANGUAGE-ts-mode-indent.el contains:
       (t-utils-test-indent test-name)))
 #+end_src
 
+** Test: Indent as you type
+
+Code should be indented correctly as you type. Consider
+
+ : someVariable = {
+ :                  ^   <== Cursor should move here when RET is typed at end 
of prior line
+
+To test this we use =t-utils-xr= to execute and record editing commands. The 
test setup:
+
+#+begin_example
+  ./LANGUAGE-ts-mode.el
+  ./tests/test-LANGUAGE-ts-mode-indent-xr.el
+  ./tests/test-LANGUAGE-ts-mode-indent-xr-files/indent_test1.lang
+  ./tests/test-LANGUAGE-ts-mode-indent-xr-files/indent_test1_expected.org  // 
generated for you
+  ....
+#+end_example
+
+where =test-LANGUAGE-ts-mode-indent-xr.el= contains:
+
+#+begin_src emacs-lisp
+  (require 't-utils)
+  (require 'LANGUAGE-ts-mode)
+
+  (defvar test-LANGUAGE-ts-mode-indent-xr--file nil)
+
+  (defun test-LANGUAGE-ts-mode-indent-xr--file (lang-file)
+    "Test an individual LANG-FILE.
+  This is provided for debugging.
+    M-: (test-LANGUAGE-ts-mode-indent-xr--file
+        \"test-LANGUAGE-ts-mode-indent-xr-files/LANG-FILE\")"
+    (let ((test-LANGUAGE-ts-mode-indent-xr--file lang-file))
+      (ert-run-tests-interactively "test-LANGUAGE-ts-mode-indent-xr")))
+
+  (ert-deftest test-LANGUAGE-ts-mode-indent-xr ()
+    "Test indent using ./test-LANGUAGE-ts-mode-indent-xr-files/NAME.lang.
+  Using ./test-LANGUAGE-ts-mode-indent-xr-files/NAME.lang, compare typing
+  commands via `t-utils-xr' Lisp commans in the *.lang files and compare
+  agains ./test-LANGUAGE-ts-mode-indent-xr-files/NAME_expected.org.  This
+  loops on all ./test-LANGUAGE-ts-mode-indent-xr-files/NAME.lang files.
+
+  To add a test, create
+    ./test-LANGUAGE-ts-mode-indent-xr-files/NAME.lang
+  and run this function.  The baseline is saved for you as
+    ./test-LANGUAGE-ts-mode-indent-xr-files/NAME_expected.org~
+  after validating it, rename it to
+    ./test-LANGUAGE-ts-mode-indent-xr-files/NAME_expected.org"
+
+    (let* ((test-name "test-LANGUAGE-ts-mode-indent-xr")
+           (lang-files (t-utils-get-files
+                     test-name
+                     (rx ".lang" eos)
+                     nil
+                     test-LANGUAGE-ts-mode-indent-xr--file)))
+      (t-utils-error-if-no-treesit-for 'LANGUAGE test-name)
+      (t-utils-test-xr test-name lang-files)))
+#+end_src
+
+An example =./tests/test-LANGUAGE-ts-mode-indent-xr-files/indent_test1.lang= 
where =%= is starts a
+comment:
+
+#+begin_example
+  % -*- LANGAUAGE-ts -*-
+  % (t-utils-xr "C-a" "C-n" (insert "someVariable = {") "C-e" "C-m" (insert 
"1234") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+#+end_example
+
+The execute and record function, =t-utils-xr= runs the Emacs commands which 
creates below the
+comment line.
+
+#+begin_example
+  someVariable = {
+                   1234
+                 }
+#+end_example
+
+The commands are recorded into
+=./tests/test-LANGUAGE-ts-mode-indent-xr-files/indent_test1_expected.org~= and 
compared against
+baseline =indent_test1_expected.org=. If the baseline doesn't exist you are 
asked to review
+=indent_test1_expected.org~= and rename it to indent_test1_expected.org if it 
looks good.
+
 ** Sweep test: Indent
 
 We define a sweep test to be a test that tries an action on a large number of 
files and reports
@@ -1545,7 +1627,7 @@ the color marker is) to see messages that were displayed 
by your test.
 If the =./tests/test-LANUGAGE-ts-mode-file-encoding-files/NAME*_expected.txt~= 
files look good
 rename them to 
=./tests/test-LANUGAGE-ts-mode-file-encoding-files/NAME*_expected.txt= (per the
 messages shown by ert).
-  
+
 * Final version
 
 TODO
diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index e7c3ec8bd0..873fd898e9 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -838,7 +838,8 @@ For optional _NODE, PARENT, and _BOL see 
`treesit-simple-indent-rules'."
   ;; - Otherwise, matlab-ts-mode--function-indent-level is 0, we will 
"upgrade" it to
   ;;   matlab-ts-mode--indent-level if function end's appear.
 
-  (let ((root (if (and parent (string= (treesit-node-type parent) 
"function_definition"))
+  (let ((root (if (and parent
+                       (string= (treesit-node-type parent) 
"function_definition"))
                   parent
                 (treesit-buffer-root-node))))
     (if (treesit-search-subtree (treesit-buffer-root-node) "\\`ERROR\\'")
@@ -849,7 +850,7 @@ For optional _NODE, PARENT, and _BOL see 
`treesit-simple-indent-rules'."
       (let ((first-fcn (treesit-search-subtree root (rx bos 
"function_definition" eos))))
         (if (not first-fcn)
             ;; assume that if functions are added they will have ends
-            (setq-local matlab-ts--function-indent-level t)
+            (setq-local matlab-ts-mode--function-indent-level 
matlab-ts-mode--indent-level)
           (let ((have-end (string= (treesit-node-type (treesit-node-child 
first-fcn -1)) "end")))
             (if (equal matlab-ts-mode--function-indent-level 'unset)
                 (setq-local matlab-ts-mode--function-indent-level
@@ -869,34 +870,6 @@ For optional _NODE, PARENT, and _BOL see 
`treesit-simple-indent-rules'."
   ;; A 0 indicates functions are not terminated by an end keyword
   (not (= matlab-ts-mode--function-indent-level 0)))
 
-(defun matlab-ts-mode--prev-real-line (_n _p bol &rest _)
-  "Return point of first non-whitespace looking backward.
-BOL, beginning-of-line point, is where to start from."
-  (save-excursion
-    (goto-char bol)
-    (forward-line -1)
-    (while (and (not (bobp))
-                (looking-at "^[ \t]*$"))
-      (forward-line -1))
-    (skip-chars-forward " \t")
-    (point)))
-
-(defun matlab-ts-mode--prev-real-line-is (node-type prev-real-line-node-type)
-  "Node type matcher and previous real line type matcher.
-Returns non-nil if the current tree-sitter node matches NODE-TYPE and
-the previous non-empty line tree-sitter node type matches
-PREV-REAL-LINE-NODE-TYPE.  NODE-TYPE can be nil when there's no current
-node or a regular expression.  PREV-REAL-LINE-NODE-TYPE is a regular
-expression."
-  (lambda (node parent bol &rest _)
-    (when (or (and (not node-type)
-                   (not node))
-              (and node-type
-                   (string-match-p node-type (or (treesit-node-type node) 
""))))
-      (let* ((prev-real-line-bol (matlab-ts-mode--prev-real-line node parent 
bol))
-             (p-node (treesit-node-at prev-real-line-bol)))
-        (string-match-p prev-real-line-node-type (or (treesit-node-type 
p-node) ""))))))
-
 (defvar matlab-ts--indent-debug-rule
   '((lambda (node parent bol)
       (message "-->N:%S P:%S BOL:%S GP:%S NPS:%S"
@@ -961,9 +934,167 @@ cell or matrix row."
           (- first-column array-column))
       matlab-ts-mode--array-indent-level)))
 
+(defvar matlab-ts-mode--error-switch-matcher-pair)
+
+(defun matlab-ts-mode--error-switch-matcher (node parent bol &rest _)
+  "Is NODE PARENT in an ERROR node for a switch statement?
+If so, set `matlab-ts-mode--error-switch-matcher-pair'.  BOL,
+beginning-of-line point, is where we start looking for the error node."
+  (when (or (not node)
+            (string= (treesit-node-type parent) "ERROR"))
+    (save-excursion
+      (goto-char bol)
+      (re-search-backward "[^ \t\n\r]" nil t)
+
+      (let ((check-node (treesit-node-at (point))))
+
+        ;; ;; when on "\n" move to prior node
+        ;; (when (and (string= (treesit-node-type check-node) "\n")
+        ;;            (re-search-backward "[^ \t\n\r]" nil t))
+        ;;   (setq check-node (treesit-node-at (point))))
+
+        (let ((error-node (treesit-node-parent check-node)))
+          (while (and error-node
+                      (not (string= (treesit-node-type error-node) "ERROR")))
+            (setq error-node (treesit-node-parent error-node)))
+
+          ;; In an error-node, see if this error is due to an incomplete 
switch statement.
+          (when error-node
+            (let ((child-node (treesit-node-child error-node 0)))
+              (when (and child-node
+                         (string-match-p (rx bos (or "switch" "case" 
"otherwise") eos)
+                                         (treesit-node-type child-node)))
+                (setq matlab-ts-mode--error-switch-matcher-pair
+                      (cons (treesit-node-start child-node)
+                            matlab-ts-mode--switch-indent-level))))))))))
+
+(defun matlab-ts-mode--error-switch-anchor (&rest _)
+  "Return the anchor computed by `matlab-ts-mode--error-switch-matcher'."
+  (car matlab-ts-mode--error-switch-matcher-pair))
+
+(defun matlab-ts-mode--error-switch-offset (&rest _)
+  "Return the offset computed by `matlab-ts-mode--error-switch-matcher'."
+  (cdr matlab-ts-mode--error-switch-matcher-pair))
+
+
+(defvar matlab-ts-mode--error-row-matcher-pair)
+
+(defun matlab-ts-mode--error-row-matcher (_node _parent bol &rest _)
+  "Is s point within an ERROR node and can we determine indent?
+If so, set `matlab-ts-mode--error-row-matcher-pair' to the anchor and
+offset we should use and return non-nil.  BOL, beginning-of-line point,
+is where we start looking for the error node."
+  ;; 1) Given
+  ;;            mat = [ [1, 2]; [3, 4];
+  ;;        TAB>
+  ;;    we have parse tree
+  ;;        (ERROR (identifier) = [
+  ;;         (row
+  ;;          (matrix [
+  ;;           (row (number) , (number))
+  ;;           ]))
+  ;;         ;
+  ;;         (row
+  ;;          (matrix [
+  ;;           (row (number) , (number))
+  ;;           ]))
+  ;;         ;)
+  ;;
+  ;; 2) Now add ellipsis continuation:
+  ;;            mat = [ [1, 2]; [3, 4]; ...
+  ;;        TAB>
+  ;; we'll have parse tree
+  ;;       (source_file
+  ;;        (ERROR (identifier) = [
+  ;;         (row
+  ;;          (matrix [
+  ;;           (row (number) , (number))
+  ;;           ]))
+  ;;         ;
+  ;;         (row
+  ;;          (matrix [
+  ;;           (row (number) , (number))
+  ;;           ]))
+  ;;         ;)
+  ;;        (line_continuation))
+  ;; Notice the line_continuation is not under the ERROR node, so we need to 
find that,
+  ;; hence below we navigate over line_continuation's.
+  ;;
+  ;; See https://github.com/acristoffers/tree-sitter-matlab/issues/46
+
+  (save-excursion
+    (goto-char bol)
+
+    ;; Move inside the error node if at an error node.
+    (when (string= (treesit-node-type (treesit-node-at (point))) "ERROR")
+      (re-search-backward "[^ \t\n\r]" nil t))
+
+    ;; If on a "[" or "{" move back (we don't want to shift the current line)
+    (when (string-match-p (rx bos (or "[" "{") eos) (treesit-node-type 
(treesit-node-at (point))))
+      (re-search-backward "[^ \t\n\r]" nil t))
+
+    ;; Walk over line_continuation, ",", ";" and identify the "check node" we 
should be looking
+    ;; at.
+    (let ((check-node (treesit-node-at (point))))
+      (while (string-match-p (rx bos (or "line_continuation" "," ";") eos)
+                             (treesit-node-type check-node))
+        (goto-char (treesit-node-start check-node))
+        (if (re-search-backward "[^ \t\n\r]" nil t)
+            (setq check-node (treesit-node-at (point)))
+          ;; at start of buffer
+          (setq check-node nil)))
+
+      (when check-node
+        ;; Look to see if we are under an ERROR node
+        (let ((error-node (treesit-node-parent check-node)))
+          (while (and error-node
+                      (not (string= (treesit-node-type error-node) "ERROR")))
+            (setq error-node (treesit-node-parent error-node)))
+
+          ;; In an error-node, see if this error is due to an incomplete cell 
or matrix,
+          ;; if so return the start point of the row to anchor against.
+          (when error-node
+            (while (and check-node
+                        (not (string-match-p (rx bos (or "row" "[" "{") eos)
+                                             (treesit-node-type check-node))))
+              (setq check-node (treesit-node-parent check-node)))
+
+            (when check-node
+              (cond
+               ((string-match-p (rx bos (or "[" "{") eos) (treesit-node-type 
check-node))
+                (setq matlab-ts-mode--error-row-matcher-pair
+                      (cons (treesit-node-start check-node) 2)))
+               ;; Case: looking at row?
+               (t
+                ;; Find first row to anchor against
+                (let ((prev-sibling (treesit-node-prev-sibling check-node)))
+                 (while prev-sibling
+                   (when (string= (treesit-node-type prev-sibling) "row")
+                     (setq check-node prev-sibling))
+                   (setq prev-sibling (treesit-node-prev-sibling 
prev-sibling))))
+                ;; In an ERROR node of a matrix or cell, return anchor
+                (setq matlab-ts-mode--error-row-matcher-pair
+                      (cons (treesit-node-start check-node) 0)))))))))))
+
+(defun matlab-ts-mode--error-row-anchor (&rest _)
+  "Return the anchor computed by `matlab-ts-mode--error-row-matcher'."
+  (car matlab-ts-mode--error-row-matcher-pair))
+
+(defun matlab-ts-mode--error-row-offset (&rest _)
+  "Return the offset computed by `matlab-ts-mode--error-row-matcher'."
+  (cdr matlab-ts-mode--error-row-matcher-pair))
+
 (defvar matlab-ts-mode--indent-rules
   `((matlab
 
+     ;; I-Rule: cell/matrix row matcher when in an error node
+     ;;         mat0 = [1, 2
+     ;;                 ^            <-- TAB or RET on prior line goes here
+     ;; See: tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat*.m
+     (,#'matlab-ts-mode--error-row-matcher
+      ,#'matlab-ts-mode--error-row-anchor
+      ,#'matlab-ts-mode--error-row-offset)
+
      ;; I-Rule: classdef's, function's, or code for a script that is at the 
top-level
      ((parent-is ,(rx bos "source_file" eos)) column-0 0)
 
@@ -1046,8 +1177,8 @@ cell or matrix row."
                       eos))
       parent ,matlab-ts-mode--indent-level)
 
-     ;; I-Rule: function a<RET>
-     ;;         end
+     ;; I-Rule: function a = indent_xr_fun()<RET>
+     ;; See: tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun.m
      ((n-p-gp nil ,(rx bos "\n" eos) ,(rx bos (or "function_definition") eos))
       grand-parent ,matlab-ts-mode--indent-level)
 
@@ -1085,14 +1216,6 @@ cell or matrix row."
      ;; <TAB>       y = 2;
      ((parent-is ,(rx bos "block" eos)) parent 0)
 
-     ;; I-Rule: "switch var" and we type RET after the var
-     (,(matlab-ts-mode--prev-real-line-is (rx bos "\n" eos) (rx bos "switch" 
eos))
-      ,#'matlab-ts-mode--prev-real-line ,matlab-ts-mode--switch-indent-level)
-
-     ;; I-Rule: "function foo()" and we type RET after the ")"
-     (,(matlab-ts-mode--prev-real-line-is nil (rx bos "function" eos))
-      ,#'matlab-ts-mode--prev-real-line ,matlab-ts-mode--indent-level)
-
      ;; I-Rule:  a = ...
      ;; <TAB>        1;
      ((parent-is ,(rx bos "assignment" eos)) parent 
,matlab-ts-mode--indent-level)
@@ -1115,6 +1238,11 @@ cell or matrix row."
      ;; See: tests/test-matlab-ts-mode-indent-files/indent_matrix.m
      ((parent-is ,(rx bos (or "cell" "matrix") eos)) parent 
,#'matlab-ts-mode--row-indent-level)
 
+     ;; I-Rule:   cell1 = { ...
+     ;; See: ./test-matlab-ts-mode-indent-xr-files/indent_xr_cell1.m
+     ((n-p-gp nil ,(rx bos "line_continuation" eos) ,(rx bos (or "cell" 
"matrix") eos))
+      grand-parent ,#'matlab-ts-mode--row-indent-level)
+
      ;; I-Rule:  function [   ...              |    function name (   ...
      ;; <TAB>              a, ... % comment    |                   a, ... % 
comment
      ((parent-is ,(rx bos (or "multioutput_variable" "function_arguments") 
eos)) parent 1)
@@ -1145,13 +1273,6 @@ cell or matrix row."
              (string= (treesit-node-type parent) "\n")))
       grand-parent 0)
 
-     ;; I-Rule: In an empty line, string, etc. just maintain indent
-     ;;         switch in
-     ;;           case 10
-     ;;             disp('11');
-     ;; <TAB>
-     (no-node ,#'matlab-ts-mode--prev-real-line 0)
-
      ;; I-Rule: comments in classdef's
      ((parent-is ,(rx bos "class_definition" eos)) parent 
,matlab-ts-mode--indent-level)
 
@@ -1177,6 +1298,15 @@ cell or matrix row."
       ;; parent ,matlab-ts-mode--indent-level)
       parent ,#'matlab-ts-mode--indent-continuation-level)
 
+     ;; I-Rule: In an empty line, string, etc. just maintain indent
+     ;;         switch fcn1(a)
+     ;;           ^                       <== TAB or RET on prior line goes 
here
+     ;; Parent node could be "source_file", so must be before
+     ;;   ((parent-is ,(rx bos "source_file" eos)) column-0 0)
+     (,#'matlab-ts-mode--error-switch-matcher
+      ,#'matlab-ts-mode--error-switch-anchor
+      ,#'matlab-ts-mode--error-switch-offset)
+
      ;; I-Rule: handle syntax errors by not indenting
      ;; See: tests/test-matlab-ts-mode-indent-files/indent_nested_error.m
      ((lambda (node &rest _)
@@ -1348,8 +1478,8 @@ cell or matrix row."
                             "methods"
                             "events"
                             "enumeration")))
-
-     (text ,(rx bos (or "comment" "string") eos))
+     ;; TODO is "line_continuation" correct here - add test
+     (text ,(rx bos (or "comment" "string" "line_continuation") eos))
 
      ))
   "Tree-sitter things for movement.")
@@ -1779,8 +1909,8 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
 
     ;; Indent.
     ;; See: ./tests/test-matlab-ts-mode-indent.el
-    (matlab-ts-mode--set-function-indent-level)
-    (setq-local indent-tabs-mode nil) ;; for consistency between Unix and 
Windows we don't use TABs.
+    (matlab-ts-mode--set-function-indent-level) ;; Set function indent level 
on file load
+    (setq-local indent-tabs-mode nil) ;; For consistency between Unix and 
Windows we don't use TABs.
     (setq-local treesit-simple-indent-rules
                 (if treesit--indent-verbose ;; add debugging print as first 
rule?
                     (list (append `,(list (caar matlab-ts-mode--indent-rules))
@@ -1838,22 +1968,9 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
     ;; TODO double check t-utils.el help, extract the help and put in treesit 
how to
     ;;
     ;; TODO double check indent rules to see if they can be simplified
-    ;; TODO update --indent-rules to have See: test file comments.
-    ;;
-    ;; TODO indent
-    ;;      mat = [1, 2
-    ;;             ^               <== RET on previous line or TAB should be 
here
+    ;; TODO update --indent-rules to have "See: test file" comments.
     ;;
     ;; TODO indent
-    ;;      c = { ...
-    ;;            {'one'           <== TAB to here (first RET or TAB, then 
type)
-    ;;
-    ;; TODO indent (good add test?):
-    ;;      c = { ...
-    ;;            {'one', 'two'}, ...
-    ;;            {'three', 'four'}, ...
-    ;;          }
-    ;; TODO indent
     ;;      function a=foo
     ;;          a = ...
     ;;              ^              <== RET on previous line or TAB should be 
here
@@ -1884,6 +2001,19 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
     ;;       s = sprintf("see %d:%d", 1, 2)
     ;;                        ^^ ^^            <== font-lock 
formatting_sequence
     ;;
+    ;; TODO Mismatched parentheses
+    ;;      Start with:
+    ;;        Line1:  mat = [ [1, 2]; [3, 4];
+    ;;        Line2:
+    ;;      then type a ']' on the 2nd line, in echo area, see: Mismatched 
parentheses
+    ;;
+    ;; TODO improve formatting t-utils-xr print's, e.g. improve:
+    ;;        standard-output:
+    ;;          "function a = indent_xr_fun()
+    ;;            a = 1;
+    ;;        end
+    ;;        "
+
     (treesit-major-mode-setup)
 
     ;; Correct forward-sexp setup created by `treesit-major-mode' so that in 
comments we do normal
diff --git a/tests/t-utils.el b/tests/t-utils.el
index 7f59fc3406..09fb1e2dc4 100644
--- a/tests/t-utils.el
+++ b/tests/t-utils.el
@@ -980,7 +980,8 @@ See `t-utils-test-indent' for LINE-MANIPULATOR."
         ;; result is nil or an error message list of strings
         error-msg))))
 
-(defun t-utils-test-indent (test-name lang-files &optional line-manipulator 
error-nodes-regexp)
+(defun t-utils-test-indent (test-name lang-files
+                                      &optional indent-checker 
line-manipulator error-nodes-regexp)
   "Test indent on each file in LANG-FILES list.
 Compare indent of each NAME.LANG in LANG-FILES against NAME_expected.LANG.
 TEST-NAME is used in messages.
@@ -999,6 +1000,9 @@ Two methods are used to indent each file in LANG-FILES,
 
  1. (indent-region (point-min) (point-man))
 
+    If optional INDENT-CHECKER function is provided, that is called with
+    the temporary buffer in context after the `indent-region'.
+
  2. Indent via typing simulation.  If lang-file has no error nodes in the
     parse tree, indent is simulated by \"typing lang-file\" to exercise
     TAB and RET, see `t-utils--test-indent-typing'.  In tree-sitter
@@ -1086,7 +1090,12 @@ To debug a specific indent test file
 
             (message "START: %s <indent-region> %s" test-name lang-file)
             (setq lang-file-major-mode major-mode)
+
             (indent-region (point-min) (point-max))
+
+            (when indent-checker
+              (funcall indent-checker))
+
             (t-utils--trim)
             (let ((got (buffer-substring (point-min) (point-max)))
                   (got-file (concat expected-file "~")))
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1.m
new file mode 100644
index 0000000000..3c9aff68c1
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1.m
@@ -0,0 +1,2 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1_expected.org
new file mode 100644
index 0000000000..19cb7cded6
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_cell1_expected.org
@@ -0,0 +1,272 @@
+#+startup: showall
+
+* Executing commands from indent_xr_cell1.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  300
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" 
(insert "{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" 
(insert "{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") 
(print (buffer-substring-no-properties (point) (point-max))))
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:  301
+  : 3:0: 
+  :      ^
+  No buffer modifications
+
+- Invoking      : (insert "cell1 = { ...")
+  Start point   :  301
+  Moved to point:  314
+  : 3:13: cell1 = { ...
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,2 +1,3 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
++cell1 = { ...
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :  314
+  No point movement
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :  314
+  Moved to point:  325
+  : 4:10:           
+  :                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,3 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+-cell1 = { ...
+\ No newline at end of file
++cell1 = { ...
++          
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : (insert "{'one', ...")
+  Start point   :  325
+  Moved to point:  336
+  : 4:21:           {'one', ...
+  :                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,4 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+ cell1 = { ...
+-          
+\ No newline at end of file
++          {'one', ...
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  336
+  Moved to point:  348
+  : 5:11:            
+  :                  ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,4 +1,5 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+ cell1 = { ...
+-          {'one', ...
+\ No newline at end of file
++          {'one', ...
++           
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : (insert "'two'}, ...")
+  Start point   :  348
+  Moved to point:  359
+  : 5:22:            'two'}, ...
+  :                             ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,4 +2,4 @@
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+ cell1 = { ...
+           {'one', ...
+-           
+\ No newline at end of file
++           'two'}, ...
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  359
+  Moved to point:  370
+  : 6:10:           
+  :                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,4 +2,5 @@
+ % (t-utils-xr "C-a" "C-n" (insert "cell1 = { ...") "C-e" "C-m" (insert 
"{'one', ...") "C-m" (insert "'two'}, ...") "C-m" (insert "...") "C-m" (insert 
"{'three'}") "C-m" (insert "};") "C-m" (re-search-backward "^cell") (print 
(buffer-substring-no-properties (point) (point-max))))
+ cell1 = { ...
+           {'one', ...
+-           'two'}, ...
+\ No newline at end of file
++           'two'}, ...
++          
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : (insert "...")
+  Start point   :  370
+  Moved to point:  373
+  : 6:13:           ...
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,4 +3,4 @@
+ cell1 = { ...
+           {'one', ...
+            'two'}, ...
+-          
+\ No newline at end of file
++          ...
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  373
+  Moved to point:  384
+  : 7:10:           
+  :                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,4 +3,5 @@
+ cell1 = { ...
+           {'one', ...
+            'two'}, ...
+-          ...
+\ No newline at end of file
++          ...
++          
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : (insert "{'three'}")
+  Start point   :  384
+  Moved to point:  393
+  : 7:19:           {'three'}
+  :                          ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -4,4 +4,4 @@
+           {'one', ...
+            'two'}, ...
+           ...
+-          
+\ No newline at end of file
++          {'three'}
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  393
+  Moved to point:  404
+  : 8:10:           
+  :                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -4,4 +4,5 @@
+           {'one', ...
+            'two'}, ...
+           ...
+-          {'three'}
+\ No newline at end of file
++          {'three'}
++          
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : (insert "};")
+  Start point   :  404
+  Moved to point:  406
+  : 8:12:           };
+  :                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -5,4 +5,4 @@
+            'two'}, ...
+           ...
+           {'three'}
+-          
+\ No newline at end of file
++          };
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  406
+  Moved to point:  405
+  : 9:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -5,4 +5,4 @@
+            'two'}, ...
+           ...
+           {'three'}
+-          };
+\ No newline at end of file
++        };
+  #+end_src diff
+
+- Invoking      : (re-search-backward "^cell")
+  Start point   :  405
+  Moved to point:  301
+  : 3:0: cell1 = { ...
+  :      ^
+  No buffer modifications
+
+- Invoking      : (print (buffer-substring-no-properties (point) (point-max)))
+  Start point   :  301
+  No point movement
+  standard-output:
+    "cell1 = { ...
+            {'one', ...
+             'two'}, ...
+            ...
+            {'three'}
+          };
+  "
+  No buffer modifications
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun.m
new file mode 100644
index 0000000000..4dcd0b5684
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun.m
@@ -0,0 +1,2 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun_expected.org
new file mode 100644
index 0000000000..8f635e30a8
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_fun_expected.org
@@ -0,0 +1,139 @@
+#+startup: showall
+
+* Executing commands from indent_xr_fun.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  227
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") 
"C-m" (insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") 
(print (buffer-substring-no-properties (point) (point-max))))
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:  228
+  : 3:0: 
+  :      ^
+  No buffer modifications
+
+- Invoking      : (insert "function a = indent_xr_fun()")
+  Start point   :  228
+  Moved to point:  256
+  : 3:28: function a = indent_xr_fun()
+  :                                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,2 +1,3 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
++function a = indent_xr_fun()
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  256
+  Moved to point:  257
+  : 4:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,3 +1,3 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+-function a = indent_xr_fun()
+\ No newline at end of file
++function a = indent_xr_fun()
+  #+end_src diff
+
+- Invoking      : (insert "a = 1;")
+  Start point   :  257
+  Moved to point:  263
+  : 4:6: a = 1;
+  :            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,3 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+ function a = indent_xr_fun()
++a = 1;
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  263
+  Moved to point:  268
+  : 5:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,4 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+ function a = indent_xr_fun()
+-a = 1;
+\ No newline at end of file
++    a = 1;
+  #+end_src diff
+
+- Invoking      : (insert "end")
+  Start point   :  268
+  Moved to point:  271
+  : 5:3: end
+  :         ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,3 +2,4 @@
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+ function a = indent_xr_fun()
+     a = 1;
++end
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  271
+  Moved to point:  272
+  : 6:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,4 +2,4 @@
+ % (t-utils-xr "C-a" "C-n" (insert "function a = indent_xr_fun()") "C-m" 
(insert "a = 1;") "C-m" (insert "end") "C-m" (re-search-backward "^fun") (print 
(buffer-substring-no-properties (point) (point-max))))
+ function a = indent_xr_fun()
+     a = 1;
+-end
+\ No newline at end of file
++end
+  #+end_src diff
+
+- Invoking      : (re-search-backward "^fun")
+  Start point   :  272
+  Moved to point:  228
+  : 3:0: function a = indent_xr_fun()
+  :      ^
+  No buffer modifications
+
+- Invoking      : (print (buffer-substring-no-properties (point) (point-max)))
+  Start point   :  228
+  No point movement
+  standard-output:
+    "function a = indent_xr_fun()
+      a = 1;
+  end
+  "
+  No buffer modifications
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0.m
new file mode 100644
index 0000000000..c7e7e1045d
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0.m
@@ -0,0 +1,6 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "3, 4]") "C-m")
+mat0 = [1, 2
+
+
+
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0_expected.org
new file mode 100644
index 0000000000..14947d9fae
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat0_expected.org
@@ -0,0 +1,81 @@
+#+startup: showall
+
+* Executing commands from indent_xr_mat0.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "3, 4]") "C-m")
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :   82
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "3, 4]") "C-m")
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:   83
+  : 3:0: mat0 = [1, 2
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :   83
+  Moved to point:   95
+  : 3:12: mat0 = [1, 2
+  :                   ^
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :   95
+  Moved to point:  104
+  : 4:8:         
+  :              ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,6 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "3, 4]") "C-m")
+ mat0 = [1, 2
++        
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "3, 4]")
+  Start point   :  104
+  Moved to point:  109
+  : 4:13:         3, 4]
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "3, 4]") "C-m")
+ mat0 = [1, 2
+-        
++        3, 4]
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  109
+  Moved to point:  110
+  : 5:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -5,3 +5,4 @@
+ 
+ 
+ 
++
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1.m
new file mode 100644
index 0000000000..ce724a2c82
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1.m
@@ -0,0 +1,8 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-n" "C-i" "C-n" "C-i" "C-e" "C-m" (insert "];\n"))
+mat1 = [ [1, 2], [3, 4], ...
+   ...
+ ...
+
+
+
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1_expected.org
new file mode 100644
index 0000000000..14113a7171
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat1_expected.org
@@ -0,0 +1,119 @@
+#+startup: showall
+
+* Executing commands from indent_xr_mat1.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-n" "C-i" "C-n" "C-i" "C-e" "C-m" (insert "];\n"))
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :   99
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-n" "C-i" "C-n" "C-i" "C-e" "C-m" (insert 
"];\n"))
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:  100
+  : 3:0: mat1 = [ [1, 2], [3, 4], ...
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :  100
+  Moved to point:  129
+  : 4:0:    ...
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  129
+  Moved to point:  138
+  : 4:9:          ...
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-n" "C-i" "C-n" "C-i" "C-e" "C-m" (insert "];\n"))
+ mat1 = [ [1, 2], [3, 4], ...
+-   ...
++         ...
+  ...
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-n" = next-line
+  Start point   :  138
+  Moved to point:  146
+  : 5:4:  ...
+  :          ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  146
+  Moved to point:  154
+  : 5:12:          ...
+  :                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,7 +2,7 @@
+ % (t-utils-xr "C-a" "C-n" "C-n" "C-i" "C-n" "C-i" "C-e" "C-m" (insert "];\n"))
+ mat1 = [ [1, 2], [3, 4], ...
+          ...
+- ...
++         ...
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :  154
+  No point movement
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :  154
+  Moved to point:  164
+  : 6:9:          
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,6 +3,7 @@
+ mat1 = [ [1, 2], [3, 4], ...
+          ...
+          ...
++         
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "];
+")
+  Start point   :  164
+  Moved to point:  167
+  : 7:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,8 @@
+ mat1 = [ [1, 2], [3, 4], ...
+          ...
+          ...
+-         
++         ];
++
+ 
+ 
+ 
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2.m
new file mode 100644
index 0000000000..2be12925e5
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2.m
@@ -0,0 +1,7 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[5, 6]];"))
+mat2 = [ [1, 2]; [3, 4];
+
+
+
+        
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2_expected.org
new file mode 100644
index 0000000000..bae21b672a
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat2_expected.org
@@ -0,0 +1,65 @@
+#+startup: showall
+
+* Executing commands from indent_xr_mat2.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[5, 6]];"))
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :   79
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[5, 6]];"))
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:   80
+  : 3:0: mat2 = [ [1, 2]; [3, 4];
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :   80
+  Moved to point:  104
+  : 3:24: mat2 = [ [1, 2]; [3, 4];
+  :                               ^
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :  104
+  Moved to point:  114
+  : 4:9:          
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,6 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[5, 6]];"))
+ mat2 = [ [1, 2]; [3, 4];
++         
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "[5, 6]];")
+  Start point   :  114
+  Moved to point:  122
+  : 4:17:          [5, 6]];
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[5, 6]];"))
+ mat2 = [ [1, 2]; [3, 4];
+-         
++         [5, 6]];
+ 
+ 
+ 
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3.m
new file mode 100644
index 0000000000..74e0176422
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3.m
@@ -0,0 +1,6 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[3, 4]];\n"))
+mat3 = [[1, 2]
+
+
+
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3_expected.org
new file mode 100644
index 0000000000..00dd5075d2
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat3_expected.org
@@ -0,0 +1,67 @@
+#+startup: showall
+
+* Executing commands from indent_xr_mat3.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[3, 4]];\n"))
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :   81
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[3, 4]];\n"))
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:   82
+  : 3:0: mat3 = [[1, 2]
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :   82
+  Moved to point:   96
+  : 3:14: mat3 = [[1, 2]
+  :                     ^
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :   96
+  Moved to point:  105
+  : 4:8:         
+  :              ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,6 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[3, 4]];\n"))
+ mat3 = [[1, 2]
++        
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "[3, 4]];
+")
+  Start point   :  105
+  Moved to point:  114
+  : 5:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,8 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[3, 4]];\n"))
+ mat3 = [[1, 2]
+-        
++        [3, 4]];
++
+ 
+ 
+ 
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4.m
new file mode 100644
index 0000000000..3e1e7c93d8
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4.m
@@ -0,0 +1,14 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+mat4 = [
+
+
+
+
+
+
+
+
+
+
+
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4_expected.org
new file mode 100644
index 0000000000..2889ab5593
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_mat4_expected.org
@@ -0,0 +1,167 @@
+#+startup: showall
+
+* Executing commands from indent_xr_mat4.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  131
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" 
(insert "[3, 4];") "C-m" (insert "];") "C-m")
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:  132
+  : 3:0: mat4 = [
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :  132
+  Moved to point:  140
+  : 3:8: mat4 = [
+  :              ^
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :  140
+  Moved to point:  150
+  : 4:9:          
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,6 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+ mat4 = [
++         
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "[1, 2];
+")
+  Start point   :  150
+  Moved to point:  158
+  : 5:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,8 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+ mat4 = [
+-         
++         [1, 2];
++
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  158
+  Moved to point:  167
+  : 5:9:          
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,7 +2,7 @@
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+ mat4 = [
+          [1, 2];
+-
++         
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "[3, 4];")
+  Start point   :  167
+  Moved to point:  174
+  : 5:16:          [3, 4];
+  :                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,7 +2,7 @@
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "[1, 2];\n") "C-i" (insert "[3, 
4];") "C-m" (insert "];") "C-m")
+ mat4 = [
+          [1, 2];
+-         
++         [3, 4];
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  174
+  Moved to point:  184
+  : 6:9:          
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,6 +3,7 @@
+ mat4 = [
+          [1, 2];
+          [3, 4];
++         
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "];")
+  Start point   :  184
+  Moved to point:  186
+  : 6:11:          ];
+  :                  ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,7 @@
+ mat4 = [
+          [1, 2];
+          [3, 4];
+-         
++         ];
+ 
+ 
+ 
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  186
+  Moved to point:  185
+  : 7:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,8 @@
+ mat4 = [
+          [1, 2];
+          [3, 4];
+-         ];
++       ];
++
+ 
+ 
+ 
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch.m 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch.m
new file mode 100644
index 0000000000..c4b0a5a8a1
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch.m
@@ -0,0 +1,16 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+switch fcn1(a)
+
+
+  
+
+
+  
+    
+
+
+
+
+
+
diff --git 
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch_expected.org 
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch_expected.org
new file mode 100644
index 0000000000..fc310ced69
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_switch_expected.org
@@ -0,0 +1,162 @@
+#+startup: showall
+
+* Executing commands from indent_xr_switch.m:2:2:
+
+  (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  132
+  Moved to point:   21
+  : 2:0: % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :   21
+  Moved to point:  133
+  : 3:0: switch fcn1(a)
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   :  133
+  Moved to point:  147
+  : 3:14: switch fcn1(a)
+  :                     ^
+  No buffer modifications
+
+- Invoking      : "C-m" = newline
+  Start point   :  147
+  Moved to point:  150
+  : 4:2:   
+  :        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,6 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+ switch fcn1(a)
++  
+ 
+ 
+   
+  #+end_src diff
+
+- Invoking      : (insert "case 1")
+  Start point   :  150
+  Moved to point:  156
+  : 4:8:   case 1
+  :              ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,7 +1,7 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+ switch fcn1(a)
+-  
++  case 1
+ 
+ 
+   
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  156
+  Moved to point:  161
+  : 5:4:     
+  :          ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,6 +2,7 @@
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+ switch fcn1(a)
+   case 1
++    
+ 
+ 
+   
+  #+end_src diff
+
+- Invoking      : (insert "disp('1');")
+  Start point   :  161
+  Moved to point:  171
+  : 5:14:     disp('1');
+  :                     ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,7 +2,7 @@
+ % (t-utils-xr "C-a" "C-n" "C-e" "C-m" (insert "case 1") "C-m" (insert 
"disp('1');") "C-m" (insert "end") "C-m")
+ switch fcn1(a)
+   case 1
+-    
++    disp('1');
+ 
+ 
+   
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  171
+  Moved to point:  172
+  : 6:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -5,6 +5,7 @@
+     disp('1');
+ 
+ 
++
+   
+ 
+ 
+  #+end_src diff
+
+- Invoking      : (insert "end")
+  Start point   :  172
+  Moved to point:  175
+  : 6:3: end
+  :         ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,7 @@
+ switch fcn1(a)
+   case 1
+     disp('1');
+-
++end
+ 
+ 
+   
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  175
+  Moved to point:  176
+  : 7:0: 
+  :      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -6,6 +6,7 @@
+ end
+ 
+ 
++
+   
+ 
+ 
+  #+end_src diff
diff --git a/tests/test-matlab-ts-mode-indent-xr.el 
b/tests/test-matlab-ts-mode-indent-xr.el
new file mode 100644
index 0000000000..a5074fd796
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr.el
@@ -0,0 +1,67 @@
+;;; test-matlab-ts-mode-indent-xr.el --- -*- 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:
+;;
+;; Run indent tests using `t-utils-xr' in
+;;   ./test-matlab-ts-mode-indent-xr-files/NAME.m
+;; comparing against
+;;   ./test-matlab-ts-mode-indent-xr-files/NAME_expected.org
+;;
+
+;;; Code:
+
+(require 't-utils)
+(require 'matlab-ts-mode)
+
+(defvar test-matlab-ts-mode-indent-xr--file nil)
+
+(defun test-matlab-ts-mode-indent-xr--file (m-file)
+  "Test an individual M-FILE.
+This is provided for debugging.
+  M-: (test-matlab-ts-mode-indent-xr--file
+      \"test-matlab-ts-mode-indent-xr-files/M-FILE\")"
+  (let ((test-matlab-ts-mode-indent-xr--file m-file))
+    (ert-run-tests-interactively "test-matlab-ts-mode-indent-xr")))
+
+(ert-deftest test-matlab-ts-mode-indent-xr ()
+  "Test indent using ./test-matlab-ts-mode-indent-xr-files/NAME.m.
+Using ./test-matlab-ts-mode-indent-xr-files/NAME.m, compare typing
+commands via `t-utils-xr' Lisp commans in the *.m files and compare
+agains ./test-matlab-ts-mode-indent-xr-files/NAME_expected.org.  This
+loops on all ./test-matlab-ts-mode-indent-xr-files/NAME.m files.
+
+To add a test, create
+  ./test-matlab-ts-mode-indent-xr-files/NAME.m
+and run this function.  The baseline is saved for you as
+  ./test-matlab-ts-mode-indent-xr-files/NAME_expected.org~
+after validating it, rename it to
+  ./test-matlab-ts-mode-indent-xr-files/NAME_expected.org"
+
+  (let* ((test-name "test-matlab-ts-mode-indent-xr")
+         (m-files (t-utils-get-files
+                   test-name
+                   (rx ".m" eos)
+                   nil
+                   test-matlab-ts-mode-indent-xr--file)))
+    (t-utils-error-if-no-treesit-for 'matlab test-name)
+    (t-utils-test-xr test-name m-files)))
+
+(provide 'test-matlab-ts-mode-indent-xr)
+;;; test-matlab-ts-mode-indent-xr.el ends here
diff --git a/tests/test-matlab-ts-mode-indent.el 
b/tests/test-matlab-ts-mode-indent.el
index bad995bbf9..4708fff80e 100644
--- a/tests/test-matlab-ts-mode-indent.el
+++ b/tests/test-matlab-ts-mode-indent.el
@@ -30,6 +30,8 @@
 (require 't-utils)
 (require 'matlab-ts-mode)
 
+(defvar test-matlab-ts-mode-indent--current-indent-level nil)
+
 (defvar test-matlab-ts-mode-indent--file nil)
 
 (defun test-matlab-ts-mode-indent--file (m-file)
@@ -59,7 +61,13 @@ after validating it, rename it to
                    (rx ".m" eos)
                    (rx "_expected.m" eos) ;; skip our *_expected.m baselines
                    test-matlab-ts-mode-indent--file))
+         (indent-checker (lambda ()
+                           (setq 
test-matlab-ts-mode-indent--current-indent-level
+                                 matlab-ts-mode--function-indent-level)))
          (line-manipulator (lambda ()
+                             ;; Set the indent level to what the m-file has
+                             (setq matlab-ts-mode--function-indent-level
+                                   
test-matlab-ts-mode-indent--current-indent-level)
                              ;; Workaround
                              ;; 
https://github.com/acristoffers/tree-sitter-matlab/issues/32
                              (goto-char (point-min))
@@ -70,7 +78,7 @@ after validating it, rename it to
                                    (insert " ")))
                                (forward-line)))))
     (t-utils-error-if-no-treesit-for 'matlab test-name)
-    (t-utils-test-indent test-name m-files line-manipulator)))
+    (t-utils-test-indent test-name m-files indent-checker line-manipulator)))
 
 (provide 'test-matlab-ts-mode-indent)
 ;;; test-matlab-ts-mode-indent.el ends here

Reply via email to