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

    matlab-ts-mode: treesit-defun-name-function setup for Change Log's
---
 contributing/treesit-mode-how-to.org               | 15 +++++
 matlab-ts-mode.el                                  | 19 ++++--
 tests/t-utils.el                                   | 77 +++++++++++++++++++---
 .../ts_defun_name_class.m                          | 14 ++++
 .../ts_defun_name_class_expected.txt               | 45 +++++++++++++
 .../ts_defun_name_fcn.m                            | 23 +++++++
 .../ts_defun_name_fcn_expected.txt                 | 70 ++++++++++++++++++++
 tests/test-matlab-ts-mode-treesit-defun-name.el    | 58 ++++++++++++++++
 8 files changed, 308 insertions(+), 13 deletions(-)

diff --git a/contributing/treesit-mode-how-to.org 
b/contributing/treesit-mode-how-to.org
index 9800eef50a..b293bfa3d1 100644
--- a/contributing/treesit-mode-how-to.org
+++ b/contributing/treesit-mode-how-to.org
@@ -1054,6 +1054,16 @@ LANGUAGE-ts-mode--thing-settings)= after you've setup 
your syntax table.
 
 TODO
 
+* treesit-defun-name-function
+
+Emacs supports the concept of Change Logs for documentating changes.  With 
version control systems
+like git, there's less need for Change Logs, though the format of the Change 
Logs.  In Emacs using
+=C-x 4 a= (add-change-log-entry-other-window) will end up calling 
=add-log-current-defun= which
+defers to the =treesit-defun-name-function= to get information for the entry 
to add to the log file.
+
+TODO
+
+
 * IMenu
 
 Emacs =M-g i= (=M-x imenu=), makes it easy to jump to items in your file. If 
our mode populates
@@ -1633,3 +1643,8 @@ well worth writing a tree-sitter mode.
   10. Accurate type of m-file detection, which improves 
matlab-sections-minor-mode.
 
       TODO
+
+  11. Change Log command now work with MATALB *.m files.
+
+      Running =C-x 4 a= (add-change-log-entry-other-window) will now insert 
the name of the function
+      or classdef for the current point.
diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index 44c1b98d6e..5d90da202a 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -789,6 +789,17 @@ expression."
     )
   "Tree-sitter rules that things for movement.")
 
+;;---------------------;;
+;; Section: Change Log ;;
+;;---------------------;;
+
+(defun matlab-ts-mode--defun-name (node)
+  "Return the defun name of NODE for Change Log entries."
+    (when (string-match-p
+           (rx bol (or "function_definition" "class_definition") eol)
+           (treesit-node-type node))
+      (treesit-node-text (treesit-node-child-by-field-name node "name"))))
+
 ;;-------------------------;;
 ;; Section: matlab-ts-mode ;;
 ;;-------------------------;;
@@ -817,12 +828,10 @@ expression."
     ;; See: ./tests/test-matlab-ts-mode-page.el
     (setq-local page-delimiter "^\\(\f\\|%%\\(\\s-\\|\n\\)\\)")
 
-    ;; TODO function end handling
-    ;; TODO add strings to syntax table?
+    ;; TODO function name vs file name prompt to fix
     ;; TODO what about syntax table and electric keywords?
     ;; TODO function / end match like matlab-mode
     ;; TODO code folding
-    ;; TODO fill paragraph, etc. look at c-ts-common.el
     ;; TODO outline: look at 
https://hg.sr.ht/~pranshu/perl-ts-mode/browse/perl-ts-mode.el?rev=tip
     ;; TODO imenu: look at 
https://hg.sr.ht/~pranshu/perl-ts-mode/browse/perl-ts-mode.el?rev=tip
     ;; TODO handle file name mismatch between function / classdef name
@@ -853,8 +862,8 @@ expression."
     ;; Movement. See: tests/test-matlab-ts-mode-movement.el
     (setq-local treesit-thing-settings matlab-ts-mode--thing-settings)
 
