branch: externals/matlab-mode
commit 64e1805d36a362111ac3518a4fd7428c60483079
Author: John Ciolfi <john.ciolfi...@gmail.com>
Commit: John Ciolfi <john.ciolfi...@gmail.com>

    Fix and improve remote matlab-shell
    
    Prior to the commit for 
https://github.com/mathworks/Emacs-MATLAB-Mode/issues/26
    remote M-x matlab-shell partially worked. You could run M-x matlab-shell on
    a tramp remote location, but debugger, hyperlinks, etc. didn't work.
    
    This commit enables remote matlab-shell and enables remote debugging, 
hyperlinks, etc. The only item
    remaining that I'm aware of, as of this commit, is to get emacsclient 
tunneling though ssh so that
    ">> edit foo" works in the remote matlab-shell.
---
 README.org                    |   6 +-
 doc/remote-matlab-shell.org   |  64 +++++++++++++++
 matlab-shell.el               | 175 +++++++++++++++++++++++++++++++-----------
 tests/mstest.el               |  10 ++-
 toolbox/+emacs/@Stack/Stack.m |  18 +++--
 toolbox/+emacs/set.m          |  11 ++-
 toolbox/ebclear.m             |  12 +--
 toolbox/ebstack.m             |  27 ++++---
 toolbox/ebstatus.m            |   6 +-
 toolbox/ebstop.m              |  12 ++-
 toolbox/emacscd.m             |   6 +-
 toolbox/emacsinit.m           |   3 +-
 toolbox/emacsnetshell.m       |   5 +-
 toolbox/emacsrun.m            |   7 +-
 toolbox/emacsrunregion.m      |  14 ++--
 toolbox/emacsstripremote.m    |  41 ++++++++++
 toolbox/emacstipstring.m      |   9 ++-
 toolbox/help.m                |   8 +-
 toolbox/opentoline.m          |  17 ++--
 19 files changed, 349 insertions(+), 102 deletions(-)

diff --git a/README.org b/README.org
index 6edf015c8d..b76d9381bc 100644
--- a/README.org
+++ b/README.org
@@ -1,7 +1,7 @@
 #+startup: showall
 #+options: toc:nil
 
-# Copyright 2024 Free Software Foundation, Inc.
+# Copyright 2016-2025 Free Software Foundation, Inc.
 
 * Emacs MATLAB-mode
 
@@ -14,7 +14,11 @@
 
 2. *M-x matlab-shell* for running and debugging MATLAB within Emacs 
(Unix-only).
 
+   - MATLAB command window errors are hyper-linked and files open in Emacs
+   - Debugging support is available from the MATLAB menu.
    - matlab-shell uses company-mode for completions.
+   - You can use Emacs TRAMP and =M-x matlab-shell= to run remote MATLAB 
within your local Emacs
+     session, see 
[[file:doc/remote-matlab-shell.org][doc/remote-matlab-shell.org]].
 
 3. *Code sections* support. MATLAB script code files often contain many 
commands and lines of text.
    You typically focus your efforts on a single part of your code at a time, 
working with the code
diff --git a/doc/remote-matlab-shell.org b/doc/remote-matlab-shell.org
new file mode 100644
index 0000000000..ef90ef65a1
--- /dev/null
+++ b/doc/remote-matlab-shell.org
@@ -0,0 +1,64 @@
+# File: doc/remote-matlab-shell.org
+#
+#+startup: showall
+#+options: toc:nil
+#
+# Copyright 2016-2025 Free Software Foundation, Inc.
+
+* Remote M-x matlab-shell
+
+You can use Emacs TRAMP to run matlab-shell on a remote system.
+
+1. First verify you can connect to the remote system in a terminal
+
+   For example, we can use ssh to connect:
+
+   #+begin_src bash
+     ssh user@system pwd
+   #+end_src
+
+   You should configure your ssh keys to avoid prompting, which will make 
things smoother with
+   Emacs.
+
+2. In Emacs visit a remote location.
+
+   A typical method is to use File menu to visit a file or ~C-x C-f~
+
+   #+begin_example
+     C-x C-f
+     Find File: /ssh:USER@SYSTEM:~
+   #+end_example
+
+   will open dired-mode to the USER home (~) directory on the remote SYSTEM.
+
+3. Run matlab-shell
+
+   With the current buffer as a remote location, e.g. dired-mode buffer of a 
remote location, run:
+
+   #+begin_example
+     M-x matlab-shell
+   #+end_example
+
+   matlab-shell will copy files to the remote system and place them in 
=~/.emacs-matlab-shell/=. These are
+   needed for matlab-shell to work correctly on the remote system.
+
+   If you get a message that Emacs couldn't find matlab on the remote system, 
you need to tell Emacs
+   where matlab is located and there are several ways to do this, see
+   
[[https://www.gnu.org/software/emacs/manual/html_node/tramp/Remote-programs.html][How
 TRAMP finds and uses programs on remote host]]. For example, suppose your 
