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

    matlab-ts-mode--ei: add alignment of multi-line struct fields
---
 NEWS.org                                           |  31 ++-
 matlab-ts-mode--ei.el                              | 309 +++++++++++++++------
 .../electric_indent_struct.m                       |  65 +++++
 .../electric_indent_struct_expected.m              |  65 +++++
 .../electric_indent_struct_expected_msgs.m         |  65 +++++
 .../electric_indent_xr_struct.m                    |  39 +++
 .../electric_indent_xr_struct_expected.org         | 204 ++++++++++++++
 .../electric_indent_struct.m                       |  65 +++++
 .../electric_indent_struct_expected.txt            | 178 ++++++++++++
 .../electric_indent_xr_struct.m                    |  39 +++
 .../electric_indent_xr_struct_expected.txt         |  34 +++
 11 files changed, 1008 insertions(+), 86 deletions(-)

diff --git a/NEWS.org b/NEWS.org
index 250038cdb5..52c0315b27 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -6,8 +6,7 @@
 * Release 8.0.0 TBD
 
 This release updates matlab-ts-mode indent to /electric indent code/. This is 
sometimes called
-pretty printing code or formatting code. Electric indent is controlled by the 
custom variable,
-~matlab-ts-mode-electric-indent~ which defaults to ~t~.
+pretty printing code or formatting code.
 
 To indent a MATLAB ~*.m~ file,
 
@@ -187,11 +186,31 @@ Electric indent will
                 1,   2,     3.41];
    #+end_src
 
-9. *Untabify (convert TAB Characters to spaces)*
+9. *Align fields of multi-line structures*
 
-   Conversion of tab characters to spaces ensures that code looks the same 
when using different
-   levels of tab stops. For example, UNIX (POSIX) uses tab stops of 8 whereas 
Microsoft uses tab
-   stops of 4.
+   Example:
+
+   #+begin_src matlab
+     myStruct = struct( ...
+       'field1',              (2+3)*4, ...
+          'longField2',"foo",...
+             'f3',2);
+   #+end_src
+
+   Indents to:
+
+   #+begin_src matlab
+     myStruct = struct( ...
+         'field1'    , (2 + 3) * 4, ...
+         'longField2', "foo", ...
+         'f3'        , 2);
+   #+end_src
+   
+10. *Untabify (convert TAB Characters to spaces)*
+
+    Conversion of tab characters to spaces ensures that code looks the same 
when using different
+    levels of tab stops. For example, UNIX (POSIX) uses tab stops of 8 whereas 
Microsoft uses tab
+    stops of 4.
 
 ** Indent Example
 
diff --git a/matlab-ts-mode--ei.el b/matlab-ts-mode--ei.el
index b35a742bf2..95732dfd47 100644
--- a/matlab-ts-mode--ei.el
+++ b/matlab-ts-mode--ei.el
@@ -466,7 +466,9 @@ If we nave a new line, returns electric indent info, 
ei-info:
 
   (list NEW-LINE-CONTENT PT-OFFSET ORIG-LINE-NODE-TYPES FIRST-NODE-IN-LINE)
 
