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