remote system is Linux and you are
+   using the Bash shell. You can setup your remote =/ssh:USER@HOST:~/.profile= 
to place the location of MATLAB on
+   your PATH:
+
+   #+begin_src bash
+     # ~/.profile
+     PATH=/usr/local/MATLAB/Ryyyyab/bin/$PATH     # Replace Ryyyyab with the 
MATLAB release you are using
+     export PATH
+   #+end_src
+
+   After that you can add to your local =~/.emacs=,
+
+   #+begin_src emacs-lisp
+     (eval-after-load
+         '(add-to-list 'tramp-remote-path 'tramp-own-remote-path))
+   #+end_src
+
+# LocalWords:  showall dired usr Ryyyyab
diff --git a/matlab-shell.el b/matlab-shell.el
index 29164a618e..91d303567f 100644
--- a/matlab-shell.el
+++ b/matlab-shell.el
@@ -330,22 +330,29 @@ otherwise an error is signaled."
          ;; Case: "matlab" (or something similar), locate it on the executable 
path
          ;;       else locate in standard install locations.
          (t
-          (setq abs-matlab-exe (executable-find matlab-shell-command))
-          (when (not abs-matlab-exe)
-            (if (string= matlab-shell-command "matlab")
-                ;; Get latest matlab command exe from the default installation 
location.
-                (let* ((default-loc (cdr (assoc system-type 
matlab-shell--default-command)))
-                       (default-matlab (when default-loc
-                                         (car (last (sort
-                                                     (file-expand-wildcards 
default-loc)
-                                                     #'string<))))))
-                  (when (not default-matlab)
-                    (matlab-shell--matlab-not-found no-error default-loc))
-                  (when (not (file-executable-p default-matlab))
-                    (user-error "%s is not executable" default-matlab))
-                  (setq abs-matlab-exe default-matlab))
-              ;; else unable to locate it
-              (matlab-shell--matlab-not-found no-error)))))
+          (let ((remote (file-remote-p default-directory)))
+            (if remote
+                (if (setq abs-matlab-exe (executable-find matlab-shell-command 
t))
+                    (setq abs-matlab-exe (concat remote abs-matlab-exe))
+                  (user-error "Unable to locate matlab executable on %s
+See https://github.com/mathworks/Emacs-MATLAB-Mode/doc/remote-matlab-emacs.org 
for tips" remote))
+            ;; else look local
+            (setq abs-matlab-exe (executable-find matlab-shell-command))
+            (when (not abs-matlab-exe)
+              (if (string= matlab-shell-command "matlab")
+                  ;; Get latest matlab command exe from the default 
installation location.
+                  (let* ((default-loc (cdr (assoc system-type 
matlab-shell--default-command)))
+                         (default-matlab (when default-loc
+                                           (car (last (sort
+                                                       (file-expand-wildcards 
default-loc)
+                                                       #'string<))))))
+                    (when (not default-matlab)
+                      (matlab-shell--matlab-not-found no-error default-loc))
+                    (when (not (file-executable-p default-matlab))
+                      (user-error "%s is not executable" default-matlab))
+                    (setq abs-matlab-exe default-matlab))
+                ;; else unable to locate it
+                (matlab-shell--matlab-not-found no-error)))))))
 
         ;; Return existing absolute path to the MATLAB command executable
         abs-matlab-exe)
@@ -596,9 +603,12 @@ Try C-h f matlab-shell RET"))
     (let* ((windowid (frame-parameter (selected-frame) 'outer-window-id))
            (newvar (concat "WINDOWID=" windowid))
            (process-environment (cons newvar process-environment))
-           (abs-matlab-exe (matlab-shell--abs-matlab-exe)))
+           (abs-matlab-exe (matlab-shell--abs-matlab-exe))
+           (matlab-exe (if (file-remote-p abs-matlab-exe)
+                           matlab-shell-command
+                         abs-matlab-exe)))
       (message "Running: %s" abs-matlab-exe)
-      (apply #'make-comint matlab-shell-buffer-name abs-matlab-exe
+      (apply #'make-comint matlab-shell-buffer-name matlab-exe
              nil matlab-shell-command-switches))
 
     ;; Enable GUD
@@ -1065,30 +1075,102 @@ system."
     (and mlfile (file-exists-p dir)))
   "Add the `matlab-shell' MATLAB toolbox to the MATLAB path on startup.")
 
-
-(defun matlab-shell-first-prompt-fcn ()
+(defun matlab--shell-toolbox-and-bin-sha1 (matlab-dir &optional recursive-call)
+  "Compute the SHA1 of the Emacs MATLAB-DIR toolbox and bin directories.
+RECURSIVE-CALL should be nil when called from top-level."
+  (let (sha1-all)
+    (when (not (directory-name-p matlab-dir))
+      (error "Directory, %s, does not end in a /" matlab-dir))
+    (when (not (file-directory-p matlab-dir))
+      (error "Directory, %s, does not exist" matlab-dir))
+    (setq matlab-dir (file-truename matlab-dir))
+    (let ((dirs (if recursive-call
+                    (list matlab-dir)
+                 (list (concat matlab-dir "toolbox/")
+                       (concat matlab-dir "bin/")))))
+      (dolist (dir dirs)
+        (when (not (file-directory-p dir))
+          (error "Directory, %s, does not exist" dir))
+        (dolist (file-name (sort (directory-files dir) #'string<))
+          (when (and (not (string-match "~$" file-name))
+                     (not (string-match "^\\(?:#\\|\\.\\)" file-name)))
+            ;; Not a: backup~, #backup, ".", ".., or .hidden file.
+            (let ((abs-file (concat dir file-name)))
+              (if (file-directory-p abs-file)
+                  (setq sha1-all
+                        (concat sha1-all
+                                (matlab--shell-toolbox-and-bin-sha1 (concat 
abs-file "/") t)))
+                ;; Plain file to add to sha1-all.
+                (with-temp-buffer
+                  (insert-file-contents-literally abs-file)
+                  (setq sha1-all (concat sha1-all (secure-hash 'sha1 
(current-buffer)))))))))))
+    (when (not recursive-call)
+      ;; sha1-all contains a long list of the individual hash's, reduce to one 
hash.
+      (when (not sha1-all)
+        (error "Directory, %s, contains no plain files" matlab-dir))
+      (setq sha1-all (secure-hash 'sha1 sha1-all)))
+    sha1-all))
+
+(defun matlab--shell-remote-toolbox-dir (local-toolbox-dir)
+  "Return matlab-emacs toolbox directory path on the remote system.
+This will be a copy the LOCAL-TOOLBOX-DIR toolbox and ../bin directories
+to the remote system.  This will copy files to the remote system if the
+remote directory is missing or out of date.  Returns:
+~/.emacs-matlab-mode/toolbox/"
+  (let* ((matlab-dir (file-name-as-directory
+                      (file-name-directory (directory-file-name 
local-toolbox-dir))))
+         (sha1 (matlab--shell-toolbox-and-bin-sha1 matlab-dir))
+         (local-dir-on-remote "~/.emacs-matlab-mode/")
+         (remote (if (file-remote-p default-directory) (file-remote-p 
default-directory)
+                   (error "%s is not remote" default-directory)))
+         (remote-dir (concat remote local-dir-on-remote))
+         (remote-sha1-file (concat remote-dir ".sha1.txt")))
+    (when (or (not (file-exists-p remote-sha1-file))
+              (not (string= sha1 (with-temp-buffer
+                                   (insert-file-contents-literally 
remote-sha1-file)
+                                   (buffer-substring (point-min) 
(point-max))))))
+      (delete-directory remote-dir t)
+      (copy-directory local-toolbox-dir remote-dir t t)
+      (copy-directory (concat local-toolbox-dir "../bin/") remote-dir t t)
+      ;; Save SHA1. This is used to avoid future copies when remote is up to 
date.
+      (write-region sha1 nil remote-sha1-file))
+    ;; result
+    (concat local-dir-on-remote "toolbox/")))
+
+(cl-defun matlab-shell-first-prompt-fcn ()
   "Hook run when the first prompt is seen.
 Sends commands to the MATLAB shell to initialize the MATLAB process."
   ;; Don't do this again
   (remove-hook 'matlab-shell-prompt-appears-hook 
#'matlab-shell-first-prompt-fcn)
 
-  ;; Init this session of MATLAB.
-  (if matlab-shell-use-emacs-toolbox
-      ;; Use our local toolbox directory.
-      (let* ((path (expand-file-name "toolbox" (file-name-directory
-                                                (locate-library "matlab"))))
-             (initcmd (expand-file-name "emacsinit" path))
-             (nsa (if matlab-shell-autostart-netshell "emacs.set('netshell', 
true);" ""))
-             (ecc (matlab-shell--get-emacsclient-command))
-             (ecca (if ecc (format "emacs.set('clientcmd', '%s');" ecc) ""))
-             (args (list nsa ecca))
-             (cmd (format "run('%s');%s" initcmd (apply #'concat args))))
-        (matlab-shell-send-command (string-replace (expand-file-name "~/") 
"~/" cmd))
-        )
-
+  (when (not matlab-shell-use-emacs-toolbox)
     ;; Setup is misconfigured - we need emacsinit because it tells us how to 
debug
     (error "Unable to initialize matlab, emacsinit.m and other files missing"))
 
+  ;; Run emacsinit.m which sets up the MATLAB environment to include the 
matlab-mode
+  ;; "toolbox".  This is used for items like debugging, e.g. ebstop.m.
+  ;; Also setup emacsclient such that ">> edit file" works.
+  (let* ((local-toolbox-dir (expand-file-name "toolbox/"
+                                              (file-name-directory 
(locate-library "matlab"))))
+         (toolbox-dir (if (file-remote-p default-directory)
+                          ;; Case: Remote matlab-shell via tramp
+                          (matlab--shell-remote-toolbox-dir local-toolbox-dir)
+                        local-toolbox-dir))
+         (emacs-init (concat toolbox-dir "emacsinit"))
+         (e-client-command (matlab-shell--get-emacsclient-command))
+         (remote-location (file-remote-p default-directory))
+         (e-set-args (replace-regexp-in-string
+                      "^, " "" ;; strip leading ", "
+                      (concat (when matlab-shell-autostart-netshell ", 
'netshell', true")
+                              (when e-client-command (format ", 'clientcmd', 
'%s'"
+                                                             e-client-command))
+                              (when remote-location (format ", 
'remoteLocation', '%s'"
+                                                            
remote-location)))))
+         (cmd (format "run('%s');%s" emacs-init (if e-set-args
+                                                    (format " emacs.set(%s);" 
e-set-args)
+                                                  ""))))
+    (matlab-shell-send-command (string-replace (expand-file-name "~/") "~/" 
cmd)))
+
   ;; Init any user commands
   (if matlab-custom-startup-command
       ;; Wait for next prompt, then send.
@@ -2039,21 +2121,26 @@ a file name, or nil if no conversion done.")
 ;; (matlab-shell-mref-to-filename "eltest.utils.testme>localfcn")
 
 (defun matlab-shell-mref-to-filename (fileref)
-  "Convert the MATLAB file reference FILEREF into an actual file name.
+  "Convert MATLAB file reference FILEREF into an file Emacs can load.
 MATLAB can refer to functions on the path by a short name, or by a .p
 extension, and a host of different ways.  Convert this reference into
-something Emacs can load."
+something Emacs can load.  If matlab-shell is running remote via tramp,
+returned file will be prefixed with the remote location."
   (interactive "sFileref: ")
   (with-current-buffer (matlab-shell-active-p)
-    (let ((C matlab-shell-mref-converters)
-          (ans nil))
+    (let ((remote-location (file-remote-p default-directory))
+          (C matlab-shell-mref-converters)
+          ans)
       (while (and C (not ans))
         (let ((tmp (funcall (car C) fileref)))
-          (when (and tmp (file-exists-p tmp))
-            (setq ans tmp))
-          )
+          (when tmp
+            (when (and remote-location (not (file-remote-p tmp)))
+              (setq tmp (concat remote-location tmp)))
+            (when (file-exists-p tmp)
+              (setq ans tmp))))
         (setq C (cdr C)))
-      (when (called-interactively-p 'any) (message "Found: %S" ans))
+      (when (called-interactively-p 'any)
+        (message "Found: %S" ans))
       ans)))
 
 (defun matlab-find-other-window-file-line-column (ef el ec &optional debug)
@@ -2601,10 +2688,10 @@ Argument FNAME specifies if we should echo the region 
to the command line."
 ;; LocalWords:  keymap subjob kbd emacscd featurep fboundp EDU msbn pc Thx 
Chappaz windowid tcp lang
 ;; LocalWords:  postoutput capturetext EMACSCAP captext STARTCAP progn eol 
dbhot erroexamples cdr
 ;; LocalWords:  ENDPT dolist overlaystack mref deref errortext ERRORTXT 
shellerror Emacsen iq nt buf
-;; LocalWords:  auth mlfile emacsinit initcmd nsa ecc ecca clientcmd EMAACSCAP 
buffname showbuff
+;; LocalWords:  auth mlfile EMAACSCAP buffname showbuff symlink'd emacsinit 
sha dirs ebstop
 ;; LocalWords:  evalforms Histed pmark memq promptend numchars integerp 
emacsdocomplete mycmd ba
 ;; LocalWords:  nreverse emacsdocompletion byteswap stringp cbuff mapcar bw 
FCN's alist substr usr
 ;; LocalWords:  BUILTINFLAG dired bol bobp numberp princ minibuffer fn 
matlabregex lastcmd notimeout
 ;; LocalWords:  stacktop eltest testme localfcn LF fileref funcall ef ec basec 
sk nondirectory utils
 ;; LocalWords:  ignoredups boundp edir sexp Fixup mapc emacsrun noshow cnt 
ellipsis newf bss noselect
-;; LocalWords:  fname mlx xemacs linux darwin truename
+;; LocalWords:  fname mlx xemacs linux darwin truename clientcmd
diff --git a/tests/mstest.el b/tests/mstest.el
index d31dc318a9..2de2416d8c 100644
--- a/tests/mstest.el
+++ b/tests/mstest.el
@@ -174,7 +174,15 @@
                  (mstest-savestate)
                  (user-error "%S" ERR))))
              (CL (cdr (nth 2 CLO)))
-             (EXP '("emacs" "emacscd" "emacsdocomplete" "emacsinit" 
"emacsnetshell" "emacsrun" "emacsrunregion" "emacstipstring"))
+             (EXP '("emacs"
+                    "emacscd"
+                    "emacsdocomplete"
+                    "emacsinit"
+                    "emacsnetshell"
+                    "emacsrun"
+                    "emacsrunregion"
+                    "emacsstripremote"
+                    "emacstipstring"))
              (cnt 1))
         (while (and CL EXP)
           (when (not (string= (car EXP) (car (car CL))))
diff --git a/toolbox/+emacs/@Stack/Stack.m b/toolbox/+emacs/@Stack/Stack.m
index 7f9b89dbc3..85b5ffbbed 100644
--- a/toolbox/+emacs/@Stack/Stack.m
+++ b/toolbox/+emacs/@Stack/Stack.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,6 +12,7 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 classdef Stack < handle
 % Class STACK - Manage Emacs' stack state.
 
@@ -150,10 +151,15 @@ function str=stackFrames(ST)
 end
 
 function nf = fixFile(filename)
-% Fix FILENAME so it has no escape chars, that way we can send to Emacs.
-   
-    nf = regexprep(filename,"\", "/");
-    
+% FIXFILE - Cleanup file for Emacs
+%
+% Prefix FILENAME with the TRAMP remote location if matlab-shell is running 
remotely. This is needed
+% to enable debugging, e.g. ebstack, etc.
+%
+% Replace Windows path separators with POSIX separators such that they do not 
look like escape
+% characters, that way we can send to Emacs.
+
+    nf = [getenv('EMACS_MATLAB_SHELL_REMOTE'), regexprep(filename, "\", "/")];
 end
 
 function thesame = stackEqual(stack1, stack2)
@@ -173,3 +179,5 @@ function thesame = stackEqual(stack1, stack2)
    end
    
 end
+
+% LocalWords:  Netshell ebstack dbhotlink progn mlg EMACSCAP newstack newframe 
gud FIXFILE
diff --git a/toolbox/+emacs/set.m b/toolbox/+emacs/set.m
index d809f3e64b..f41a40b83a 100644
--- a/toolbox/+emacs/set.m
+++ b/toolbox/+emacs/set.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,17 +12,22 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function set(varargin)
-% Setup an Emacs option based on Name/Value pairs.
+% emacs.set: setup an Emacs option based on Name/Value pairs.
+%
 % Valid options include:
 %
 %  netshell - Initialize a netshell connection.
 %  clientcmd - What to use for `edit' client command
+%  followstack - Used by Emacs Server
+%  remoteLocation - Use by remote matlab-shell, see doc/remote-matlab-shell.org
 
     P = inputParser;
     addParameter(P, 'netshell', 0, @isnumeric)
     addParameter(P, 'clientcmd', "", @ischar)
     addParameter(P, 'followstack', -1, @isnumeric)
+    addParameter(P, 'remoteLocation', "", @ischar)
 
     parse(P, varargin{:});
 
@@ -30,6 +35,8 @@ function set(varargin)
     netshellport = P.Results.netshell;
     followstack = P.Results.followstack;
 
+    setenv('EMACS_MATLAB_SHELL_REMOTE', P.Results.remoteLocation);
+
     %% Client Command
     if ~isempty(clientcommand)
         if usejava('jvm')
diff --git a/toolbox/ebclear.m b/toolbox/ebclear.m
index 0e7e6f4d32..9841249f25 100644
--- a/toolbox/ebclear.m
+++ b/toolbox/ebclear.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,14 +12,16 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function ebclear(varargin)
 % Emacs version of dbstop.  Tells emacs which breakpoints are active.
-    
-    dbclear(varargin{:});
-    
+
+    args = emacsstripremote(varargin);
+
+    dbclear(args);
+
     % Send emacs some breakpoints
     bp = getappdata(groot, 'EmacsBreakpoints');
     bp.updateEmacs;
 
 end
-
diff --git a/toolbox/ebstack.m b/toolbox/ebstack.m
index cfa9af5af8..10d15de410 100644
--- a/toolbox/ebstack.m
+++ b/toolbox/ebstack.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,18 +12,23 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function ebstack(FRAMEIDX)
-% Emacs version of dbstack.  Updates Emacs for where we are in the stack.
-    
+% EBSTACK - Emacs version of dbstack.
+%
+% Updates Emacs for where we are in the stack.
+
     [ST, I] = dbstack('-completenames');
-    
-   % Send emacs our updated stack
-   es = getappdata(groot, 'EmacsStack');
 
-   if nargin == 1
-       I = FRAMEIDX;
-   end
-       
-   es.updateEmacs(ST, I);
+    % Send emacs our updated stack
+    es = getappdata(groot, 'EmacsStack');
+
+    if nargin == 1
+        I = FRAMEIDX;
+    end
+
+    es.updateEmacs(ST, I);
 
 end
+
+% LocalWords:  completenames
diff --git a/toolbox/ebstatus.m b/toolbox/ebstatus.m
index b886380bbb..ea48e6df41 100644
--- a/toolbox/ebstatus.m
+++ b/toolbox/ebstatus.m
@@ -1,4 +1,5 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,8 +13,9 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function ebstatus
-% Send emacs some breakpoints
+%EBSTATUS - Send emacs some breakpoints
 
    bp = getappdata(groot, 'EmacsBreakpoints');
    bp.updateEmacs(true);
diff --git a/toolbox/ebstop.m b/toolbox/ebstop.m
index 6c17d6e94d..dce2c59e1e 100644
--- a/toolbox/ebstop.m
+++ b/toolbox/ebstop.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,14 +12,18 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function ebstop(varargin)
-% Emacs version of dbstop.  Tells emacs which breakpoints are active.
+% EBSTOP - Emacs version of dbstop.
+
+% Tells Emacs which breakpoints are active.
+
+    args = emacsstripremote(varargin);
     
-    dbstop(varargin{:});
+    dbstop(args{:});
     
     % Send emacs some breakpoints
     bp = getappdata(groot, 'EmacsBreakpoints');
     bp.updateEmacs;
-
 end
 
diff --git a/toolbox/emacscd.m b/toolbox/emacscd.m
index 0bfadf5620..28c0cb7f9d 100644
--- a/toolbox/emacscd.m
+++ b/toolbox/emacscd.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,8 +12,10 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function emacscd(dir)
-% CD to DIRECTORY in a way that Emacs Shell won't see via dirtrack.
+% EMACSCD - CD to DIRECTORY in a way that Emacs Shell won't see via dirtrack.
+%
 % Instead, show example of how to tell Emacs what the new directory is.
     
     if nargin == 1
diff --git a/toolbox/emacsinit.m b/toolbox/emacsinit.m
index 165c1b792f..aaceb630f6 100644
--- a/toolbox/emacsinit.m
+++ b/toolbox/emacsinit.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,6 +12,7 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function emacsinit()
 % EMACSINIT Initialize the current MATLAB session for matlab-shell-mode
 %
diff --git a/toolbox/emacsnetshell.m b/toolbox/emacsnetshell.m
index 9b8f93c3ab..98b0a602c9 100644
--- a/toolbox/emacsnetshell.m
+++ b/toolbox/emacsnetshell.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,8 +12,9 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function nso = emacsnetshell(cmd, data)
-% Create a connection to an EMACS editor server.
+% EMACSNETSHELL - Create a connection to an EMACS editor server.
 %
 % emacsnetshell('init') - Initialize the connection with Emacs.
 %    emacs will send commands to MATLAB with additional connectivity
diff --git a/toolbox/emacsrun.m b/toolbox/emacsrun.m
index 1a9f8025d2..30fd81b22b 100644
--- a/toolbox/emacsrun.m
+++ b/toolbox/emacsrun.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,12 +12,15 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function emacsrun(mfile, varargin)
-% Run code from MFILE.
+% EMACSRUN - Run code from MFILE.
+%
 % Assumes MFILE was recently edited, and proactively clears that function.
 %
 % Command sent by Emacs for save-and-go functionality
 
+    mfile = emacsstripremote(mfile)
     % Now figure out if shortFileName is on the path.
     [ fullFilePath, shortFileName ] = fileparts(mfile);
     onpath = ~isempty(which(shortFileName));
diff --git a/toolbox/emacsrunregion.m b/toolbox/emacsrunregion.m
index 3ec9e87b03..cce7e2d1ee 100644
--- a/toolbox/emacsrunregion.m
+++ b/toolbox/emacsrunregion.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,17 +12,15 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function emacsrunregion(file, startchar, endchar)
-% Run code from FILE between STARTCHAR and ENDCHAR.
+% EMACSRUNREGION - Run code from FILE between STARTCHAR and ENDCHAR.
+%
 % Command sent by Emacs for run code sections and run-region functionality.
 
-    % Filter out emacs tramp file path prefix
-    trampMatch = regexp(file, {'/*:',':/'});
-    if (~isempty(trampMatch{1}))
-        file = file((trampMatch{2}+1):end);
-    end
+    file = emacsstripremote(file);
 
-    if ~exist(file,'file')
+    if ~exist(file, 'file')
         error('You must save your region into a file accessible by MATLAB 
process.');
     end
 
diff --git a/toolbox/emacsstripremote.m b/toolbox/emacsstripremote.m
new file mode 100644
index 0000000000..2554f3be8a
--- /dev/null
+++ b/toolbox/emacsstripremote.m
@@ -0,0 +1,41 @@
+% 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 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+function out = emacsstripremote(in)
+% EMACSSTRIPREMOTE - strip Emacs TRAMP remote file path prefix
+%
+% IN can be either a string or cell array. If it's a cell array it's assumed to
+% be arguments to one of the MATLAB debugger commands.
+%
+% OUT will be IN updated if needed.
+%
+% TRAMP remote file syntax:
+%   /method:host:/path/to/file
+%
+% Examples:
+%   /ssh:user@host:~/project/file.m       => ~/project/file.m
+%   /ssh:user@host:/work/project/file.m   => /work/file.m
+%   /ssh:user@host:C:/work/file.m         => C:/work/file.m
+
+    re = '^/[^:]+:[^:]+:';
+    if iscell(in)
+        out = in;
+        if length(out) >= 2 && strcmp(out{1},'in')
+            out{2} = regexprep(in{2}, '^/[^:]+:[^:]+:', '', 'once');
+        end
+    else
+        out = regexprep(in, '^/[^:]+:[^:]+:', '', 'once');
+    end
+end
diff --git a/toolbox/emacstipstring.m b/toolbox/emacstipstring.m
index 754cee1af9..af795f1a2c 100644
--- a/toolbox/emacstipstring.m
+++ b/toolbox/emacstipstring.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,11 +12,12 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function emacstipstring(expr)
-% Take EXPR, and convert into a tooltip friendly string.
+% EMACSSTRIPSTRING - Take EXPR, and convert into a tooltip friendly string.
+%
 % This utility is mean to be used by emacs to create text to display
 % whie debugging code.
-    
+
     disp(expr)
 end
-
diff --git a/toolbox/help.m b/toolbox/help.m
index 55932bc6d1..eca0e64a45 100644
--- a/toolbox/help.m
+++ b/toolbox/help.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,8 +12,10 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function [out, docTopic] = help(varargin)
-% Provide help, augmented so Emacs picks it up to display in a special buffer.
+% HELP- Provide help, augmented so Emacs picks it up to display in a special 
buffer.
+%
 % See the help for the built-in help command by asking for "help help" in
 % MATLAB, which will redirect to the correct location.
 
@@ -92,3 +94,5 @@ function [out, docTopic] = help(varargin)
         [out, docTopic] = help(args{:});
     end
 end
+
+% LocalWords:  completenames EMACSCAP
diff --git a/toolbox/opentoline.m b/toolbox/opentoline.m
index 0f0017b84e..5622596e57 100644
--- a/toolbox/opentoline.m
+++ b/toolbox/opentoline.m
@@ -1,4 +1,4 @@
-% Copyright (C) 2024  Eric Ludlam (and others)
+% Copyright 2019-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
@@ -12,26 +12,29 @@
 
 % You should have received a copy of the GNU General Public License
 % along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 function opentoline(file, line, column)
-%OPENTOLINE Open to specified line in function file in Emacs.
+% OPENTOLINE Open to specified line in function file in Emacs.
+%
 %   This is a hack to override the built-in opentoline program in MATLAB.
 %
 %   Remove this M file from your path to get the old behavior.
 
+    file = emacsstripremote(file)
     editor = system_dependent('getpref', 'EditorOtherEditor');
     editor = editor(2:end);
-    
+
     if nargin==3
         linecol = sprintf('+%d:%d',line,column);
     else
         linecol = sprintf('+%d',line);
     end
-    
+
     f = which(file);
     if ~isempty(f)
         file=f;
-    end    
-    
+    end
+
     if ispc
         % On Windows, we need to wrap the editor command in double quotes
         % in case it contains spaces
@@ -42,3 +45,5 @@ function opentoline(file, line, column)
         system([editor ' "' linecol '" "' file '" &']);
     end
 end
+
+% LocalWords:  linecol


Reply via email to