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

    matlab-shell: make it work with matlab-ts-mode or matlab-mode
---
 contributing/treesit-mode-how-to.org               |   5 +
 matlab--shell-bridge.el                            |  79 +++++++
 matlab--shell-map.el                               |  20 ++
 matlab-complete.el                                 |   1 +
 matlab-shell.el                                    | 107 ++++------
 matlab-topic.el                                    |   3 +-
 matlab-ts-mode.el                                  | 220 ++++++++++++-------
 matlab.el                                          |  69 ++----
 .../movement_statements.m                          |  22 ++
 .../movement_statements_expected.org               | 235 +++++++++++++++++++++
 tests/test-matlab-ts-mode-movement.el              |  69 ++++++
 11 files changed, 638 insertions(+), 192 deletions(-)

diff --git a/contributing/treesit-mode-how-to.org 
b/contributing/treesit-mode-how-to.org
index 1793a302c3..3750b7cab7 100644
--- a/contributing/treesit-mode-how-to.org
+++ b/contributing/treesit-mode-how-to.org
@@ -2038,6 +2038,11 @@ Install, using default branch
 
        TODO - show how to do in lsp-mode and update lsp-mode org on this.
 
+ 14. matlab-shell now has one correct ways of running code sections, via menu 
=MATLAB -> Code
+     Sections -> Run Section (C-c C-<return>)= or and the older =MATLAB -> Run 
Code Section=
+     (matlab-shell-run-code-section) because it was duplication and wasn't 
correctly handling all
+     cases.
+
 # LocalWords:  showall usepackage parskip tocloft cftsecnumwidth 
cftsubsecindent cftsubsecnumwidth
 # LocalWords:  lang utils Imenu LSP defun ELPA tuils setq SLIB libtree dylib 
sexp xr defcusom
 # LocalWords:  defface EDebug ielm fontify Fontifying fontified defcustom 
