branch: externals/matlab-mode
commit 52c6d5ede078c232a30c7f190126e76d3eb0206a
Author: John Ciolfi <john.ciolfi...@gmail.com>
Commit: John Ciolfi <john.ciolfi...@gmail.com>

    matlab-ts-mode: add electric ends
---
 matlab-ts-mode.el                                  |  217 +++-
 .../electric_ends_cases.m                          |   39 +
 .../electric_ends_cases_expected.org               | 1241 ++++++++++++++++++++
 .../electric_ends_classdef.m                       |   14 +
 .../electric_ends_classdef_expected.org            |  143 +++
 .../electric_ends_if_continued.m                   |   19 +
 .../electric_ends_if_continued_expected.org        |  164 +++
 tests/test-matlab-ts-mode-electric-ends.el         |   72 ++
 8 files changed, 1855 insertions(+), 54 deletions(-)

diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index bb00427a85..61a598e328 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -147,16 +147,18 @@ Then restart Emacs and run
 You can also install via use-package or other methods."
   :type 'boolean)
 
-;; TODO
-;; (defcustom matlab-ts-mode-electric-ends t
-;;   "*If t, insert end keywords to complete statements.
-;; For example, if you type
-;;    classdef foo<RET>
-;; an end statement will be inserted resulting in:
-;;    classdef foo<RET>
-;;        ^                  <== point here
-;;    end"
-;;   :type 'boolean)
+(defcustom matlab-ts-mode-electric-ends t
+  "*If t, insert end keywords to complete statements.
+For example, if you type
+   classdef foo<RET>
+an end statement will be inserted resulting in:
+   classdef foo<RET>
+       ^                  <== point here
+   end
+Insertion of end keywords works well when the code is
+indented.  If you are editing code that is not indented,
+you may wish to turn this off."
+  :type 'boolean)
 
 ;;; Global variables used in multiple code ";;; sections"
 
@@ -1301,8 +1303,8 @@ Returns indent-level relative to ANCHOR-NODE."
            (when (> (point) 1)
              (goto-char (1- (point)))
              (looking-at "%")))
-         ;; Ancored under a block comment:   %{
-         ;;                                    ^    <== TAB to here
+         ;; Anchored under a block comment:   %{
+         ;;                                     ^    <== TAB to here, one more 
than the "{"
          1
        matlab-ts-mode--array-indent-level))
 
@@ -1563,16 +1565,6 @@ Prev-siblings:
                                                                
last-child-of-error-node))))
               (setq matlab-ts-mode--i-next-line-pair
                     (cons (treesit-node-start anchor-node) indent-level))
