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

    matlab-shell: improve matlab-shell-command handling
    
    matlab-shell.el:
     - When matlab-shell-command is set to "matlab" we now
       1. Look for matlab on the system path.
       2. If not on the system path, we look for matlab in the default
          install locations
     - If unable to find the matlab command executable a more informative
       error is now produced. A *matlab-shell-help* window will appear
       giving options to provide the location of matlab.
    
    mlint.el:
     - In mlint-reset-program, set program to nil if we do not have matlabroot
    
    DEBUGGING.org:
     - fixed typo
    
    See: https://github.com/mathworks/Emacs-MATLAB-Mode/issues/26
---
 DEBUGGING.org   |   6 +--
 matlab-shell.el | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 mlint.el        |  37 ++++++++--------
 3 files changed, 136 insertions(+), 40 deletions(-)

diff --git a/DEBUGGING.org b/DEBUGGING.org
index 1f6162f201..53f2aeded7 100644
--- a/DEBUGGING.org
+++ b/DEBUGGING.org
@@ -8,13 +8,13 @@
 To isolate problems, it's often good to run stock Emacs with matlab-mode:
 
   #+begin_src bash
-    # In /path/to/Emacs-MATLAB-mode
+    # In /path/to/Emacs-MATLAB-Mode
     make clean
     make lisp    # Build only. If you want to build and run tests remove the 
lisp target
 
     # From anywhere
     echo "% empty" > startup.m
-    M=/path/to/Emacs-MATLAB-mode
+    M=/path/to/Emacs-MATLAB-Mode
     env MATLABPATH=  emacs -Q -L $M -l $M/matlab-autoload.el
 
     # If you want to run a specific version of MATLAB, 
/matlab-install/bin/matlab:
@@ -29,7 +29,7 @@ variable. This ensures you are running MATLAB with an 
unaltered environment. Pro
 matlab-shell can occur when either MATLABPATH or startup.m is altering the 
behavior of
 MATLAB. Therefore, if you see problems, try running with an unaltered 
environment.
 
-If you open *.m you will be using the matlab-mode from 
/path/to/Emacs-MATLAB-mode.
+If you open *.m you will be using the matlab-mode from 
/path/to/Emacs-MATLAB-Mode.
 
 To run MATLAB in Emacs:
 
diff --git a/matlab-shell.el b/matlab-shell.el
index ecd507b883..89432b4dc7 100644
--- a/matlab-shell.el
+++ b/matlab-shell.el
@@ -58,8 +58,22 @@
   "List of functions to call on entry to MATLAB shell mode."
   :type 'hook)
 
