matlab-shell-gud.el:

(gud-matlab-marker-filter): Remove old hotlink url output scanning.
Replace with evaluating forms sent from MATLAB.
When exiting debug mode, also find and close the stack window.
(mlg-set-stack-frame-via-gud): New, called from MATLAB.
(mlg-stack-quit): New for stack mode.  Replaces 'delete-window which
sometimes fails.
(matlab-shell-gud-minor-mode-map): Add 'v' binding to view stack frame
window.
(easy-menu-define): Add "Show Stack" menu item to show stack window.

toolbox/+emacs/@Stack/Stack.m:
Add `StackPending' and `FramePending' flags for when that part changes.
(captureStack): New method - uses what was in updateEmacs for computing
the part of the stack we will use.
(updateEmacs): Remove chunk, put in above.
Always post the request to show, as this is used from ebstack, and it should
always do something.
(updateForHotLinks): New method, called from dbhotlinks.
Sends over stack only if needed.
(stackEqual): Rename output arg to better indicate what it is.

toolbox/dbhotlink.m:
Remove most of the content.
Replace with use of EmacsStack object, using updateForHotLinks method.
This commit is contained in:
Eric Ludlam 2019-12-14 18:58:10 -05:00
parent 3330f03f3d
commit aef2037284
3 changed files with 134 additions and 50 deletions

View file