-    ;; TODO's
-    (setq-local treesit-defun-name-function nil) ;; TODO
+    ;; Change Logs. See: tests/test-matlab-ts-mode-treesit-defun-name.el
+    (setq-local treesit-defun-name-function #'matlab-ts-mode--defun-name)
 
     (setq-local treesit-simple-imenu-settings nil) ;; TODO
     ;; 
https://www.reddit.com/r/emacs/comments/1c216kr/experimenting_with_tree_sitter_and_imenulist/
diff --git a/tests/t-utils.el b/tests/t-utils.el
index 467bd40084..7acb5b7558 100644
--- a/tests/t-utils.el
+++ b/tests/t-utils.el
@@ -24,9 +24,10 @@
 
 ;;; Code:
 
-(require 'cl-seq)
 (require 'cl-macs)
+(require 'cl-seq)
 (require 'diff)
+(require 'treesit)
 
 ;; Add abs-path of ".." to load-path so we can require packages from above us.
 (let* ((lf (or load-file-name (buffer-file-name (current-buffer))))
@@ -619,9 +620,10 @@ match NAME_expected.txt, NAME_expected.EXT~ will be 
created.  You are
 then instructured to validate the indent and rename NAME_expected.EXT~
 to NAME_expected.EXT.
 
-To add a test for TEST-NAME.el, in it's corresponding TEST-NAME-files/
-directory, create TEST-NAME-files/NAME.EXT, then run the test.  Follow
-the messages to accept the generated baseline after validating it.
+To add a test for TEST-NAME.el which call this function, in the
+corresponding TEST-NAME-files/ directory, create
+TEST-NAME-files/NAME.EXT, then run the test.  Follow the messages to
+accept the generated baseline after validating it.
 
 Two methods are used to indent each file in LANG-FILES,
  1. (indent-region (point-min) (point-man))
@@ -676,14 +678,15 @@ See `t-utils--test-indent-type' for LINE-MANIPULATOR."
 Compare syntax-table of each NAME.EXT in LANG-FILES against NAME_expected.txt.
 TEST-NAME is used in messages.
 
-If NAME_expected.txt does not exist or the syntax-table of NAME.txt doesn't
+If NAME_expected.txt does not exist or the syntax-table of NAME.ext doesn't
 match NAME_expected.txt, NAME_expected.txt~ will be created.  You are
 then instructured to validate the syntax-table and rename NAME_expected.txt~
 to NAME_expected.txt.
 
-To add a test for TEST-NAME.el, in it's corresponding TEST-NAME-files/
-directory, create TEST-NAME-files/NAME.EXT, then run the test.  Follow
-the messages to accept the generated baseline after validating it."
+To add a test for TEST-NAME.el which call this function, in the
+corresponding TEST-NAME-files/ directory, create
+TEST-NAME-files/NAME.EXT, then run the test.  Follow the messages to
+accept the generated baseline after validating it."
 
   (dolist (lang-file lang-files)
     (with-temp-buffer
@@ -727,5 +730,63 @@ See %s and if it looks good rename it to %s"
                    lang-file got-file expected-file)))
         (message "PASS: %s %s %s" test-name lang-file (t-utils--took 
start-time))))))
 
+(defun t-utils-test-treesit-defun-name (test-name lang-files)
+  "Test `treesit-defun-name-function' setup.
+Compare the result of `treesit-defun-name-function' against each
+tree-sitter node in each NAME.EXT of LANG-FILES against
+NAME_expected.txt.  TEST-NAME is used in messages.
+
+If NAME_expected.txt does not exist or the result from NAME.EXT doesn't
+match NAME_expected.txt, NAME_expected.txt~ will be created.  You are
+then instructured to validate the syntax-table and rename NAME_expected.txt~
+to NAME_expected.txt.
+
+To add a test for TEST-NAME.el which call this function, in the
+corresponding TEST-NAME-files/ directory, create
+TEST-NAME-files/NAME.EXT, then run the test.  Follow the messages to
+accept the generated baseline after validating it."
+
+  (dolist (lang-file lang-files)
+    (with-temp-buffer
+
+      (let ((start-time (current-time)))
+        (message "START: %s %s" test-name lang-file)
+
+        (t-utils--insert-file-for-test lang-file)
+
+        (let* ((root (treesit-buffer-root-node))
+               (expected-file (replace-regexp-in-string "\\.[^.]+$" 
"_expected.txt" lang-file))
+               (expected (when (file-exists-p expected-file)
+                           (with-temp-buffer
+                             (insert-file-contents-literally expected-file)
+                             (buffer-string))))
+               (got "")
+               (got-file (concat expected-file "~")))
+
+          (treesit-search-subtree
+           root
+           (lambda (node)
+             (let ((defun-name (funcall treesit-defun-name-function node))
+                   (node-type (replace-regexp-in-string "\n" "\\n" 
(treesit-node-type node)))
+                   (node-start (treesit-node-start node))
+                   (node-end (treesit-node-end node)))
+               (setq got (concat
+                          got
+                          (format "Node %25s at %4d to %4d: defun-name = %s\n"
+                                  node-type node-start node-end (if defun-name 
defun-name "nil")))))
+             nil))
+
+          (kill-buffer)
+          (when (not (string= got expected))
+            (let ((coding-system-for-write 'raw-text-unix))
+              (write-region got nil got-file))
+            (when (not expected)
+              (error "Baseline for %s does not exists.  \
+See %s and if it looks good rename it to %s"
+                     lang-file got-file expected-file))
+            (error "Baseline for %s does not match, got: %s, expected: %s"
+                   lang-file got-file expected-file)))
+        (message "PASS: %s %s %s" test-name lang-file (t-utils--took 
start-time))))))
+
 (provide 't-utils)
 ;;; t-utils.el ends here
diff --git 
a/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class.m 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class.m
new file mode 100644
index 0000000000..1cf1bc090b
--- /dev/null
+++ b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class.m
@@ -0,0 +1,14 @@
+% -*- matlab-ts -*-
+classdef ts_defun_name_class
+    properties
+        Value {mustBeNumeric}
+    end
+    methods
+        function r = roundOff(obj)
+            r = round([obj.Value], 2);
+        end
+        function r = multiplyBy(obj, n)
+            r = [obj.Value] * n;
+        end
+    end
+end
diff --git 
a/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class_expected.txt
 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class_expected.txt
new file mode 100644
index 0000000000..b2853e28de
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_class_expected.txt
@@ -0,0 +1,45 @@
+Node               source_file at    1 to  298: defun-name = nil
+Node                   comment at    1 to   20: defun-name = nil
+Node          class_definition at   21 to  297: defun-name = 
ts_defun_name_class
+Node                identifier at   30 to   49: defun-name = nil
+Node                properties at   54 to  102: defun-name = nil
+Node                  property at   73 to   95: defun-name = nil
+Node                identifier at   73 to   78: defun-name = nil
+Node      validation_functions at   79 to   94: defun-name = nil
+Node                identifier at   80 to   93: defun-name = nil
+Node                   methods at  107 to  293: defun-name = nil
+Node       function_definition at  123 to  200: defun-name = roundOff
+Node           function_output at  132 to  135: defun-name = nil
+Node                identifier at  132 to  133: defun-name = nil
+Node                identifier at  136 to  144: defun-name = nil
+Node        function_arguments at  144 to  149: defun-name = nil
+Node                identifier at  145 to  148: defun-name = nil
+Node                     block at  162 to  188: defun-name = nil
+Node                assignment at  162 to  187: defun-name = nil
+Node                identifier at  162 to  163: defun-name = nil
+Node             function_call at  166 to  187: defun-name = nil
+Node                identifier at  166 to  171: defun-name = nil
+Node                 arguments at  172 to  186: defun-name = nil
+Node                    matrix at  172 to  183: defun-name = nil
+Node                       row at  173 to  182: defun-name = nil
+Node          field_expression at  173 to  182: defun-name = nil
+Node                identifier at  173 to  176: defun-name = nil
+Node                identifier at  177 to  182: defun-name = nil
+Node                    number at  185 to  186: defun-name = nil
+Node       function_definition at  209 to  285: defun-name = multiplyBy
+Node           function_output at  218 to  221: defun-name = nil
+Node                identifier at  218 to  219: defun-name = nil
+Node                identifier at  222 to  232: defun-name = nil
+Node        function_arguments at  232 to  240: defun-name = nil
+Node                identifier at  233 to  236: defun-name = nil
+Node                identifier at  238 to  239: defun-name = nil
+Node                     block at  253 to  273: defun-name = nil
+Node                assignment at  253 to  272: defun-name = nil
+Node                identifier at  253 to  254: defun-name = nil
+Node           binary_operator at  257 to  272: defun-name = nil
+Node                    matrix at  257 to  268: defun-name = nil
+Node                       row at  258 to  267: defun-name = nil
+Node          field_expression at  258 to  267: defun-name = nil
+Node                identifier at  258 to  261: defun-name = nil
+Node                identifier at  262 to  267: defun-name = nil
+Node                identifier at  271 to  272: defun-name = nil
diff --git 
a/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn.m 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn.m
new file mode 100644
index 0000000000..4333104f6a
--- /dev/null
+++ b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn.m
@@ -0,0 +1,23 @@
+% -*- matlab-ts -*-
+function b = ts_defun_name_fcn(a)
+
+    b = 0;
+    if a > 1
+        if a > 2
+            disp('1 2')
+            switch a
+              case 2
+                disp('case 2')
+                b = fcn1(a);
+            end
+        end
+    end
+end
+
+function b = fcn1(a)
+    b = 5 * fcn2(a);
+
+    function d = fcn2(c)
+        d = c * 2;
+    end
+end
diff --git 
a/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn_expected.txt
 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn_expected.txt
new file mode 100644
index 0000000000..31739fb6b3
--- /dev/null
+++ 
b/tests/test-matlab-ts-mode-treesit-defun-name-files/ts_defun_name_fcn_expected.txt
@@ -0,0 +1,70 @@
+Node               source_file at    1 to  363: defun-name = nil
+Node                   comment at    1 to   20: defun-name = nil
+Node       function_definition at   21 to  262: defun-name = ts_defun_name_fcn
+Node           function_output at   30 to   33: defun-name = nil
+Node                identifier at   30 to   31: defun-name = nil
+Node                identifier at   34 to   51: defun-name = nil
+Node        function_arguments at   51 to   54: defun-name = nil
+Node                identifier at   52 to   53: defun-name = nil
+Node                     block at   60 to  259: defun-name = nil
+Node                assignment at   60 to   65: defun-name = nil
+Node                identifier at   60 to   61: defun-name = nil
+Node                    number at   64 to   65: defun-name = nil
+Node              if_statement at   71 to  258: defun-name = nil
+Node       comparison_operator at   74 to   79: defun-name = nil
+Node                identifier at   74 to   75: defun-name = nil
+Node                    number at   78 to   79: defun-name = nil
+Node                     block at   88 to  251: defun-name = nil
+Node              if_statement at   88 to  250: defun-name = nil
+Node       comparison_operator at   91 to   96: defun-name = nil
+Node                identifier at   91 to   92: defun-name = nil
+Node                    number at   95 to   96: defun-name = nil
+Node                     block at  109 to  239: defun-name = nil
+Node             function_call at  109 to  120: defun-name = nil
+Node                identifier at  109 to  113: defun-name = nil
+Node                 arguments at  114 to  119: defun-name = nil
+Node                    string at  114 to  119: defun-name = nil
+Node            string_content at  115 to  118: defun-name = nil
+Node          switch_statement at  133 to  238: defun-name = nil
+Node                identifier at  140 to  141: defun-name = nil
+Node               case_clause at  156 to  222: defun-name = nil
+Node                    number at  161 to  162: defun-name = nil
+Node                     block at  179 to  222: defun-name = nil
+Node             function_call at  179 to  193: defun-name = nil
+Node                identifier at  179 to  183: defun-name = nil
+Node                 arguments at  184 to  192: defun-name = nil
+Node                    string at  184 to  192: defun-name = nil
+Node            string_content at  185 to  191: defun-name = nil
+Node                assignment at  210 to  221: defun-name = nil
+Node                identifier at  210 to  211: defun-name = nil
+Node             function_call at  214 to  221: defun-name = nil
+Node                identifier at  214 to  218: defun-name = nil
+Node                 arguments at  219 to  220: defun-name = nil
+Node                identifier at  219 to  220: defun-name = nil
+Node       function_definition at  264 to  362: defun-name = fcn1
+Node           function_output at  273 to  276: defun-name = nil
+Node                identifier at  273 to  274: defun-name = nil
+Node                identifier at  277 to  281: defun-name = nil
+Node        function_arguments at  281 to  284: defun-name = nil
+Node                identifier at  282 to  283: defun-name = nil
+Node                     block at  289 to  359: defun-name = nil
+Node                assignment at  289 to  304: defun-name = nil
+Node                identifier at  289 to  290: defun-name = nil
+Node           binary_operator at  293 to  304: defun-name = nil
+Node                    number at  293 to  294: defun-name = nil
+Node             function_call at  297 to  304: defun-name = nil
+Node                identifier at  297 to  301: defun-name = nil
+Node                 arguments at  302 to  303: defun-name = nil
+Node                identifier at  302 to  303: defun-name = nil
+Node       function_definition at  311 to  358: defun-name = fcn2
+Node           function_output at  320 to  323: defun-name = nil
+Node                identifier at  320 to  321: defun-name = nil
+Node                identifier at  324 to  328: defun-name = nil
+Node        function_arguments at  328 to  331: defun-name = nil
+Node                identifier at  329 to  330: defun-name = nil
+Node                     block at  340 to  350: defun-name = nil
+Node                assignment at  340 to  349: defun-name = nil
+Node                identifier at  340 to  341: defun-name = nil
+Node           binary_operator at  344 to  349: defun-name = nil
+Node                identifier at  344 to  345: defun-name = nil
+Node                    number at  348 to  349: defun-name = nil
diff --git a/tests/test-matlab-ts-mode-treesit-defun-name.el 
b/tests/test-matlab-ts-mode-treesit-defun-name.el
new file mode 100644
index 0000000000..7dfe09796e
--- /dev/null
+++ b/tests/test-matlab-ts-mode-treesit-defun-name.el
@@ -0,0 +1,58 @@
+;;; test-matlab-ts-mode-treesit-defun-name.el --- -*- lexical-binding: t -*-
+;;
+;; Copyright 2025 Free Software Foundation, Inc.
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;
+
+;;; Commentary:
+;;
+;; Validate matlab-ts-mode indent.
+;; Load ../matlab-ts-mode.el via require and run indent tests using
+;; ./test-matlab-ts-mode-treesit-defun-name-files/NAME.m comparing against
+;; ./test-matlab-ts-mode-treesit-defun-name-files/NAME_expected.org
+;;
+
+;;; Code:
+
+(require 't-utils)
+(require 'matlab-ts-mode)
+
+(cl-defun test-matlab-ts-mode-treesit-defun-name (&optional m-file)
+  "Test defun movement using 
./test-matlab-ts-mode-treesit-defun-name-files/NAME.m.
+Using ./test-matlab-ts-mode-treesit-defun-name-files/NAME.m, compare defun
+movement against
+./test-matlab-ts-mode-treesit-defun-name-files/NAME_expected.org.  If M-FILE is
+not provided, loop comparing all
+./test-matlab-ts-mode-treesit-defun-name-files/NAME.m files.
+
+To add a test, create
+  ./test-matlab-ts-mode-treesit-defun-name-files/NAME.m
+and run this function.  The baseline is saved for you as
+  ./test-matlab-ts-mode-treesit-defun-name-files/NAME_expected.org~
+after validating it, rename it to
+  ./test-matlab-ts-mode-treesit-defun-name-files/NAME_expected.org"
+
+  (let ((test-name "test-matlab-ts-mode-treesit-defun-name"))
+
+    (when (not (t-utils-is-treesit-available 'matlab test-name))
+      (cl-return-from test-matlab-ts-mode-font-lock))
+
+    (let ((m-files (t-utils-get-files (concat test-name "-files") "\\.m$" nil 
m-file)))
+      (t-utils-test-treesit-defun-name test-name m-files)))
+    "success")
+
+(provide 'test-matlab-ts-mode-treesit-defun-name)
+;;; test-matlab-ts-mode-treesit-defun-name.el ends here

Reply via email to