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

    matlab-shell: hyperlink syntax error
    
    Given foo.m:
    
      %
      function = 1
    
    we get:
    
      >> foo
      File: /home/ciolfi/tmp/foo.m Line: 2 Column: 10
      Incorrect use of '=' operator. Assign a value to a variable using '=' and 
compare values for equality using '=='.
    
    which is now hperlinked.
    
    Also added ert based test for the hyperlink logic
    
    See issue #53
---
 matlab-shell.el                                    | 55 +++++++------
 tests/errexamples.shell.m                          | 67 ----------------
 .../matlab-error-patterns.org                      | 86 +++++++++++++++++++++
 .../matlab-error-patterns_expected.txt             | 11 +++
 tests/test-matlab-shell-scan-for-error.el          | 90 ++++++++++++++++++++++
 5 files changed, 220 insertions(+), 89 deletions(-)

diff --git a/matlab-shell.el b/matlab-shell.el
index 202885b5ec..132c164033 100644
--- a/matlab-shell.el
+++ b/matlab-shell.el
@@ -725,9 +725,17 @@ Argument STR is the text for the anchor."
 
 ;;; ERROR HANDLING
 ;;
+;;  See: tests/test-matlab-shell-scan-for-error.el
+;;  Error patterns in tests/test-matlab-shell-scan-for-error-files/*.org
+
+(defvar matlab-shell--file-line-column-re
+  (rx bol
+      "File: " (group (1+ (not (or "\n" "\r"))) ".m") " "
+      "Line: " (group (1+ digit)) " "
+      "Column: " (group (1+ digit))
+      eol)
+  "Match error lines of form \"File: /path/to/f.m Line: M Column N\".")
 
-;; The regular expression covers to forms in tests/erroexamples.shell.m
-;;
 (defvar matlab-shell-error-anchor-expression
   (concat "^>?\\s-*\\(\\(Error \\(in\\|using\\)\\s-+\\|Syntax error in 
\\)\\(?:==> \\)?\\|"
           "In\\s-+\\(?:workspace belonging 
to\\s-+\\)?\\|Error:\\s-+File:\\s-+\\|Warning:\\s-+[^\n]+\n\\)")
@@ -766,33 +774,36 @@ Each expression should have the following match strings:
       (pulse-momentary-highlight-region (car ans) (car (cdr ans))))
     (message "Found: %S" ans)))
 
-
 (defun matlab-shell-scan-for-error (limit)
   "Scan backward for a MATLAB error in the current buffer until LIMIT.
 Uses `matlab-shell-error-anchor-expression' to find the error.
 Uses `matlab-shell-error-location-expression' to find where the error is.
 Returns a list of the form:
-  ( STARTPT ENDPT FILE LINE COLUMN )"
+  ( STARTPT ENDPT FILE LINE COLUMN )
+Point is let at error match when a match is found."
   (with-syntax-table matlab-shell-errorscanning-syntax-table
     (let ((ans nil)
           (beginning nil))
-      (when (re-search-backward matlab-shell-error-anchor-expression
-                                limit
-                                t)
-        (save-excursion
-          (setq beginning (save-excursion (goto-char (match-beginning 0))
-                                          (back-to-indentation)
-                                          (point)))
-          (goto-char (match-end 0))
-          (dolist (EXP matlab-shell-error-location-expression)
-            (when (looking-at EXP)
-              (setq ans (list beginning
-                              (match-end 0)
-                              (match-string-no-properties 1)
-                              (match-string-no-properties 2)
-                              (match-string-no-properties 3)
-                              )))))
-        )
+      (if (re-search-backward matlab-shell-error-anchor-expression limit t)
+          (save-excursion
+            (setq beginning (save-excursion (goto-char (match-beginning 0))
+                                            (back-to-indentation)
+                                            (point)))
+            (goto-char (match-end 0))
+            (dolist (EXP matlab-shell-error-location-expression)
+              (when (looking-at EXP)
+                (setq ans (list beginning
+                                (match-end 0)
+                                (match-string-no-properties 1)
+                                (match-string-no-properties 2)
+                                (match-string-no-properties 3)
+                                )))))
+        (when (re-search-backward matlab-shell--file-line-column-re limit t)
+          (setq ans (list (match-beginning 0)
+                          (match-end 0)
+                          (match-string-no-properties 1)
+                          (match-string-no-properties 2)
+                          (match-string-no-properties 3)))))
       ans)))
 
 (defvar matlab-shell-last-anchor-as-frame nil
@@ -2495,7 +2506,7 @@ 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 EMAACSCAP buffname showbuff symlink'd emacsinit 
sha dirs ebstop
+;; LocalWords:  auth mlfile EMAACSCAP buffname showbuff symlink'd emacsinit 
sha dirs ebstop subr
 ;; 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:  dired bol bobp numberp princ minibuffer fn matlabregex lastcmd 
notimeout
diff --git a/tests/errexamples.shell.m b/tests/errexamples.shell.m
deleted file mode 100644
index e7859b3640..0000000000
--- a/tests/errexamples.shell.m
+++ /dev/null
@@ -1,67 +0,0 @@
-% -*- matlab -*-
-
-% Copyright (C) 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
-% 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/>.
-
-% Newer MATLABs.  This text copied from MATLAB R2019b
-
-% Errors:
-Error using ls (line 49)
-ls: cannot access 'blarg': No such file or directory
-
-
-Error in buggy (line 12)
-        ls blarg
-
-% Errors:
-Error using buggy (line 7)
-You encounered an error in buggy.m
-
-% Syntax
-Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 8 
Column: 12
-Invalid expression. When calling a function or indexing a variable, use 
parentheses. Otherwise, check for mismatched delimiters.
-
-% Syntax
-Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 4 
Column: 9
-Character vector is not terminated properly.
-    
-
-% Warning:
-Warning: You enountered a warning in buggy.m 
-> In buggy (line 15) 
-
-
-% Oldest MATLABs.  This text taken from comments in MATLAB.el from
-% a long time ago.
-
-% Errors:
->> ls
-Error in ==> buggy
-On line 12 ==> ls blarg
-
-% Errors:
-
-Error using ==> buggy at 7
-
-% Syntax:
-
-Syntax error in ==> syntaxerr.m
-On line 8 ==> B = A(1
-
-% Warning: 
-In buggy at line 15 You encountered a warning
-
-
-% End
diff --git 
a/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns.org 
b/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns.org
new file mode 100644
index 0000000000..8af9b2d993
--- /dev/null
+++ b/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns.org
@@ -0,0 +1,86 @@
+* Syntax error, R2026b
+
+Given
+
+#+begin_src matlab
+  %
+  function = 1
+#+end_src
+
+We get:
+
+#+begin_example
+>> foo
+File: /home/ciolfi/tmp/foo.m Line: 2 Column: 10
+Incorrect use of '=' operator. Assign a value to a variable using '=' and 
compare values for equality using '=='.
+#+end_example
+
+* Error using command
+
+#+begin_example
+Error using ls (line 49)
+ls: cannot access 'blarg': No such file or directory
+#+end_example
+
+* Error in function
+
+#+begin_example
+Error in buggy (line 12)
+        ls blarg
+#+end_example
+
+* Error in function with more text
+
+#+begin_example
+Error using buggy (line 7)
+You encounered an error in buggy.m
+#+end_example
+
+* Syntax error example 1
+
+#+begin_example
+Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 8 
Column: 12
+Invalid expression. When calling a function or indexing a variable, use 
parentheses. Otherwise, check for mismatched delimiters.
+#+end_example
+
+* Syntax error example 2
+
+#+begin_example
+Error: File: /home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m Line: 4 
Column: 9
+Character vector is not terminated properly.
+#+end_example
+    
+* Warning
+
+#+begin_example
+Warning: You enountered a warning in buggy.m 
+> In buggy (line 15) 
+#+end_example
+
+* Old MATLAB style error example 1
+
+#+begin_example
+>> ls
+Error in ==> buggy
+On line 12 ==> ls blarg
+#+end_example
+
+* Old MATLAB style error example 2
+
+#+begin_example
+Error using ==> buggy at 7
+#+end_example
+
+
+* Old MATLAB syntax error
+
+#+begin_example
+Syntax error in ==> syntaxerr.m
+On line 8 ==> B = A(1
+#+end_example
+
+* Old MATLAB warning
+
+#+begin_example
+In buggy at line 15 You encountered a warning
+#+end_example
diff --git 
a/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns_expected.txt
 
b/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns_expected.txt
new file mode 100644
index 0000000000..f0033ae1ee
--- /dev/null
+++ 
b/tests/test-matlab-shell-scan-for-error-files/matlab-error-patterns_expected.txt
@@ -0,0 +1,11 @@
+matlab-error-patterns.org:14: (113 160 "/home/ciolfi/tmp/foo.m" "2" "10")
+matlab-error-patterns.org:21: (329 353 "ls" "49" nil)
+matlab-error-patterns.org:28: (459 483 "buggy" "12" nil)
+matlab-error-patterns.org:35: (568 594 "buggy" "7" nil)
+matlab-error-patterns.org:42: (687 772 
"/home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m" "8" "12")
+matlab-error-patterns.org:49: (959 1043 
"/home/eludlam/WORK/matlab-emacs-src/tests/syntaxerr.m" "4" "9")
+matlab-error-patterns.org:57: (1181 1201 "buggy" "15" nil)
+matlab-error-patterns.org:64: (1276 1306 "buggy" "12" nil)
+matlab-error-patterns.org:71: (1386 1412 "buggy" "7" nil)
+matlab-error-patterns.org:78: (1472 1514 "syntaxerr.m" "8" nil)
+matlab-error-patterns.org:85: (1579 1599 "buggy" "15" nil)
diff --git a/tests/test-matlab-shell-scan-for-error.el 
b/tests/test-matlab-shell-scan-for-error.el
new file mode 100644
index 0000000000..c5d47dc072
--- /dev/null
+++ b/tests/test-matlab-shell-scan-for-error.el
@@ -0,0 +1,90 @@
+;;; test-matlab-shell-scan-for-error.el --- -*- lexical-binding: t -*-
+
+;; Copyright (C) 2026 Free Software Foundation, Inc.
+;;
+;; This file 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 file 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 file.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;; Test `matlab-shell-scan-for-error'
+
+;;; Code:
+
+(require 't-utils)
+(require 'matlab-shell)
+
+(ert-deftest test-matlab-shell-scan-for-error ()
+  "Test `matlab-shell-scan-for-error'.
+Each ./test-matlab-shell-scan-for-error-files/NAME.org representing
+MATLAB shell output is scanned for errors and the results are compared
+against ./test-matlab-shell-scan-for-error-files/NAME_expected.txt.
+
+The format of NAME.org is
+  * Error patter description
+
+  #+begin_example
+  <Output in matlab-shell starting at column 0>
+  #=end_example
+
+To add a test, create
+  ./test-matlab-shell-scan-for-error-files/NAME.org
+and run this function.  The baseline is saved for you as
+  ./test-matlab-shell-scan-for-error-files/NAME_expected.txt~
+after validating them, rename them to
+  ./test-matlab-shell-scan-for-error-files/NAME_expected.txt"
+
+  (let* ((test-name "test-matlab-shell-scan-for-error")
+         (org-files (t-utils-get-files test-name
+                                       :base-regexp (rx ".org" eos)))
+         (error-msgs '()))
+    (dolist (org-file org-files)
+      (let ((start-time (current-time))
+            (got-matches '()) ;; Entries of form (linenum . 
ans-from--matlab-shell-scan-for-error)
+            (expected-file (replace-regexp-in-string "\\.[^.]+\\'" 
"_expected.txt" org-file))
+            limit)
+        (with-temp-buffer
+          (insert-file-contents-literally org-file)
+          (goto-char (point-max))
+          (re-search-backward "#\\+end_example")
+          (while (setq limit (save-excursion (when (re-search-backward 
"#\\+begin_example" nil t)
+                                               (point))))
+            (let ((ans (matlab-shell-scan-for-error limit)))
+              (when (not ans)
+                (error "%s:%d: error: failed to find error pattern in example 
block"
+                       org-file (line-number-at-pos limit)))
+              (push `(,(line-number-at-pos (car ans)) . ,ans) got-matches))
+            (goto-char limit)))
+
+        (let ((got (let ((org-file-base (file-name-nondirectory org-file))
+                         (s ""))
+                     (dolist (got-match got-matches)
+                       (setq s (concat s (format "%s:%d: %S\n"
+                                                 org-file-base (car got-match) 
(cdr got-match)))))
+                     s)))
+          (let* ((expected (when (file-exists-p expected-file)
+                             (with-temp-buffer
+                               (insert-file-contents-literally expected-file)
+                               (buffer-string))))
+                 (error-msg (t-utils--baseline-check test-name start-time
+                                                     org-file got (concat 
expected-file "~")
+                                                     expected expected-file)))
+            (when error-msg
+              (push error-msg error-msgs))))))
+
+    (setq error-msgs (reverse error-msgs))
+    (should (equal error-msgs '()))))
+
+(provide 'test-matlab-shell-scan-for-error)
+;;; test-matlab-shell-scan-for-error.el ends here
+
+;; LocalWords:  utils dolist setq

Reply via email to