+(defvar matlab-shell--default-command
+  '((gnu/linux  . "/usr/local/MATLAB/R*/bin/matlab")
+    (darwin     . "/Applications/MATLAB_R*.app/bin/matlab")
+    (windows-nt . "C:/Program Files/MATLAB/R*/bin/matlab.exe"))
+  "Standard MATLAB command installation locations, SYSTEM => GLOB.")
+
 (defcustom matlab-shell-command "matlab"
-  "The name of the command to be run which will start the MATLAB process."
+  "The MATLAB command executable used to start MATLAB.
+This can be:
+ - the name of the MATLAB command (e.g. \"matlab\") which is
+   found on the system PATH.
+ - an absolute path to the matlab executable.  For example,
+   \"/<path-to-MATLAB-install-dir>/bin/matlab\"
+If matlab-shell-command is set to \"matlab\" and \"matlab\" is not
+on the system PATH, `matlab-shell' will look for the matlab
+command executable in the default MATLAB installation locations."
   :type 'string)
 
 (defcustom matlab-shell-command-switches '("-nodesktop")
@@ -253,22 +267,101 @@ mode.")
           matlab-really-gaudy-font-lock-keywords)
   "Keyword symbol used for really gaudy font-lock for MATLAB shell.")
 
-;;; ROOT
+(defun matlab-shell--matlab-not-found (no-help-window &optional default-loc)
+  "Signal error, MATLAB command not on system PATH or in optional DEFAULT-LOC.
+If NO-HELP-WINDOW is t, do not show the help window"
+  (let ((msg (format "Unable to locate \"%s\" on the system PATH%s"
+                     matlab-shell-command
+                     (if default-loc
+                         (format " or in the default installation location, %s"
+                                 default-loc)
+                       ""))))
+    (when (not no-help-window)
+      (let ((help-buf-name "*matlab-shell-help*"))
+        (with-current-buffer (get-buffer-create help-buf-name)
+          (with-help-window help-buf-name
+            (insert msg "
+
+To fix, you update your system PATH to include
+  \"/<path-to-MATLAB-install>/bin\"
+To verify matlab is on your path, run \"matlab -h\" in a terminal.
+
+Alternatively, you can provide the full path to the
+MATLAB command executable by customizing option
+`matlab-shell-command'\n")))))
+
+    (user-error "%s" msg)))
+
+(cl-defun matlab-shell--abs-matlab-exe (&optional no-error)
+  "Absolute path to the MATLAB command executable.
+When `matlab-shell-command' is an absolute path, then this will
+be resolved to its true name.  Otherwise, `matlab-shell-command'
+is found using `executable-find'.  If `matlab-shell-command' is
+\"matlab\" and not the system PATH, this will return the latest
+MATLAB installed command found using
+`matlab-shell--default-command'.
+
+If NO-ERROR is t, and matlab command is not found, nil is return,
+otherwise an error is signaled."
+  (condition-case err
+      (let (abs-matlab-exe)
+        (cond
+
+         ;;Case: the path to the matlab executable was provided, validate it 
exists and
+         ;;      return the true path.
+         ((file-name-absolute-p matlab-shell-command)
+          (when (not (file-exists-p matlab-shell-command))
+            (user-error "Invalid setting for `matlab-shell-command', %s does 
not exist"
+                        matlab-shell-command))
+          (when (not (file-executable-p matlab-shell-command))
+            (user-error "Invalid setting for `matlab-shell-command', %s is not 
executable"
+                        matlab-shell-command))
+          (setq abs-matlab-exe (file-truename matlab-shell-command)))
+
+         ;; Case: set to a relative path
+         ;;
+         ((when (file-name-directory matlab-shell-command)
+            (user-error "Relative paths are not supported for 
`matlab-shell-command', %s"
+                        matlab-shell-command)))
+
+         ;; 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)))))
+
+        ;; Return existing absolute path to the MATLAB command executable
+        abs-matlab-exe)
+    (error (when (not no-error) (error "%s" (error-message-string err))))))
+
+;;; ROOT: matlabroot
 ;;
 ;;;###autoload
 (defun matlab-mode-determine-matlabroot ()
-  "Return the MATLABROOT for the `matlab-shell-command'."
-  (let ((path (file-name-directory matlab-shell-command)))
-    ;; if we don't have a path, find the MATLAB executable on our path.
-    (unless path
-      (setq path  (matlab-find-executable-directory matlab-shell-command)))
-    (when path
-      ;; When we find the path, we need to massage it to identify where
-      ;; the M files are that we need for our completion lists.
-      (if (string-match "/bin/?$" path)
-          (setq path (substring path 0 (match-beginning 0)))))
-    path))
-
+  "Return the MATLABROOT for the `matlab-shell-command'.
+The MATLABROOT does not have a trailing slash.
+Returns nil if unable to determine the MATLABROOT."
+  ;; strip "/bin/matlab" from /path/to/matlabroot/bin/matlab
+  (let ((abs-matlab-exe (matlab-shell--abs-matlab-exe 'no-error)))
+    (when abs-matlab-exe
+      (let ((bin-dir (directory-file-name (file-name-directory 
abs-matlab-exe))))
+        ;; matlabroot no slash
+        (directory-file-name (file-name-directory bin-dir))))))
+    
 
 ;;; Keymaps & Menus
 ;;
@@ -278,7 +371,7 @@ mode.")
     (set-keymap-parent km comint-mode-map)
 
     ;; We can jump to errors, so take over this keybinding.
-    ;; FIXME: Shoulw we set `next-error-function' instead?
+    ;; FIXME: Should we set `next-error-function' instead?
     ;;        https://github.com/mathworks/Emacs-MATLAB-Mode/issues/23
     (substitute-key-definition #'next-error #'matlab-shell-last-error
                                km global-map)
@@ -499,8 +592,10 @@ Try C-h f matlab-shell RET"))
     ;; Thx David Chappaz for reminding me about this patch.
     (let* ((windowid (frame-parameter (selected-frame) 'outer-window-id))
            (newvar (concat "WINDOWID=" windowid))
-           (process-environment (cons newvar process-environment)))
-      (apply #'make-comint matlab-shell-buffer-name matlab-shell-command
+           (process-environment (cons newvar process-environment))
+           (abs-matlab-exe (matlab-shell--abs-matlab-exe)))
+      (message "Running: %s" abs-matlab-exe)
+      (apply #'make-comint matlab-shell-buffer-name abs-matlab-exe
              nil matlab-shell-command-switches))
 
     ;; Enable GUD
@@ -2502,11 +2597,11 @@ Argument FNAME specifies if we should echo the region 
to the command line."
 ;; LocalWords:  emacsclient commandline emacsrunregion errorscanning cco 
defconst defun setq Keymaps
 ;; 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
+;; 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:  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
+;; LocalWords:  fname mlx xemacs linux darwin truename
diff --git a/mlint.el b/mlint.el
index ac2b1d94e8..2bfe25a366 100644
--- a/mlint.el
+++ b/mlint.el
@@ -1,6 +1,6 @@
 ;;; mlint.el --- run mlint in a MATLAB buffer -*- lexical-binding: t -*-
 
-;; Copyright (C) 2024 Free Software Foundation, Inc.
+;; Copyright (C) 2002-2025 Free Software Foundation, Inc.
 
 ;; Author: Eric M. Ludlam <elud...@mathworks.com>
 ;; Maintainer: Eric M. Ludlam <elud...@mathworks.com>
@@ -129,23 +129,24 @@ This value can be automatically set by 
\\=`mlint-programs\\='.")
 (defun mlint-reset-program ()
   "Reset `mlint-program'."
   (setq mlint-program
-        (let* ((root (matlab-mode-determine-matlabroot))
-               (bin (expand-file-name "bin" root))
-               (mlp mlint-programs)
-               (ans nil))
-          (while (and mlp (not ans))
-            (cond ((null (car mlp))
-                   nil)
-                  ((file-executable-p (car mlp))
-                   (setq ans (car mlp)))
-                  ((executable-find (car mlp))
-                   (setq ans (executable-find (car mlp))))
-                  ;; Use the matlabroot found by matlab-shell
-                  ((file-executable-p (expand-file-name (car mlp) bin))
-                   (setq ans (expand-file-name (car mlp) bin)))
-                  (t nil))
-            (setq mlp (cdr mlp)))
-          ans)))
+        (let ((root (matlab-mode-determine-matlabroot)))
+          (when root
+            (let ((bin (expand-file-name "bin" root))
+                  (mlp mlint-programs)
+                  (ans nil))
+              (while (and mlp (not ans))
+                (cond ((null (car mlp))
+                       nil)
+                      ((file-executable-p (car mlp))
+                       (setq ans (car mlp)))
+                      ((executable-find (car mlp))
+                       (setq ans (executable-find (car mlp))))
+                      ;; Use the matlabroot found by matlab-shell
+                      ((file-executable-p (expand-file-name (car mlp) bin))
+                       (setq ans (expand-file-name (car mlp) bin)))
+                      (t nil))
+                (setq mlp (cdr mlp)))
+              ans)))))
 
 (defcustom mlint-programs (list
                            "mlint"

Reply via email to