@ -187,19 +187,6 @@ FILE is ignored, and ARGS is returned."
;; DEBUG PROMPTS
(when (string-match gud-matlab-marker-regexp-K>> gud-marker-acc)
;; Look for any frames for case of a debug prompt.
(let ((url gud-marker-acc)
ef el)
;; We use dbhotlinks to create the below syntax. If we see it we have a frame,
;; and should tell gud to go there.
(when (string-match "opentoline('\\([^']+\\)',\\([0-9]+\\),\\([0-9]+\\))" url)
(setq ef (substring url (match-beginning 1) (match-end 1))
el (substring url (match-beginning 2) (match-end 2)))
(setq frame (cons ef (string-to-number el)))))
;; Newer MATLAB's don't print useful info. We'll have to
;; search backward for the previous line to see if a frame was
;; displayed.
@ -232,7 +219,28 @@ FILE is ignored, and ARGS is returned."
;; (when matlab-shell-io-testing (message "!!xx [%s]" (substring gud-marker-acc 0 endprompt)))
;; We're done with the text! Remove it from the accumulator.
;; We're done with the text!
;; Capture the text that describes the new stack frame.
(save-match-data
(let* ((expr-end (match-beginning 0))
(m1 (string-match "dbhotlink()%%%\n" gud-marker-acc))
(expr-start (match-end 0))
(expression (substring gud-marker-acc expr-start expr-end)))
(when (> (length expression) 0)
(condition-case ERR
(let ((forms (read expression)))
(when forms
;;(message "About to evaluate forms: \"%S\"" forms)
(eval forms)))
(error
(message "Failed to evaluate dbhotlink expression: \"%s\"" expression)
(message "Error is: %S" ERR)
)
))
))
;;Remove it from the accumulator.
(setq gud-marker-acc (substring gud-marker-acc endprompt))
;; If we got all this at the same time, push output back onto the accumulator for
;; the next code bit to push it out.
@ -276,9 +284,18 @@ FILE is ignored, and ARGS is returned."
(if (and (string-match (concat gud-matlab-marker-regexp->> "\\s-*$") output)
gud-last-last-frame)
(progn
;; Clean up gud stuff.
(setq overlay-arrow-position nil
gud-last-last-frame nil
gud-overlay-arrow-position nil)
;; If stack is showing, clean it up.
(let* ((buff (mlg-set-stack nil))
(win (get-buffer-window buff)))
(when win
(select-window win)
(mlg-stack-quit)
))
;; Refresh stuff
(sit-for 0)
))
@ -352,6 +369,7 @@ LONGESTNAME specifies the how long the longest name we can expect is."
mlg-stack))
(setq mlg-stack (nreverse mlg-stack))
(mlg-refresh-stack-buffer)
;;(message "Updated Stack")
)
(defun mlg-set-stack-frame (newframe)
@ -361,6 +379,17 @@ LONGESTNAME specifies the how long the longest name we can expect is."
(mlg-show-frame newframe)
)
(defun mlg-set-stack-frame-via-gud (newframe)
"Specify a NEWFRAME provided by MATLAB we should visit."
(setq mlg-frame newframe)
(let ((file (oref (nth (1- newframe) mlg-stack) file))
(line (oref (nth (1- newframe) mlg-stack) line)))
(if (< line 0) (setq line (- line)))
(setq gud-last-frame (cons file line))
;;(message "Gud FRAME set to %S" gud-last-frame)
)
)
(defun mlg-show-frame (&optional frame)
"Setup windows to show FRAME from the current stack frame."
(let ((newframe (or frame mlg-frame)))
@ -431,7 +460,7 @@ LONGESTNAME specifies the how long the longest name we can expect is."
(defvar mlg-stack-mode-map
(let ((km (make-sparse-keymap)))
(define-key km [return] 'mlg-stack-choose)
(define-key km "q" 'delete-window)
(define-key km "q" 'mlg-stack-quit)
(define-key km "n" 'mlg-stack-next)
(define-key km "p" 'mlg-stack-prev)
(define-key km [mouse-2] 'mlg-stack-click)
@ -453,6 +482,13 @@ Commands:
(setq buffer-read-only t)
)
(defun mlg-stack-quit ()
"Quit the MATLAB stack view."
(interactive)
(if (= (length (window-list)) 1)
(bury-buffer)
(delete-window (selected-window))))
(defun mlg-stack-next ()
"Visit stack on next line."
(interactive)
@ -682,6 +718,7 @@ Call debug activate/deactivate features."
(define-key km "d" 'gud-down)
(define-key km "<" 'gud-up)
(define-key km ">" 'gud-down)
(define-key km "v" 'mlg-show-stack)
(define-key km "p" 'matlab-shell-gud-show-symbol-value)
;; (define-key km "p" gud-print)
@ -717,8 +754,11 @@ Call debug activate/deactivate features."
["dbcont" gud-cont
:active (matlab-shell-active-p)
:help "When MATLAB debugger is active, run to next break point or finish"]
["Show Stack" mlg-show-stack
:active (matlab-any-shell-active-p)
:help "When MATLAB debugger is active, show value of the symbol under point."]
["Show symbol value" matlab-shell-gud-show-symbol-value
:active (matlab-shell-active-p)
:active (matlab-any-shell-active-p)
:help "When MATLAB debugger is active, show value of the symbol under point."]
["dbquit" gud-finish
:active (matlab-shell-active-p)

View file

@ -4,9 +4,12 @@ classdef Stack < handle
properties
% Stack last sent to Emacs
EmacsStack = [];
EmacsFrame = [];
EmacsFrame = 1;
% Netshell object if we should direct that way instead.
NetShellObject = [];
NetShellObject = [];
% Flag - do we need to tell Emacs what changed?
StackPending = false;
FramePending = false;
end
methods
@ -17,10 +20,10 @@ classdef Stack < handle
es.resetEmacs();
end
function updateEmacs(es, newstack, newframe)
% Update Emacs' view of the stack
if ~isempty(newstack) && strcmp(newstack(1).name, 'ebstack')
function captureStack(es, newstack, newframe)
if ~isempty(newstack) && ...
( strcmp(newstack(1).name, 'ebstack') ||...
strcmp(newstack(1).name, 'dbhotlink') )
newstack = newstack(2:end);
end
@ -40,26 +43,76 @@ classdef Stack < handle
if ~stackEqual(es.EmacsStack, newstack)
es.EmacsStack = newstack;
es.StackPending = true;
end
str = [ '(progn ' newline ...
stackFrames(newstack) ...
newline ...
' (mlg-set-stack-frame ' num2str(newframe) ')' ...
')'];
if newframe ~= es.EmacsFrame
es.EmacsFrame = newframe;
es.FramePending = true;
end
end
function updateEmacs(es, newstack, newframe)
% Update Emacs' view of the stack
if isempty(es.NetShellObject)
disp('<EMACSCAP>(eval)');
disp(str);
disp('</EMACSCAP>')
else
es.NetShellObject.SendEval(str);
end
es.captureStack(newstack, newframe);
str = [ '(progn ' newline ];
if es.StackPending
str = [ str ...
stackFrames(es.EmacsStack) ...
newline ];
end
str = [ str ...
' (mlg-set-stack-frame ' num2str(es.EmacsFrame) ')' ];
str = [ str ')'];
es.StackPending = false;
es.FramePending = false;
if isempty(es.NetShellObject)
disp('<EMACSCAP>(eval)');
disp(str);
disp('</EMACSCAP>')
else
es.NetShellObject.SendEval(str);
end
end
function resetEmacs(es)
end
function updateForHotLinks(es, newstack, newframe)
es.captureStack(newstack, newframe);
% updateEmacs(es, newstack, newframe);
str = [ '(progn ' newline ];
if es.StackPending
str = [ str ...
stackFrames(es.EmacsStack) ...
newline ];
end
str = [ str ...
' (mlg-set-stack-frame-via-gud ' num2str(es.EmacsFrame) ')' ];
str = [ str ')'];
es.StackPending = false;
es.FramePending = false;
if isempty(es.NetShellObject)
disp(str);
else
es.NetShellObject.SendEval(str);
end
end
end
end
@ -83,18 +136,18 @@ function nf = fixFile(filename)
end
function changed = stackEqual(stack1, stack2)
changed = true;
function thesame = stackEqual(stack1, stack2)
thesame = true;
if length(stack1) ~= length(stack2)
changed=false;
thesame=false;
return;
end
for i=1:length(stack1)
if ~strcmp(stack1(i).name, stack2(i).name) || ...
stack1(i).line ~= stack2(i).line
changed = false;
thesame = false;
return
end
end

View file

@ -1,4 +1,4 @@
function dbhotlink(L)
function dbhotlink()
% Display text that EMACS can interpret as a hotlink
% so the debugger can auto move to the right spot.
% Input L is the stack frame to specify.
@ -6,15 +6,6 @@ function dbhotlink(L)
[ST, I] = dbstack('-completenames');
if nargin == 0
L = I;
LINESTR = '';
else
LINESTR = num2str(L);
end
if L+1 <=numel(ST)
fprintf('<a href="matlab: opentoline(''%s'',%i,1)">%i</a>\n', ST(L+1).file, ...
ST(L+1).line, LINESTR);
end
es = getappdata(groot, 'EmacsStack');
es.updateForHotLinks(ST, I);
end