-otherwise returns nil."
+where NEW-LINE-CONTENT, during `indent-region', does not contain the
+final amount of leading whitespace because we do electric indent before
+`treesit-indent-region'."
   (save-excursion
     ;; Move to first non-whitespace character on the line
     (let ((have-non-empty-line (matlab-ts-mode--ei-fast-back-to-indentation)))
@@ -850,7 +852,6 @@ column."
 
   (let* ((start-linenum (line-number-at-pos (treesit-node-start matrix)))
          (cache-value (alist-get start-linenum 
matlab-ts-mode--ei-is-m-matrix-alist)))
-
     (when cache-value ;; 0 or 1
       (cl-return-from matlab-ts-mode--ei-is-m-matrix (= cache-value 1)))
 
@@ -914,59 +915,210 @@ column."
           (push `(,start-linenum . ,(if ans 1 0)) 
matlab-ts-mode--ei-is-m-matrix-alist))
         ans))))
 
-(defun matlab-ts-mode--ei-is-assign (first-node type)
-  "Is FIRST-NODE of line for an assignment that matches TYPE?
-TYPE can be \\='single-line or \\='multi-line-matrix.  When
-\\='multi-line-matrix, the assignment is to a matrix with two or more
-rows and two or more columns, where each row is on its own line.  The
-assignment node is return or nil."
-  (let ((assign-node (treesit-node-parent first-node)))
-    (while (and assign-node
-                (not (string= (treesit-node-type assign-node) "assignment")))
-      (setq assign-node (treesit-node-parent assign-node)))
-    (when assign-node
-      (save-excursion
-        (forward-line 0)
-        (when (re-search-forward "=" (pos-eol) t)
-          (backward-char)
-          (let ((eq-node (treesit-node-at (point))))
-            ;; First "=" must be an assignment (assumptions elsewhere require 
this).
-            (when (and (equal (treesit-node-type eq-node) "=")
-                       (equal (treesit-node-type (treesit-node-parent 
eq-node)) "assignment"))
-              (cond
-               ;; Single-line assignment? Example: v1 = [1, 2];
-               ((eq type 'single-line)
-                (when (<= (treesit-node-end assign-node) (pos-eol))
-                  assign-node))
-
-               ;; Multi-line matrix assignment? Example: m1 = [1 2
-               ;;                                              3 4];
-               ((eq type 'multi-line-matrix)
-                (goto-char (treesit-node-end eq-node))
-                (let ((next-node (treesit-node-next-sibling eq-node)))
-                  (while (equal (treesit-node-type next-node) 
"line_continuation")
-                    (setq next-node (treesit-node-next-sibling next-node)))
-                  (when (and (equal (treesit-node-type next-node) "matrix")
-                             (matlab-ts-mode--ei-is-m-matrix next-node))
-                    assign-node)))
-               (t
-                (error "Assert: bad type %S" type))))))))))
-
-(defun matlab-ts-mode--ei-point-in-m-matrix (ei-info)
+(cl-defun matlab-ts-mode--ei-struct-ends-on-line (struct)
+  "Does function_call STRUCT node end on a line?"
+  (save-excursion
+    (goto-char (treesit-node-end struct))
+    (let ((pt-eol (pos-eol)))
+      (while (re-search-forward "^[ \t]" pt-eol t)
+        (backward-char)
+        (if (looking-at "%") ;; comment after end?
+            (goto-char pt-eol)
+          (if (looking-at ";") ;; semicolon okay
+              (forward-char)
+            (cl-return-from matlab-ts-mode--ei-struct-ends-on-line)))))
+    t))
+
+(defun matlab-ts-mode--ei-struct-starts-on-first-line (struct)
+  "Is function_call STRUCT node have opening paren on start line?"
+  (let ((start-linenum (line-number-at-pos (treesit-node-start struct)))
+        paren-node)
+    (cl-loop
+     for child in (treesit-node-children struct) do
+     (when (string= (treesit-node-type child) "(")
+       (setq paren-node child)
+       (cl-return)))
+    (and paren-node
+         (= start-linenum (line-number-at-pos (treesit-node-start 
paren-node))))))
+
+(defvar-local matlab-ts-mode--ei-is-m-struct-alist nil) ;; cache
+
+(cl-defun matlab-ts-mode--ei-is-m-struct (struct)
+  "Is function_call STRUCT node a multi-line struct that can be aligned?
+If so return the max field width of the struct."
+
+  (let* ((start-linenum (line-number-at-pos (treesit-node-start struct)))
+         (cache-value (alist-get start-linenum 
matlab-ts-mode--ei-is-m-struct-alist))
+         (max-field-width 0))
+
+    (when cache-value ;; 0 or max-field-width
+      (cl-return-from matlab-ts-mode--ei-is-m-struct (when (> cache-value 0) 
cache-value)))
+
+    (let* ((end-line (line-number-at-pos (treesit-node-end struct)))
+           arguments-node
+           (is-m-struct (and
+                         ;; Is candidate for a multi-line struct we can align 
if more than one line
+                         (> end-line start-linenum)
+                         ;; AND the opening "(" is on the same line as the 
struct
+                         (matlab-ts-mode--ei-struct-starts-on-first-line 
struct)
+                         ;; AND the closing ")" ends on it's own line
+                         (matlab-ts-mode--ei-struct-ends-on-line struct)
+                         ;; AND we have an arguments node struct(arg1, .....)
+                         (setq arguments-node (treesit-search-subtree struct
+                                                                      (rx bos 
"arguments" eos))))))
+      (when is-m-struct
+        (let (tracking-line-num ;; iterate through arguments, tracking how 
many args on a line
+              (n-args-on-tracking-line 0))
+          (cl-loop
+           for child in (treesit-node-children arguments-node) do
+           (when (not (string-match-p (rx bos (or "," "line_continuation" eos))
+                                      (treesit-node-type child)))
+             ;; either field or value?
+             (let ((arg-line-num (line-number-at-pos (treesit-node-start 
child))))
+               (if (and tracking-line-num (= tracking-line-num arg-line-num))
+                   (progn (setq n-args-on-tracking-line (1+ 
n-args-on-tracking-line))
+                          (when (> n-args-on-tracking-line 2)
+                            (setq is-m-struct nil)
+                            (cl-return)))
+                 ;; on new line
+                 (when (= n-args-on-tracking-line 1) ;; 0 or 2 is good, 1 is 
bad
+                   (setq is-m-struct nil)
+                   (cl-return))
+                 (when (not (string= (treesit-node-type child) "string"))
+                   (setq is-m-struct nil) ;; first arg on line must be the 
struct field string
+                   (cl-return))
+                 (when (string-match-p "," (treesit-node-text child))
+                   (setq is-m-struct nil) ;; we need the first comma to be end 
of the field string
+                   (cl-return))
+                 (let ((field-width (length (treesit-node-text child))))
+                   (when (> field-width max-field-width)
+                     (setq max-field-width field-width)))
+                 (setq tracking-line-num arg-line-num
+                       n-args-on-tracking-line 1)))))
+          (when (and n-args-on-tracking-line (= n-args-on-tracking-line 1)) ;; 
0 or 2 is good
+            (setq is-m-struct nil))))
+
+      (when matlab-ts-mode--ei-is-m-struct-alist
+        ;; Use 1 or 0 so we can differentiate between nil and not a multi-line 
struct
+        (push `(,start-linenum . ,(if is-m-struct max-field-width 0))
+              matlab-ts-mode--ei-is-m-struct-alist))
+
+      (when (and is-m-struct (> max-field-width 0))
+        max-field-width))))
+
+(cl-defun matlab-ts-mode--ei-align-line-in-m-struct (struct-assign-node 
max-field-width ei-info)
+  "Align multi-line struct in STRUCT-ASSIGN-NODE to MAX-FIELD-WIDTH.
+See `matlab-ts-mode--ei-get-new-line' for EI-INFO."
+  (let* ((ei-line (nth 0 ei-info))
+         (assign-linenum (line-number-at-pos (treesit-node-start 
struct-assign-node)))
+         comma-offset
+         new-comma-offset)
+
+    (if (= (line-number-at-pos) assign-linenum)
+        ;; On "var = struct(........" line?
+        (let* ((eq-offset (string-match-p "=" ei-line))
+               (open-paren-offset (string-match-p "(" ei-line eq-offset))
+               (arg (progn (string-match "\\([^ \t]+\\)" ei-line (1+ 
open-paren-offset))
+                           (match-string 0 ei-line))))
+          (when (string-match-p (rx bos "...") arg) ;; skip continuations
+            (cl-return-from matlab-ts-mode--ei-align-line-in-m-struct ei-info))
+          (setq comma-offset (string-match-p "," ei-line open-paren-offset)
+                new-comma-offset (+ 1 open-paren-offset max-field-width)))
+      ;; Else on later line
+      (when (string-match-p (rx bos (0+ (or " " "\t")) "...") ei-line) ;; skip 
continuations
+        (cl-return-from matlab-ts-mode--ei-align-line-in-m-struct ei-info))
+      (setq comma-offset (string-match-p "," ei-line)
+            new-comma-offset (+ (string-match-p "[^ \t]" ei-line) 
max-field-width)))
+
+    (let ((n-spaces-to-add (- new-comma-offset comma-offset)))
+      (when (not (= n-spaces-to-add 0))
+        (setq ei-line (concat (substring ei-line 0 comma-offset)
+                              (make-string n-spaces-to-add ? )
+                              (substring ei-line comma-offset)))
+        (let ((pt-offset (nth 1 ei-info)))
+          (when (and pt-offset (> pt-offset comma-offset))
+            (setq pt-offset (+ n-spaces-to-add pt-offset)))
+          (setq ei-info (list ei-line pt-offset (nth 2 ei-info) (nth 3 
ei-info)))))))
+  ei-info)
+
+(defun matlab-ts-mode--ei-is-assign (first-node a-type &optional assign-node)
+  "Is FIRST-NODE of line for an assignment that matches A-TYPE?
+If caller knows FIRST-NODE is in an assignment, ASSIGN-NODE should be
+supplied to avoid search ancestors for the assignment node.  Returns
+assignment node when A-TYPE is met, else nil.  A-TYPE can be:
+ - \\='single-line : varName = value            % on a single line, e.g. v1 = 1
+ - \\='m-matrix    : varName = [100   2         % matrix on multiple lines
+                                  3 400];
+ - \\='m-struct    : varName = struct(...       % struct on multiple lines
+                        \\='field1\\', value1, ...
+                        \\='otherField2\\', value2);
+Note, \\='m-struct returns (cons assignment-node max-field-width) or nil."
+  (when (not assign-node)
+    (setq assign-node (treesit-parent-until first-node (rx bos "assignment" 
eos))))
+
+  (when assign-node
+    (save-excursion
+      (forward-line 0)
+      (when (re-search-forward "=" (pos-eol) t)
+        (backward-char)
+        (let ((eq-node (treesit-node-at (point))))
+          ;; First "=" must be an assignment (assumptions elsewhere require 
this).
+          (when (and (equal (treesit-node-type eq-node) "=")
+                     (equal (treesit-node-type (treesit-node-parent eq-node)) 
"assignment"))
+            (cond
+             ;; Single-line assignment? Example: v1 = [1, 2];
+             ((eq a-type 'single-line)
+              (when (<= (treesit-node-end assign-node) (pos-eol))
+                assign-node))
+
+             ;; Multi-line matrix or struct assignment? Examples:
+             ;;   m1 = [100   2
+             ;;          3 400];
+             ;;   s = struct('field1',      value1, ...
+             ;;              'otherField2', value2);
+             ((or (eq a-type 'm-matrix) (eq a-type 'm-struct))
+              (let ((next-node (treesit-node-next-sibling eq-node)))
+                (while (equal (treesit-node-type next-node) 
"line_continuation")
+                  (setq next-node (treesit-node-next-sibling next-node)))
+                (when next-node
+                  (cond
+                   ((eq a-type 'm-matrix)
+                    (when (and (string= (treesit-node-type next-node) "matrix")
+                               (matlab-ts-mode--ei-is-m-matrix next-node))
+                      assign-node))
+                   ((eq a-type 'm-struct)
+                    (when (and (string= (treesit-node-type next-node) 
"function_call")
+                               (string= (treesit-node-text 
(treesit-node-child-by-field-name
+                                                            next-node "name"))
+                                        "struct"))
+                      (let ((max-field-width (matlab-ts-mode--ei-is-m-struct 
next-node)))
+                        (when max-field-width
+                          (cons assign-node max-field-width)))))))))
+
+             (t
+              (error "Assert: bad a-type %S" a-type)))))))))
+
+(defun matlab-ts-mode--ei-point-in-m-type (ei-info m-type)
   "Are we in a multi-line matrix?
-See `matlab-ts-mode--ei-get-new-line' for EI-INFO contents.  Returns
-mat-info a (list matrix-node n-rows n-cols) if in a multi-line matrix."
+See `matlab-ts-mode--ei-get-new-line' for EI-INFO.
+If M-TYPE is \\='m-matrix, return the assignment node else nil.
+We define an m-matrix to be an assignment where there's more than one
+line, each row is on the same line, with same number of columns:
+  m = [100   2;
+         3 400];
+If M-TYPE is \\='m-struct, return the assignment node else nil.
+We define an m-matrix to be an assignment where there's more than one
+line, each row is on the same line, with same number of columns:
+  m = [100   2;
+         3 400];"
   (let* ((first-node-in-line (nth 3 ei-info))
-         (parent (treesit-node-parent first-node-in-line)))
-    (while (and parent
-                (not (string= (treesit-node-type parent) "assignment")))
-      (setq parent (treesit-node-parent parent)))
-    (when parent ;; In an assignment?
+         (assign-node (treesit-parent-until first-node-in-line (rx bos 
"assignment" eos))))
+    (when assign-node
       (save-excursion
-        (goto-char (treesit-node-start parent))
+        (goto-char (treesit-node-start assign-node))
         (matlab-ts-mode--ei-fast-back-to-indentation)
         (let ((first-node (treesit-node-at (point))))
-          (matlab-ts-mode--ei-is-assign first-node 'multi-line-matrix))))))
+          (matlab-ts-mode--ei-is-assign first-node m-type assign-node))))))
 
 (defun matlab-ts-mode--ei-assign-offset (ei-line)
   "Get the assignment offset from the indent-level in EI-LINE."
@@ -1218,13 +1370,16 @@ See `matlab-ts-mode--ei-get-new-line' for EI-INFO 
contents."
 (defun matlab-ts-mode--ei-align (ei-info)
   "Align elements in EI-INFO.
 See `matlab-ts-mode--ei-get-new-line' for EI-INFO contents."
-  (let ((matrix-node (matlab-ts-mode--ei-point-in-m-matrix ei-info)))
-    (if matrix-node
-        (setq ei-info (matlab-ts-mode--ei-align-line-in-m-matrix matrix-node 
ei-info))
-      ;; else do single-line alignments
-      (setq ei-info (matlab-ts-mode--ei-align-assignments ei-info))
-      (setq ei-info (matlab-ts-mode--ei-align-properties ei-info))
-      (setq ei-info (matlab-ts-mode--ei-align-trailing-comments ei-info))))
+  (let ((matrix-assign-node (matlab-ts-mode--ei-point-in-m-type ei-info 
'm-matrix)))
+    (if matrix-assign-node
+        (setq ei-info (matlab-ts-mode--ei-align-line-in-m-matrix 
matrix-assign-node ei-info))
+      (let ((pair (matlab-ts-mode--ei-point-in-m-type ei-info 'm-struct)))
+        (if pair
+            (setq ei-info (matlab-ts-mode--ei-align-line-in-m-struct (car 
pair) (cdr pair) ei-info))
+          ;; else do single-line alignments
+          (setq ei-info (matlab-ts-mode--ei-align-assignments ei-info))
+          (setq ei-info (matlab-ts-mode--ei-align-properties ei-info))
+          (setq ei-info (matlab-ts-mode--ei-align-trailing-comments 
ei-info))))))
   ei-info)
 
 (defun matlab-ts-mode--ei-get-start-info ()
@@ -1238,31 +1393,22 @@ Example where point is on the \"d\" in width:
            ^
 start-node is the identifier node for width and start-offset is 2."
   (let (start-node
-        start-offset
-        (at-eol (looking-at "[ \t]*$")))
-
-    ;; The node when at-eol is not on the current line.
-    (when (not at-eol)
-      (let ((node (treesit-node-at (point))))
-        ;; Consider:  A = [B   C];
-        ;;                   ^
-        ;; node is the invisible "," and moving to start gives us node for C
-        (save-excursion
-          (when (< (point) (treesit-node-start node))
-            (if (re-search-forward "[^ \t]" (pos-eol) t)
-                (progn
-                  (backward-char)
-                  (setq node (treesit-node-at (point))))
-              (if (re-search-backward "[^ \t]" (pos-bol) t)
-                  (setq node (treesit-node-at (point)))
-                (setq node nil))))
+        start-offset)
+    (when (not (looking-at "[ \t]*$")) ;; when not at EOL
+      (save-excursion
+        (let ((node (car (matlab-ts-mode--ei-get-node-to-use (treesit-node-at 
(point))))))
+          (when (not (and node
+                          (>= (point) (treesit-node-start node))
+                          (<= (point) (treesit-node-end node))))
+            (when (re-search-forward "[^ \t]" (pos-eol) t)
+              (backward-char)
+              (setq node (car (matlab-ts-mode--ei-get-node-to-use 
(treesit-node-at (point)))))))
 
           (when (and node
                      (>= (point) (treesit-node-start node))
                      (<= (point) (treesit-node-end node)))
-            (setq start-node (car (matlab-ts-mode--ei-get-node-to-use node)))
+            (setq start-node node)
             (setq start-offset (- (point) (treesit-node-start node)))))))
-
     (cons start-node start-offset)))
 
 (defun matlab-ts-mode--ei-workaround-143 (beg end &optional line-pt)
@@ -1378,7 +1524,7 @@ to it's logical location when the line is updated."
   (let* ((start-pair (when (or (not is-indent-region)
                                start-pt-offset)
                        (matlab-ts-mode--ei-get-start-info)))
-         (start-node (car start-pair)) ;; may be nil
+         (start-node (car start-pair)) ;; nil if at EOL
          (start-offset (cdr start-pair))
          (at-eol (and start-offset (looking-at "[ \t]*$")))
          (orig-line (buffer-substring (pos-bol) (pos-eol)))
@@ -1441,6 +1587,7 @@ If INIT is non-nil, set to initial value, otherwise set 
to nil"
                 matrix-ts-mode--ei-m-matrix-first-col-extra-alist value
                 matlab-ts-mode--ei-is-m-matrix-alist value
                 matlab-ts-mode--ei-align-matrix-alist value
+                matlab-ts-mode--ei-is-m-struct-alist value
                 matlab-ts-mode--ei-errors-alist (when init 
(matlab-ts-mode--ei-get-errors-alist))
                 matlab-ts-mode--ei-orig-line-node-types-alist value)))
 
@@ -1502,7 +1649,9 @@ This expansion of the region is done to simplify electric 
indent."
 
                   (let* ((tuple (matlab-ts-mode--ei-indent-elements-in-line
                                  'indent-region
-                                 (when (= i-linenum start-pt-linenum) 
start-pt-offset)))
+                                 (when (= i-linenum start-pt-linenum)
+                                   (move-to-column start-pt-offset)
+                                   start-pt-offset)))
                          (new-line (nth 0 tuple))
                          (line-updated (nth 1 tuple))
                          (new-start-pt-offset (nth 2 tuple)))
@@ -1528,7 +1677,7 @@ This expansion of the region is done to simplify electric 
indent."
                     (forward-line end-linenum)
                     (setq end (point)))))))
 
-          (matlab-ts-mode--ei-move-to-loc start-pt-linenum start-pt-offset) ;; 
for indent-region
+          (matlab-ts-mode--ei-move-to-loc start-pt-linenum start-pt-offset)
           (treesit-indent-region beg end))
 
       (matlab-ts-mode--ei-set-alist-caches nil)
diff --git 
a/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m 
b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m
new file mode 100644
index 0000000000..e045fafe4d
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m
@@ -0,0 +1,65 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - line-by-line typing results in 
error nodes
+
+if 1
+    s1 = struct  (     'field1'  , value1 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+s2 = struct(   'field1', value1, ...
+                'otherfield2', value2)
+
+a(1,1)   = struct(  'field1'    , value1, ...
+            'otherfield2', value2)
+
+a(1, 2) = struct( ...
+    'field1'     , value1, ...
+    'otherfield2', value2, ...
+    'foo'        , 1 + 2 * 3)
+
+s3 = struct( ...
+    'field1', value1, ...
+    'otherfield2', value2);
+
+s4 = struct( ...
+    'longField1', value1, ...
+    'field2', value2)
+
+s5 = struct(            "a", 1, ...
+               ...
+         "foobar", (2+3)*4+5, ...
+            "g", 3);
+
+not1 = struct('one', value1, 'two', value2, ...
+              'three', value3);
+
+not2 = struct(['field1', 'foo'], value1 + 10, ...
+              ...
+              'otherfield2', value2);
+
+% Comma in field name not allowed because that isn't supported (yet) by 
matlab-ts-mode--ei.el
+% In matlab-ts-mode--ei.el, we assume (string-match-p "," ei-line 
open-paren-offset) goes
+% to the comma after the first field. To support fields with commas in the 
name, we'll need
+% to do more work and commas are a real edge case.
+not3 = struct('field1,foo', value1 + 10, ...
+              ...
+              'otherLongField2', value2);
+
+not5 = struct ...
+    ('field1', 1, ...
+     'longField2', 2);
+
+not6 = ...
+struct ...
+    ('field1', 1, ...
+     'lognField2', 2);
+
+not7 ...
+= ...
+struct ...
+    ('field1', 1, ...
+     'longField2', 2);
+
+not8 = struct; % (this shouldn't be aligned!
diff --git 
a/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.m
 
b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.m
new file mode 100644
index 0000000000..0d72160c04
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.m
@@ -0,0 +1,65 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - line-by-line typing results in 
error nodes
+
+if 1
+    s1 = struct('field1'     , value1 + 10, ...
+                ...
+                'otherfield2', value2);
+end
+
+s2 = struct('field1'     , value1, ...
+            'otherfield2', value2)
+
+a(1, 1) = struct('field1'     , value1, ...
+                 'otherfield2', value2)
+
+a(1, 2) = struct( ...
+    'field1'     , value1, ...
+    'otherfield2', value2, ...
+    'foo'        , 1 + 2 * 3)
+
+s3 = struct( ...
+    'field1'     , value1, ...
+    'otherfield2', value2);
+
+s4 = struct( ...
+    'longField1', value1, ...
+    'field2'    , value2)
+
+s5 = struct("a"     , 1, ...
+            ...
+            "foobar", (2 + 3) * 4 + 5, ...
+            "g"     , 3);
+
+not1 = struct('one', value1, 'two', value2, ...
+              'three', value3);
+
+not2 = struct(['field1', 'foo'], value1 + 10, ...
+              ...
+              'otherfield2', value2);
+
+% Comma in field name not allowed because that isn't supported (yet) by 
matlab-ts-mode--ei.el
+% In matlab-ts-mode--ei.el, we assume (string-match-p "," ei-line 
open-paren-offset) goes
+% to the comma after the first field. To support fields with commas in the 
name, we'll need
+% to do more work and commas are a real edge case.
+not3 = struct('field1,foo', value1 + 10, ...
+              ...
+              'otherLongField2', value2);
+
+not5 = struct ...
+    ('field1', 1, ...
+     'longField2', 2);
+
+not6 = ...
+    struct ...
+        ('field1', 1, ...
+         'lognField2', 2);
+
+not7 ...
+    = ...
+    struct ...
+        ('field1', 1, ...
+         'longField2', 2);
+
+not8 = struct; % (this shouldn't be aligned!
diff --git 
a/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected_msgs.m
 
b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected_msgs.m
new file mode 100644
index 0000000000..d2817f20cf
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected_msgs.m
@@ -0,0 +1,65 @@
+% -*- matlab-ts -*- %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+
+% t-utils-test-indent: no-line-by-line-indent - line-by-line typing results in 
error nodes %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+
+if 1 %  <{Matched rule: (matlab-ts-mode--i-top-level matlab-ts-mode--column-0 
0)}>
+    s1 = struct('field1'     , value1 + 10, ... %  <{Matched rule: ((node-is 
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
 parent 4)}>
+                ... %  <{Matched rule: ((parent-is "\\`arguments\\'") parent 
0)}>
+                'otherfield2', value2); %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+end %  <{Matched rule: ((node-is 
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'") 
parent 0)}>
+
+s2 = struct('field1'     , value1, ... %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
+            'otherfield2', value2) %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+a(1, 1) = struct('field1'     , value1, ... %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
+                 'otherfield2', value2) %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+a(1, 2) = struct( ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    'field1'     , value1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+    'otherfield2', value2, ... %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+    'foo'        , 1 + 2 * 3) %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+s3 = struct( ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    'field1'     , value1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+    'otherfield2', value2); %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+s4 = struct( ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    'longField1', value1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+    'field2'    , value2) %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+s5 = struct("a"     , 1, ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+            ... %  <{Matched rule: ((parent-is "\\`arguments\\'") parent 0)}>
+            "foobar", (2 + 3) * 4 + 5, ... %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+            "g"     , 3); %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+not1 = struct('one', value1, 'two', value2, ... %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
+              'three', value3); %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+not2 = struct(['field1', 'foo'], value1 + 10, ... %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
+              ... %  <{Matched rule: ((parent-is "\\`arguments\\'") parent 0)}>
+              'otherfield2', value2); %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+% Comma in field name not allowed because that isn't supported (yet) by 
matlab-ts-mode--ei.el %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+% In matlab-ts-mode--ei.el, we assume (string-match-p "," ei-line 
open-paren-offset) goes %  <{Matched rule: 
(matlab-ts-mode--i-block-comment-end-matcher 
matlab-ts-mode--i-block-comment-end-anchor 0)}>
+% to the comma after the first field. To support fields with commas in the 
name, we'll need %  <{Matched rule: 
(matlab-ts-mode--i-block-comment-end-matcher 
matlab-ts-mode--i-block-comment-end-anchor 0)}>
+% to do more work and commas are a real edge case. %  <{Matched rule: 
(matlab-ts-mode--i-block-comment-end-matcher 
matlab-ts-mode--i-block-comment-end-anchor 0)}>
+not3 = struct('field1,foo', value1 + 10, ... %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
+              ... %  <{Matched rule: ((parent-is "\\`arguments\\'") parent 0)}>
+              'otherLongField2', value2); %  <{Matched rule: ((parent-is 
"\\`arguments\\'") parent 0)}>
+
+not5 = struct ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    ('field1', 1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+     'longField2', 2); %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+not6 = ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    struct ... %  <{Matched rule: ((parent-is "\\`assignment\\'") parent 4)}>
+        ('field1', 1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+         'lognField2', 2); %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+not7 ... %  <{Matched rule: (matlab-ts-mode--i-top-level 
matlab-ts-mode--column-0 0)}>
+    = ... %  <{Matched rule: ((parent-is "\\`assignment\\'") parent 4)}>
+    struct ... %  <{Matched rule: ((parent-is "\\`assignment\\'") parent 4)}>
+        ('field1', 1, ... %  <{Matched rule: 
(matlab-ts-mode--i-assign-cont-matcher matlab-ts-mode--i-assign-cont-anchor 
matlab-ts-mode--i-assign-cont-offset)}>
+         'longField2', 2); %  <{Matched rule: ((parent-is "\\`arguments\\'") 
parent 0)}>
+
+not8 = struct; % (this shouldn't be aligned! %  <{Matched rule: 
(matlab-ts-mode--i-top-level matlab-ts-mode--column-0 0)}>
diff --git 
a/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
 
