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])