Skip to content

Commit bb1166d

Browse files
committed
matlab-shell: handle case insensitive TAB completions
Some MATLAB completions are case insensitive. Consider: set_param(bdroot, 'simulationc<TAB> and this now completes to set_param(bdroot, 'SimulationCommand
1 parent 815d417 commit bb1166d

File tree

4 files changed

+134
-49
lines changed

4 files changed

+134
-49
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ clean:
107107
#
108108
# This requires that you define EMACS27, etc. in the environment or specify them when invoking make.
109109

110-
SUPPORTED_EMACS_VERSIONS = 27 28 29
110+
SUPPORTED_EMACS_VERSIONS = 27 28 29 30
111111

112112
define CHECK_EMACS_VER
113113
ifeq ($$(shell which $(EMACS${1})),)

matlab-shell.el

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,10 @@ is the common starting substring of each completion in completions."
14171417
(chomp-num-chars nil))
14181418
(while (> i 0)
14191419
(let ((part (substring completion 0 i)))
1420-
(if (string-suffix-p part last-cmd)
1420+
;; Do case insensitive comparison on the substring suffix. Consider
1421+
;; set_param(bdroot, 'simulationc<TAB>
1422+
;; which gives completions == '(("SimulationCommand"))
1423+
(if (string-suffix-p part last-cmd t)
14211424
(progn
14221425
(setq chomp-num-chars i)
14231426
(setq i 0))
@@ -1504,11 +1507,32 @@ No completions are provided anywhere else in the buffer."
15041507
(re-search-forward comint-prompt-regexp)
15051508
(setq common-substr-start-pt (+ (point) limit-pos))
15061509
(setq common-substr-end-pt (line-end-position))
1507-
(if (and (eq (length completions) 1)
1508-
(string-equal (buffer-substring-no-properties
1509-
common-substr-start-pt common-substr-end-pt)
1510-
(car (car completions))))
1511-
(setq completions nil))) ;; force display of "No completions"
1510+
1511+
;; Some MATLAB completions are case insensitive. Consider:
1512+
;; set_param(bdroot, 'simulationc<TAB>
1513+
;; We'll get completions == '(("SimulationCommand")) and the common-substr
1514+
;; ignoring case will be "simulationc" whereas the common substring in completions
1515+
;; is "SimulationC". In this case replace "simulationc" with "SimulationC" for the
1516+
;; completion engine and after TAB completion completes, we'll see
1517+
;; set_param(bdroot, 'SimulationCommand
1518+
(when (and (< common-substr-start-pt common-substr-end-pt)
1519+
(> (length completions) 0))
1520+
(let* ((common-substr-len (- common-substr-end-pt common-substr-start-pt))
1521+
(c-common-substr (substring (caar completions) 0 common-substr-len)))
1522+
1523+
(when (and (not (string-equal c-common-substr common-substr))
1524+
;; compare-strings case insensitive
1525+
(eq t (compare-strings c-common-substr 0 nil common-substr 0 nil t)))
1526+
(save-excursion
1527+
(delete-region common-substr-start-pt common-substr-end-pt)
1528+
(goto-char common-substr-start-pt)
1529+
(insert c-common-substr)))))
1530+
1531+
;; If completion is same as what we have, then it's not a completion
1532+
(when (and (eq (length completions) 1)
1533+
(string-equal common-substr (car (car completions))))
1534+
(setq completions nil)) ;; force display of "No completions"
1535+
)
15121536
;; Result
15131537
(list (cons 'last-cmd last-cmd)
15141538
(cons 'common-substr common-substr)
@@ -2694,4 +2718,4 @@ Argument FNAME specifies if we should echo the region to the command line."
26942718
;; LocalWords: BUILTINFLAG dired bol bobp numberp princ minibuffer fn matlabregex lastcmd notimeout
26952719
;; LocalWords: stacktop eltest testme localfcn LF fileref funcall ef ec basec sk nondirectory utils
26962720
;; LocalWords: ignoredups boundp edir sexp Fixup mapc emacsrun noshow cnt ellipsis newf bss noselect
2697-
;; LocalWords: fname mlx xemacs linux darwin truename clientcmd
2721+
;; LocalWords: fname mlx xemacs linux darwin truename clientcmd simulationc caar

tests/mstest-completions.el

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
;;; mstest-completions.el --- MATLAB Shell sections tests -*- lexical-binding: t; -*-
2+
3+
;; Copyright 2019-2025 Free Software Foundation, Inc.
4+
;; Author: John Ciolfi <[email protected]>, Eric Ludlam <zappo@ballista>
5+
6+
;; This program is free software; you can redistribute it and/or
7+
;; modify it under the terms of the GNU General Public License as
8+
;; published by the Free Software Foundation, either version 3 of the
9+
;; License, or (at your option) any later version.
10+
11+
;; This program is distributed in the hope that it will be useful, but
12+
;; WITHOUT ANY WARRANTY; without even the implied warranty of
13+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
;; General Public License for more details.
15+
16+
;; You should have received a copy of the GNU General Public License
17+
;; along with this program. If not, see https://www.gnu.org/licenses/.
18+
19+
;;; Commentary:
20+
;; Test matlab-shell completions
21+
22+
;;; Code:
23+
24+
(require 'matlab-shell)
25+
26+
(declare-function mstest-savestate "mstest.el")
27+
28+
(defun mstest-completion-test-point (test-point-desc cmd expected)
29+
"Run TEST-POINT-DESC tab completion on CMD and compare against EXPECTED.
30+
31+
*MATLAB* buffer should be current."
32+
(goto-char (point-max))
33+
(message "TEST: %s" test-point-desc)
34+
(let* ((CLO
35+
(condition-case ERR
36+
(matlab-shell-completion-list cmd)
37+
(error
38+
(mstest-savestate)
39+
(user-error "%S" ERR))))
40+
(CL (cdr (nth 2 CLO)))
41+
(cnt 1))
42+
(while (and CL expected)
43+
(when (not (string= (car expected) (car (car CL))))
44+
(mstest-savestate)
45+
(user-error "Expected %S /= %S TS for %d completion"
46+
(car expected) (car (car CL)) cnt))
47+
(setq cnt (1+ cnt)
48+
CL (cdr CL)
49+
expected (cdr expected))))
50+
(message "PASS: %s" test-point-desc))
51+
52+
(defun mstest-completion ()
53+
"Test emacsdocomplete and verifies result."
54+
(let ((msb (matlab-shell-active-p)))
55+
(when (not msb)
56+
(user-error "Test, mstest-completion, must run after mstest-start"))
57+
58+
(with-current-buffer msb
59+
(mstest-completion-test-point "mstest-completion: emacs<TAB>"
60+
"emacs"
61+
'("emacs"
62+
"emacscd"
63+
"emacsdocomplete"
64+
"emacsinit"
65+
"emacsnetshell"
66+
"emacsrun"
67+
"emacsrunregion"
68+
"emacsstripremote"
69+
"emacstipstring"))
70+
71+
;; When Simulink is installed, test completion where we need to change case of command in the
72+
;; matlab-shell buffer.
73+
(let* ((cmd "set_param('untitledTabTest', 'simulationcomma")
74+
(test-point-desc (format "mstest-completion: %s<TAB>" cmd)))
75+
(when (file-exists-p (concat (matlab-shell-matlabroot)
76+
"/toolbox/simulink/blocks/library/simulink.slx"))
77+
(message "TEST: %s" test-point-desc)
78+
(goto-char (point-max))
79+
(message "starting Simulink: new_system('untitledTabTest')")
80+
(matlab-shell-collect-command-output
81+
"new_system('untitledTabTest'); disp('new_sys')")
82+
(insert "set_param('untitledTabTest', 'simulationcomma")
83+
(goto-char (point-max))
84+
(matlab-shell-tab)
85+
(goto-char (point-max))
86+
(beginning-of-line)
87+
(when (not (looking-at "set_param('untitledTabTest', 'SimulationCommand"))
88+
(mstest-savestate)
89+
(user-error "FAILED %s" test-point-desc))
90+
(kill-line)
91+
(matlab-shell-collect-command-output
92+
"close_system('untitledTabTest', 0); disp('close_sys')")))
93+
))
94+
;; Indicate success. Useful when debugging.
95+
t)
96+
97+
(provide 'mstest-completions)
98+
;;; mstest-completions.el ends here (emacs-lisp-checkdoc)

tests/mstest.el

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
(require 'matlab-topic)
4242

4343
(add-to-list 'load-path ".")
44+
(require 'mstest-completions)
4445
(require 'mstest-sections)
4546

4647
;;; Code:
@@ -158,44 +159,6 @@
158159
(goto-char (point-max)))))
159160

160161
;;; Command Sending Tests
161-
(defun mstest-completion ()
162-
"Test emacsdocomplete and verifies result."
163-
(let ((msb (matlab-shell-active-p)))
164-
(when (not msb)
165-
(user-error "Test, mstest-completion, must run after mstest-start"))
166-
167-
(with-current-buffer msb
168-
(goto-char (point-max))
169-
170-
;; TEST completion fcn
171-
(message "COMPLETION TEST: emacs")
172-
(let* ((CLO
173-
(condition-case ERR
174-
(matlab-shell-completion-list "emacs")
175-
(error
176-
(mstest-savestate)
177-
(user-error "%S" ERR))))
178-
(CL (cdr (nth 2 CLO)))
179-
(EXP '("emacs"
180-
"emacscd"
181-
"emacsdocomplete"
182-
"emacsinit"
183-
"emacsnetshell"
184-
"emacsrun"
185-
"emacsrunregion"
186-
"emacsstripremote"
187-
"emacstipstring"))
188-
(cnt 1))
189-
(while (and CL EXP)
190-
(when (not (string= (car EXP) (car (car CL))))
191-
(user-error "Expected %S /= %S TS for %d completion"
192-
(car EXP) (car (car CL)) cnt))
193-
(setq cnt (1+ cnt)
194-
CL (cdr CL)
195-
EXP (cdr EXP))))
196-
(message "PASS")
197-
198-
)))
199162

200163
(defvar mstest-EVAL-TEST)
201164

@@ -452,7 +415,7 @@ If LINE is negative then do not test the line number."
452415
(mstest-savestate)
453416
(message "DEBUG: txt = %S" txt)
454417
(user-error "DEBUG: Stack buffer did not contain stack frame for %S, found [%s]"
455-
SK (buffer-substring (point-at-bol) (point-at-eol))))
418+
SK (buffer-substring (line-beginning-position) (line-end-position))))
456419
(forward-line 1)
457420
(setq cnt (1+ cnt)))
458421

@@ -479,7 +442,7 @@ If LINE is negative then do not test the line number."
479442
(mstest-savestate)
480443
(message "DEBUG: txt=%S" txt)
481444
(user-error "DEBUG: Breakpoints buffer did not contain breakpoint for %S, found [%s]"
482-
BP (buffer-substring (point-at-bol) (point-at-eol))))
445+
BP (buffer-substring (line-beginning-position) (line-end-position))))
483446
(forward-line 1)
484447
(setq cnt (1+ cnt)))
485448

@@ -640,7 +603,7 @@ set in the same order as specified."
640603

641604
(defun mstest-org-next-code-block-results (action)
642605
"Perform ACTION on the next #+RESULTS area of an Org code block.
643-
ACTION can be 'delete or 'get."
606+
ACTION can be \\='delete or \\='get."
644607
(save-excursion
645608
(let* ((results-begin (save-excursion
646609
(re-search-forward "^[ \t]*#\\+RESULTS:")

0 commit comments

Comments
 (0)