branch: externals/matlab-mode
commit 43bd7ce861bf3b1bcdeb9c1b220424947c5504b2
Author: John Ciolfi <[email protected]>
Commit: John Ciolfi <[email protected]>
matlab-ts-mode: update to latest ts .so, add more indent support
Update to be based on https://github.com/acristoffers/tree-sitter-matlab
commit 2d30fdbb8fc7ec55d2fbf05172437ff0299878a5 from Jul-27-2025.
There's still a couple items we're waiting on, but things are looking
better.
This also adds more support for indent items when the *.m code is
incomplete (typing it in).
This area needs more work.
---
matlab-sections.el | 5 +-
matlab-ts-mode.el | 101 +++++++++++++-
tests/t-utils.el | 27 ++--
.../font_lock_error.skip.txt | 3 +
tests/test-matlab-ts-mode-font-lock.el | 13 +-
.../indent_old_indents.skip.txt | 1 -
.../indent_xr_i_cont_incomplete2.skip.txt} | 2 +-
.../indent_xr_next_line_matcher1.m | 3 +
.../indent_xr_next_line_matcher1.skip.txt | 1 +
.../indent_xr_next_line_matcher1_expected.org | 147 +++++++++++++++++++++
10 files changed, 280 insertions(+), 23 deletions(-)
diff --git a/matlab-sections.el b/matlab-sections.el
index bfe7cd6966..4926a13273 100644
--- a/matlab-sections.el
+++ b/matlab-sections.el
@@ -488,8 +488,9 @@ See `matlab-sections-help' for details on MATLAB code
sections."
:init-value nil
:keymap matlab-sections-minor-mode-map
- (make-local-variable 'page-delimiter)
- (setq page-delimiter matlab-sections-section-break-regexp)
+ (when (eq major-mode 'matlab-mode)
+ ;; page-delimiter is setup by matlab-ts-mode (and is more accurate there)
+ (setq-local page-delimiter matlab-sections-section-break-regexp))
(when matlab-sections-highlight-section
(matlab-sections-setup-section-highlight)))
diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index a4cd50731f..53ba66ab14 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -147,6 +147,17 @@ Then restart Emacs and run
You can also install via use-package or other methods."
:type 'boolean)
+;; TODO
+;; (defcustom matlab-ts-mode-electric-ends t
+;; "*If t, insert end keywords to complete statements.
+;; For example, if you type
+;; classdef foo<RET>
+;; an end statement will be inserted resulting in:
+;; classdef foo<RET>
+;; ^ <== point here
+;; end"
+;; :type 'boolean)
+
;;; Global variables used in multiple code ";;; sections"
(defvar matlab-ts-mode--comment-heading-re
@@ -832,6 +843,7 @@ than the FILED-EXPRESSION-NODE start-point and end-point."
(defvar matlab-ts-mode--array-indent-level 2
"Indentation level for elements in an array.")
+;; TODO - is this right? Perhaps it's better to understand the context and
indent?
(defun matlab-ts-mode--i-error-matcher (node _parent _bol &rest _)
"Is NODE an ERROR node or is it under an ERROR node?"
(let (in-error-node)
@@ -890,13 +902,16 @@ is where we start looking for the error node."
(save-excursion
(goto-char bol)
+ (when (string= (treesit-node-type (treesit-node-at (point))) "\n")
+ (re-search-backward "[^ \t\n\r]" nil t))
+
;; Move inside the error node if at an error node.
(when (string= (treesit-node-type (treesit-node-at (point))) "ERROR")
(re-search-backward "[^ \t\n\r]" nil t))
- ;; If on a "[" or "{" move back (we don't want to shift the current line)
- (when (string-match-p (rx bos (or "[" "{") eos) (treesit-node-type
(treesit-node-at (point))))
- (re-search-backward "[^ \t\n\r]" nil t))
+ ;; ;; If on a "[" or "{" move back (we don't want to shift the current
line)
+ ;; (when (string-match-p (rx bos (or "[" "{") eos) (treesit-node-type
(treesit-node-at (point))))
+ ;; (re-search-backward "[^ \t\n\r]" nil t))
;; Walk over line_continuation, ",", ";" and identify the "check node" we
should be looking
;; at.
@@ -1229,9 +1244,86 @@ incomplete statements where NODE is nil and PARENT is
line_continuation."
"Return the offset computed by `matlab-ts-mode--i-cont-incomplete-matcher'."
(cdr matlab-ts-mode--i-cont-incomplete-matcher-pair))
+(defvar matlab-ts-mode--i-next-line-pair)
+
+(defun matlab-ts-mode--i-next-line-matcher (_node parent _bol &rest _)
+ "Matcher for indent on a newline being inserted.
+If so, set `matlab-ts-mode--i-next-line-pair'.
+NODE may or may not be nil. PARENT will be a newline, so
+Example: in this case NODE will be nil and PARENT is a newline. Example:
+ % -*- matlab-ts -*-
+ classdef foo
+ ^ <== TAB or RET on prior line goes here.
+Heirarchy:
+ #<treesit-node source_file in 1-36>
+ #<treesit-node ERROR in 21-36>
+ #<treesit-node \"
+\" in 33-36>
+Prev-siblings:
+ > #<treesit-node \"classdef\" in 21-29>
+ > #<treesit-node identifier in 30-33>
+ > #<treesit-node \""
+
+ (when (string= (treesit-node-type parent) "\n")
+ (let* ((anchors-rx (rx (seq bos (or "classdef"
+ "properties"
+ "property"
+ "methods")
+ eos)))
+ (anchor-node (or
+ ;; Look to previous siblings to see if they match
anchors-rx
+ (let ((prev-sibling (treesit-node-prev-sibling
parent)))
+ (while (and prev-sibling
+ (not (string-match-p anchors-rx
+
(treesit-node-type prev-sibling))))
+ (setq prev-sibling (treesit-node-prev-sibling
prev-sibling)))
+ prev-sibling)
+ ;; Look to ancestors siblings to see if they match
anchors-rx
+ (let* ((ancestor (treesit-node-parent parent))
+ (ancestor-type (treesit-node-type ancestor)))
+ (while (and ancestor
+ (not (string-match-p anchors-rx
ancestor-type)))
+ (setq ancestor (if (string= ancestor-type "ERROR")
+ nil
+ (treesit-node-parent ancestor))))
+ ancestor))))
+ (when anchor-node
+ (let ((indent-level (if (string= (treesit-node-type anchor-node)
"property")
+ 0
+ matlab-ts-mode--indent-level)))
+ (setq matlab-ts-mode--i-next-line-pair
+ (cons (treesit-node-start anchor-node) indent-level))
+
+ ;; TODO Rough sketch of electric-ends
+ ;; We need to look at the parse tree to see if we should insert
it.
+ ;; Also, we should verify this doesn't happen with
indent-region, only on RET.
+ ;; (when matlab-ts-mode-electric-ends
+ ;; (save-excursion
+ ;; (let ((insert-column (save-excursion
+ ;; (goto-char (treesit-node-start
anchor-node))
+ ;; (current-column))))
+ ;; (insert "\n" (make-string insert-column ?\ ) "end\n")))
+ t)))))
+
+(defun matlab-ts-mode--i-next-line-anchor (&rest _)
+ "Return the anchor computed by `matlab-ts-mode--i-next-line-matcher'."
+ (car matlab-ts-mode--i-next-line-pair))
+
+(defun matlab-ts-mode--i-next-line-offset (&rest _)
+ "Return the offset computed by `matlab-ts-mode--i-next-line-matcher'."
+ (cdr matlab-ts-mode--i-next-line-pair))
+
(defvar matlab-ts-mode--indent-rules
`((matlab
+ ;; I-Rule: RET on an incomplete statement. Example:
+ ;; classdef foo
+ ;; ^ <== TAB or RET on prior line goes
here.
+ (,#'matlab-ts-mode--i-next-line-matcher
+ ,#'matlab-ts-mode--i-next-line-anchor
+ ,#'matlab-ts-mode--i-next-line-offset)
+
+ ;; xxx move down?
;; I-Rule: syntax errors
;; See: tests/test-matlab-ts-mode-indent-files/indent_syntax_error1.m
;; See: tests/test-matlab-ts-mode-indent-files/indent_syntax_error2.m
@@ -1274,6 +1366,7 @@ incomplete statements where NODE is nil and PARENT is
line_continuation."
;; I-Rule: elseif, else, catch, end statements go back to parent level
((node-is ,(rx bos (or "elseif_clause" "else_clause" "catch_clause"
"end") eos)) parent 0)
+
;; I-Rule: first line of code witin a switch case or otherwise statement,
node is block
((parent-is ,(rx bos (or "switch_statement" "case_clause"
"otherwise_clause") eos))
parent ,matlab-ts-mode--switch-indent-level)
@@ -2388,7 +2481,7 @@ is t, add the following to an Init File (e.g.
`user-init-file' or
;; Setup `forward-page' and `backward-page' to use ^L or "%% heading"
comments
;; See: ./tests/test-matlab-ts-mode-page.el
- (setq-local page-delimiter "^\\(?:\f\\|%%\\(?:\\s-\\|\n\\)\\)")
+ (setq-local page-delimiter "^\\(?:\f\\|\\s-*%%\\(?:\\s-\\|\n\\)\\)")
;; Font-lock.
;; See: ./tests/test-matlab-ts-mode-font-lock.el
diff --git a/tests/t-utils.el b/tests/t-utils.el
index 26010c86e5..be3aa5fcc6 100644
--- a/tests/t-utils.el
+++ b/tests/t-utils.el
@@ -226,10 +226,10 @@ skipping all *_expected.lang files."
(replace-regexp-in-string "^" " "
(buffer-string))))))
(message "%s:1: warning: skipping this test input because %s
exists\n%s"
- file skip-file skip-file-contents)))
- ;; Not skipped. Note we ignore hidden link files, e.g. .#foo.lang
- (when (not (string-match-p "\\`\\.#" file))
- (push file files-not-skipped))))
+ file skip-file skip-file-contents))
+ ;; Not skipped. Note we ignore hidden link files, e.g. .#foo.lang
+ (when (not (string-match-p "\\`\\.#" file))
+ (push file files-not-skipped)))))
;; Sorted result
(sort files-not-skipped))))
@@ -368,7 +368,7 @@ containing RESULT."
(read-only-mode 1))
(display-buffer result-buf))))
-(defun t-utils--insert-file-for-test (file &optional file-major-mode
skip-corrupt-check)
+(defun t-utils--insert-file-for-test (file &optional file-major-mode
setup-callback skip-corrupt-check)
"Insert FILE into current temporary buffer for testing.
If optional FILE-MAJOR-MODE function is provided, run that, otherwise
we examine the first line of the file for the major mode:
@@ -378,6 +378,9 @@ we examine the first line of the file for the major mode:
and run that.
+If optional SETUP-CALLBACK is specified, it is invoked after setting
+the major mode in the temporary buffer.
+
If optional SKIP-CORRUPT-CHECK is non-nil, the check for corrupted content is
skipped."
@@ -410,6 +413,9 @@ skipped."
(mode-cmd (intern (concat mode "-mode"))))
(funcall mode-cmd)))
+ (when setup-callback
+ (funcall setup-callback))
+
;; Incase the mode moves the point, reset to point-min.
(goto-char (point-min))
@@ -517,7 +523,7 @@ You can run `t-utils--diff-check' to debug"))))
(not (save-excursion (goto-char (1- (point))) (looking-at ")"))))
(error "Expected point to be after a closing parenthisis, \")\""))
- (let* ((line-move-visual nil) ;; C-n moves by true lines and not the width
+ (let* ((line-move-visual nil) ;; C-n, next-line: moves by true lines and not
the width
(buf-file (t-utils--get-buf-file))
(start-line (line-number-at-pos))
(xr-end-point (point))
@@ -786,7 +792,7 @@ got code-to-face (\"%s\" . %S), expected code-to-face
(\"%s\" . %S)"
got-code got-face
expected-code expected-face))))
-(defun t-utils-test-font-lock (test-name lang-files code-to-face)
+(defun t-utils-test-font-lock (test-name lang-files code-to-face &optional
setup-callback)
"Test font-lock using on each lang-file in LANG-FILES list.
Foreach file NAME.LANG in LANG-FILES compare the file against
NAME_expected.txt, where NAME the file name minus the lang-file
@@ -800,6 +806,9 @@ got, a NAME_expected.txt~ will be generated. After
reviewing
NAME_expected.txt~, you should rename it to NAME_expected.txt or fix
your code and rerun the test.
+If optional SETUP-CALLBACK is specified it is called after setting the
+major mode on the temporary buffer for lang-file.
+
For example, suppose our LANG-FILE contains
int foo(void) {
return 1;
@@ -871,7 +880,7 @@ To debug a specific font-lock test file
(error-msgs '()))
(dolist (lang-file lang-files)
(with-temp-buffer
- (t-utils--insert-file-for-test lang-file)
+ (t-utils--insert-file-for-test lang-file nil setup-callback)
(let ((start-time (current-time)))
(message "START: %s %s" test-name lang-file)
@@ -1672,7 +1681,7 @@ To debug a specific file-encoding test file
;; Load lang-file in temp buffer and activate file-major-mode
(condition-case err
- (t-utils--insert-file-for-test lang-file file-major-mode
'skip-corrupt-check)
+ (t-utils--insert-file-for-test lang-file file-major-mode nil
'skip-corrupt-check)
(error
(setq got (concat "Major mode errored with message\n"
(error-message-string err)))))
diff --git a/tests/test-matlab-ts-mode-font-lock-files/font_lock_error.skip.txt
b/tests/test-matlab-ts-mode-font-lock-files/font_lock_error.skip.txt
new file mode 100644
index 0000000000..200874cc05
--- /dev/null
+++ b/tests/test-matlab-ts-mode-font-lock-files/font_lock_error.skip.txt
@@ -0,0 +1,3 @@
+Remove when these are addressed
+ https://github.com/acristoffers/tree-sitter-matlab/issues/50
+ https://github.com/acristoffers/tree-sitter-matlab/issues/36
diff --git a/tests/test-matlab-ts-mode-font-lock.el
b/tests/test-matlab-ts-mode-font-lock.el
index b4f94d593a..c8949ddc00 100644
--- a/tests/test-matlab-ts-mode-font-lock.el
+++ b/tests/test-matlab-ts-mode-font-lock.el
@@ -66,29 +66,30 @@ after validating it, rename it to
("a" . matlab-ts-mode-command-arg-face)
("b" . font-lock-bracket-face)
("B" . font-lock-builtin-face)
- ("c" . font-lock-comment-face)
("C" . font-lock-comment-delimiter-face)
+ ("c" . font-lock-comment-face)
("d" . default)
("D" . font-lock-delimiter-face)
("E" . font-lock-escape-face)
- ("f" . font-lock-function-name-face)
("F" . font-lock-function-call-face)
+ ("f" . font-lock-function-name-face)
("h" . font-lock-doc-face) ;; function doc help
comment
("H" . matlab-ts-mode-comment-heading-face)
("k" . font-lock-keyword-face)
("M" . matlab-ts-mode-comment-to-do-marker-face)
("n" . matlab-ts-mode-number-face)
- ("s" . font-lock-string-face)
- ("S" . matlab-ts-mode-string-delimiter-face)
("o" . matlab-ts-mode-operator-face)
("p" . matlab-ts-mode-pragma-face)
("P" . matlab-ts-mode-property-face)
+ ("s" . font-lock-string-face)
+ ("S" . matlab-ts-mode-string-delimiter-face)
("t" . font-lock-type-face)
("v" . font-lock-variable-name-face)
("w" . font-lock-warning-face)
- )))
+ ))
+ (test-buf-setup (lambda () (matlab-sections-minor-mode -1))))
(t-utils-error-if-no-treesit-for 'matlab test-name)
- (t-utils-test-font-lock test-name m-files code-to-face)))
+ (t-utils-test-font-lock test-name m-files code-to-face test-buf-setup)))
(provide 'test-matlab-ts-mode-font-lock)
;;; test-matlab-ts-mode-font-lock.el ends here
diff --git a/tests/test-matlab-ts-mode-indent-files/indent_old_indents.skip.txt
b/tests/test-matlab-ts-mode-indent-files/indent_old_indents.skip.txt
deleted file mode 100644
index 27ce1c2972..0000000000
--- a/tests/test-matlab-ts-mode-indent-files/indent_old_indents.skip.txt
+++ /dev/null
@@ -1 +0,0 @@
-Remove when https://github.com/acristoffers/tree-sitter-matlab/issues/44 is
fixed
diff --git
a/tests/test-matlab-ts-mode-indent-files/indent_matrix_issue44.skip.txt
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_i_cont_incomplete2.skip.txt
similarity index 78%
rename from
tests/test-matlab-ts-mode-indent-files/indent_matrix_issue44.skip.txt
rename to
tests/test-matlab-ts-mode-indent-xr-files/indent_xr_i_cont_incomplete2.skip.txt
index 27ce1c2972..f7d67eceae 100644
--- a/tests/test-matlab-ts-mode-indent-files/indent_matrix_issue44.skip.txt
+++
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_i_cont_incomplete2.skip.txt
@@ -1 +1 @@
-Remove when https://github.com/acristoffers/tree-sitter-matlab/issues/44 is
fixed
+Remove when https://github.com/acristoffers/tree-sitter-matlab/issues/51 is
fixed
diff --git
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.m
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.m
new file mode 100644
index 0000000000..46d586e180
--- /dev/null
+++ b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.m
@@ -0,0 +1,3 @@
+% -*- matlab-ts -*-
+% (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+classdef indent_xr_next_line_matcher1
diff --git
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.skip.txt
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.skip.txt
new file mode 100644
index 0000000000..4c685c2d77
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1.skip.txt
@@ -0,0 +1 @@
+Need to fix end location after properties.
diff --git
a/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1_expected.org
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1_expected.org
new file mode 100644
index 0000000000..7370d6f3bf
--- /dev/null
+++
b/tests/test-matlab-ts-mode-indent-xr-files/indent_xr_next_line_matcher1_expected.org
@@ -0,0 +1,147 @@
+#+startup: showall
+
+* Executing commands from indent_xr_next_line_matcher1.m:2:2:
+
+ (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+
+- Invoking : "C-n" = next-line
+ Start point : 225
+ Moved to point: 263
+ : 3:37: classdef indent_xr_next_line_matcher1
+ : ^
+ No buffer modifications
+
+- Invoking : "C-e" = move-end-of-line
+ Start point : 263
+ No point movement
+ No buffer modifications
+
+- Invoking : "C-m" = newline
+ Start point : 263
+ Moved to point: 268
+ : 4:4:
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,3 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+ classdef indent_xr_next_line_matcher1
++
+ #+end_src diff
+
+- Invoking : (insert "properties")
+ Start point : 268
+ Moved to point: 278
+ : 4:14: properties
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -1,4 +1,4 @@
+ % -*- matlab-ts -*-
+ % (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+ classdef indent_xr_next_line_matcher1
+-
++ properties
+ #+end_src diff
+
+- Invoking : "C-m" = newline
+ Start point : 278
+ Moved to point: 287
+ : 5:8:
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,3 +2,4 @@
+ % (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+ classdef indent_xr_next_line_matcher1
+ properties
++
+ #+end_src diff
+
+- Invoking : (insert "end")
+ Start point : 287
+ Moved to point: 290
+ : 5:11: end
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -2,4 +2,4 @@
+ % (t-utils-xr "C-n" "C-e" "C-m" (insert "properties") "C-m" (insert "end")
"C-i" "C-e" "C-m" (insert "end\n") (re-search-backward "^classdef") (print
(buffer-substring-no-properties (point) (point-max))))
+ classdef indent_xr_next_line_matcher1
+ properties
+-
++ end
+ #+end_src diff
+
+- Invoking : "C-i" = indent-for-tab-command
+ Start point : 290
+ No point movement
+ No buffer modifications
+
+- Invoking : "C-e" = move-end-of-line
+ Start point : 290
+ No point movement
+ No buffer modifications
+
+- Invoking : "C-m" = newline
+ Start point : 290
+ Moved to point: 291
+ : 6:0:
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,3 +3,4 @@
+ classdef indent_xr_next_line_matcher1
+ properties
+ end
++
+ #+end_src diff
+
+- Invoking : (insert "end
+")
+ Start point : 291
+ Moved to point: 295
+ : 7:0:
+ : ^
+ Buffer modified:
+ #+begin_src diff
+--- start_contents
++++ end_contents
+@@ -3,4 +3,5 @@
+ classdef indent_xr_next_line_matcher1
+ properties
+ end
++end
+
+ #+end_src diff
+
+- Invoking : (re-search-backward "^classdef")
+ Start point : 295
+ Moved to point: 226
+ : 3:0: classdef indent_xr_next_line_matcher1
+ : ^
+ No buffer modifications
+
+- Invoking : (print (buffer-substring-no-properties (point) (point-max)))
+ Start point : 226
+ No point movement
+ standard-output:
+ #+begin_example
+classdef indent_xr_next_line_matcher1
+ properties
+ end
+end
+
+ #+end_example
+ No buffer modifications