alist eos bol NPS prev BUF
diff --git a/matlab--shell-bridge.el b/matlab--shell-bridge.el
new file mode 100644
index 0000000000..f993408c30
--- /dev/null
+++ b/matlab--shell-bridge.el
@@ -0,0 +1,79 @@
+;;; matlab--shell-bridge.el --- -*- lexical-binding: t -*-
+
+;;; Commentary:
+;; Utilities to brdige between the older matlab-mode and matlab-ts-mode
+;;
+
+;;; Code:
+
+
+(defun matlab--is-matlab-ts-mode-active ()
+  "Return t if `matlab-ts-mode' is active.
+Is `matlab-ts-mode' active per
+  (add-to-list \\='major-mode-remap-alist \\='(matlab-mode . matlab-ts-mode))
+If so, require `matlab-ts-mode' else require `matlab-mode'?"
+  (cond
+   ((rassoc 'matlab-ts-mode major-mode-remap-alist)
+    (require 'matlab-ts-mode)
+    t)
+   (t
+    (require 'matlab-mode)
+    nil)))
+
+(defvar matlab-ts-mode--syntax-table)
+(defvar matlab-mode-syntax-table)
+
+(defun matlab--shell-get-syntax-table ()
+  "Get either matlab-ts-mode or matlab-mode syntax table.
+If using `matlab-ts-mode' per
+  (add-to-list \\='major-mode-remap-alist \\='(matlab-mode . matlab-ts-mode))
+use `matlab-ts-mode--syntax-table' else use
+`matlab-mode-syntax-table'.  The tables are the same with respect
+to strings and comments."
+  (if (matlab--is-matlab-ts-mode-active)
+      matlab-ts-mode--syntax-table
+    matlab-mode-syntax-table))
+
+(declare-function matlab-scan-beginning-of-command "matlab-mode")
+(declare-function matlab-scan-end-of-command "matlab-mode")
+
+(declare-function matlab-ts-mode-beginning-of-command "matlab-ts-mode")
+(declare-function matlab-ts-mode-end-of-command "matlab-ts-mode")
+
+(defun matlab--get-command-at-point-to-run ()
+  "Based on the major-mode get the MATLAB command to run in `matlab-shell'."
+  (save-excursion
+    (pcase major-mode
+      ('matlab-ts-mode
+       (require 'matlab-ts-mode)
+       (let* ((start-point (save-excursion
+                             (matlab-ts-mode-beginning-of-command)))
+              (end-point   (when start-point
+                             (save-excursion
+                               (matlab-ts-mode-end-of-command)))))
+         (if start-point
+             (buffer-substring-no-properties start-point end-point)
+           "")))
+      ('matlab-mode
+       (require 'matlab-mode)
+       (save-excursion
+         (buffer-substring-no-properties
+          (matlab-scan-beginning-of-command)
+          (matlab-scan-end-of-command))))
+      (_
+       (error "This cannot be run in %s" (symbol-name major-mode))))))
+
+(defun matlab--function-called-at-point ()
+  "Return a string representing the function called nearby point."
+  (save-excursion
+    (beginning-of-line)
+    (cond ((looking-at "\\s-*\\([a-zA-Z]\\w+\\)[^=][^=]")
+           (match-string 1))
+          ((and (re-search-forward "=" (line-end-position) t)
+                (looking-at "\\s-*\\([a-zA-Z]\\w+\\)\\s-*[^=]"))
+           (match-string 1))
+          (t nil))))
+
+(provide 'matlab--shell-bridge)
+;;; matlab--shell-bridge.el ends here
+
diff --git a/matlab--shell-map.el b/matlab--shell-map.el
new file mode 100644
index 0000000000..1809500a0b
--- /dev/null
+++ b/matlab--shell-map.el
@@ -0,0 +1,20 @@
+;;; matlab--shell-map.el --- -*- lexical-binding: t -*-
+
+;;; Commentary:
+;; Keymaps used in both matlab-ts-mode and matlab-mode for matlab-shell.
+;;
+
+;;; Code:
+
+(defvar matlab--shell-help-map
+  (let ((km (make-sparse-keymap)))
+    (define-key km "r" 'matlab-shell-run-command)
+    (define-key km "f" 'matlab-shell-describe-command)
+    (define-key km "a" 'matlab-shell-apropos)
+    (define-key km "v" 'matlab-shell-describe-variable)
+    km)
+  "Help key map for `matlab-ts-mode' / `matlab-mode' and `matlab-shell-mode'.")
+
+(provide 'matlab--shell-map)
+;;; matlab--shell-map.el ends here
+
diff --git a/matlab-complete.el b/matlab-complete.el
index 82428ecc96..147e7b09e2 100644
--- a/matlab-complete.el
+++ b/matlab-complete.el
@@ -33,6 +33,7 @@
 (require 'cl-macs)
 (require 'matlab)
 (require 'matlab-shell)
+(require 'matlab--shell-bridge)
 
 (defun matlab-uniquify-list (lst)
   "Return a list that is a subset of LST where all elements are unique."
diff --git a/matlab-shell.el b/matlab-shell.el
index 797a860e3d..8129308178 100644
--- a/matlab-shell.el
+++ b/matlab-shell.el
@@ -26,10 +26,17 @@
 
 ;;; Code:
 
-(require 'matlab)
 (require 'matlab-compat)
 (eval-and-compile
   (require 'matlab--access))
+(require 'matlab--shell-bridge)
+(require 'matlab--shell-map)
+
+;; Note this should *NOT*
+;;   (require 'matlab) ;; or (require 'matlab-mode)
+;; or
+;;   (require 'matlab-ts-mode)
+;; because it is designed to work with both `matlab-mode' and `matlab-ts-mode'
 
 (require 'comint)
 (require 'server)
@@ -157,12 +164,18 @@ narrow completions, you may find the responses slow and 
if so,
 you can try turning this off."
   :type 'boolean)
 
+(defcustom matlab-change-current-directory nil
+  "*If non nil, make file's directory the current directory when evaluating 
it."
+  :type 'boolean)
+
+(make-variable-buffer-local 'matlab-change-current-directory)
+
 (defvar matlab-shell-tab-company-available (if (locate-library "company") t 
nil)
   "Non-nil if we have `company' installed.
 Use this to override initial check.")
 
 (defvar matlab-shell-errorscanning-syntax-table
-  (let ((st (copy-syntax-table matlab-mode-syntax-table)))
+  (let ((st (copy-syntax-table (matlab--shell-get-syntax-table))))
     ;; Make \n be whitespace when scanning output.
     (modify-syntax-entry ?\n  " " st)
     st)
@@ -188,8 +201,9 @@ If multiple prompts are seen together, only call this 
once.")
 
 ;;; Font Lock
 ;;
-;; Extra font lock keywords for the MATLAB shell.
-(defconst matlab-shell-font-lock-keywords
+;; Font lock keywords for the MATLAB shell.
+
+(defconst matlab-shell-error-font-lock-keywords
   (list
    ;; How about Errors?
    '("^\\(Error in\\|Syntax error in\\)\\s-+==>\\s-+\\(.+\\)$"
@@ -199,13 +213,7 @@ If multiple prompts are seen together, only call this 
once.")
    ;; User beep things
    '("\\(\\?\\?\\?[^\n]+\\)" 1 font-lock-comment-face)
    )
-  "Additional keywords used by MATLAB when reporting errors in interactive\
-mode.")
-
-(defconst matlab-shell-font-lock-keywords-1
-  (append matlab-basic-font-lock-keywords
-          matlab-shell-font-lock-keywords)
-  "Keyword symbol used for basic font-lock for MATLAB shell.")
+  "The matlab-shell error keywords.")
 
 (defconst matlab-shell-object-output-font-lock-keywords
   (list
@@ -240,18 +248,12 @@ mode.")
       (1 font-lock-variable-name-face)))
    '("[[{]\\([0-9]+\\(?:x[0-9]+\\)+ \\w+\\)[]}]" (1 font-lock-comment-face))
    )
-  "Highlight various extra outputs that are typical for MATLAB.")
+  "The matlab-shell output related keywords.")
 
-(defconst matlab-shell-font-lock-keywords-2
-  (append matlab-shell-font-lock-keywords-1
-          matlab-function-font-lock-keywords
+(defconst matlab-shell-font-lock-keywords
+  (append matlab-shell-error-font-lock-keywords
           matlab-shell-object-output-font-lock-keywords)
-  "Keyword symbol used for gaudy font-lock for MATLAB shell.")
-
-(defconst matlab-shell-font-lock-keywords-3
-  (append matlab-shell-font-lock-keywords-2
-          matlab-really-gaudy-font-lock-keywords)
-  "Keyword symbol used for really gaudy font-lock for MATLAB shell.")
+  "The matlab-shell keywords.")
     
 
 ;;; Keymaps & Menus
@@ -271,7 +273,7 @@ mode.")
     (define-key km [(control c) (control c)] #'matlab-shell-interrupt-subjob)
 
     ;; Help system
-    (define-key km [(control h) (control m)] matlab-help-map)
+    (define-key km [(control h) (control m)] matlab--shell-help-map)
 
     ;; Completion
     (define-key km (kbd "TAB") #'matlab-shell-tab)
@@ -382,18 +384,16 @@ in a popup buffer.
   (if (fboundp 'comint-read-input-ring)
       (comint-read-input-ring t))
 
-  ;;; MODE Settings
-  (make-local-variable 'comment-start)
-  (setq comment-start "%")
+  (setq-local comment-start "%")
 
   (use-local-map matlab-shell-mode-map)
-  (set-syntax-table matlab-mode-syntax-table)
+  (set-syntax-table (matlab--shell-get-syntax-table))
 
-  (make-local-variable 'font-lock-defaults)
-  (setq font-lock-defaults '((matlab-shell-font-lock-keywords-1
-                              matlab-shell-font-lock-keywords-2
-                              matlab-shell-font-lock-keywords-3)
-                             t nil ((?_ . "w"))))
+  (setq-local font-lock-defaults '((matlab-shell-font-lock-keywords)
+                             t   ;; syntatic fontification (strings and 
comments) is not performed.
+                             nil ;; keywords are case sensitive
+                             ;; Put _ as a word constituent, simplifying 
keywords
+                             ((?_ . "w"))))
 
   ;; GUD support
   (matlab-shell-mode-gud-enable-bindings)
@@ -1620,10 +1620,7 @@ Snatched and hacked from dired-x.el"
               ""
             (search-forward-regexp comint-prompt-regexp)
             (buffer-substring (point) (line-end-position)))))
-    (save-excursion
-      (buffer-substring-no-properties
-       (matlab-scan-beginning-of-command)
-       (matlab-scan-end-of-command)))))
+    (matlab--get-command-at-point-to-run)))
 
 (defun matlab-non-empty-lines-in-string (str)
   "Return number of non-empty lines in STR."
@@ -1655,7 +1652,7 @@ This command requires an active MATLAB shell."
                       "MATLAB command line: "
                       (cons (matlab-read-line-at-point) 0))))
   (let ((doc (matlab-shell-collect-command-output command)))
-    (matlab-output-to-temp-buffer "*MATLAB Help*" doc)))
+    (matlab-output-to-temp-buffer "*MATLAB Run Command Result*" doc)))
 
 (defun matlab-shell-describe-variable (variable)
   "Get the contents of VARIABLE and display them in a buffer.
@@ -1672,7 +1669,7 @@ This command requires an active MATLAB shell."
 This uses the lookfor command to find viable commands.
 This command requires an active MATLAB shell."
   (interactive
-   (let ((fn (matlab-function-called-at-point))
+   (let ((fn (matlab--function-called-at-point))
          val)
      (setq val (read-string (if fn
                                 (format "Describe function (default %s): " fn)
@@ -2254,30 +2251,10 @@ Similar to  `comint-send-input'."
 ;;
 ;; Run some subset of the buffer in matlab-shell.
 
-(defun matlab-shell-run-code-section ()
-  "Run the code-section the cursor is in."
-  (interactive)
-  (let ((start (save-excursion
-                 (forward-page -1)
-                 (if (looking-at "function")
-                     (error "You are not in a code-section.  Try 
`matlab-shell-save-and-go' instead"))
-                 (when (matlab-line-comment-p (matlab-compute-line-context 1))
-                   ;; Skip over starting comment from the current code-section.
-                   (matlab-end-of-command)
-                   (end-of-line)
-                   (forward-char 1))
-                 (point)))
-        (end (save-excursion
-               (forward-page 1)
-               (when (matlab-line-comment-p (matlab-compute-line-context 1))
-                 (beginning-of-line)
-                 (forward-char -1))
-               (point))))
-    (matlab-shell-run-region start end t)))
-
 (defun matlab-shell-run-region-or-line ()
   "Run region from BEG to END and display result in MATLAB shell.
-pIf region is not active run the current line.
+This should be called from a *.m file in `matlab-ts-mode' or
+`matlab-mode'.  If region is not active run the current line.
 This command requires an active MATLAB shell."
   (interactive)
   (if (and transient-mark-mode mark-active)
@@ -2288,8 +2265,9 @@ This command requires an active MATLAB shell."
 ;;;###autoload
 (defun matlab-shell-run-region (beg end &optional noshow)
   "Run region from BEG to END and display result in MATLAB shell.
-If NOSHOW is non-nil, replace newlines with commas to suppress
-output.  This command requires an active MATLAB shell."
+If NOSHOW is non-nil, replace newlines with commas to suppress output.
+This should be called from a *.m file in `matlab-ts-mode' or
+`matlab-mode'.  This command requires an active MATLAB shell."
   (interactive "r")
   (if (> beg end) (let (mid) (setq mid beg  beg end  end mid)))
 
@@ -2364,6 +2342,9 @@ Optional argument NOSHOW specifies if we should echo the 
region to the
    (t
     (funcall matlab-shell-run-region-function beg end noshow))))
 
+(defsubst matlab--cursor-in-string ()
+  "Return t if the cursor is in a valid MATLAB character vector or string 
scalar."
+  (nth 3 (syntax-ppss (point))))
 
 (defun matlab-shell-region->commandline (beg end &optional noshow)
   "Convert the region between BEG and END into a MATLAB command.
@@ -2377,7 +2358,7 @@ When NOSHOW is non-nil, suppress output by adding ; to 
commands."
       (goto-char (point-min))
       ;; Delete all the comments
       (while (search-forward "%" nil t)
-        (unless (matlab-cursor-in-string)
+        (unless (matlab--cursor-in-string)
           (delete-region (1- (point)) (line-end-position))))
       (setq str (buffer-substring-no-properties (point-min) (point-max))))
 
@@ -2393,7 +2374,7 @@ When NOSHOW is non-nil, suppress output by adding ; to 
commands."
       ;; Remove continuations
       (while (string-match
               (concat "\\s-*"
-                      (regexp-quote matlab-ellipsis-string)
+                      (regexp-quote "...")
                       "\\s-*\n")
               str)
         (setq str (replace-match " " t t str)))
diff --git a/matlab-topic.el b/matlab-topic.el
index 852dd9fc7a..66db975835 100644
--- a/matlab-topic.el
+++ b/matlab-topic.el
@@ -24,6 +24,7 @@
 
 ;;; Code:
 (require 'matlab)
+(require 'matlab--shell-map)
 (require 'matlab-shell)
 (require 'view)
 
@@ -71,7 +72,7 @@
   (let ((km (make-sparse-keymap)))
     (define-key km [return] 'matlab-shell-help-choose)
     (define-key km "q" 'bury-buffer)
-    (define-key km [(control h) (control m)] matlab-help-map)
+    (define-key km [(control h) (control m)] matlab--shell-help-map)
     (if (string-match "XEmacs" emacs-version)
        (define-key km [button2] 'matlab-shell-help-click)
       (define-key km [mouse-2] 'matlab-shell-help-click)
diff --git a/matlab-ts-mode.el b/matlab-ts-mode.el
index 59b03bf64a..0918ea20c8 100644
--- a/matlab-ts-mode.el
+++ b/matlab-ts-mode.el
@@ -1,4 +1,4 @@
- ;;; matlab-ts-mode.el --- MATLAB(R) Tree-Sitter Mode -*- lexical-binding: t 
-*-
+;;; matlab-ts-mode.el --- MATLAB(R) Tree-Sitter Mode -*- lexical-binding: t -*-
 
 ;; Copyright 2025 Free Software Foundation, Inc.
 ;;
@@ -1486,84 +1486,144 @@ incomplete statements where NODE is nil and PARENT is 
line_continuation."
 
 ;;; Thing settings for movement, etc.
 
+
+(defvar matlab-ts-mode--statements-type-re
+  (rx (seq
+       bos
+       (or "arguments_statement"
+           "assignment"
+           "lambda"
+           "class_definition"
+           "enumeration"
+           "events"
+           "for_statement"
+           "function_call"
+           "function_definition"
+           "if_statement"
+           "methods"
+           "property"
+           "properties"
+           "spmd_statement"
+           "switch_statement"
+           "try_statement"
+           "while_statement")
+       eos))
+  "MATLAB command statements.")
+
 ;; TODO should we use following for M-a, M-e?
 ;; This needs tune up, but could workout better than using 
matlab-ts-mode--thing-settings
-;;
-;; (defvar matlab-ts-mode--statements-ht
-;;   #s(hash-table
-;;      test equal
-;;      data ("arguments_statement" t
-;;            "assignment" t
-;;            "lambda" t
-;;            "class_definition" t
-;;            "enumeration" t
-;;            "events" t
-;;            "for_statement" t
-;;            "function_definition" t
-;;            "if_statement" t
-;;            "methods" t
-;;            "property" t
-;;            "properties" t
-;;            "spmd_statement" t
-;;            "switch_statement" t
-;;            "try_statement" t
-;;            "while_statement" t))
-;;   "MATLAB command statements.")
-;;
-;; (cl-defun matlab-ts-mode-beginning-of-statement (&optional goto-end)
-;;   "Move to the beginning of a command statement.
-;; If optional GOTO-END is \\='end, move to end of the current statement.
-;;
-;; We define a command statement to be a complete syntatic unit that has
-;; a start and end.  For example, if point is in an assigment statement
-;;        var = ...
-;;          1;
-;; move point to the \"v\" when GOTO-END is nil, otherwise move to the
-;; \";\".  If point is in an if statement, move to the start or end of
-;; that.  Likewise for other command statements.
-;;
-;; The point is moved to the start or end of the innermost statement that
-;; the point is on.  No movement is performed if point is not in a
-;; statement.  This can occur when there are syntax errors or the buffer
-;; has no content.
-;;
-;; Returns nil if not in a statement, otherwise the `point' which
-;; will be a new point if the starting point was not at the start
-;; or end of the command statement."
-;;   (interactive)
-;;
-;;   (cl-assert (or (not goto-end) (eq goto-end 'end)))
-;;
-;;   (let ((node (treesit-node-at (point))))
-;;
-;;     (when (and (> (point) 1)
-;;                (equal (treesit-node-type node) "\n")
-;;                (re-search-backward "[^ \t\n\r]" nil t))
-;;       (setq node (treesit-node-at (point))))
-;;
-;;     (while (and node
-;;                 (let ((type (treesit-node-type node)))
-;;                   (when (equal type "ERROR")
-;;                     ;; No movement if we have a syntax error
-;;                     (message "Not in statement due to syntax error.")
-;;                     (cl-return nil))
-;;                   (not (gethash type matlab-ts-mode--statements-ht))))
-;;       (setq node (treesit-node-parent node)))
-;;
-;;     (when (not node)
-;;       (message "Not in a statement.")
-;;       (cl-return))
-;;
-;;     (when node
-;;       (goto-char (if (eq goto-end 'end)
-;;                      (treesit-node-end node)
-;;                    (treesit-node-start node))))))
-;;
-;; (defun matlab-ts-mode-end-of-statement ()
-;;   "Move to the end of a command statement.
-;; This is the opposite of `matlab-ts-mode-beginning-of-statement'."
-;;   (interactive)
-;;   (matlab-ts-mode-beginning-of-statement 'end))
+
+(cl-defun matlab-ts-mode-beginning-of-statement (&optional goto-end 
statement-type-re)
+  "Move to the beginning of a statement.
+If optional GOTO-END is \\='end, move to end of the current statement.
+
+We define a command statement to be a complete syntatic unit that has
+a start and end.  For example, if point is in an assigment statement
+    var = ...
+      1;
+move point to the \"v\" when GOTO-END is nil, otherwise move to the the
+point after \";\".  Likewise for other command statements.
+
+The point is moved to the start or end of the innermost statement that
+the point is on.  No movement is performed if point is not in a
+statement.  This can occur when there are syntax errors or the buffer
+has no content.
+
+Optional STATEMENT-TYPE-RE is a regular expression matching the type of
+statement to look for.  For example, to move to the begining of the
+current assignment statement, use
+
+  (matlab-ts-mode-beginning-of-statement nil
+    (rx (seq bos \"assignment\" eos))
+
+If STATEMENT-TYPE-RE is not specified, `matlab-ts-mode--statements-type-re'
+is used.
+
+Returns nil if not in a statement, otherwise the `point' which
+will be a new point if the starting point was not at the start
+or end of the command statement."
+  (interactive)
+
+  (cl-assert (or (not goto-end) (eq goto-end 'end)))
+
+  (when (not statement-type-re)
+    (setq statement-type-re matlab-ts-mode--statements-type-re))
+  
+  (let ((start-point (point))
+        (node (treesit-node-at (point))))
+
+    ;; When on a newline, back up to prior statement
+    (when (and (> (point) 1)
+               (equal (treesit-node-type node) "\n")
+               (re-search-backward "[^ \t\n\r]" nil t))
+      (setq node (treesit-node-at (point))))
+
+    ;; When at ";" use prev-sibling
+    (when (equal (treesit-node-type node) ";")
+      (setq node (treesit-node-prev-sibling node)))
+
+    ;; find nearest ancestor that matches statement-type-re
+    (while (and node
+                (let ((type (treesit-node-type node)))
+                  (when (equal type "ERROR")
+                    ;; No movement if we have a syntax error
+                    (message "Not in statement due to syntax error.")
+                    (cl-return-from matlab-ts-mode-beginning-of-statement))
+                  (not (string-match-p statement-type-re type))))
+      (setq node (treesit-node-parent node)))
+
+    (when (not node)
+      (message "Not in a statement")
+      (cl-return-from matlab-ts-mode-beginning-of-statement))
+
+    (when node
+      (let* ((statement-start-point (treesit-node-start node))
+             (end-node (or
+                        ;; Use next sibling node if it's a ";" for the an
+                        ;; assignment or function_call.
+                        (and (string-match-p (rx (seq bos (or "assignment"
+                                                              "function_call")
+                                                      eos))
+                                             (treesit-node-type node))
+                             (let ((next-node (treesit-node-next-sibling 
node)))
+                               (when (and next-node
+                                          (string= ";" (treesit-node-type 
next-node)))
+                                 next-node)))
+                        node))
+             (statement-end-point (treesit-node-end end-node)))
+        (when (and (>= start-point statement-start-point)
+                   (<= start-point statement-end-point))
+          (goto-char (if (eq goto-end 'end)
+                         statement-end-point
+                       statement-start-point)))))))
+
+(defun matlab-ts-mode-end-of-statement (&optional statement-type)
+  "Move to the end of a command statement.
+This is the opposite of `matlab-ts-mode-beginning-of-statement'.
+Optional STATEMENT-TYPE is the type of statement to look for.  If
+not specified statement in `matlab-ts-mode--statements-ht' are used."
+  (interactive)
+  (matlab-ts-mode-beginning-of-statement 'end statement-type))
+
+(defun matlab-ts-mode-beginning-of-command ()
+  "Move to the beginning of the command at point.
+Commands are either assignement or function_call statements that can be
+evaluated at the matlab prompt.  Movement occurs only if the point is in
+an assignment or function call.  Note array indexing is considered a
+function call."
+  (matlab-ts-mode-beginning-of-statement nil (rx (seq bos (or "assignment"
+                                                              "function_call")
+                                                      eos))))
+
+(defun matlab-ts-mode-end-of-command ()
+  "Move to the end of the command at point.
+Commands are either assignement or function_call statements that can be
+evaluated at the matlab prompt.  Movement occurs only if the point is in
+an assignment or function call.  Note array indexing is considered a
+function call."
+  (matlab-ts-mode-beginning-of-statement 'end (rx (seq bos (or "assignment"
+                                                               "function_call")
+                                                       eos))))
 
 (defvar matlab-ts-mode--thing-settings
   `((matlab
@@ -2206,7 +2266,11 @@ is t, add the following to an Init File (e.g. 
`user-init-file' or
     ;;
     ;; TODO create defcustom matlab-ts-mode-electric-ends that inserts end 
statements
     ;;      when a function, switch, while, for, etc. is entered. This should 
handle continuations.
-
+    ;;
+    ;; TODO on load enter matlab-ts-mode when file contains mcode and
+    ;;      (add-to-list 'major-mode-remap-alist '(matlab-mode . 
matlab-ts-mode))
+    ;;      is active. Also look at matlab-mode magic-mode-alist setup.
+    ;;   
     (treesit-major-mode-setup)
 
     ;; Correct forward-sexp setup created by `treesit-major-mode' so that for 
parenthesis, brackets,
diff --git a/matlab.el b/matlab.el
index e23c189d2a..0a570ee441 100644
--- a/matlab.el
+++ b/matlab.el
@@ -56,6 +56,7 @@
 (require 'matlab-scan)
 (require 'matlab-sections)
 (require 'matlab-syntax)
+(require 'matlab--shell-map)
 
 (require 'derived)
 (require 'easymenu)
@@ -540,13 +541,6 @@ point, but it will be restored for them."
 
 (make-variable-buffer-local 'matlab-return-add-semicolon)
 
-(defcustom matlab-change-current-directory nil
-  "*If non nil, make file's directory the current directory when evaluating 
it."
-  :group 'matlab
-  :type 'boolean)
-
-(make-variable-buffer-local 'matlab-change-current-directory)
-
 (defvar matlab-mode-abbrev-table nil
   "The abbrev table used in `matlab-mode' buffers.")
 (define-abbrev-table 'matlab-mode-abbrev-table ())
@@ -554,15 +548,6 @@ point, but it will be restored for them."
 
 ;;; Keybindings ===============================================================
 
-(defvar matlab-help-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km "r" 'matlab-shell-run-command)
-    (define-key km "f" 'matlab-shell-describe-command)
-    (define-key km "a" 'matlab-shell-apropos)
-    (define-key km "v" 'matlab-shell-describe-variable)
-    km)
-  "The help key map for `matlab-mode' and `matlab-shell-mode'.")
-
 (defvar matlab-mode-map
   (let ((km (make-sparse-keymap)))
     ;; Navigation Commands
@@ -588,11 +573,10 @@ point, but it will be restored for them."
     ;; Connecting to MATLAB Shell
     (define-key km [(control c) (control s)] 'matlab-shell-save-and-go)
     (define-key km [(control c) (control r)] 'matlab-shell-run-region)
-    (define-key km [(meta control return)] 'matlab-shell-run-code-section)
     (define-key km [(control return)] 'matlab-shell-run-region-or-line)
     (define-key km [(control c) (control t)] 'matlab-show-line-info)
     (define-key km [(control c) ?. ] 'matlab-shell-locate-fcn)
-    (define-key km [(control h) (control m)] matlab-help-map)
+    (define-key km [(control h) (control m)] matlab--shell-help-map)
     (define-key km [(meta s)] 'matlab-show-matlab-shell-buffer)
     (define-key km [(control meta mouse-2)] 'matlab-find-file-click)
     ;; Debugger interconnect
@@ -614,25 +598,6 @@ point, but it will be restored for them."
      :active (matlab-any-shell-active-p)]
     ["Run Region" matlab-shell-run-region
      :active (matlab-any-shell-active-p)]
-    ["Run Code Section" matlab-shell-run-code-section
-     :active (matlab-any-shell-active-p)]
-    "----"
-    ["Locate MATLAB function" matlab-shell-locate-fcn
-     :active (matlab-shell-active-p)
-     :help "Run 'which FCN' in matlab-shell, then open the file in Emacs"]
-    ["Show M-Lint Warnings" matlab-toggle-show-mlint-warnings
-     :active (and (locate-library "mlint") (fboundp 'mlint-minor-mode))
-     :style toggle :selected  matlab-show-mlint-warnings
-     ]
-    ("Auto Fix"
-     ["Verify/Fix source" matlab-mode-verify-fix-file t]
-     ["Quiesce source" matlab-mode-vf-quiesce-buffer t]
-     )
-    ("Format"
-     ["Justify Line" matlab-justify-line t]
-     ["Fill Comment" fill-paragraph]
-     ["Comment Region" matlab-comment-region t]
-     ["Uncomment Region" matlab-uncomment-region t])
     ("Code Sections"
      ["Run section" matlab-sections-run-section
       :active matlab-sections-minor-mode
@@ -666,6 +631,23 @@ mark at the beginning of the \"%% section\" and point at 
the end of the section"
       :help "Move the current \"%% section\" down."]
      "--"
      ["Sections help" matlab-sections-help])
+    "----"
+    ["Locate MATLAB function" matlab-shell-locate-fcn
+     :active (matlab-shell-active-p)
+     :help "Run 'which FCN' in matlab-shell, then open the file in Emacs"]
+    ["Show M-Lint Warnings" matlab-toggle-show-mlint-warnings
+     :active (and (locate-library "mlint") (fboundp 'mlint-minor-mode))
+     :style toggle :selected  matlab-show-mlint-warnings
+     ]
+    ("Auto Fix"
+     ["Verify/Fix source" matlab-mode-verify-fix-file t]
+     ["Quiesce source" matlab-mode-vf-quiesce-buffer t]
+     )
+    ("Format"
+     ["Justify Line" matlab-justify-line t]
+     ["Fill Comment" fill-paragraph]
+     ["Comment Region" matlab-comment-region t]
+     ["Uncomment Region" matlab-uncomment-region t])
     ("Debug"
      ["Edit File (toggle read-only)" matlab-shell-gud-mode-edit
       :help "Exit MATLAB debug minor mode to edit without exiting MATLAB's K>> 
prompt."
@@ -1785,19 +1767,6 @@ A negative number means there were more ends than starts.
 
         depth))))
 
-
-(defun matlab-function-called-at-point ()
-  "Return a string representing the function called nearby point."
-  (save-excursion
-    (beginning-of-line)
-    (cond ((looking-at "\\s-*\\([a-zA-Z]\\w+\\)[^=][^=]")
-           (match-string 1))
-          ((and (re-search-forward "=" (line-end-position) t)
-                (looking-at "\\s-*\\([a-zA-Z]\\w+\\)\\s-*[^=]"))
-           (match-string 1))
-          (t nil))))
-
-
 (defun matlab-comment-on-line ()
   "Place the cursor on the beginning of a valid comment on this line.
 If there isn't one, then return nil, point otherwise."
diff --git a/tests/test-matlab-ts-mode-movement-files/movement_statements.m 
b/tests/test-matlab-ts-mode-movement-files/movement_statements.m
new file mode 100644
index 0000000000..bc8863a88f
--- /dev/null
+++ b/tests/test-matlab-ts-mode-movement-files/movement_statements.m
@@ -0,0 +1,22 @@
+% -*- matlab-ts -*-
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+s = 'my string';
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+m = [1, 2;
+     3, 4];
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+plot(1, 10)
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+sprintf("%s", s)
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+v=1:10;
+
+% (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+v(2:3)
+
+
diff --git 
a/tests/test-matlab-ts-mode-movement-files/movement_statements_expected.org 
b/tests/test-matlab-ts-mode-movement-files/movement_statements_expected.org
new file mode 100644
index 0000000000..95474c48d7
--- /dev/null
+++ b/tests/test-matlab-ts-mode-movement-files/movement_statements_expected.org
@@ -0,0 +1,235 @@
+#+startup: showall
+
+* Executing commands from movement_statements.m:3:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  124
+  Moved to point:  141
+  : 4:16: s = 'my string';
+  :                       ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  141
+  Moved to point:  125
+  : 4:0: s = 'my string';
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  125
+  Moved to point:  126
+  : 4:1: s = 'my string';
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  126
+  Moved to point:  125
+  : 4:0: s = 'my string';
+  :      ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  125
+  Moved to point:  141
+  : 4:16: s = 'my string';
+  :                       ^
+  No buffer modifications
+
+* Executing commands from movement_statements.m:6:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  245
+  Moved to point:  256
+  : 7:10: m = [1, 2;
+  :                 ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  256
+  Moved to point:  246
+  : 7:0: m = [1, 2;
+  :      ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  246
+  Moved to point:  247
+  : 7:1: m = [1, 2;
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  247
+  Moved to point:  246
+  : 7:0: m = [1, 2;
+  :      ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  246
+  Moved to point:  268
+  : 8:11:      3, 4];
+  :                  ^
+  No buffer modifications
+
+* Executing commands from movement_statements.m:10:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  372
+  Moved to point:  384
+  : 11:11: plot(1, 10)
+  :                   ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  384
+  Moved to point:  373
+  : 11:0: plot(1, 10)
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  373
+  Moved to point:  374
+  : 11:1: plot(1, 10)
+  :        ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  374
+  Moved to point:  373
+  : 11:0: plot(1, 10)
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  373
+  Moved to point:  384
+  : 11:11: plot(1, 10)
+  :                   ^
+  No buffer modifications
+
+* Executing commands from movement_statements.m:13:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  488
+  Moved to point:  505
+  : 14:16: sprintf("%s", s)
+  :                        ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  505
+  Moved to point:  489
+  : 14:0: sprintf("%s", s)
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  489
+  Moved to point:  490
+  : 14:1: sprintf("%s", s)
+  :        ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  490
+  Moved to point:  489
+  : 14:0: sprintf("%s", s)
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  489
+  Moved to point:  505
+  : 14:16: sprintf("%s", s)
+  :                        ^
+  No buffer modifications
+
+* Executing commands from movement_statements.m:16:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  609
+  Moved to point:  617
+  : 17:7: v=1:10;
+  :              ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  617
+  Moved to point:  610
+  : 17:0: v=1:10;
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  610
+  Moved to point:  611
+  : 17:1: v=1:10;
+  :        ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  611
+  Moved to point:  610
+  : 17:0: v=1:10;
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  610
+  Moved to point:  617
+  : 17:7: v=1:10;
+  :              ^
+  No buffer modifications
+
+* Executing commands from movement_statements.m:19:2:
+
+  (t-utils-xr "C-n" "C-a" "C-f" (matlab-ts-mode-beginning-of-command) 
(matlab-ts-mode-end-of-command))
+
+- Invoking      : "C-n" = next-line
+  Start point   :  721
+  Moved to point:  728
+  : 20:6: v(2:3)
+  :             ^
+  No buffer modifications
+
+- Invoking      : "C-a" = move-beginning-of-line
+  Start point   :  728
+  Moved to point:  722
+  : 20:0: v(2:3)
+  :       ^
+  No buffer modifications
+
+- Invoking      : "C-f" = forward-char
+  Start point   :  722
+  Moved to point:  723
+  : 20:1: v(2:3)
+  :        ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-beginning-of-command)
+  Start point   :  723
+  Moved to point:  722
+  : 20:0: v(2:3)
+  :       ^
+  No buffer modifications
+
+- Invoking      : (matlab-ts-mode-end-of-command)
+  Start point   :  722
+  Moved to point:  728
+  : 20:6: v(2:3)
+  :             ^
+  No buffer modifications
diff --git a/tests/test-matlab-ts-mode-movement.el 
b/tests/test-matlab-ts-mode-movement.el
new file mode 100644
index 0000000000..162d7553ab
--- /dev/null
+++ b/tests/test-matlab-ts-mode-movement.el
@@ -0,0 +1,69 @@
+;;; test-matlab-ts-mode-movement.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-movement-files/NAME.m comparing against
+;; ./test-matlab-ts-mode-movement-files/NAME_expected.org
+;;
+
+;;; Code:
+
+(require 't-utils)
+(require 'matlab-ts-mode)
+
+(defvar test-matlab-ts-mode-movement--file nil)
+
+(defun test-matlab-ts-mode-movement--file (m-file)
+  "Test an individual M-FILE.
+This is provided for debugging.
+  M-: (test-matlab-ts-mode-movement--file
+      \"test-matlab-ts-mode-movement-files/M-FILE\")"
+  (let ((test-matlab-ts-mode-movement--file m-file))
+    (ert-run-tests-interactively "test-matlab-ts-mode-movement")))
+
+(ert-deftest test-matlab-ts-mode-movement ()
+  "Test movement commands using ./test-matlab-ts-mode-movement-files/NAME.m.
+Using ./test-matlab-ts-mode-movement-files/NAME.m, compare
+movement of `matlab-mode-ts-beginning-of-command' and
+`matlab-mode-ts-end-of-command' against baseline:
+./test-matlab-ts-mode-movement-files/NAME_expected.org.
+This loops on all
+./test-matlab-ts-mode-movement-files/NAME.m files.
+
+To add a test, create
+  ./test-matlab-ts-mode-movement-files/NAME.m
+and run this function.  The baseline is saved for you as
+  ./test-matlab-ts-mode-movement-files/NAME_expected.org~
+after validating it, rename it to
+  ./test-matlab-ts-mode-movement-files/NAME_expected.org"
+
+  (let* ((test-name "test-matlab-ts-mode-movement")
+         (m-files (t-utils-get-files
+                   test-name
+                   (rx ".m" eos)
+                   nil
+                   test-matlab-ts-mode-movement--file)))
+    (t-utils-error-if-no-treesit-for 'matlab test-name)
+    (t-utils-test-xr test-name m-files)))
+
+(provide 'test-matlab-ts-mode-movement)
+;;; test-matlab-ts-mode-movement.el ends here


Reply via email to