-
-              ;; TODO Rough sketch of electric-ends
-              ;;      We need to look at the parse tree to see if we should 
insert it.
-              ;;      Also, we should verify this doesn't happen with 
indent-region, only on RET.
-              ;; (when matlab-ts-mode-electric-ends
-              ;;   (save-excursion
-              ;;     (let ((insert-column (save-excursion
-              ;;                            (goto-char (treesit-node-start 
anchor-node))
-              ;;                            (current-column))))
-              ;;       (insert "\n" (make-string insert-column ?\ ) "end\n")))
               t)))))))
 
 (defun matlab-ts-mode--i-next-line-anchor (&rest _)
@@ -2422,17 +2414,144 @@ THERE-END MISMATCH) or nil."
         (list here-begin here-end there-begin there-end mismatch)
      (funcall #'show-paren--default))))
 
-;;; post-command-hook
+;;; post-self-command-hook
+
+(defun matlab-ts-mode--is-electric-end-missing (node)
+  "Is statement NODE missing an \"end\"."
+  (let ((last-child (treesit-node-child (treesit-node-parent node) -1)))
+    (cond
+     ;; Case: no end keyword for the node in the parse tree
+     ((not (equal (treesit-node-type last-child) "end"))
+      t)
+
+     ;; Case: see if this is an end for a different statement. Consider:
+     ;;   function foo(a)
+     ;;       if a > 0            <== When RET typed, we want the electric end 
to be inserted
+     ;;   end
+     ;; The parse tree is:
+     ;;   (function_definition function name: (identifier)
+     ;;    (function_arguments ( arguments: (identifier) ))
+     ;;    \n
+     ;;    (block
+     ;;     (if_statement if
+     ;;      condition: (comparison_operator (identifier) > (number))
+     ;;      \n end)
+     ;;     \n))
+     ;; Notice the end is attached to the if_statement.  Therefore, we use 
indent level
+     ;; to see if it is really attached.
+     ;; This works well assuming that the code is indented when one is editing 
it.
+     ((let ((node-indent-level (save-excursion
+                                  (goto-char (treesit-node-start node))
+                                 (current-indentation)))
+            (end-indent-level (save-excursion
+                                 (goto-char (treesit-node-start last-child))
+                                 (current-indentation))))
+        (not (= node-indent-level end-indent-level)))
+      t)
+
+     ;; Case: newely added statement casues a parse error, so it's missing the 
end, Consider:
+     ;;   classdef foo
+     ;;       properties                   <== properties typed, followed by 
RET
+     ;;       methods
+     ;;       end
+     ;;   end
+     ;; which gives us parse tree:
+     ;;   (source_file
+     ;;    (class_definition classdef name: (identifier) \n
+     ;;     (properties properties \n
+     ;;      (ERROR , methods)
+     ;;      \n end)
+     ;;     end)
+     ;;    \n)
+     ((let* ((statement-node (treesit-node-parent node))
+             (child-idx 0)
+             (child (treesit-node-child statement-node child-idx))
+             (node-type (treesit-node-type node)))
+
+        (while (and child
+                    (string-match-p (rx-to-string `(seq bos (or ,node-type 
"\n" "comment") eos) t)
+                                    (treesit-node-type child)))
+          (setq child-idx (1+ child-idx))
+          (setq child (treesit-node-child statement-node child-idx)))
+        (and child (string= (treesit-node-type child) "ERROR")))
+      ;; An ERROR immediately after node
+      t)
+
+     ;; Otherwise: statement has an end
+     (t
+      nil
+     ))))
+
+(defun matlab-ts-mode--insert-electric-ends ()
+  "A RET was type, insert the electric \"end\" if needed."
+
+  (let (end-indent-level
+        extra-insert
+        move-point-to-extra-insert)
 
-(defun matlab-ts-mode--post-command-newline ()
-  "Ensure buffer always has a newline.
-The matlab tree-sitter requires a newline, see
-https://github.com/acristoffers/tree-sitter-matlab/issues/34";
-  (when (eq major-mode 'matlab-ts-mode)
     (save-excursion
-      (goto-char (point-max))
-      (when (not (= (char-before) ?\n))
-        (insert "\n")))))
+      (forward-line -1)
+      (back-to-indentation)
+      (let* ((node (treesit-node-at (point)))
+             (node-type (treesit-node-type node)))
+        (when (and node
+                   ;; AND: Was a statement entered that requires and end?
+                   (string-match-p
+                    (rx bos (or "function" "arguments" "if" "switch" "while" 
"for" "parfor"
+                                "spmd" "try" "classdef" "enumeration" 
"properties" "methods"
+                                "events")
+                        eos)
+                    node-type)
+                   ;; AND: Is the statement missing an end?
+                   (matlab-ts-mode--is-electric-end-missing node))
+
+          ;; Statement for the RET doesn't have an end, so add one at 
end-indent-level
+          (setq end-indent-level (current-indentation))
+
+          ;; Extra insert, e.g. case for the switch statement.
+          (pcase node-type
+            ("switch"
+             (setq extra-insert "  case "
+                   move-point-to-extra-insert t))
+            ("try"
+             (setq extra-insert "catch me"))
+          ))))
+
+    (when end-indent-level
+      (let ((indent-level-spaces (make-string end-indent-level ? )))
+        (save-excursion
+          (when extra-insert
+            (when (not move-point-to-extra-insert)
+              (insert "\n"))
+            (insert indent-level-spaces extra-insert))
+          (insert "\n" indent-level-spaces "end\n"))
+        ))))
+
+(defun matlab-ts-mode--post-insert-callback ()
+  "Callback attached to `post-self-insert-hook'.
+
+The matlab tree-sitter requires a final newline.  Setting
+`require-final-newline' to \\='visit-save doesn't guarantee we have a newline
+when typing code into the buffer, so we leverage `post-self-command-hook'
+to insert a newline if needed.
+
+  https://github.com/acristoffers/tree-sitter-matlab/issues/34
+
+This callback also implements `matlab-ts-mode-electric-ends'."
+
+  (when (eq major-mode 'matlab-ts-mode)
+    (let ((ret-typed (eq last-command-event ?\n)))
+
+      ;; Add final newline to the buffer?
+      (save-excursion
+        (goto-char (point-max))
+        (when (not (= (char-before) ?\n))
+          (insert "\n")))
+
+      ;; Add "end" (for `matlab-ts-mode-electric-ends')
+      (when (and ret-typed
+                   matlab-ts-mode-electric-ends)
+        (matlab-ts-mode--insert-electric-ends)))))
 
 ;;; MLint Flycheck
 
@@ -2696,21 +2815,6 @@ mark at the beginning of the \"%% section\" and point at 
the end of the section"
       :help "When MATLAB debugger is active, stop debugging"]
      )
 
-    ;; TODO - how to autoload these?  Do we want this menu?
-    ;;     ("Insert"
-    ;;      ["Complete Symbol" matlab-complete-symbol t]
-    ;;      ["Comment" matlab-comment t]
-    ;;      ["if end" tempo-template-matlab-if t]
-    ;;      ["if else end" tempo-template-matlab-if-else t]
-    ;;      ["for end" tempo-template-matlab-for t]
-    ;;      ["switch otherwise end" tempo-template-matlab-switch t]
-    ;;      ["Next case" matlab-insert-next-case t]
-    ;;      ["try catch end" tempo-template-matlab-try t]
-    ;;      ["while end" tempo-template-matlab-while t]
-    ;;      ["End of block" matlab-insert-end-block t]
-    ;;      ["Function" tempo-template-matlab-function t]
-    ;;      ["Stringify Region" matlab-stringify-region t]
-    ;;      )
     "----"
     ["View mlint code analyzer messages" (flycheck-list-errors)
      :help "View mlint code analyzer messages.
@@ -2847,12 +2951,9 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
     ;; See: tests/test-matlab-ts-mode-show-paren.el
     (setq-local show-paren-data-function #'matlab-ts-mode--show-paren-or-block)
 
-    ;; Final newline.  The matlab tree-sitter requires a final newline, see
-    ;;    https://github.com/acristoffers/tree-sitter-matlab/issues/34
-    ;; Setting require-final-newline to 'visit-save doesn't guarantee we have 
a newline when typing
-    ;; code into the buffer, so we also setup a post-command-hook to insert a 
newline if needed.
+    ;; Final newline and electric ends.
     (setq-local require-final-newline 'visit-save)
-    (add-hook 'post-self-insert-hook #'matlab-ts-mode--post-command-newline 
-99 t)
+    (add-hook 'post-self-insert-hook #'matlab-ts-mode--post-insert-callback 
-99 t)
 
     ;; give each file it's own parameter history
     (setq-local matlab-shell-save-and-go-history '("()"))
@@ -2860,13 +2961,21 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
     ;; Activate MATLAB script ";; heading" matlab-sections-minor-mode if needed
     (matlab-sections-auto-enable-on-mfile-type-fcn 
(matlab-ts-mode--mfile-type))
 
+    ;; TODO indent
+    ;;      if a > 1 && ...
+    ;;         ^                  <== TAB or RET on prior line to here
+    ;;      end
+    ;;
+    ;; TODO indent
+    ;;      if (a > 1 && ...
+    ;;          x > 1) ...
+    ;;         ^                  <== TAB or RET on prior line to here (e.g. 
to enter "&& y")
+    ;;      end
+    ;;
     ;; TODO font-lock when errors
     ;;      can we add light error indicator somewhere, e.g. put an underline 
marker on the error
     ;;      region?
     ;;
-    ;; TODO create defcustom matlab-ts-mode-electric-ends that inserts end 
statements
-    ;;      when a function, switch, while, for, etc. is entered. This should 
handle continuations.
-    ;;
     ;; TODO on load enter matlab-ts-mode when file contains mcode and
     ;;      (add-to-list 'major-mode-remap-alist '(matlab-mode . 
matlab-ts-mode))
     ;;      is active. Also look at matlab-mode magic-mode-alist setup.
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases.m 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases.m
new file mode 100644
index 0000000000..498a62e8f9
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases.m
@@ -0,0 +1,39 @@
+% -*- matlab-ts -*-
+
+classdef electric_ends_cases
+
+    %(t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+
+    %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+
+    %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+
+    methods
+
+        %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+
+        function foo(a)
+
+            %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+
+            %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+
+        end
+    end
+
+end
+
+%(t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
+
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases_expected.org
 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases_expected.org
new file mode 100644
index 0000000000..50bf633144
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_cases_expected.org
@@ -0,0 +1,1241 @@
+#+startup: showall
+
+* Executing commands from electric_ends_cases.m:5:5:
+
+  (t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  123
+  Moved to point:  124
+  : 6:0: 
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  124
+  Moved to point:  128
+  : 6:4:     
+  :          ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,7 @@
+ classdef electric_ends_cases
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+-
++    
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+  #+end_src diff
+
+- Invoking      : (insert "properties")
+  Start point   :  128
+  Moved to point:  138
+  : 6:14:     properties
+  :                     ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,7 +3,7 @@
+ classdef electric_ends_cases
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+-    
++    properties
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  138
+  Moved to point:  147
+  : 7:8:         
+  :              ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -4,6 +4,9 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+     properties
++        
++    end
++
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+  #+end_src diff
+
+- Invoking      : (insert "p1")
+  Start point   :  147
+  Moved to point:  149
+  : 7:10:         p1
+  :                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -4,7 +4,7 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "properties") "C-m" (insert "p1"))
+     properties
+-        
++        p1
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:10:5:
+
+  (t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  242
+  Moved to point:  243
+  : 11:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  243
+  Moved to point:  247
+  : 11:4:     
+  :           ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -8,7 +8,7 @@
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+-
++    
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+ 
+     methods
+  #+end_src diff
+
+- Invoking      : (insert "methods")
+  Start point   :  247
+  Moved to point:  254
+  : 11:11:     methods
+  :                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -8,7 +8,7 @@
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+-    
++    methods
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+ 
+     methods
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  254
+  Moved to point:  263
+  : 12:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -9,6 +9,9 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+     methods
++        
++    end
++
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+ 
+     methods
+  #+end_src diff
+
+- Invoking      : (insert "% methods-comment")
+  Start point   :  263
+  Moved to point:  280
+  : 12:25:         % methods-comment
+  :                                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -9,7 +9,7 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "methods") "C-m" (insert "% 
methods-comment"))
+     methods
+-        
++        % methods-comment
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:15:5:
+
+  (t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% events-comment"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  371
+  Moved to point:  372
+  : 16:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  372
+  Moved to point:  376
+  : 16:4:     
+  :           ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -13,7 +13,7 @@
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+-
++    
+     methods
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+  #+end_src diff
+
+- Invoking      : (insert "events")
+  Start point   :  376
+  Moved to point:  382
+  : 16:10:     events
+  :                  ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -13,7 +13,7 @@
+     end
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+-    
++    events
+     methods
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  382
+  Moved to point:  391
+  : 17:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -14,6 +14,9 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+     events
++        
++    end
++
+     methods
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+  #+end_src diff
+
+- Invoking      : (insert "% events-comment")
+  Start point   :  391
+  Moved to point:  407
+  : 17:24:         % events-comment
+  :                                ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -14,7 +14,7 @@
+ 
+     %(t-utils-xr "C-n" "C-i" (insert "events") "C-m" (insert "% 
events-comment"))
+     events
+-        
++        % events-comment
+     end
+ 
+     methods
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:22:9:
+
+  (t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert "disp('bar')"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  516
+  Moved to point:  517
+  : 23:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  517
+  Moved to point:  525
+  : 23:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -20,7 +20,7 @@
+     methods
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+-
++        
+         function foo(a)
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+  #+end_src diff
+
+- Invoking      : (insert "function bar")
+  Start point   :  525
+  Moved to point:  537
+  : 23:20:         function bar
+  :                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -20,7 +20,7 @@
+     methods
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+-        
++        function bar
+         function foo(a)
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  537
+  Moved to point:  550
+  : 24:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -21,6 +21,9 @@
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+         function bar
++            
++        end
++
+         function foo(a)
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+  #+end_src diff
+
+- Invoking      : (insert "disp('bar')")
+  Start point   :  550
+  Moved to point:  561
+  : 24:23:             disp('bar')
+  :                               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -21,7 +21,7 @@
+ 
+         %(t-utils-xr "C-n" "C-i" (insert "function bar") "C-m" (insert 
"disp('bar')"))
+         function bar
+-            
++            disp('bar')
+         end
+ 
+         function foo(a)
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:29:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  677
+  Moved to point:  678
+  : 30:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  678
+  Moved to point:  690
+  : 30:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -27,7 +27,7 @@
+         function foo(a)
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+  #+end_src diff
+
+- Invoking      : (insert "arguments")
+  Start point   :  690
+  Moved to point:  699
+  : 30:21:             arguments
+  :                             ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -27,7 +27,7 @@
+         function foo(a)
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+-            
++            arguments
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  699
+  Moved to point:  716
+  : 31:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -28,6 +28,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+             arguments
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+  #+end_src diff
+
+- Invoking      : (insert "a")
+  Start point   :  716
+  Moved to point:  717
+  : 31:17:                 a
+  :                         ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -28,7 +28,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "arguments") "C-m" (insert "a"))
+             arguments
+-                
++                a
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:34:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert "disp('if')"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  816
+  Moved to point:  817
+  : 35:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  817
+  Moved to point:  829
+  : 35:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -32,7 +32,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+- Invoking      : (insert "if a")
+  Start point   :  829
+  Moved to point:  833
+  : 35:16:             if a
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -32,7 +32,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+-            
++            if a
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  833
+  Moved to point:  850
+  : 36:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -33,6 +33,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+             if a
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+- Invoking      : (insert "disp('if')")
+  Start point   :  850
+  Moved to point:  860
+  : 36:26:                 disp('if')
+  :                                  ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -33,7 +33,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "if a") "C-m" (insert 
"disp('if')"))
+             if a
+-                
++                disp('if')
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:39:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert "1") "C-m" 
(insert "disp('case 1')"))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  992
+  Moved to point:  993
+  : 40:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  993
+  Moved to point: 1005
+  : 40:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -37,7 +37,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : (insert "switch a")
+  Start point   : 1005
+  Moved to point: 1013
+  : 40:20:             switch a
+  :                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -37,7 +37,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+-            
++            switch a
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1013
+  Moved to point: 1028
+  : 41:14:               case 
+  :                      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -38,6 +38,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+             switch a
++              case 
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : "C-e" = move-end-of-line
+  Start point   : 1028
+  Moved to point: 1033
+  : 41:19:               case 
+  :                           ^
+  No buffer modifications
+
+- Invoking      : (insert "1")
+  Start point   : 1033
+  Moved to point: 1034
+  : 41:20:               case 1
+  :                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -38,7 +38,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+             switch a
+-              case 
++              case 1
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1034
+  Moved to point: 1051
+  : 42:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -39,6 +39,7 @@
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+             switch a
+               case 1
++                
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+- Invoking      : (insert "disp('case 1')")
+  Start point   : 1051
+  Moved to point: 1065
+  : 42:30:                 disp('case 1')
+  :                                      ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -39,7 +39,7 @@
+             %(t-utils-xr "C-n" "C-i" (insert "switch a") "C-m" "C-e" (insert 
"1") "C-m" (insert "disp('case 1')"))
+             switch a
+               case 1
+-                
++                disp('case 1')
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:45:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert "break"))
+
+- Invoking      : "C-n" = next-line
+  Start point   : 1165
+  Moved to point: 1166
+  : 46:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   : 1166
+  Moved to point: 1178
+  : 46:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -43,7 +43,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : (insert "while true")
+  Start point   : 1178
+  Moved to point: 1188
+  : 46:22:             while true
+  :                              ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -43,7 +43,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+-            
++            while true
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1188
+  Moved to point: 1205
+  : 47:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -44,6 +44,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+             while true
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+- Invoking      : (insert "break")
+  Start point   : 1205
+  Moved to point: 1210
+  : 47:21:                 break
+  :                             ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -44,7 +44,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "while true") "C-m" (insert 
"break"))
+             while true
+-                
++                break
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:50:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert "disp(idx)"))
+
+- Invoking      : "C-n" = next-line
+  Start point   : 1315
+  Moved to point: 1316
+  : 51:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   : 1316
+  Moved to point: 1328
+  : 51:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -48,7 +48,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+  #+end_src diff
+
+- Invoking      : (insert "for idx=1:a")
+  Start point   : 1328
+  Moved to point: 1339
+  : 51:23:             for idx=1:a
+  :                               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -48,7 +48,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+-            
++            for idx=1:a
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1339
+  Moved to point: 1356
+  : 52:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -49,6 +49,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+             for idx=1:a
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+  #+end_src diff
+
+- Invoking      : (insert "disp(idx)")
+  Start point   : 1356
+  Moved to point: 1365
+  : 52:25:                 disp(idx)
+  :                                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -49,7 +49,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "for idx=1:a") "C-m" (insert 
"disp(idx)"))
+             for idx=1:a
+-                
++                disp(idx)
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:55:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert "disp(idx)"))
+
+- Invoking      : "C-n" = next-line
+  Start point   : 1473
+  Moved to point: 1474
+  : 56:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   : 1474
+  Moved to point: 1486
+  : 56:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -53,7 +53,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+  #+end_src diff
+
+- Invoking      : (insert "parfor idx=1:a")
+  Start point   : 1486
+  Moved to point: 1500
+  : 56:26:             parfor idx=1:a
+  :                                  ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -53,7 +53,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+-            
++            parfor idx=1:a
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1500
+  Moved to point: 1517
+  : 57:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -54,6 +54,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+             parfor idx=1:a
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+  #+end_src diff
+
+- Invoking      : (insert "disp(idx)")
+  Start point   : 1517
+  Moved to point: 1526
+  : 57:25:                 disp(idx)
+  :                                 ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -54,7 +54,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "parfor idx=1:a") "C-m" (insert 
"disp(idx)"))
+             parfor idx=1:a
+-                
++                disp(idx)
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:60:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = magic(spmdIndex + 
2)"))
+
+- Invoking      : "C-n" = next-line
+  Start point   : 1639
+  Moved to point: 1640
+  : 61:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   : 1640
+  Moved to point: 1652
+  : 61:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -58,7 +58,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+-
++            
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+ 
+         end
+  #+end_src diff
+
+- Invoking      : (insert "spmd")
+  Start point   : 1652
+  Moved to point: 1656
+  : 61:16:             spmd
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -58,7 +58,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+-            
++            spmd
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+ 
+         end
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1656
+  Moved to point: 1673
+  : 62:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -59,6 +59,9 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+             spmd
++                
++            end
++
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+ 
+         end
+  #+end_src diff
+
+- Invoking      : (insert "q = magic(spmdIndex + 2)")
+  Start point   : 1673
+  Moved to point: 1697
+  : 62:40:                 q = magic(spmdIndex + 2)
+  :                                                ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -59,7 +59,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "spmd") "C-m" (insert "q = 
magic(spmdIndex + 2)"))
+             spmd
+-                
++                q = magic(spmdIndex + 2)
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:65:13:
+
+  (t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert "disp('try')"))
+
+- Invoking      : "C-n" = next-line
+  Start point   : 1796
+  Moved to point: 1797
+  : 66:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   : 1797
+  Moved to point: 1809
+  : 66:12:             
+  :                    ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -63,7 +63,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+-
++            
+         end
+     end
+ 
+  #+end_src diff
+
+- Invoking      : (insert "try")
+  Start point   : 1809
+  Moved to point: 1812
+  : 66:15:             try
+  :                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -63,7 +63,7 @@
+             end
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+-            
++            try
+         end
+     end
+ 
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   : 1812
+  Moved to point: 1829
+  : 67:16:                 
+  :                        ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -64,6 +64,10 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+             try
++                
++            catch me
++            end
++
+         end
+     end
+ 
+  #+end_src diff
+
+- Invoking      : (insert "disp('try')")
+  Start point   : 1829
+  Moved to point: 1840
+  : 67:27:                 disp('try')
+  :                                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -64,7 +64,7 @@
+ 
+             %(t-utils-xr "C-n" "C-i" (insert "try") "C-m" (insert 
"disp('try')"))
+             try
+-                
++                disp('try')
+             catch me
+             end
+ 
+  #+end_src diff
+
+* Executing commands from electric_ends_cases.m:76:1:
+
+  (t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
+
+- Invoking      : (t-utils-xr-print-code (point-min) (point-max))
+  Start point   : 1966
+  No point movement
+  standard-output:
+  #+begin_src matlab-ts
+% -*- matlab-ts -*-
+
+classdef electric_ends_cases
+
+    %(t-utils-xr \"C-n\" \"C-i\" (insert \"properties\") \"C-m\" (insert 
\"p1\"))
+    properties
+        p1
+    end
+
+    %(t-utils-xr \"C-n\" \"C-i\" (insert \"methods\") \"C-m\" (insert \"% 
methods-comment\"))
+    methods
+        % methods-comment
+    end
+
+    %(t-utils-xr \"C-n\" \"C-i\" (insert \"events\") \"C-m\" (insert \"% 
events-comment\"))
+    events
+        % events-comment
+    end
+
+    methods
+
+        %(t-utils-xr \"C-n\" \"C-i\" (insert \"function bar\") \"C-m\" (insert 
\"disp('bar')\"))
+        function bar
+            disp('bar')
+        end
+
+        function foo(a)
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"arguments\") \"C-m\" 
(insert \"a\"))
+            arguments
+                a
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"if a\") \"C-m\" (insert 
\"disp('if')\"))
+            if a
+                disp('if')
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"switch a\") \"C-m\" \"C-e\" 
(insert \"1\") \"C-m\" (insert \"disp('case 1')\"))
+            switch a
+              case 1
+                disp('case 1')
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"while true\") \"C-m\" 
(insert \"break\"))
+            while true
+                break
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"for idx=1:a\") \"C-m\" 
(insert \"disp(idx)\"))
+            for idx=1:a
+                disp(idx)
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"parfor idx=1:a\") \"C-m\" 
(insert \"disp(idx)\"))
+            parfor idx=1:a
+                disp(idx)
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"spmd\") \"C-m\" (insert \"q 
= magic(spmdIndex + 2)\"))
+            spmd
+                q = magic(spmdIndex + 2)
+            end
+
+            %(t-utils-xr \"C-n\" \"C-i\" (insert \"try\") \"C-m\" (insert 
\"disp('try')\"))
+            try
+                disp('try')
+            catch me
+            end
+
+        end
+    end
+
+end
+
+%(t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
+
+  #+end_src
+  No buffer modifications
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef.m 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef.m
new file mode 100644
index 0000000000..9ebe43bc3a
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef.m
@@ -0,0 +1,14 @@
+% -*- matlab-ts -*-
+
+%{
+  (t-utils-xr
+  (re-search-forward "%}") "C-n"
+
+  (insert "classdef electric_ends_classdef")    "C-m"
+  (insert     "enumeration")                    "C-m"
+  (insert          "red")
+
+  (re-search-backward "^classdef")
+  (t-utils-xr-print-code (point) (point-max))
+  )
+%}
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef_expected.org
 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef_expected.org
new file mode 100644
index 0000000000..72b88ab71c
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_classdef_expected.org
@@ -0,0 +1,143 @@
+#+startup: showall
+
+* Executing commands from electric_ends_classdef.m:4:2:
+
+  (t-utils-xr
+  (re-search-forward "%}") "C-n"
+
+  (insert "classdef electric_ends_classdef")    "C-m"
+  (insert     "enumeration")                    "C-m"
+  (insert          "red")
+
+  (re-search-backward "^classdef")
+  (t-utils-xr-print-code (point) (point-max))
+  )
+
+- Invoking      : (re-search-forward "%}")
+  Start point   :  292
+  Moved to point:  295
+  : 14:2: %}
+  :         ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :  295
+  Moved to point:  296
+  : 15:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : (insert "classdef electric_ends_classdef")
+  Start point   :  296
+  Moved to point:  327
+  : 15:31: classdef electric_ends_classdef
+  :                                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -12,3 +12,4 @@
+   (t-utils-xr-print-code (point) (point-max))
+   )
+ %}
++classdef electric_ends_classdef
+\ No newline at end of file
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  327
+  Moved to point:  332
+  : 16:4:     
+  :           ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -12,4 +12,6 @@
+   (t-utils-xr-print-code (point) (point-max))
+   )
+ %}
+-classdef electric_ends_classdef
+\ No newline at end of file
++classdef electric_ends_classdef
++    
++end
+  #+end_src diff
+
+- Invoking      : (insert "enumeration")
+  Start point   :  332
+  Moved to point:  343
+  : 16:15:     enumeration
+  :                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -13,5 +13,5 @@
+   )
+ %}
+ classdef electric_ends_classdef
+-    
++    enumeration
+ end
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  343
+  Moved to point:  352
+  : 17:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -14,4 +14,7 @@
+ %}
+ classdef electric_ends_classdef
+     enumeration
++        
++    end
++
+ end
+  #+end_src diff
+
+- Invoking      : (insert "red")
+  Start point   :  352
+  Moved to point:  355
+  : 17:11:         red
+  :                   ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -14,7 +14,7 @@
+ %}
+ classdef electric_ends_classdef
+     enumeration
+-        
++        red
+     end
+ 
+ end
+  #+end_src diff
+
+- Invoking      : (re-search-backward "^classdef")
+  Start point   :  355
+  Moved to point:  296
+  : 15:0: classdef electric_ends_classdef
+  :       ^
+  No buffer modifications
+
+- Invoking      : (t-utils-xr-print-code (point) (point-max))
+  Start point   :  296
+  No point movement
+  standard-output:
+  #+begin_src matlab-ts
+classdef electric_ends_classdef
+    enumeration
+        red
+    end
+
+end
+  #+end_src
+  No buffer modifications
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued.m 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued.m
new file mode 100644
index 0000000000..83cd4d8181
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued.m
@@ -0,0 +1,19 @@
+% -*- matlab-ts -*-
+
+function electric_ends_if_statement(a)
+
+    %{
+      (t-utils-xr
+      (re-search-forward "%}") "C-n"
+
+      "C-i"
+      (insert "if (a > 1 && ...")             "C-m"
+      (insert     "a < 10)")                  "C-m"
+      (insert     "disp('a > 1 && a < 10')")
+
+      (re-search-backward "^    if")
+      (t-utils-xr-print-code (point) (re-search-forward "end"))
+      )
+    %}
+
+end
diff --git 
a/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued_expected.org
 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued_expected.org
new file mode 100644
index 0000000000..453c87a89e
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-ends-files/electric_ends_if_continued_expected.org
@@ -0,0 +1,164 @@
+#+startup: showall
+
+* Executing commands from electric_ends_if_continued.m:6:6:
+
+  (t-utils-xr
+      (re-search-forward "%}") "C-n"
+
+      "C-i"
+      (insert "if (a > 1 && ...")             "C-m"
+      (insert     "a < 10)")                  "C-m"
+      (insert     "disp('a > 1 && a < 10')")
+
+      (re-search-backward "^    if")
+      (t-utils-xr-print-code (point) (re-search-forward "end"))
+      )
+
+- Invoking      : (re-search-forward "%}")
+  Start point   :  395
+  Moved to point:  402
+  : 17:6:     %}
+  :             ^
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :  402
+  Moved to point:  403
+  : 18:0: 
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  403
+  Moved to point:  407
+  : 18:4:     
+  :           ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -15,5 +15,5 @@
+       (t-utils-xr-print-code (point) (re-search-forward "end"))
+       )
+     %}
+-
++    
+ end
+  #+end_src diff
+
+- Invoking      : (insert "if (a > 1 && ...")
+  Start point   :  407
+  Moved to point:  423
+  : 18:20:     if (a > 1 && ...
+  :                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -15,5 +15,5 @@
+       (t-utils-xr-print-code (point) (re-search-forward "end"))
+       )
+     %}
+-    
++    if (a > 1 && ...
+ end
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  423
+  Moved to point:  432
+  : 19:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -16,4 +16,7 @@
+       )
+     %}
+     if (a > 1 && ...
++        
++    end
++
+ end
+  #+end_src diff
+
+- Invoking      : (insert "a < 10)")
+  Start point   :  432
+  Moved to point:  439
+  : 19:15:         a < 10)
+  :                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -16,7 +16,7 @@
+       )
+     %}
+     if (a > 1 && ...
+-        
++        a < 10)
+     end
+ 
+ end
+  #+end_src diff
+
+- Invoking      : "C-m" = newline
+  Start point   :  439
+  Moved to point:  448
+  : 20:8:         
+  :               ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -17,6 +17,7 @@
+     %}
+     if (a > 1 && ...
+         a < 10)
++        
+     end
+ 
+ end
+  #+end_src diff
+
+- Invoking      : (insert "disp('a > 1 && a < 10')")
+  Start point   :  448
+  Moved to point:  471
+  : 20:31:         disp('a > 1 && a < 10')
+  :                                       ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -17,7 +17,7 @@
+     %}
+     if (a > 1 && ...
+         a < 10)
+-        
++        disp('a > 1 && a < 10')
+     end
+ 
+ end
+  #+end_src diff
+
+- Invoking      : (re-search-backward "^    if")
+  Start point   :  471
+  Moved to point:  403
+  : 18:0:     if (a > 1 && ...
+  :       ^
+  No buffer modifications
+
+- Invoking      : (t-utils-xr-print-code (point) (re-search-forward "end"))
+  Start point   :  403
+  Moved to point:  479
+  : 21:7:     end
+  :              ^
+  standard-output:
+  #+begin_src matlab-ts
+    if (a > 1 && ...
+        a < 10)
+        disp('a > 1 && a < 10')
+    end
+  #+end_src
+  No buffer modifications
diff --git a/tests/test-matlab-ts-mode-electric-ends.el 
b/tests/test-matlab-ts-mode-electric-ends.el
new file mode 100644
index 0000000000..0256585782
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-ends.el
@@ -0,0 +1,72 @@
+;;; test-matlab-ts-mode-electric-ends.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:
+;;
+;; Validate matlab-ts-mode indent.
+;; Load ../matlab-ts-mode.el via require and run indent tests using
+;; ./test-matlab-ts-mode-electric-ends-files/NAME.m comparing against
+;; ./test-matlab-ts-mode-electric-ends-files/NAME_expected.org
+;;
+
+;;; Code:
+
+(require 't-utils)
+(require 'matlab-ts-mode)
+
+(defvar test-matlab-ts-mode-electric-ends--file nil)
+
+(defun test-matlab-ts-mode-electric-ends--file (m-file)
+  "Test an individual M-FILE.
+This is provided for debugging.
+  M-: (test-matlab-ts-mode-electric-ends--file
+      \"test-matlab-ts-mode-electric-ends-files/M-FILE\")"
+  (let ((test-matlab-ts-mode-electric-ends--file m-file))
+    (ert-run-tests-interactively "test-matlab-ts-mode-electric-ends")))
+
+(ert-deftest test-matlab-ts-mode-electric-ends ()
+  "Test electric ends using ./test-matlab-ts-mode-electric-ends-files/NAME.m.
+Using ./test-matlab-ts-mode-electric-ends-files/NAME.m,
+exercise `matlab-ts-mode-electric-ends' automatic insertion
+of ends by looping over the
+  ./test-matlab-ts-mode-electric-ends-files/NAME.m files
+which insert statements that cause `matlab-ts-mode-electric-ends' to
+insert end keywords.  The results are compared against baseline:
+  ./test-matlab-ts-mode-electric-ends-files/NAME_expected.org
+
+To add a test, create
+  ./test-matlab-ts-mode-electric-ends-files/NAME.m
+and run this function.  The baseline is saved for you as
+  ./test-matlab-ts-mode-electric-ends-files/NAME_expected.org~
+after validating it, rename it to
+  ./test-matlab-ts-mode-electric-ends-files/NAME_expected.org"
+
+  (let* ((test-name "test-matlab-ts-mode-electric-ends")
+         (m-files (t-utils-get-files
+                   test-name
+                   (rx ".m" eos)
+                   nil
+                   test-matlab-ts-mode-electric-ends--file)))
+    (t-utils-error-if-no-treesit-for 'matlab test-name)
+    (t-utils-test-xr test-name m-files)))
+
+(provide 'test-matlab-ts-mode-electric-ends)
+;;; test-matlab-ts-mode-electric-ends.el ends here
+
+;; LocalWords:  utils defun eos treesit xr

Reply via email to