b/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
new file mode 100644
index 0000000000..e93264f183
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
@@ -0,0 +1,39 @@
+% -*- matlab-ts -*-
+
+%{
+  case1-point-before-comma:
+  (t-utils-xr
+
+  (re-search-forward "fiel")  ;; point left on the "d"
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+%}
+
+if 1
+       s1 = struct(     'field1', value1 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+%{
+  case2-point-after-comma:
+  (t-utils-xr
+
+  (re-search-forward "value")  ;; point left on the 9
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+%}
+
+if 1
+       s2 = struct(     'field1', value9 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+% case3: (t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
diff --git 
a/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.org
 
b/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.org
new file mode 100644
index 0000000000..cb0b06dab2
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.org
@@ -0,0 +1,204 @@
+#+startup: showall
+
+* Executing commands from electric_indent_xr_struct.m:5:2:
+
+  comma: (t-utils-xr
+
+  (re-search-forward "fiel")  ;; point left on the "d"
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+- Invoking      : (re-search-forward "fiel")
+  Start point   :  162
+  Moved to point:  202
+  : 16:29:        s1 = struct(     'field1', value1 + 10, ...
+  :                                     ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  202
+  Moved to point:  194
+  : 16:21:     s1 = struct('field1'     , value1 + 10, ...
+  :                             ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -13,7 +13,7 @@
+ %}
+ 
+ if 1
+-       s1 = struct(     'field1', value1 + 10, ...
++    s1 = struct('field1'     , value1 + 10, ...
+                 ...
+   'otherfield2', value2);
+ end
+  #+end_src diff
+
+- Invoking      : "C-n" = next-line
+  Start point   :  194
+  Moved to point:  240
+  : 17:19:                 ...
+  :                           ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  240
+  No point movement
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :  240
+  Moved to point:  260
+  : 18:19:   'otherfield2', value2);
+  :                           ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  260
+  Moved to point:  274
+  : 18:33:                 'otherfield2', value2);
+  :                                         ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -15,7 +15,7 @@
+ if 1
+     s1 = struct('field1'     , value1 + 10, ...
+                 ...
+-  'otherfield2', value2);
++                'otherfield2', value2);
+ end
+ 
+ %{
+  #+end_src diff
+
+* Executing commands from electric_indent_xr_struct.m:23:2:
+
+  comma: (t-utils-xr
+
+  (re-search-forward "value")  ;; point left on the 9
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+- Invoking      : (re-search-forward "value")
+  Start point   :  424
+  Moved to point:  474
+  : 34:39:        s2 = struct(     'field1', value9 + 10, ...
+  :                                               ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  474
+  Moved to point:  471
+  : 34:36:     s2 = struct('field1'     , value9 + 10, ...
+  :                                            ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -31,7 +31,7 @@
+ %}
+ 
+ if 1
+-       s2 = struct(     'field1', value9 + 10, ...
++    s2 = struct('field1'     , value9 + 10, ...
+                 ...
+   'otherfield2', value2);
+ end
+  #+end_src diff
+
+- Invoking      : "C-n" = next-line
+  Start point   :  471
+  Moved to point:  502
+  : 35:19:                 ...
+  :                           ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  502
+  No point movement
+  No buffer modifications
+
+- Invoking      : "C-n" = next-line
+  Start point   :  502
+  Moved to point:  522
+  : 36:19:   'otherfield2', value2);
+  :                           ^
+  No buffer modifications
+
+- Invoking      : "C-i" = indent-for-tab-command
+  Start point   :  522
+  Moved to point:  536
+  : 36:33:                 'otherfield2', value2);
+  :                                         ^
+  Buffer modified:
+  #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -33,7 +33,7 @@
+ if 1
+     s2 = struct('field1'     , value9 + 10, ...
+                 ...
+-  'otherfield2', value2);
++                'otherfield2', value2);
+ end
+ 
+ % case3: (t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
+  #+end_src diff
+
+* Executing commands from electric_indent_xr_struct.m:39:9:
+
+  case3: (t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
+
+- Invoking      : (t-utils-xr-print-code (point-min) (point-max))
+  Start point   :  617
+  No point movement
+  standard-output:
+  #+begin_src matlab-ts
+% -*- matlab-ts -*-
+
+%{
+  case1-point-before-comma:
+  (t-utils-xr
+
+  (re-search-forward \"fiel\")  ;; point left on the \"d\"
+  \"C-i\"
+  \"C-n\" \"C-i\"
+  \"C-n\" \"C-i\"
+  )
+
+%}
+
+if 1
+    s1 = struct('field1'     , value1 + 10, ...
+                ...
+                'otherfield2', value2);
+end
+
+%{
+  case2-point-after-comma:
+  (t-utils-xr
+
+  (re-search-forward \"value\")  ;; point left on the 9
+  \"C-i\"
+  \"C-n\" \"C-i\"
+  \"C-n\" \"C-i\"
+  )
+
+%}
+
+if 1
+    s2 = struct('field1'     , value9 + 10, ...
+                ...
+                'otherfield2', value2);
+end
+
+% case3: (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-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m
 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m
new file mode 100644
index 0000000000..e045fafe4d
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct.m
@@ -0,0 +1,65 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - line-by-line typing results in 
error nodes
+
+if 1
+    s1 = struct  (     'field1'  , value1 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+s2 = struct(   'field1', value1, ...
+                'otherfield2', value2)
+
+a(1,1)   = struct(  'field1'    , value1, ...
+            'otherfield2', value2)
+
+a(1, 2) = struct( ...
+    'field1'     , value1, ...
+    'otherfield2', value2, ...
+    'foo'        , 1 + 2 * 3)
+
+s3 = struct( ...
+    'field1', value1, ...
+    'otherfield2', value2);
+
+s4 = struct( ...
+    'longField1', value1, ...
+    'field2', value2)
+
+s5 = struct(            "a", 1, ...
+               ...
+         "foobar", (2+3)*4+5, ...
+            "g", 3);
+
+not1 = struct('one', value1, 'two', value2, ...
+              'three', value3);
+
+not2 = struct(['field1', 'foo'], value1 + 10, ...
+              ...
+              'otherfield2', value2);
+
+% Comma in field name not allowed because that isn't supported (yet) by 
matlab-ts-mode--ei.el
+% In matlab-ts-mode--ei.el, we assume (string-match-p "," ei-line 
open-paren-offset) goes
+% to the comma after the first field. To support fields with commas in the 
name, we'll need
+% to do more work and commas are a real edge case.
+not3 = struct('field1,foo', value1 + 10, ...
+              ...
+              'otherLongField2', value2);
+
+not5 = struct ...
+    ('field1', 1, ...
+     'longField2', 2);
+
+not6 = ...
+struct ...
+    ('field1', 1, ...
+     'lognField2', 2);
+
+not7 ...
+= ...
+struct ...
+    ('field1', 1, ...
+     'longField2', 2);
+
+not8 = struct; % (this shouldn't be aligned!
diff --git 
a/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.txt
 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.txt
new file mode 100644
index 0000000000..084e49ba08
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-files/electric_indent_struct_expected.txt
@@ -0,0 +1,178 @@
+# -*- t-utils-ts-parse-tree -*-
+(source_file<1,1619> (comment[1,20]@{% -*- matlab-ts -*-}@) 
(comment[22,112]@{% t-utils-test-indent: no-line-by-line-indent - li...}@) 
\n[112,114]
+ (if_statement<114,220> if[114,116] condition: (number[117,118]@{1}@)
+  (block<123,217>
+   (assignment<123,215> left: (identifier[123,125]@{s1}@) =[126,127]
+    right: 
+     (function_call<128,215> name: (identifier[128,134]@{struct}@) ([136,137]
+      (arguments<142,214>
+       argument: (string<142,150> '[142,143] 
(string_content[143,149]@{field1}@) '[149,150])
+       ,[152,153]
+       (binary_operator<154,165> left: (identifier[154,160]@{value1}@) 
+[161,162] right: (number[163,165]@{10}@))
+       ,[165,166] (line_continuation[167,171]@{...\n}@) 
(line_continuation[187,191]@{...\n}@)
+       (string<193,206> '[193,194] (string_content[194,205]@{otherfield2}@) 
'[205,206])
+       ,[206,207] (identifier[208,214]@{value2}@))
+      )[214,215]))
+   ;[215,216] \n[216,217])
+  end[217,220])
+ \n[220,222]
+ (assignment<222,297> left: (identifier[222,224]@{s2}@) =[225,226]
+  right: 
+   (function_call<227,297> name: (identifier[227,233]@{struct}@) ([233,234]
+    (arguments<237,296>
+     argument: (string<237,245> '[237,238] (string_content[238,244]@{field1}@) 
'[244,245])
+     ,[245,246] (identifier[247,253]@{value1}@) ,[253,254] 
(line_continuation[255,259]@{...\n}@)
+     (string<275,288> '[275,276] (string_content[276,287]@{otherfield2}@) 
'[287,288])
+     ,[288,289] (identifier[290,296]@{value2}@))
+    )[296,297]))
+ \n[297,299]
+ (assignment<299,379>
+  left: 
+   (function_call<299,305> name: (identifier[299,300]@{a}@) ([300,301]
+    (arguments<301,304> argument: (number[301,302]@{1}@) ,[302,303] 
(number[303,304]@{1}@))
+    )[304,305])
+  =[308,309]
+  right: 
+   (function_call<310,379> name: (identifier[310,316]@{struct}@) ([316,317]
+    (arguments<319,378>
+     argument: (string<319,327> '[319,320] (string_content[320,326]@{field1}@) 
'[326,327])
+     ,[331,332] (identifier[333,339]@{value1}@) ,[339,340] 
(line_continuation[341,345]@{...\n}@)
+     (string<357,370> '[357,358] (string_content[358,369]@{otherfield2}@) 
'[369,370])
+     ,[370,371] (identifier[372,378]@{value2}@))
+    )[378,379]))
+ \n[379,381]
+ (assignment<381,494>
+  left: 
+   (function_call<381,388> name: (identifier[381,382]@{a}@) ([382,383]
+    (arguments<383,387> argument: (number[383,384]@{1}@) ,[384,385] 
(number[386,387]@{2}@))
+    )[387,388])
+  =[389,390]
+  right: 
+   (function_call<391,494> name: (identifier[391,397]@{struct}@) ([397,398] 
(line_continuation[399,403]@{...\n}@)
+    (arguments<407,493>
+     argument: (string<407,415> '[407,408] (string_content[408,414]@{field1}@) 
'[414,415])
+     ,[420,421] (identifier[422,428]@{value1}@) ,[428,429] 
(line_continuation[430,434]@{...\n}@)
+     (string<438,451> '[438,439] (string_content[439,450]@{otherfield2}@) 
'[450,451])
+     ,[451,452] (identifier[453,459]@{value2}@) ,[459,460] 
(line_continuation[461,465]@{...\n}@)
+     (string<469,474> '[469,470] (string_content[470,473]@{foo}@) '[473,474])
+     ,[482,483]
+     (binary_operator<484,493> left: (number[484,485]@{1}@) +[486,487]
+      right: (binary_operator<488,493> left: (number[488,489]@{2}@) *[490,491] 
right: (number[492,493]@{3}@))))
+    )[493,494]))
+ \n[494,496]
+ (assignment<496,565> left: (identifier[496,498]@{s3}@) =[499,500]
+  right: 
+   (function_call<501,565> name: (identifier[501,507]@{struct}@) ([507,508] 
(line_continuation[509,513]@{...\n}@)
+    (arguments<517,564>
+     argument: (string<517,525> '[517,518] (string_content[518,524]@{field1}@) 
'[524,525])
+     ,[525,526] (identifier[527,533]@{value1}@) ,[533,534] 
(line_continuation[535,539]@{...\n}@)
+     (string<543,556> '[543,544] (string_content[544,555]@{otherfield2}@) 
'[555,556])
+     ,[556,557] (identifier[558,564]@{value2}@))
+    )[564,565]))
+ ;[565,566]
+ (assignment<568,636> left: (identifier[568,570]@{s4}@) =[571,572]
+  right: 
+   (function_call<573,636> name: (identifier[573,579]@{struct}@) ([579,580] 
(line_continuation[581,585]@{...\n}@)
+    (arguments<589,635>
+     argument: (string<589,601> '[589,590] 
(string_content[590,600]@{longField1}@) '[600,601])
+     ,[601,602] (identifier[603,609]@{value1}@) ,[609,610] 
(line_continuation[611,615]@{...\n}@)
+     (string<619,627> '[619,620] (string_content[620,626]@{field2}@) 
'[626,627])
+     ,[627,628] (identifier[629,635]@{value2}@))
+    )[635,636]))
+ \n[636,638]
+ (assignment<638,746> left: (identifier[638,640]@{s5}@) =[641,642]
+  right: 
+   (function_call<643,746> name: (identifier[643,649]@{struct}@) ([649,650]
+    (arguments<662,745>
+     argument: (string<662,665> "[662,663] (string_content[663,664]@{a}@) 
"[664,665])
+     ,[665,666] (number[667,668]@{1}@) ,[668,669] 
(line_continuation[670,674]@{...\n}@) (line_continuation[689,693]@{...\n}@)
+     (string<702,710> "[702,703] (string_content[703,709]@{foobar}@) 
"[709,710])
+     ,[710,711]
+     (binary_operator<712,721>
+      left: 
+       (binary_operator<712,719>
+        left: 
+         (parenthesis<712,717> ([712,713]
+          (binary_operator<713,716> left: (number[713,714]@{2}@) +[714,715] 
right: (number[715,716]@{3}@))
+          )[716,717])
+        *[717,718] right: (number[718,719]@{4}@))
+      +[719,720] right: (number[720,721]@{5}@))
+     ,[721,722] (line_continuation[723,727]@{...\n}@)
+     (string<739,742> "[739,740] (string_content[740,741]@{g}@) "[741,742])
+     ,[742,743] (number[744,745]@{3}@))
+    )[745,746]))
+ ;[746,747]
+ (assignment<749,827> left: (identifier[749,753]@{not1}@) =[754,755]
+  right: 
+   (function_call<756,827> name: (identifier[756,762]@{struct}@) ([762,763]
+    (arguments<763,826>
+     argument: (string<763,768> '[763,764] (string_content[764,767]@{one}@) 
'[767,768])
+     ,[768,769] (identifier[770,776]@{value1}@) ,[776,777]
+     (string<778,783> '[778,779] (string_content[779,782]@{two}@) '[782,783])
+     ,[783,784] (identifier[785,791]@{value2}@) ,[791,792] 
(line_continuation[793,797]@{...\n}@)
+     (string<811,818> '[811,812] (string_content[812,817]@{three}@) '[817,818])
+     ,[818,819] (identifier[820,826]@{value3}@))
+    )[826,827]))
+ ;[827,828]
+ (assignment<830,934> left: (identifier[830,834]@{not2}@) =[835,836]
+  right: 
+   (function_call<837,934> name: (identifier[837,843]@{struct}@) ([843,844]
+    (arguments<844,933>
+     argument: 
+      (matrix<844,861> [[844,845]
+       (row<845,860>
+        (string<845,853> '[845,846] (string_content[846,852]@{field1}@) 
'[852,853])
+        ,[853,854]
+        (string<855,860> '[855,856] (string_content[856,859]@{foo}@) 
'[859,860]))
+       ][860,861])
+     ,[861,862]
+     (binary_operator<863,874> left: (identifier[863,869]@{value1}@) 
+[870,871] right: (number[872,874]@{10}@))
+     ,[874,875] (line_continuation[876,880]@{...\n}@) 
(line_continuation[894,898]@{...\n}@)
+     (string<912,925> '[912,913] (string_content[913,924]@{otherfield2}@) 
'[924,925])
+     ,[925,926] (identifier[927,933]@{value2}@))
+    )[933,934]))
+ ;[934,935] (comment[937,1263]@{% Comma in field name not allowed because that 
isn...}@)
+ (assignment<1264,1367> left: (identifier[1264,1268]@{not3}@) =[1269,1270]
+  right: 
+   (function_call<1271,1367> name: (identifier[1271,1277]@{struct}@) 
([1277,1278]
+    (arguments<1278,1366>
+     argument: (string<1278,1290> '[1278,1279] 
(string_content[1279,1289]@{field1,foo}@) '[1289,1290])
+     ,[1290,1291]
+     (binary_operator<1292,1303> left: (identifier[1292,1298]@{value1}@) 
+[1299,1300] right: (number[1301,1303]@{10}@))
+     ,[1303,1304] (line_continuation[1305,1309]@{...\n}@) 
(line_continuation[1323,1327]@{...\n}@)
+     (string<1341,1358> '[1341,1342] 
(string_content[1342,1357]@{otherLongField2}@) '[1357,1358])
+     ,[1358,1359] (identifier[1360,1366]@{value2}@))
+    )[1366,1367]))
+ ;[1367,1368]
+ (assignment<1370,1431> left: (identifier[1370,1374]@{not5}@) =[1375,1376]
+  right: 
+   (function_call<1377,1431> name: (identifier[1377,1383]@{struct}@) 
(line_continuation[1384,1388]@{...\n}@) ([1392,1393]
+    (arguments<1393,1430>
+     argument: (string<1393,1401> '[1393,1394] 
(string_content[1394,1400]@{field1}@) '[1400,1401])
+     ,[1401,1402] (number[1403,1404]@{1}@) ,[1404,1405] 
(line_continuation[1406,1410]@{...\n}@)
+     (string<1415,1427> '[1415,1416] (string_content[1416,1426]@{longField2}@) 
'[1426,1427])
+     ,[1427,1428] (number[1429,1430]@{2}@))
+    )[1430,1431]))
+ ;[1431,1432]
+ (assignment<1434,1499> left: (identifier[1434,1438]@{not6}@) =[1439,1440] 
(line_continuation[1441,1445]@{...\n}@)
+  right: 
+   (function_call<1445,1499> name: (identifier[1445,1451]@{struct}@) 
(line_continuation[1452,1456]@{...\n}@) ([1460,1461]
+    (arguments<1461,1498>
+     argument: (string<1461,1469> '[1461,1462] 
(string_content[1462,1468]@{field1}@) '[1468,1469])
+     ,[1469,1470] (number[1471,1472]@{1}@) ,[1472,1473] 
(line_continuation[1474,1478]@{...\n}@)
+     (string<1483,1495> '[1483,1484] (string_content[1484,1494]@{lognField2}@) 
'[1494,1495])
+     ,[1495,1496] (number[1497,1498]@{2}@))
+    )[1498,1499]))
+ ;[1499,1500]
+ (assignment<1502,1571> left: (identifier[1502,1506]@{not7}@) 
(line_continuation[1507,1511]@{...\n}@) =[1511,1512] 
(line_continuation[1513,1517]@{...\n}@)
+  right: 
+   (function_call<1517,1571> name: (identifier[1517,1523]@{struct}@) 
(line_continuation[1524,1528]@{...\n}@) ([1532,1533]
+    (arguments<1533,1570>
+     argument: (string<1533,1541> '[1533,1534] 
(string_content[1534,1540]@{field1}@) '[1540,1541])
+     ,[1541,1542] (number[1543,1544]@{1}@) ,[1544,1545] 
(line_continuation[1546,1550]@{...\n}@)
+     (string<1555,1567> '[1555,1556] (string_content[1556,1566]@{longField2}@) 
'[1566,1567])
+     ,[1567,1568] (number[1569,1570]@{2}@))
+    )[1570,1571]))
+ ;[1571,1572]
+ (assignment<1574,1587> left: (identifier[1574,1578]@{not8}@) =[1579,1580] 
right: (identifier[1581,1587]@{struct}@))
+ ;[1587,1588] (comment[1589,1618]@{% (this shouldn't be aligned!}@) 
\n[1618,1619])
diff --git 
a/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
new file mode 100644
index 0000000000..e93264f183
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct.m
@@ -0,0 +1,39 @@
+% -*- matlab-ts -*-
+
+%{
+  case1-point-before-comma:
+  (t-utils-xr
+
+  (re-search-forward "fiel")  ;; point left on the "d"
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+%}
+
+if 1
+       s1 = struct(     'field1', value1 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+%{
+  case2-point-after-comma:
+  (t-utils-xr
+
+  (re-search-forward "value")  ;; point left on the 9
+  "C-i"
+  "C-n" "C-i"
+  "C-n" "C-i"
+  )
+
+%}
+
+if 1
+       s2 = struct(     'field1', value9 + 10, ...
+                ...
+  'otherfield2', value2);
+end
+
+% case3: (t-utils-xr (t-utils-xr-print-code (point-min) (point-max)))
diff --git 
a/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.txt
 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.txt
new file mode 100644
index 0000000000..2b5fb01b8c
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-parser-files/copy-of-test-matlab-ts-mode-electric-indent-xr-files/electric_indent_xr_struct_expected.txt
@@ -0,0 +1,34 @@
+# -*- t-utils-ts-parse-tree -*-
+(source_file<1,596> (comment[1,20]@{% -*- matlab-ts -*-}@) \n[20,22] 
(comment[22,166]@{%\{\n  case1-point-before-comma:\n  (t-utils-xr\n\...}@) 
\n[166,168]
+ (if_statement<168,273> if[168,170] condition: (number[171,172]@{1}@)
+  (block<180,270>
+   (assignment<180,268> left: (identifier[180,182]@{s1}@) =[183,184]
+    right: 
+     (function_call<185,268> name: (identifier[185,191]@{struct}@) ([191,192]
+      (arguments<197,267>
+       argument: (string<197,205> '[197,198] 
(string_content[198,204]@{field1}@) '[204,205])
+       ,[205,206]
+       (binary_operator<207,218> left: (identifier[207,213]@{value1}@) 
+[214,215] right: (number[216,218]@{10}@))
+       ,[218,219] (line_continuation[220,224]@{...\n}@) 
(line_continuation[240,244]@{...\n}@)
+       (string<246,259> '[246,247] (string_content[247,258]@{otherfield2}@) 
'[258,259])
+       ,[259,260] (identifier[261,267]@{value2}@))
+      )[267,268]))
+   ;[268,269] \n[269,270])
+  end[270,273])
+ \n[273,275] (comment[275,417]@{%\{\n  case2-point-after-comma:\n  
(t-utils-xr\n\n...}@) \n[417,419]
+ (if_statement<419,524> if[419,421] condition: (number[422,423]@{1}@)
+  (block<431,521>
+   (assignment<431,519> left: (identifier[431,433]@{s2}@) =[434,435]
+    right: 
+     (function_call<436,519> name: (identifier[436,442]@{struct}@) ([442,443]
+      (arguments<448,518>
+       argument: (string<448,456> '[448,449] 
(string_content[449,455]@{field1}@) '[455,456])
+       ,[456,457]
+       (binary_operator<458,469> left: (identifier[458,464]@{value9}@) 
+[465,466] right: (number[467,469]@{10}@))
+       ,[469,470] (line_continuation[471,475]@{...\n}@) 
(line_continuation[491,495]@{...\n}@)
+       (string<497,510> '[497,498] (string_content[498,509]@{otherfield2}@) 
'[509,510])
+       ,[510,511] (identifier[512,518]@{value2}@))
+      )[518,519]))
+   ;[519,520] \n[520,521])
+  end[521,524])
+ (comment[526,595]@{% case3: (t-utils-xr (t-utils-xr-print-code (point...}@) 
\n[595,596])

Reply via email to