branch: externals/matlab-mode
commit 8e636ce3d05bbd41f0b520471840cdc31aebbe72
Author: John Ciolfi <[email protected]>
Commit: John Ciolfi <[email protected]>
matlab-ts-mode: update to latest matlab tree-sitter, abi/14 ad79863
---
NEWS.org | 5 +
matlab-mode.el | 2 +-
matlab-ts-mode.el | 12 +--
matlab.el | 2 +-
.../electric_pair_before_fcn.m | 8 ++
.../electric_pair_before_fcn_expected.org | 41 ++++++++
.../electric_pair_single_quote.m | 3 +-
.../electric_pair_single_quote_expected.org | 28 ++---
...ont_lock_empty_endless_fun_issue93_expected.txt | 14 +++
.../font_lock_if_greedness_issue92_expected.txt | 11 ++
.../indent_comment_after_argument_end.m | 11 ++
.../indent_comment_after_argument_end_expected.m | 11 ++
...dent_comment_after_argument_end_expected_msgs.m | 11 ++
.../indent_if_with_transpose_issue93.m | 113 +++++++++++++++++++++
.../indent_if_with_transpose_issue93_expected.m | 113 +++++++++++++++++++++
...ndent_if_with_transpose_issue93_expected_msgs.m | 113 +++++++++++++++++++++
.../indent_multiple_arg_blocks_issue113_expected.m | 16 +++
...nt_multiple_arg_blocks_issue113_expected_msgs.m | 16 +++
.../indent_namespace_class_issue114.m | 49 +++++++++
.../indent_namespace_class_issue114_expected.m | 49 +++++++++
...indent_namespace_class_issue114_expected_msgs.m | 49 +++++++++
.../indent_xr_classdef3_expected.org | 96 ++++++++---------
.../thing_sexp_expected.org | 8 +-
tests/test-package-version.el | 19 +++-
24 files changed, 721 insertions(+), 79 deletions(-)
diff --git a/NEWS.org b/NEWS.org
index 3ec76ddcee..880bdba9cc 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -3,6 +3,11 @@
# Copyright (C) 2025 Free Software Foundation, Inc.
+* Release 7.3.0 Nov 3, 2025
+
+1. Updated matlab-ts-mode to work with tree-sitter-matlab commit. This fixes
all known parse issues
+ in matlab-ts-mode.
+
* Release 7.2.1 Oct 30, 2025
1. In matlab-ts-mode, fix indent involving catch clause with a comment.
diff --git a/matlab-mode.el b/matlab-mode.el
index 8ab47c4a0f..1fb3e8521c 100644
--- a/matlab-mode.el
+++ b/matlab-mode.el
@@ -1,6 +1,6 @@
;;; matlab-mode.el --- Major mode for MATLAB(R) dot-m files -*-
lexical-binding: t -*-
-;; Version: 7.2.1
+;; Version: 7.3.0
;; URL: https://github.com/mathworks/Emacs-MATLAB-Mode
;; SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index 4733107f67..a9adff5981 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -1,6 +1,6 @@
;;; matlab-ts-mode.el --- MATLAB(R) Tree-Sitter Mode -*- lexical-binding: t -*-
-;; Version: 7.2.1
+;; Version: 7.3.0
;; URL: https://github.com/mathworks/Emacs-MATLAB-Mode
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -3225,12 +3225,10 @@ single quote string."
;; double up if starting a new string => return nil
;; For: s = '
;; we have: (source_file
- ;; (postfix_operator operand: (identifier)
- ;; (ERROR =)
- ;; ')
- ;; \n)
- ((string= "'" type-back1 )
- (not (equal (treesit-node-type (treesit-node-prev-sibling node-back1))
"ERROR")))
+ ;; (ERROR (identifier) = '))
+ ((string= "'" type-back1)
+ (not (or (equal (treesit-node-type (treesit-node-prev-sibling
node-back1)) "ERROR")
+ (equal (treesit-node-type (treesit-node-parent node-back1))
"ERROR"))))
;; Case: inside a single quote string
;; s = 'foobar'
diff --git a/matlab.el b/matlab.el
index 12f4e98011..439d440b4e 100644
--- a/matlab.el
+++ b/matlab.el
@@ -1,6 +1,6 @@
;;; matlab.el --- major mode for MATLAB(R) dot-m files -*- lexical-binding: t
-*-
-;; Version: 7.2.1
+;; Version: 7.3.0
;; URL: https://github.com/mathworks/Emacs-MATLAB-Mode
;; SPDX-License-Identifier: GPL-3.0-or-later
diff --git
a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn.m
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn.m
new file mode 100644
index 0000000000..9e4317aa6c
--- /dev/null
+++ b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn.m
@@ -0,0 +1,8 @@
+% -*- matlab-ts -*-
+
+% String: s1 = ''
+% Case1: (t-utils-xr (re-search-forward "s1") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))))
+s1
+
+fcn('asdf','asdf')
+
diff --git
a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn_expected.org
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn_expected.org
new file mode 100644
index 0000000000..474adbde8c
--- /dev/null
+++
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_before_fcn_expected.org
@@ -0,0 +1,41 @@
+#+startup: showall
+
+* Executing commands from electric_pair_before_fcn.m:4:9:
+
+ Case1: (t-utils-xr (re-search-forward "s1") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))))
+
+- Invoking : (re-search-forward "s1")
+ Start point : 174
+ Moved to point: 177
+ : 5:2: s1
+ : ^
+ No buffer modifications
+
+- Invoking : (insert " = '")
+ Start point : 177
+ Moved to point: 181
+ : 5:6: s1 = '
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,7 +2,7 @@
+
+ % String: s1 = ''
+ % Case1: (t-utils-xr (re-search-forward "s1") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))))
+-s1
++s1 = '
+
+ fcn('asdf','asdf')
+
+ #+end_src diff
+
+- Invoking : (print (matlab-ts-mode--electric-pair-inhibit-predicate
(char-before)))
+ Start point : 181
+ No point movement
+ standard-output:
+ #+begin_example
+nil
+ #+end_example
+ No buffer modifications
diff --git
a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote.m
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote.m
index d67e0d0a92..319fa21ad8 100644
--- a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote.m
+++ b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote.m
@@ -11,6 +11,7 @@ b = "foo'bar"
% Case3: (t-utils-xr (re-search-forward "foo") (insert "'") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(1- (point)) (point)))
s='foobar'
-% start string
+% Start string: s2 = ''
% Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
s2
+
diff --git
a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote_expected.org
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote_expected.org
index c569353d3c..c198e86f04 100644
---
a/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote_expected.org
+++
b/tests/test-matlab-ts-mode-electric-pair-files/electric_pair_single_quote_expected.org
@@ -67,7 +67,7 @@ t
-s='foobar'
+s='foo'bar'
- % start string
+ % Start string: s2 = ''
% Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
#+end_src diff
@@ -96,7 +96,7 @@ t
-s='foo'bar'
+s='foobar'
- % start string
+ % Start string: s2 = ''
% Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
#+end_src diff
@@ -105,31 +105,32 @@ t
Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
- Invoking : (re-search-forward "s2")
- Start point : 658
- Moved to point: 661
+ Start point : 667
+ Moved to point: 670
: 16:2: s2
: ^
No buffer modifications
- Invoking : (insert " = '")
- Start point : 661
- Moved to point: 665
+ Start point : 670
+ Moved to point: 674
: 16:6: s2 = '
: ^
Buffer modified:
#+begin_src diff
--- start_contents
+++ end_contents
-@@ -13,4 +13,4 @@
+@@ -13,5 +13,5 @@
- % start string
+ % Start string: s2 = ''
% Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
-s2
+s2 = '
+
#+end_src diff
- Invoking : (print (matlab-ts-mode--electric-pair-inhibit-predicate
(char-before)))
- Start point : 665
+ Start point : 674
No point movement
standard-output:
#+begin_example
@@ -138,18 +139,19 @@ nil
No buffer modifications
- Invoking : (delete-region (- (point) 4) (point))
- Start point : 665
- Moved to point: 661
+ Start point : 674
+ Moved to point: 670
: 16:2: s2
: ^
Buffer modified:
#+begin_src diff
--- start_contents
+++ end_contents
-@@ -13,4 +13,4 @@
+@@ -13,5 +13,5 @@
- % start string
+ % Start string: s2 = ''
% Case4: (t-utils-xr (re-search-forward "s2") (insert " = '") (print
(matlab-ts-mode--electric-pair-inhibit-predicate (char-before))) (delete-region
(- (point) 4) (point)))
-s2 = '
+s2
+
#+end_src diff
diff --git
a/tests/test-matlab-ts-mode-font-lock-files/font_lock_empty_endless_fun_issue93_expected.txt
b/tests/test-matlab-ts-mode-font-lock-files/font_lock_empty_endless_fun_issue93_expected.txt
new file mode 100644
index 0000000000..f73ae71c19
--- /dev/null
+++
b/tests/test-matlab-ts-mode-font-lock-files/font_lock_empty_endless_fun_issue93_expected.txt
@@ -0,0 +1,14 @@
+c ccc ccccccccc ccc
+
+c cccc cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+
+
+kkkkkkkk fffffffffffffffffffffffffff
+ddddD
+ddddD
+
+kkkkkkkk ffff
+h hhhhh hhhh
+
+kkkkkkkk ffff
+BBBBbSss ssssSb
diff --git
a/tests/test-matlab-ts-mode-font-lock-files/font_lock_if_greedness_issue92_expected.txt
b/tests/test-matlab-ts-mode-font-lock-files/font_lock_if_greedness_issue92_expected.txt
new file mode 100644
index 0000000000..325514e42f
--- /dev/null
+++
b/tests/test-matlab-ts-mode-font-lock-files/font_lock_if_greedness_issue92_expected.txt
@@ -0,0 +1,11 @@
+c ccc ccccccccc ccc
+
+c ccc cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
+
+kk ddo o ddddd o bBBBoBBBBbddddDSsssSbb
+ v o nD
+kkk
+
+kk ddooddobddooddbonobddooddboddddddon
+ d
+kkk
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end.m
b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end.m
new file mode 100644
index 0000000000..f0db51d217
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end.m
@@ -0,0 +1,11 @@
+% -*- matlab-ts -*-
+
+function indent_comment_after_argument_end(in1)
+ arguments
+ in1 double;
+ end
+
+ % comment after arguments end
+
+ disp(in1);
+end
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected.m
b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected.m
new file mode 100644
index 0000000000..f0db51d217
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected.m
@@ -0,0 +1,11 @@
+% -*- matlab-ts -*-
+
+function indent_comment_after_argument_end(in1)
+ arguments
+ in1 double;
+ end
+
+ % comment after arguments end
+
+ disp(in1);
+end
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected_msgs.m
b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected_msgs.m
new file mode 100644
index 0000000000..f5d0a41bb2
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_comment_after_argument_end_expected_msgs.m
@@ -0,0 +1,11 @@
+% -*- matlab-ts -*- % <{Matched rule: ((lambda (node parent _bol &rest _)
(and node (not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+function indent_comment_after_argument_end(in1) % <{Matched rule: ((lambda
(node parent _bol &rest _) (and node (not (string= (treesit-node-type node)
"line_continuation")) (equal (treesit-node-type parent) "source_file")))
(lambda (_node _parent bol &rest _) (save-excursion (goto-char bol)
(line-beginning-position))) 0)}>
+ arguments % <{Matched rule: ((parent-is "\\`function_definition\\'")
parent matlab-ts-mode--set-function-indent-level-for-gp)}>
+ in1 double; % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ % comment after arguments end % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+
+ disp(in1); % <{Matched rule: ((parent-is "\\`function_definition\\'")
parent matlab-ts-mode--set-function-indent-level-for-gp)}>
+end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93.m
b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93.m
new file mode 100644
index 0000000000..b07b866c70
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93.m
@@ -0,0 +1,113 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - if conditions don't require a
terminator
+
+% The following produces
+% ans =
+%
+% 2
+%
+% here
+
+a=1;
+b=2;
+
+if a'b'
+ disp('here')
+end
+
+% MATLAB does not require a statement terminator after the if-condition, so,
for example, you can
+% write:
+%
+% if 1 < 2 a = 3; end
+%
+% Thus above is equivalent to:
+
+a=1;
+b=2;
+
+if a'
+ b'
+ disp('here')
+end
+
+% Note, the Code Analyzer suggests that a line terminator should be added.
+
+% Information on tree-sitter handling from
+% https://github.com/acristoffers/tree-sitter-matlab/issues/93
+
+%{
+
+Is there a look-ahead concept in tree-sitter? If you have an identifier
followed by the single-quote, then the single-quote has to be a transpose,
correct? Likewise for other constructs such as m(10:12,20:22)' where m a matrix
and we're taking a slice of it, then transposing. Though, I'm not very
confident on this observation.
+
+
+Not really. It's a bit complicated the notion of look-ahead in tree-sitter.
+
+This is how tree-sitter works (overly-simplified):
+
+tree-sitter is both a parser and a lexer. A parser takes chars from a file and
produces a series of tokens and a lexer takes a sequence of tokens and produces
nodes (the tree you manipulate in Emacs).
+
+The parser is written descriptively in grammar.js. You say what makes a
token/node and not how to generate it. For example, word: /[a-zA-Z]+/ says that
a sequence of letters should be tokenized as a (word), or punctuation:
choice('.', ',', ';') to say that the chars ., , and ; should be tokenized as
(punctuation).
+
+You can then say that a group of tokens make a node, like
+
+sentence: seq(repeat(word), punctuation)
+which would make the input the book. be parsed as (sentence (word "the") (word
"book") (punctuation ".")), for example. But then you create
+
+exclamation: seq(repeat(word), "!")
+and now the book! would be parsed as (exclamation (word "the") (word "book")
"!"). In these cases, the parser would go as this (I'm going word-by-word, but
the parser works char-by-char):
+
+read "The", emit a (word) token;
+read " ", ignore;
+read "book", emit a (word) token;
+read either "." or "!", and emit the appropriate token;
+It has enough tokens to satisfy both sentence and exclamation, so it will
backtrack and emit the full corresponding node, consuming the nodes it emitted
so far before continuing with the parsing.
+The file src/parser.c is automatically generated from grammar.js, implementing
a state-machine that does all parsing and lexing. But since there are some
limitations with what that implementation can do, tree-sitter also offers the
possibility of writing a src/scanner.c to complement (not replace) it. parser.c
is then the internal scanner and scanner.c the external scanner.
+
+Code written in scanner.c does have the liberty of looking ahead, but there
are still some constraints: tree-sitter will invoke the external scanner to
produce a token, and if it does not produce one, it will invoke the internal
scanner to produce one. When tokens are produced, it will try to backtrack and
generate nodes (that is, the scanner has no say on the lexing at all, it can
only tokenize).
+
+When invoking the external scanner, not much information is provided about
what is going on. The scanner is given a list of valid nodes it can produce at
this point (which is the only way I have to somewhat contextualize the
decision) and a saved state generated by the previous, successful attempt to
tokenize right before this point. Unsuccessful attempts do not generate nor
modify such saved state. The scanner can only see one char at time and can only
move forward, but it can mark the [...]
+
+Let's now use as example something we fixed recently:
+
+[0xDEADBEEFu64]
+This is the entire file, and this is how the parsing goes:
+
+The external scanner is called, and the following nodes are in the list of
valid tokens:
+
+COMMENT
+LINE_CONTINUATION
+COMMAND_NAME
+SINGLE_QUOTE_STRING_START
+DOUBLE_QUOTE_STRING_START
+MULTIOUTPUT_VARIABLE_START
+IDENTIFIER
+And the following are marked as not valid:
+
+- `ENTRY_DELIMITER`
+- `FORMATTING_SEQUENCE`
+- `ESCAPE_SEQUENCE`
+- `STRING_CONTENT`
+- `SINGLE_QUOTE_STRING_END`
+- `DOUBLE_QUOTE_STRING_END`
+- `CATCH_IDENTIFIER`
+- `COMMAND_ARGUMENT`
+The external scanner will see [ and will mark this as the token
MULTIOUTPUT_VARIABLE_START, but will not emit the token yet: it will look ahead
to find ]=. Since it's not there, it will not emit any token at all.
+The internal scanner kicks in and matches on the [ as the opening of a matrix
and will emit the unnamed [ token.
+The external scanner will be called, but will not produce any token again,
because the char it sees is a number.
+The internal scanner has the number rule that matches on hexadecimals, so it
consumes 0xDEADBEEF and emits an unnamed token for it.
+The external scanner is called and emits an IDENTIFIER token for u64.
+The external scanner is called and produces nothing for ].
+The internal scanner sees ] and produces an unnamed token.
+The lexer kicks in and tries to reduce ("[" "0xDEADBEEF" (identifier) "]") and
fails, the matrix node requires [ seq(expression, ENTRY_DELIMITER) ] and it
fould [ (expression) (expression) ] (no ENTRY_DELIMITER). That's why it was
breaking inside matrices and cells but not anywhere else.
+To fix it, we go back to 5. and parse the entire number (u64 included) as a
single number node. I modified the number regex for that.
+
+For the transpose, the external scanner is necessary to look ahead and see if
we have a transpose or a string. It works by a combination of 1) only having
the string token as a valid one in certain scenarios, 2) by the requirement
that a single-quoted string has to end on the same line and 3) that I have
token to separate entries in many contexts.
+
+The place it used to break is, again, inside matrices and cells, where we can
have a sequence of not-delimited expressions, so [A' B'] can be either (matrix
(transpose (identifier)) (transpose (identifier))) or (matrix (identifer)
(string)) (as humans is easy to see which one it is, but when all you see is '
without any context, that's hard). That's why I used the ENTRY_DELIMITER token,
to use the scanner to say if that's a string or not. Now, the external scanner
can see the ' and, if w [...]
+
+The problem if the if statement itself is that it sees if a < b c end and
tokenizes as "if" (identifier) "<" (identifier) (identifier) (end-keyword), but
then the lexer is doing: (if (condition (identifier)) (ERROR "<")), because it
takes as little as it can and turns into the (condition) node, so it leaves the
"<" token there and no rule will match it, as no expression or statement can
start with that. It does not happen in multiple lines because then I force
(condition) to be everythin [...]
+
+So, that's my not 100% accurate but hopefully comprehensible enough
explanation of how it works. I hope I made things clearer instead of more
confusing :)
+
+%}
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected.m
b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected.m
new file mode 100644
index 0000000000..2cfb8b3e79
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected.m
@@ -0,0 +1,113 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - if conditions don't require a
terminator
+
+% The following produces
+% ans =
+%
+% 2
+%
+% here
+
+a=1;
+b=2;
+
+if a'b'
+ disp('here')
+end
+
+% MATLAB does not require a statement terminator after the if-condition, so,
for example, you can
+% write:
+%
+% if 1 < 2 a = 3; end
+%
+% Thus above is equivalent to:
+
+a=1;
+b=2;
+
+if a'
+ b'
+ disp('here')
+end
+
+% Note, the Code Analyzer suggests that a line terminator should be added.
+
+% Information on tree-sitter handling from
+% https://github.com/acristoffers/tree-sitter-matlab/issues/93
+
+%{
+
+ Is there a look-ahead concept in tree-sitter? If you have an identifier
followed by the single-quote, then the single-quote has to be a transpose,
correct? Likewise for other constructs such as m(10:12,20:22)' where m a matrix
and we're taking a slice of it, then transposing. Though, I'm not very
confident on this observation.
+
+
+ Not really. It's a bit complicated the notion of look-ahead in tree-sitter.
+
+ This is how tree-sitter works (overly-simplified):
+
+ tree-sitter is both a parser and a lexer. A parser takes chars from a file
and produces a series of tokens and a lexer takes a sequence of tokens and
produces nodes (the tree you manipulate in Emacs).
+
+ The parser is written descriptively in grammar.js. You say what makes a
token/node and not how to generate it. For example, word: /[a-zA-Z]+/ says that
a sequence of letters should be tokenized as a (word), or punctuation:
choice('.', ',', ';') to say that the chars ., , and ; should be tokenized as
(punctuation).
+
+ You can then say that a group of tokens make a node, like
+
+ sentence: seq(repeat(word), punctuation)
+ which would make the input the book. be parsed as (sentence (word "the")
(word "book") (punctuation ".")), for example. But then you create
+
+ exclamation: seq(repeat(word), "!")
+ and now the book! would be parsed as (exclamation (word "the") (word "book")
"!"). In these cases, the parser would go as this (I'm going word-by-word, but
the parser works char-by-char):
+
+ read "The", emit a (word) token;
+ read " ", ignore;
+ read "book", emit a (word) token;
+ read either "." or "!", and emit the appropriate token;
+ It has enough tokens to satisfy both sentence and exclamation, so it will
backtrack and emit the full corresponding node, consuming the nodes it emitted
so far before continuing with the parsing.
+ The file src/parser.c is automatically generated from grammar.js,
implementing a state-machine that does all parsing and lexing. But since there
are some limitations with what that implementation can do, tree-sitter also
offers the possibility of writing a src/scanner.c to complement (not replace)
it. parser.c is then the internal scanner and scanner.c the external scanner.
+
+ Code written in scanner.c does have the liberty of looking ahead, but there
are still some constraints: tree-sitter will invoke the external scanner to
produce a token, and if it does not produce one, it will invoke the internal
scanner to produce one. When tokens are produced, it will try to backtrack and
generate nodes (that is, the scanner has no say on the lexing at all, it can
only tokenize).
+
+ When invoking the external scanner, not much information is provided about
what is going on. The scanner is given a list of valid nodes it can produce at
this point (which is the only way I have to somewhat contextualize the
decision) and a saved state generated by the previous, successful attempt to
tokenize right before this point. Unsuccessful attempts do not generate nor
modify such saved state. The scanner can only see one char at time and can only
move forward, but it can mark th [...]
+
+ Let's now use as example something we fixed recently:
+
+ [0xDEADBEEFu64]
+ This is the entire file, and this is how the parsing goes:
+
+ The external scanner is called, and the following nodes are in the list of
valid tokens:
+
+ COMMENT
+ LINE_CONTINUATION
+ COMMAND_NAME
+ SINGLE_QUOTE_STRING_START
+ DOUBLE_QUOTE_STRING_START
+ MULTIOUTPUT_VARIABLE_START
+ IDENTIFIER
+ And the following are marked as not valid:
+
+ - `ENTRY_DELIMITER`
+ - `FORMATTING_SEQUENCE`
+ - `ESCAPE_SEQUENCE`
+ - `STRING_CONTENT`
+ - `SINGLE_QUOTE_STRING_END`
+ - `DOUBLE_QUOTE_STRING_END`
+ - `CATCH_IDENTIFIER`
+ - `COMMAND_ARGUMENT`
+ The external scanner will see [ and will mark this as the token
MULTIOUTPUT_VARIABLE_START, but will not emit the token yet: it will look ahead
to find ]=. Since it's not there, it will not emit any token at all.
+ The internal scanner kicks in and matches on the [ as the opening of a
matrix and will emit the unnamed [ token.
+ The external scanner will be called, but will not produce any token again,
because the char it sees is a number.
+ The internal scanner has the number rule that matches on hexadecimals, so it
consumes 0xDEADBEEF and emits an unnamed token for it.
+ The external scanner is called and emits an IDENTIFIER token for u64.
+ The external scanner is called and produces nothing for ].
+ The internal scanner sees ] and produces an unnamed token.
+ The lexer kicks in and tries to reduce ("[" "0xDEADBEEF" (identifier) "]")
and fails, the matrix node requires [ seq(expression, ENTRY_DELIMITER) ] and it
fould [ (expression) (expression) ] (no ENTRY_DELIMITER). That's why it was
breaking inside matrices and cells but not anywhere else.
+ To fix it, we go back to 5. and parse the entire number (u64 included) as a
single number node. I modified the number regex for that.
+
+ For the transpose, the external scanner is necessary to look ahead and see
if we have a transpose or a string. It works by a combination of 1) only having
the string token as a valid one in certain scenarios, 2) by the requirement
that a single-quoted string has to end on the same line and 3) that I have
token to separate entries in many contexts.
+
+ The place it used to break is, again, inside matrices and cells, where we
can have a sequence of not-delimited expressions, so [A' B'] can be either
(matrix (transpose (identifier)) (transpose (identifier))) or (matrix
(identifer) (string)) (as humans is easy to see which one it is, but when all
you see is ' without any context, that's hard). That's why I used the
ENTRY_DELIMITER token, to use the scanner to say if that's a string or not.
Now, the external scanner can see the ' and, if [...]
+
+ The problem if the if statement itself is that it sees if a < b c end and
tokenizes as "if" (identifier) "<" (identifier) (identifier) (end-keyword), but
then the lexer is doing: (if (condition (identifier)) (ERROR "<")), because it
takes as little as it can and turns into the (condition) node, so it leaves the
"<" token there and no rule will match it, as no expression or statement can
start with that. It does not happen in multiple lines because then I force
(condition) to be everyth [...]
+
+ So, that's my not 100% accurate but hopefully comprehensible enough
explanation of how it works. I hope I made things clearer instead of more
confusing :)
+
+%}
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected_msgs.m
b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected_msgs.m
new file mode 100644
index 0000000000..54f13fbe28
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_if_with_transpose_issue93_expected_msgs.m
@@ -0,0 +1,113 @@
+% -*- matlab-ts -*- % <{Matched rule: ((lambda (node parent _bol &rest _)
(and node (not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+% t-utils-test-indent: no-line-by-line-indent - if conditions don't require a
terminator % <{Matched rule: ((lambda (node parent _bol &rest _) (and node
(not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+% The following produces % <{Matched rule: ((lambda (node parent _bol &rest
_) (and node (not (string= (treesit-node-type node) "line_continuation"))
(equal (treesit-node-type parent) "source_file"))) (lambda (_node _parent bol
&rest _) (save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+% ans = % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher
parent 0)}>
+% % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+% 2 % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher
parent 0)}>
+% % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+% here % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher
parent 0)}>
+
+a=1; % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+b=2; % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+if a'b' % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+ disp('here') % <{Matched rule: ((parent-is "\\`block\\'") parent 0)}>
+end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+% MATLAB does not require a statement terminator after the if-condition, so,
for example, you can % <{Matched rule: ((lambda (node parent _bol &rest _)
(and node (not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+% write: % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher
parent 0)}>
+% % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+% if 1 < 2 a = 3; end % <{Matched rule:
(matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+% % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+% Thus above is equivalent to: % <{Matched rule:
(matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+
+a=1; % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+b=2; % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+if a' % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+ b' % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ disp('here') % <{Matched rule: ((parent-is "\\`block\\'") parent 0)}>
+end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+% Note, the Code Analyzer suggests that a line terminator should be added. %
<{Matched rule: ((lambda (node parent _bol &rest _) (and node (not (string=
(treesit-node-type node) "line_continuation")) (equal (treesit-node-type
parent) "source_file"))) (lambda (_node _parent bol &rest _) (save-excursion
(goto-char bol) (line-beginning-position))) 0)}>
+
+% Information on tree-sitter handling from % <{Matched rule: ((lambda (node
parent _bol &rest _) (and node (not (string= (treesit-node-type node)
"line_continuation")) (equal (treesit-node-type parent) "source_file")))
(lambda (_node _parent bol &rest _) (save-excursion (goto-char bol)
(line-beginning-position))) 0)}>
+% https://github.com/acristoffers/tree-sitter-matlab/issues/93 % <{Matched
rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
+
+%{ % <{Matched rule: ((lambda (node parent _bol &rest _) (and node (not
(string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+ Is there a look-ahead concept in tree-sitter? If you have an identifier
followed by the single-quote, then the single-quote has to be a transpose,
correct? Likewise for other constructs such as m(10:12,20:22)' where m a matrix
and we're taking a slice of it, then transposing. Though, I'm not very
confident on this observation. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+
+ Not really. It's a bit complicated the notion of look-ahead in tree-sitter.
% <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ This is how tree-sitter works (overly-simplified): % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ tree-sitter is both a parser and a lexer. A parser takes chars from a file
and produces a series of tokens and a lexer takes a sequence of tokens and
produces nodes (the tree you manipulate in Emacs). % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ The parser is written descriptively in grammar.js. You say what makes a
token/node and not how to generate it. For example, word: /[a-zA-Z]+/ says that
a sequence of letters should be tokenized as a (word), or punctuation:
choice('.', ',', ';') to say that the chars ., , and ; should be tokenized as
(punctuation). % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+
+ You can then say that a group of tokens make a node, like % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ sentence: seq(repeat(word), punctuation) % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ which would make the input the book. be parsed as (sentence (word "the")
(word "book") (punctuation ".")), for example. But then you create % <{Matched
rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ exclamation: seq(repeat(word), "!") % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ and now the book! would be parsed as (exclamation (word "the") (word "book")
"!"). In these cases, the parser would go as this (I'm going word-by-word, but
the parser works char-by-char): % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ read "The", emit a (word) token; % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ read " ", ignore; % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ read "book", emit a (word) token; % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ read either "." or "!", and emit the appropriate token; % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ It has enough tokens to satisfy both sentence and exclamation, so it will
backtrack and emit the full corresponding node, consuming the nodes it emitted
so far before continuing with the parsing. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The file src/parser.c is automatically generated from grammar.js,
implementing a state-machine that does all parsing and lexing. But since there
are some limitations with what that implementation can do, tree-sitter also
offers the possibility of writing a src/scanner.c to complement (not replace)
it. parser.c is then the internal scanner and scanner.c the external scanner. %
<{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ Code written in scanner.c does have the liberty of looking ahead, but there
are still some constraints: tree-sitter will invoke the external scanner to
produce a token, and if it does not produce one, it will invoke the internal
scanner to produce one. When tokens are produced, it will try to backtrack and
generate nodes (that is, the scanner has no say on the lexing at all, it can
only tokenize). % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+
+ When invoking the external scanner, not much information is provided about
what is going on. The scanner is given a list of valid nodes it can produce at
this point (which is the only way I have to somewhat contextualize the
decision) and a saved state generated by the previous, successful attempt to
tokenize right before this point. Unsuccessful attempts do not generate nor
modify such saved state. The scanner can only see one char at time and can only
move forward, but it can mark th [...]
+
+ Let's now use as example something we fixed recently: % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ [0xDEADBEEFu64] % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ This is the entire file, and this is how the parsing goes: % <{Matched
rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ The external scanner is called, and the following nodes are in the list of
valid tokens: % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+
+ COMMENT % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+ LINE_CONTINUATION % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ COMMAND_NAME % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+ SINGLE_QUOTE_STRING_START % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ DOUBLE_QUOTE_STRING_START % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ MULTIOUTPUT_VARIABLE_START % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ IDENTIFIER % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+ And the following are marked as not valid: % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ - `ENTRY_DELIMITER` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `FORMATTING_SEQUENCE` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `ESCAPE_SEQUENCE` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `STRING_CONTENT` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `SINGLE_QUOTE_STRING_END` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `DOUBLE_QUOTE_STRING_END` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `CATCH_IDENTIFIER` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ - `COMMAND_ARGUMENT` % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The external scanner will see [ and will mark this as the token
MULTIOUTPUT_VARIABLE_START, but will not emit the token yet: it will look ahead
to find ]=. Since it's not there, it will not emit any token at all. %
<{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The internal scanner kicks in and matches on the [ as the opening of a
matrix and will emit the unnamed [ token. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The external scanner will be called, but will not produce any token again,
because the char it sees is a number. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The internal scanner has the number rule that matches on hexadecimals, so it
consumes 0xDEADBEEF and emits an unnamed token for it. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The external scanner is called and emits an IDENTIFIER token for u64. %
<{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The external scanner is called and produces nothing for ]. % <{Matched
rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The internal scanner sees ] and produces an unnamed token. % <{Matched
rule: (matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ The lexer kicks in and tries to reduce ("[" "0xDEADBEEF" (identifier) "]")
and fails, the matrix node requires [ seq(expression, ENTRY_DELIMITER) ] and it
fould [ (expression) (expression) ] (no ENTRY_DELIMITER). That's why it was
breaking inside matrices and cells but not anywhere else. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+ To fix it, we go back to 5. and parse the entire number (u64 included) as a
single number node. I modified the number regex for that. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ For the transpose, the external scanner is necessary to look ahead and see
if we have a transpose or a string. It works by a combination of 1) only having
the string token as a valid one in certain scenarios, 2) by the requirement
that a single-quoted string has to end on the same line and 3) that I have
token to separate entries in many contexts. % <{Matched rule:
(matlab-ts-mode--i-in-block-comment-matcher parent 2)}>
+
+ The place it used to break is, again, inside matrices and cells, where we
can have a sequence of not-delimited expressions, so [A' B'] can be either
(matrix (transpose (identifier)) (transpose (identifier))) or (matrix
(identifer) (string)) (as humans is easy to see which one it is, but when all
you see is ' without any context, that's hard). That's why I used the
ENTRY_DELIMITER token, to use the scanner to say if that's a string or not.
Now, the external scanner can see the ' and, if [...]
+
+ The problem if the if statement itself is that it sees if a < b c end and
tokenizes as "if" (identifier) "<" (identifier) (identifier) (end-keyword), but
then the lexer is doing: (if (condition (identifier)) (ERROR "<")), because it
takes as little as it can and turns into the (condition) node, so it leaves the
"<" token there and no rule will match it, as no expression or statement can
start with that. It does not happen in multiple lines because then I force
(condition) to be everyth [...]
+
+ So, that's my not 100% accurate but hopefully comprehensible enough
explanation of how it works. I hope I made things clearer instead of more
confusing :) % <{Matched rule: (matlab-ts-mode--i-in-block-comment-matcher
parent 2)}>
+
+%} % <{Matched rule: (matlab-ts-mode--i-block-comment-end-matcher parent 0)}>
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected.m
b/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected.m
new file mode 100644
index 0000000000..f7520a24f8
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected.m
@@ -0,0 +1,16 @@
+% -*- matlab-ts -*-
+
+% See: https://github.com/acristoffers/tree-sitter-matlab/issues/113
+
+function y = indent_multiple_arg_blocks_issue113(a, b, varargin)
+ arguments
+ a uint32
+ b uint32
+ end
+ arguments (Repeating)
+ varargin
+ end
+
+ y = a + b + length(varargin);
+
+end
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected_msgs.m
b/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected_msgs.m
new file mode 100644
index 0000000000..7500b4efb9
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_multiple_arg_blocks_issue113_expected_msgs.m
@@ -0,0 +1,16 @@
+% -*- matlab-ts -*- % <{Matched rule: ((lambda (node parent _bol &rest _)
(and node (not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+% See: https://github.com/acristoffers/tree-sitter-matlab/issues/113 %
<{Matched rule: ((lambda (node parent _bol &rest _) (and node (not (string=
(treesit-node-type node) "line_continuation")) (equal (treesit-node-type
parent) "source_file"))) (lambda (_node _parent bol &rest _) (save-excursion
(goto-char bol) (line-beginning-position))) 0)}>
+
+function y = indent_multiple_arg_blocks_issue113(a, b, varargin) % <{Matched
rule: ((lambda (node parent _bol &rest _) (and node (not (string=
(treesit-node-type node) "line_continuation")) (equal (treesit-node-type
parent) "source_file"))) (lambda (_node _parent bol &rest _) (save-excursion
(goto-char bol) (line-beginning-position))) 0)}>
+ arguments % <{Matched rule: ((parent-is "\\`function_definition\\'")
parent matlab-ts-mode--set-function-indent-level-for-gp)}>
+ a uint32 % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ b uint32 % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+ arguments (Repeating) % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+ varargin % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ y = a + b + length(varargin); % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+
+end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114.m
b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114.m
new file mode 100644
index 0000000000..812deee586
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114.m
@@ -0,0 +1,49 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - function comments don't work
+
+classdef indent_namespace_class_issue114
+% Test namespace class for MATLAB parser
+% This class demonstrates classes within namespaces.
+%
+% Properties:
+% Value - The stored value
+%
+% Methods:
+% NamespaceClass - Constructor
+% increment - Increases the value
+% getValue - Returns the current value
+%
+% See: https://github.com/acristoffers/tree-sitter-matlab/issues/114
+
+ properties
+ Value double = 0
+ % The stored value
+ end
+
+ methods
+ function obj = NamespaceClass(init_value)
+ % NamespaceClass constructor
+ % Initialize the class with a value
+
+ arguments
+ init_value (1,1) double {mustBeNumeric} = 0
+ % Initial value to store
+ end
+
+ obj.Value = init_value;
+ end
+
+ function obj = increment(obj, amount)
+ % Increment the stored value
+
+ arguments
+ obj
+ amount (1,1) double {mustBeNumeric} = 1
+ % Amount to increment by
+ end
+
+ obj.Value = obj.Value + amount;
+ end
+ end
+end
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected.m
b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected.m
new file mode 100644
index 0000000000..f85093eced
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected.m
@@ -0,0 +1,49 @@
+% -*- matlab-ts -*-
+
+% t-utils-test-indent: no-line-by-line-indent - function comments don't work
+
+classdef indent_namespace_class_issue114
+% Test namespace class for MATLAB parser
+% This class demonstrates classes within namespaces.
+%
+% Properties:
+% Value - The stored value
+%
+% Methods:
+% NamespaceClass - Constructor
+% increment - Increases the value
+% getValue - Returns the current value
+%
+% See: https://github.com/acristoffers/tree-sitter-matlab/issues/114
+
+ properties
+ Value double = 0
+ % The stored value
+ end
+
+ methods
+ function obj = NamespaceClass(init_value)
+ % NamespaceClass constructor
+ % Initialize the class with a value
+
+ arguments
+ init_value (1,1) double {mustBeNumeric} = 0
+ % Initial value to store
+ end
+
+ obj.Value = init_value;
+ end
+
+ function obj = increment(obj, amount)
+ % Increment the stored value
+
+ arguments
+ obj
+ amount (1,1) double {mustBeNumeric} = 1
+ % Amount to increment by
+ end
+
+ obj.Value = obj.Value + amount;
+ end
+ end
+end
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected_msgs.m
b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected_msgs.m
new file mode 100644
index 0000000000..c58c89dce0
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-files/indent_namespace_class_issue114_expected_msgs.m
@@ -0,0 +1,49 @@
+% -*- matlab-ts -*- % <{Matched rule: ((lambda (node parent _bol &rest _)
(and node (not (string= (treesit-node-type node) "line_continuation")) (equal
(treesit-node-type parent) "source_file"))) (lambda (_node _parent bol &rest _)
(save-excursion (goto-char bol) (line-beginning-position))) 0)}>
+
+% t-utils-test-indent: no-line-by-line-indent - function comments don't work %
<{Matched rule: ((lambda (node parent _bol &rest _) (and node (not (string=
(treesit-node-type node) "line_continuation")) (equal (treesit-node-type
parent) "source_file"))) (lambda (_node _parent bol &rest _) (save-excursion
(goto-char bol) (line-beginning-position))) 0)}>
+
+classdef indent_namespace_class_issue114 % <{Matched rule: ((lambda (node
parent _bol &rest _) (and node (not (string= (treesit-node-type node)
"line_continuation")) (equal (treesit-node-type parent) "source_file")))
(lambda (_node _parent bol &rest _) (save-excursion (goto-char bol)
(line-beginning-position))) 0)}>
+% Test namespace class for MATLAB parser % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% This class demonstrates classes within namespaces. % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% % <{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+% Properties: % <{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+% Value - The stored value % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% % <{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+% Methods: % <{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+% NamespaceClass - Constructor % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% increment - Increases the value % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% getValue - Returns the current value % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+% % <{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+% See: https://github.com/acristoffers/tree-sitter-matlab/issues/114 %
<{Matched rule: (matlab-ts-mode--i-doc-comment-matcher
matlab-ts-mode--i-doc-comment-anchor matlab-ts-mode--i-doc-comment-offset)}>
+
+ properties % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ Value double = 0 % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ % The stored value % <{Matched rule: ((n-p-gp nil "\\`property\\'"
"\\`properties\\'") grand-parent 4)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ methods % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ function obj = NamespaceClass(init_value) % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ % NamespaceClass constructor % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+ % Initialize the class with a value % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+
+ arguments % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+ init_value (1,1) double {mustBeNumeric} = 0 % <{Matched rule:
((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ % Initial value to store % <{Matched rule: ((node-is
"comment") parent 0)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ obj.Value = init_value; % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ function obj = increment(obj, amount) % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ % Increment the stored value % <{Matched rule:
(matlab-ts-mode--i-doc-comment-matcher matlab-ts-mode--i-doc-comment-anchor
matlab-ts-mode--i-doc-comment-offset)}>
+
+ arguments % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+ obj % <{Matched rule: ((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ amount (1,1) double {mustBeNumeric} = 1 % <{Matched rule:
((node-is
"\\`\\(?:arguments_statement\\|block\\|e\\(?:num\\(?:eration\\)?\\|vents\\)\\|function_definition\\|methods\\|propert\\(?:ies\\|y\\)\\)\\'")
parent 4)}>
+ % Amount to increment by % <{Matched rule: ((node-is
"comment") parent 0)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+
+ obj.Value = obj.Value + amount; % <{Matched rule: ((parent-is
"\\`function_definition\\'") parent
matlab-ts-mode--set-function-indent-level-for-gp)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+ end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
+end % <{Matched rule: ((node-is
"\\`\\(?:catch_clause\\|e\\(?:lse\\(?:\\(?:if\\)?_clause\\)\\|nd\\)\\)\\'")
parent 0)}>
diff --git
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_classdef3_expected.org
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_classdef3_expected.org
index 0111c3a60a..d4e29364f2 100644
--- a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_classdef3_expected.org
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_classdef3_expected.org
@@ -856,9 +856,9 @@
- Invoking : "C-m" = newline
Start point : 1995
- Moved to point: 2008
- : 67:12:
- : ^
+ Moved to point: 2012
+ : 67:16:
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -870,15 +870,15 @@
- arguments
\ No newline at end of file
+ arguments
-+
++
\ No newline at end of file
#+end_src diff
- Invoking : (insert "a")
- Start point : 2008
- Moved to point: 2009
- : 67:13: a
- : ^
+ Start point : 2012
+ Moved to point: 2013
+ : 67:17: a
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -887,17 +887,17 @@
% comment8
arguments
--
+-
\ No newline at end of file
-+ a
++ a
\ No newline at end of file
#+end_src diff
- Invoking : "C-m" = newline
- Start point : 2009
- Moved to point: 2026
- : 68:12:
- : ^
+ Start point : 2013
+ Moved to point: 2030
+ : 68:16:
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -906,18 +906,18 @@
% comment8
arguments
-- a
+- a
\ No newline at end of file
+ a
-+
++
\ No newline at end of file
#+end_src diff
- Invoking : (insert "% comment9")
- Start point : 2026
- Moved to point: 2036
- : 68:22: % comment9
- : ^
+ Start point : 2030
+ Moved to point: 2040
+ : 68:26: % comment9
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -926,17 +926,17 @@
% comment8
arguments
a
--
+-
\ No newline at end of file
-+ % comment9
++ % comment9
\ No newline at end of file
#+end_src diff
- Invoking : "C-m" = newline
- Start point : 2036
- Moved to point: 2053
- : 69:12:
- : ^
+ Start point : 2040
+ Moved to point: 2057
+ : 69:16:
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -945,18 +945,18 @@
% comment8
arguments
a
-- % comment9
+- % comment9
\ No newline at end of file
+ % comment9
-+
++
\ No newline at end of file
#+end_src diff
- Invoking : (insert "end")
- Start point : 2053
- Moved to point: 2056
- : 69:15: end
- : ^
+ Start point : 2057
+ Moved to point: 2060
+ : 69:19: end
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -965,17 +965,17 @@
arguments
a
% comment9
--
+-
\ No newline at end of file
-+ end
++ end
\ No newline at end of file
#+end_src diff
- Invoking : "C-m" = newline
- Start point : 2056
- Moved to point: 2069
- : 70:12:
- : ^
+ Start point : 2060
+ Moved to point: 2073
+ : 70:16:
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -984,18 +984,18 @@
arguments
a
% comment9
-- end
+- end
\ No newline at end of file
+ end
-+
++
\ No newline at end of file
#+end_src diff
- Invoking : (insert "end")
- Start point : 2069
- Moved to point: 2072
- : 70:15: end
- : ^
+ Start point : 2073
+ Moved to point: 2076
+ : 70:19: end
+ : ^
Buffer modified:
#+begin_src diff
--- start_contents
@@ -1004,14 +1004,14 @@
a
% comment9
end
--
+-
\ No newline at end of file
-+ end
++ end
\ No newline at end of file
#+end_src diff
- Invoking : "C-m" = newline
- Start point : 2072
+ Start point : 2076
Moved to point: 2077
: 71:8:
: ^
@@ -1023,7 +1023,7 @@
a
% comment9
end
-- end
+- end
\ No newline at end of file
+ end
+
diff --git
a/tests/test-matlab-ts-mode-thing-settings-files/thing_sexp_expected.org
b/tests/test-matlab-ts-mode-thing-settings-files/thing_sexp_expected.org
index 5c415a4993..e5d9bd57a9 100644
--- a/tests/test-matlab-ts-mode-thing-settings-files/thing_sexp_expected.org
+++ b/tests/test-matlab-ts-mode-thing-settings-files/thing_sexp_expected.org
@@ -183,13 +183,13 @@
- Invoking : "C-M-f" = forward-sexp
Start point : 615
- Moved to point: 706
- : 29:15: end
- : ^
+ Moved to point: 708
+ : 31:0: global gVar1 gVar2
+ : ^
No buffer modifications
- Invoking : "C-M-f" = forward-sexp
- Start point : 706
+ Start point : 708
Moved to point: 738
: 31:30: global gVar1 gVar2
: ^
diff --git a/tests/test-package-version.el b/tests/test-package-version.el
index 8fa0c2db2a..dfc1c49e5e 100644
--- a/tests/test-package-version.el
+++ b/tests/test-package-version.el
@@ -23,15 +23,24 @@
(require 't-utils)
(require 'matlab-ts-mode)
+(defvar test-package-version--el-files-dir nil
+ "Cache location of the *.el files.
+This enables running of the test be run from any directory after it's
+been run once.")
+
(ert-deftest test-package-version ()
"Validate the package version numbers in ../*.el are the same.
Emacs MATLAB mode package consists of several major and minor modes and
for simplicity we require that the versions of these be the same."
- (when (not (file-exists-p "../matlab-ts-mode.el"))
- (error "../matlab-ts-mode.el doesn't exist, is the current directory
correct?"))
+ (when (not test-package-version--el-files-dir)
+ (when (not (file-exists-p "../matlab-ts-mode.el"))
+ (error "../matlab-ts-mode.el doesn't exist, is the current directory
correct?"))
+ (setq test-package-version--el-files-dir
+ (file-name-as-directory (file-truename ".."))))
- (let ((el-files (directory-files ".." t "\\.el\\'"))
+ (let ((el-files (directory-files test-package-version--el-files-dir t
+ "\\.el\\'"))
(version-lines "")
(all-versions-consistent t)
first-ver)
@@ -55,7 +64,9 @@ for simplicity we require that the versions of these be the
same."
(error "Failed to find Version's in *.el files"))
(let ((news-release-version (with-temp-buffer
- (insert-file-contents-literally
"../NEWS.org")
+ (insert-file-contents-literally
+ (concat test-package-version--el-files-dir
+ "NEWS.org"))
(goto-char (point-min))
(re-search-forward "^\\* Release
\\([.0-9]+\\)")
(let ((ver (match-string 1))