2019-11-29 00:14:25 +01:00
|
|
|
|
;;; matlab-shell-gud.el --- GUD support in matlab-shell.
|
|
|
|
|
;;
|
|
|
|
|
;; Copyright (C) 2019 Eric Ludlam
|
|
|
|
|
;;
|
|
|
|
|
;; Author: Eric Ludlam <eludlam@emacsvm>
|
|
|
|
|
;;
|
|
|
|
|
;; 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 https://www.gnu.org/licenses/.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;
|
|
|
|
|
;; GUD (grand unified debugger) support for MATLAB shell.
|
|
|
|
|
;;
|
|
|
|
|
;; Includes setting up gud mode in the shell, and all filters, etc specific
|
|
|
|
|
;; to supporting gud.
|
|
|
|
|
|
|
|
|
|
(require 'matlab-shell)
|
|
|
|
|
|
|
|
|
|
(eval-and-compile
|
|
|
|
|
(require 'gud)
|
2019-12-08 01:41:49 +01:00
|
|
|
|
(require 'eieio)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
2019-12-01 02:15:45 +01:00
|
|
|
|
(defcustom matlab-shell-debug-tooltips-p nil
|
|
|
|
|
"*Enable tooltips displaying data values when at the K>> prompt.
|
|
|
|
|
Disable this option if the tooltips are too slow in your setup."
|
|
|
|
|
:group 'matlab-shell
|
2019-12-13 04:22:09 +01:00
|
|
|
|
:type 'boolean)
|
2019-12-01 02:15:45 +01:00
|
|
|
|
|
2019-11-29 17:21:56 +01:00
|
|
|
|
(defvar gud-matlab-debug-active nil
|
|
|
|
|
"Non-nil if MATLAB has a K>> prompt up.")
|
|
|
|
|
(defvar gud-matlab-debug-activate-hook nil
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Hooks run when MATLAB detects a K>> prompt after a >> prompt.")
|
2019-11-29 17:21:56 +01:00
|
|
|
|
(defvar gud-matlab-debug-deactivate-hook nil
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Hooks run when MATLAB detects a >> prompt after a K>> prompt.")
|
2019-11-29 17:21:56 +01:00
|
|
|
|
|
2019-11-29 15:55:42 +01:00
|
|
|
|
(defvar gud-matlab-tool-bar-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(dolist (x '((gud-break . "gud/break")
|
|
|
|
|
(gud-remove . "gud/remove")
|
|
|
|
|
(gud-cont . "gud/cont")
|
|
|
|
|
(gud-next . "gud/next")
|
|
|
|
|
(gud-step . "gud/step")
|
|
|
|
|
(gud-stop-subjob . "gud/stop")
|
|
|
|
|
(gud-finish . "gud/finish")
|
|
|
|
|
(gud-up . "gud/up")
|
|
|
|
|
(gud-down . "gud/down"))
|
|
|
|
|
map)
|
|
|
|
|
(tool-bar-local-item-from-menu
|
|
|
|
|
(car x) (cdr x) map gud-minor-mode-map))))
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
2019-12-08 18:33:37 +01:00
|
|
|
|
(declare-function matlab-netshell-eval "matlab-netshell" (mode))
|
|
|
|
|
|
|
|
|
|
(defmacro matlab-at-fcn (cmd)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Define CMD to be a GUD command that works w/ shell or netshell."
|
2019-12-08 18:33:37 +01:00
|
|
|
|
;; Note `arg' comes from gud-def declaration
|
|
|
|
|
`(if (matlab-shell-active-p)
|
|
|
|
|
(gud-call ,cmd arg)
|
|
|
|
|
(if (matlab-netshell-active-p)
|
|
|
|
|
(matlab-netshell-eval (gud-format-command ,cmd arg))
|
2019-12-13 04:22:09 +01:00
|
|
|
|
(error "No MATLAB shell active"))))
|
2019-12-08 18:33:37 +01:00
|
|
|
|
|
2019-11-29 17:21:56 +01:00
|
|
|
|
(defmacro matlab-gud-fcn (cmd)
|
2019-12-08 18:33:37 +01:00
|
|
|
|
"Define CMD forms to be sent to a MATLAB shell."
|
2019-11-29 17:21:56 +01:00
|
|
|
|
;; Note `arg' comes from gud-def declaration
|
|
|
|
|
`(if gud-matlab-debug-active
|
2019-12-08 18:33:37 +01:00
|
|
|
|
(matlab-at-fcn ,cmd)
|
2019-11-29 17:21:56 +01:00
|
|
|
|
(error "MATLAB debugging not active")))
|
|
|
|
|
|
2019-11-29 00:14:25 +01:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun matlab-shell-mode-gud-enable-bindings ()
|
|
|
|
|
"Enable GUD features for `matlab-shell' in the current buffer."
|
|
|
|
|
|
|
|
|
|
;; Make sure this is safe to use gud to debug MATLAB
|
|
|
|
|
(when (not (fboundp 'gud-def))
|
2019-12-13 04:22:09 +01:00
|
|
|
|
(error "Your Emacs is missing `gud-def' which means matlab-shell won't work correctly. Stopping"))
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
2019-12-08 18:33:37 +01:00
|
|
|
|
(gud-def gud-break (matlab-at-fcn "ebstop in %d%f at %l") "\C-b" "Set breakpoint at current line.")
|
|
|
|
|
(gud-def gud-remove (matlab-at-fcn "ebclear in %d%f at %l") "\C-d" "Remove breakpoint at current line.")
|
2019-11-29 17:21:56 +01:00
|
|
|
|
(gud-def gud-step (matlab-gud-fcn "dbstep in") "\C-s" "Step one source line, possibly into a function.")
|
|
|
|
|
(gud-def gud-next (matlab-gud-fcn "dbstep %p") "\C-n" "Step over one source line.")
|
|
|
|
|
(gud-def gud-cont (matlab-gud-fcn "dbcont") "\C-r" "Continue with display.")
|
|
|
|
|
(gud-def gud-stop-subjob (matlab-gud-fcn "dbquit") nil "Quit debugging.") ;; gud toolbar stop
|
|
|
|
|
(gud-def gud-finish (matlab-gud-fcn "dbquit") "\C-f" "Finish executing current function.")
|
|
|
|
|
(gud-def gud-up (matlab-gud-fcn "dbup") "<" "Up N stack frames (numeric arg).")
|
|
|
|
|
(gud-def gud-down (matlab-gud-fcn "dbdown") ">" "Down N stack frames (numeric arg).")
|
2019-11-29 00:14:25 +01:00
|
|
|
|
;; using (gud-def gud-print "%e" "\C-p" "Eval expression at point") fails
|
2019-12-07 19:17:57 +01:00
|
|
|
|
;; (gud-def gud-print "% gud-print not available" "\C-p" "gud-print not available.")
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
|
|
|
|
(if (fboundp 'gud-make-debug-menu)
|
|
|
|
|
(gud-make-debug-menu))
|
2019-11-29 15:55:42 +01:00
|
|
|
|
|
|
|
|
|
(when (boundp 'tool-bar-map) ; not --without-x
|
|
|
|
|
(kill-local-variable 'tool-bar-map))
|
2019-11-29 00:14:25 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun matlab-shell-gud-startup ()
|
|
|
|
|
"Configure GUD when a new `matlab-shell' is initialized."
|
|
|
|
|
(gud-mode)
|
|
|
|
|
|
2019-11-29 17:21:56 +01:00
|
|
|
|
;; type of gud mode
|
|
|
|
|
(setq gud-minor-mode 'matlab)
|
|
|
|
|
|
2019-11-29 05:31:00 +01:00
|
|
|
|
;; This starts us supporting gud tooltips.
|
|
|
|
|
(add-to-list 'gud-tooltip-modes 'matlab-mode)
|
|
|
|
|
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(make-local-variable 'gud-marker-filter)
|
|
|
|
|
(setq gud-marker-filter 'gud-matlab-marker-filter)
|
|
|
|
|
(make-local-variable 'gud-find-file)
|
|
|
|
|
(setq gud-find-file 'gud-matlab-find-file)
|
|
|
|
|
|
|
|
|
|
;; XEmacs doesn't seem to have this concept already. Oh well.
|
|
|
|
|
(make-local-variable 'gud-marker-acc)
|
|
|
|
|
(setq gud-marker-acc nil)
|
|
|
|
|
|
2019-11-29 02:31:30 +01:00
|
|
|
|
;; Setup our debug tracker.
|
|
|
|
|
(add-hook 'matlab-shell-prompt-appears-hook #'gud-matlab-debug-tracker)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
|
|
|
|
(gud-set-buffer))
|
|
|
|
|
|
|
|
|
|
;;; GUD Functions
|
|
|
|
|
(defun gud-matlab-massage-args (file args)
|
|
|
|
|
"Argument message for starting matlab file.
|
|
|
|
|
I don't think I have to do anything, but I'm not sure.
|
|
|
|
|
FILE is ignored, and ARGS is returned."
|
|
|
|
|
args)
|
|
|
|
|
|
|
|
|
|
(defun gud-matlab-find-file (f)
|
|
|
|
|
"Find file F when debugging frames in MATLAB."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let* ((realfname (if (string-match "\\.\\(p\\)$" f)
|
|
|
|
|
(progn
|
|
|
|
|
(aset f (match-beginning 1) ?m)
|
|
|
|
|
f)
|
|
|
|
|
f))
|
2019-12-12 23:22:53 +01:00
|
|
|
|
(buf (find-file-noselect realfname t)))
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(set-buffer buf)
|
|
|
|
|
(if (fboundp 'gud-make-debug-menu)
|
|
|
|
|
(gud-make-debug-menu))
|
|
|
|
|
buf)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; GUD Filter Function
|
|
|
|
|
;;
|
|
|
|
|
;; MATLAB's process filter handles output from the MATLAB process and
|
|
|
|
|
;; interprets it for formatting text, and for running the debugger.
|
|
|
|
|
|
|
|
|
|
(defvar gud-matlab-marker-regexp-plain-prompt "^K?>>"
|
|
|
|
|
"Regular expression for finding a prompt.")
|
|
|
|
|
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(defvar gud-matlab-marker-regexp-K>> "^K>>"
|
|
|
|
|
"Regular expression for finding a file line-number.")
|
|
|
|
|
(defvar gud-matlab-marker-regexp->> "^>>"
|
2019-11-29 00:14:25 +01:00
|
|
|
|
"Regular expression for finding a file line-number.")
|
|
|
|
|
|
|
|
|
|
(defvar gud-matlab-dbhotlink nil
|
|
|
|
|
"Track if we've sent a dbhotlink request.")
|
|
|
|
|
(make-variable-buffer-local 'gud-matlab-dbhotlink)
|
|
|
|
|
|
|
|
|
|
(defun gud-matlab-marker-filter (string)
|
|
|
|
|
"Filters STRING for the Unified Debugger based on MATLAB output."
|
|
|
|
|
|
|
|
|
|
(setq gud-marker-acc (concat gud-marker-acc string))
|
|
|
|
|
(let ((output "") (frame nil))
|
|
|
|
|
|
|
|
|
|
;; ERROR DELIMITERS
|
|
|
|
|
;; Newer MATLAB's wrap error text in {^H }^H characters.
|
|
|
|
|
;; Convert into something COMINT won't delete so we can scan them.
|
|
|
|
|
(while (string-match "{" gud-marker-acc)
|
|
|
|
|
(setq gud-marker-acc (replace-match matlab-shell-errortext-start-text t t gud-marker-acc 0)))
|
|
|
|
|
|
|
|
|
|
(while (string-match "}" gud-marker-acc)
|
|
|
|
|
(setq gud-marker-acc (replace-match matlab-shell-errortext-end-text t t gud-marker-acc 0)))
|
|
|
|
|
|
|
|
|
|
;; DEBUG PROMPTS
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(when (string-match gud-matlab-marker-regexp-K>> gud-marker-acc)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
|
|
|
|
;; 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.
|
|
|
|
|
(when (and (not frame) (not gud-matlab-dbhotlink))
|
|
|
|
|
(let ((dbhlcmd (if matlab-shell-echoes
|
|
|
|
|
"dbhotlink()%%%\n"
|
|
|
|
|
;; If no echo, force an echo
|
|
|
|
|
"disp(['dbhotlink()%%%' newline]);dbhotlink();\n")))
|
|
|
|
|
;;(when matlab-shell-io-testing (message "!!> [%s]" dbhlcmd))
|
|
|
|
|
(process-send-string (get-buffer-process gud-comint-buffer) dbhlcmd)
|
|
|
|
|
)
|
|
|
|
|
(setq gud-matlab-dbhotlink t)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
;; If we're forced to ask for a stack hotlink, we will see it come in via the
|
|
|
|
|
;; process output. Don't output anything until a K prompt is seen after the display
|
|
|
|
|
;; of the dbhotlink command.
|
|
|
|
|
(when gud-matlab-dbhotlink
|
|
|
|
|
(let ((start (string-match "dbhotlink()%%%" gud-marker-acc))
|
|
|
|
|
(endprompt nil))
|
|
|
|
|
(if start
|
|
|
|
|
(progn
|
|
|
|
|
(setq output (substring gud-marker-acc 0 start)
|
|
|
|
|
gud-marker-acc (substring gud-marker-acc start))
|
|
|
|
|
|
|
|
|
|
;; The hotlink text will persist until we see the K prompt.
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(when (string-match gud-matlab-marker-regexp-plain-prompt gud-marker-acc)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(setq endprompt (match-end 0))
|
|
|
|
|
|
|
|
|
|
;; (when matlab-shell-io-testing (message "!!xx [%s]" (substring gud-marker-acc 0 endprompt)))
|
|
|
|
|
|
2019-12-15 00:58:10 +01:00
|
|
|
|
;; 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.
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(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.
|
|
|
|
|
(setq gud-marker-acc (concat output gud-marker-acc)
|
|
|
|
|
output ""
|
|
|
|
|
gud-matlab-dbhotlink nil)
|
|
|
|
|
))
|
|
|
|
|
;; Else, waiting for a link, but hasn't shown up yet.
|
|
|
|
|
;; TODO - what can I do here to fix var setting if it gets
|
|
|
|
|
;; locked?
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(when (string-match gud-matlab-marker-regexp->> gud-marker-acc)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
;; A non-k prompt showed up. We're not going to get out request.
|
|
|
|
|
(setq gud-matlab-dbhotlink nil))
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
;; This if makes sure that the entirety of an error output is brought in
|
|
|
|
|
;; so that matlab-shell-mode doesn't try to display a file that only partially
|
|
|
|
|
;; exists in the buffer. Thus, if MATLAB output:
|
|
|
|
|
;; error: /home/me/my/mo/mello.m,10,12
|
|
|
|
|
;; All of that is in the buffer, and it goes to mello.m, not just
|
|
|
|
|
;; the first half of that file name.
|
|
|
|
|
;; The below used to match against the prompt, not \n, but then text that
|
|
|
|
|
;; had error: in it for some other reason wouldn't display at all.
|
|
|
|
|
(if (and matlab-prompt-seen ;; don't pause output if prompt not seen
|
|
|
|
|
gud-matlab-dbhotlink ;; pause output if waiting on debugger
|
|
|
|
|
)
|
|
|
|
|
;; We could be collecting debug info. Wait before output.
|
|
|
|
|
nil
|
|
|
|
|
;; Finish off this part of the output. None of our special stuff
|
|
|
|
|
;; ends with a \n, so display those as they show up...
|
|
|
|
|
(while (string-match "^[^\n]*\n" gud-marker-acc)
|
|
|
|
|
(setq output (concat output (substring gud-marker-acc 0 (match-end 0)))
|
|
|
|
|
gud-marker-acc (substring gud-marker-acc (match-end 0))))
|
|
|
|
|
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(if (string-match (concat gud-matlab-marker-regexp-plain-prompt "\\s-*$") gud-marker-acc)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(setq output (concat output gud-marker-acc)
|
|
|
|
|
gud-marker-acc ""))
|
|
|
|
|
|
|
|
|
|
;; Check our output for a prompt, and existence of a frame.
|
|
|
|
|
;; If this is true, throw out the debug arrow stuff.
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(if (and (string-match (concat gud-matlab-marker-regexp->> "\\s-*$") output)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
gud-last-last-frame)
|
|
|
|
|
(progn
|
2019-12-15 00:58:10 +01:00
|
|
|
|
;; Clean up gud stuff.
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(setq overlay-arrow-position nil
|
|
|
|
|
gud-last-last-frame nil
|
|
|
|
|
gud-overlay-arrow-position nil)
|
2019-12-15 00:58:10 +01:00
|
|
|
|
;; 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
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(sit-for 0)
|
2019-12-03 03:53:48 +01:00
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
;; Check for any text that would be embarrasing to display partially.
|
|
|
|
|
;; If we don't see any, feel free to dump the rest of the accumulation buffer
|
|
|
|
|
(unless (or (string-match (regexp-quote "<a href=") gud-marker-acc)
|
|
|
|
|
(string-match (regexp-quote "<EMACSCAP") gud-marker-acc)
|
|
|
|
|
(string-match (regexp-quote "<ERROR") gud-marker-acc))
|
|
|
|
|
(setq output (concat output gud-marker-acc)
|
|
|
|
|
gud-marker-acc "")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
|
|
|
|
(if frame (setq gud-last-frame frame))
|
|
|
|
|
|
|
|
|
|
(when matlab-shell-io-testing
|
|
|
|
|
(message "-->[%s] [%s]" output gud-marker-acc))
|
|
|
|
|
|
|
|
|
|
;;(message "Looking for prompt in %S" output)
|
|
|
|
|
(when (and (not matlab-shell-suppress-prompt-hooks)
|
|
|
|
|
(string-match gud-matlab-marker-regexp-plain-prompt output))
|
|
|
|
|
;; Now that we are about to dump this, run our prompt hook.
|
|
|
|
|
;;(message "PROMPT!")
|
|
|
|
|
(setq matlab-shell-prompt-hook-cookie t))
|
|
|
|
|
|
|
|
|
|
output))
|
|
|
|
|
|
2019-12-09 04:20:02 +01:00
|
|
|
|
;;; Stack tracking
|
|
|
|
|
;;
|
|
|
|
|
(defclass mlg-stack-frame ()
|
|
|
|
|
((file :initarg :file
|
|
|
|
|
:type string
|
|
|
|
|
:documentation
|
|
|
|
|
"The filename this frame belongs to.")
|
|
|
|
|
(name :initarg :name
|
|
|
|
|
:type string
|
|
|
|
|
:documentation
|
|
|
|
|
"The name of the location of this frame")
|
|
|
|
|
(line :initarg :line
|
|
|
|
|
:type integer
|
|
|
|
|
:documentation
|
|
|
|
|
"The line number for this frame"))
|
|
|
|
|
"A single stack frame from MATLAB.")
|
|
|
|
|
|
|
|
|
|
(cl-defmethod mlg-print ((frame mlg-stack-frame) longestname)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Use print to output this stack FRAME.
|
|
|
|
|
LONGESTNAME specifies the how long the longest name we can expect is."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(let* ((namefmt (concat "%" (number-to-string (or longestname 10)) "s"))
|
|
|
|
|
(str (concat (propertize (format namefmt (oref frame name)) 'face 'font-lock-function-name-face)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (format "%3d" (oref frame line)) 'face 'bold)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (oref frame file) 'face 'font-lock-constant-face))))
|
|
|
|
|
(setq str (propertize str 'object frame))
|
|
|
|
|
str))
|
|
|
|
|
|
|
|
|
|
(defvar mlg-stack nil
|
|
|
|
|
"The last stack sent to us from MATLAB.")
|
|
|
|
|
(defvar mlg-frame nil
|
|
|
|
|
"The last frame sent to use from MATLAB.")
|
|
|
|
|
|
|
|
|
|
(defun mlg-set-stack (newstack)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Specify a NEWSTACK provided by MATLAB to replace the old one."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(setq mlg-stack nil)
|
|
|
|
|
(dolist (L newstack)
|
2019-12-13 01:05:26 +01:00
|
|
|
|
(push (mlg-stack-frame ""
|
2019-12-09 04:20:02 +01:00
|
|
|
|
:file (nth 0 L)
|
|
|
|
|
:name (nth 1 L)
|
|
|
|
|
:line (nth 2 L))
|
|
|
|
|
mlg-stack))
|
|
|
|
|
(setq mlg-stack (nreverse mlg-stack))
|
|
|
|
|
(mlg-refresh-stack-buffer)
|
2019-12-15 00:58:10 +01:00
|
|
|
|
;;(message "Updated Stack")
|
2019-12-09 04:20:02 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defun mlg-set-stack-frame (newframe)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Specify a NEWFRAME provided by MATLAB we should visit."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(setq mlg-frame newframe)
|
|
|
|
|
(mlg-show-stack)
|
|
|
|
|
(mlg-show-frame newframe)
|
|
|
|
|
)
|
|
|
|
|
|
2019-12-15 00:58:10 +01:00
|
|
|
|
(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)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(defun mlg-show-frame (&optional frame)
|
|
|
|
|
"Setup windows to show FRAME from the current stack frame."
|
|
|
|
|
(let ((newframe (or frame mlg-frame)))
|
2019-12-15 01:19:22 +01:00
|
|
|
|
(if (and mlg-stack (<= newframe (length mlg-stack)))
|
2019-12-09 04:20:02 +01:00
|
|
|
|
;; Make sure we have a stack window.
|
|
|
|
|
(let* ((buff (get-buffer "*MATLAB stack*"))
|
|
|
|
|
(win (get-buffer-window buff)))
|
|
|
|
|
(if (or (not buff) (not win))
|
|
|
|
|
(mlg-show-stack)
|
|
|
|
|
;; else, do refresh stuff.
|
|
|
|
|
(select-window win))
|
|
|
|
|
|
|
|
|
|
;; Still around, go do it.
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- frame))
|
|
|
|
|
(mlg-stack-choose)
|
|
|
|
|
)
|
|
|
|
|
;; Else no frame. Look for the window, and close it.
|
|
|
|
|
(let* ((buff (get-buffer "*MATLAB stack*"))
|
|
|
|
|
(win (get-buffer-window buff)))
|
|
|
|
|
|
|
|
|
|
(when win (delete-window win)))
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
(defun mlg-refresh-stack-buffer ()
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Refresh the buffer displaying stack."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((buff (get-buffer-create "*MATLAB stack*"))
|
|
|
|
|
(namelen 5)
|
|
|
|
|
(inhibit-read-only t))
|
|
|
|
|
|
|
|
|
|
(dolist (S mlg-stack)
|
|
|
|
|
(when (> (length (oref S name)) namelen)
|
|
|
|
|
(setq namelen (length (oref S name)))))
|
|
|
|
|
|
|
|
|
|
(set-buffer buff)
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
|
|
|
|
|
(let ((cnt 1))
|
|
|
|
|
(dolist (F mlg-stack)
|
|
|
|
|
(insert (format "%2d" cnt))
|
2019-12-13 01:05:26 +01:00
|
|
|
|
(if (and mlg-frame (= cnt mlg-frame))
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(insert " >> ")
|
|
|
|
|
(insert " -- "))
|
|
|
|
|
(insert (mlg-print F namelen) "\n")
|
|
|
|
|
(setq cnt (1+ cnt))))
|
|
|
|
|
|
|
|
|
|
(mlg-stack-mode)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(current-buffer))))
|
|
|
|
|
|
|
|
|
|
(defun mlg-show-stack ()
|
|
|
|
|
"Display the MATLAB stack in an interactive buffer."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((buff (mlg-refresh-stack-buffer)))
|
|
|
|
|
|
|
|
|
|
(display-buffer
|
|
|
|
|
buff
|
|
|
|
|
'((display-buffer-at-bottom)
|
|
|
|
|
(inhibit-same-window . t)
|
|
|
|
|
(window-height . fit-window-to-buffer))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(select-window (get-buffer-window buff))
|
|
|
|
|
(goto-char 3)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
(defvar mlg-stack-mode-map
|
|
|
|
|
(let ((km (make-sparse-keymap)))
|
|
|
|
|
(define-key km [return] 'mlg-stack-choose)
|
2019-12-15 00:58:10 +01:00
|
|
|
|
(define-key km "q" 'mlg-stack-quit)
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(define-key km "n" 'mlg-stack-next)
|
|
|
|
|
(define-key km "p" 'mlg-stack-prev)
|
|
|
|
|
(define-key km [mouse-2] 'mlg-stack-click)
|
|
|
|
|
(define-key km [mouse-1] 'mlg-stack-click)
|
|
|
|
|
km)
|
|
|
|
|
"Keymap used in MATLAB stack mode.")
|
|
|
|
|
|
|
|
|
|
;; Need this to fix wierd problem in define-derived-mode
|
|
|
|
|
(defvar mlg-stack-mode-syntax-table (make-syntax-table)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Syntax table used in `matlab-shell-help-mode'.")
|
2019-12-09 04:20:02 +01:00
|
|
|
|
|
|
|
|
|
(define-derived-mode mlg-stack-mode
|
|
|
|
|
fundamental-mode "MStack"
|
|
|
|
|
"Major mode for viewing a MATLAB stack.
|
|
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
|
\\{mlg-stack-mode-map}"
|
|
|
|
|
:syntax-table mlg-stack-mode-syntax-table
|
|
|
|
|
(setq buffer-read-only t)
|
|
|
|
|
)
|
|
|
|
|
|
2019-12-15 00:58:10 +01:00
|
|
|
|
(defun mlg-stack-quit ()
|
|
|
|
|
"Quit the MATLAB stack view."
|
|
|
|
|
(interactive)
|
|
|
|
|
(if (= (length (window-list)) 1)
|
|
|
|
|
(bury-buffer)
|
|
|
|
|
(delete-window (selected-window))))
|
|
|
|
|
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(defun mlg-stack-next ()
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Visit stack on next line."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(interactive)
|
|
|
|
|
(forward-line 1)
|
|
|
|
|
(forward-char 2)
|
|
|
|
|
(mlg-stack-choose))
|
|
|
|
|
|
|
|
|
|
(defun mlg-stack-prev ()
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Visit stack on next line."
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(interactive)
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(forward-char 2)
|
|
|
|
|
(mlg-stack-choose))
|
|
|
|
|
|
|
|
|
|
(defun mlg-stack-click (e)
|
|
|
|
|
"Click on a stack frame to visit it.
|
|
|
|
|
Must be bound to event E."
|
|
|
|
|
(interactive "e")
|
|
|
|
|
(mouse-set-point e)
|
|
|
|
|
(mlg-stack-choose))
|
|
|
|
|
|
|
|
|
|
(defun mlg-stack-choose ()
|
|
|
|
|
"Choose the stack the under the cursor.
|
|
|
|
|
Visit the file presented in that stack frame."
|
|
|
|
|
(interactive)
|
|
|
|
|
(let ((topic nil) (fun nil) (p (point)))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(forward-char 10)
|
|
|
|
|
(let* ((sf (get-text-property (point) 'object))
|
|
|
|
|
(f (oref sf file))
|
|
|
|
|
(l (oref sf line))
|
2019-12-12 23:22:53 +01:00
|
|
|
|
(buff (find-file-noselect f t)))
|
2019-12-09 04:20:02 +01:00
|
|
|
|
(display-buffer
|
|
|
|
|
buff
|
|
|
|
|
'((display-buffer-reuse-window display-buffer-use-some-window)
|
|
|
|
|
(inhibit-same-window . t))
|
|
|
|
|
)
|
|
|
|
|
(let ((win (selected-window)))
|
|
|
|
|
(select-window (get-buffer-window buff))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- l))
|
|
|
|
|
(select-window win))
|
|
|
|
|
))))
|
|
|
|
|
|
2019-12-08 01:41:49 +01:00
|
|
|
|
;;; Breakpoint Trackers
|
|
|
|
|
;;
|
|
|
|
|
(defclass mlg-breakpoint ()
|
|
|
|
|
((file :initarg :file
|
|
|
|
|
:type string
|
|
|
|
|
:documentation
|
|
|
|
|
"The filename this brekpoint belongs to.")
|
|
|
|
|
(line :initarg :line
|
|
|
|
|
:type integer
|
|
|
|
|
:documentation
|
|
|
|
|
"The line number for this breakpoint")
|
|
|
|
|
(overlay :documentation
|
|
|
|
|
:default nil
|
|
|
|
|
"The overlay indicating the preense of this breakpoint.")
|
|
|
|
|
)
|
|
|
|
|
"Representation of a breakpoint.
|
|
|
|
|
Used to track active breakpoints, and how to show them.")
|
|
|
|
|
|
|
|
|
|
(defvar matlab-gud-visible-breakpoints nil
|
|
|
|
|
"List of breakpoints MATLAB has sent to us.")
|
|
|
|
|
|
2019-12-12 04:02:41 +01:00
|
|
|
|
;;;###autoload
|
2019-12-08 01:41:49 +01:00
|
|
|
|
(defun mlg-reset-breakpoints ()
|
|
|
|
|
"Remove all cached breakpoints."
|
|
|
|
|
(dolist (BP matlab-gud-visible-breakpoints)
|
|
|
|
|
(mlg-deactivate BP))
|
|
|
|
|
(setq matlab-gud-visible-breakpoints nil))
|
|
|
|
|
|
|
|
|
|
(defun mlg-add-breakpoint (file line)
|
|
|
|
|
"Add a visible breakpoint to FILE at LINE."
|
|
|
|
|
(let ((found nil))
|
|
|
|
|
(dolist (BP matlab-gud-visible-breakpoints)
|
|
|
|
|
(when (and (string= (oref BP file) file)
|
|
|
|
|
(= (oref BP line) line))
|
|
|
|
|
(setq found t)))
|
|
|
|
|
(when (not found)
|
|
|
|
|
(setq matlab-gud-visible-breakpoints
|
2019-12-13 01:05:26 +01:00
|
|
|
|
(cons (mlg-breakpoint "" :file file
|
2019-12-08 01:41:49 +01:00
|
|
|
|
:line line)
|
|
|
|
|
matlab-gud-visible-breakpoints))
|
|
|
|
|
(mlg-activate (car matlab-gud-visible-breakpoints))
|
|
|
|
|
))
|
|
|
|
|
;; The first time breakpoints are added, make sure we can activate breakpoints
|
|
|
|
|
;; when new files are opened in a buffer.
|
|
|
|
|
(add-hook 'matlab-mode-hook 'mlg-breakpoint-activate-buffer-opened-hook)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defun mlg-del-breakpoint (file line)
|
|
|
|
|
"Add a visible breakpoint to FILE at LINE."
|
|
|
|
|
(let ((BPS matlab-gud-visible-breakpoints)
|
|
|
|
|
(NBPS nil))
|
|
|
|
|
(while BPS
|
|
|
|
|
(if (and (string= (oref (car BPS) file) file)
|
|
|
|
|
(= (oref (car BPS) line) line))
|
|
|
|
|
;; Deactivate
|
|
|
|
|
(mlg-deactivate (car BPS))
|
|
|
|
|
;; Not being removed, add to list.
|
|
|
|
|
(setq NBPS (cons (car BPS) NBPS)))
|
|
|
|
|
(setq BPS (cdr BPS)))
|
|
|
|
|
|
|
|
|
|
(setq matlab-gud-visible-breakpoints
|
|
|
|
|
(nreverse NBPS))))
|
|
|
|
|
|
|
|
|
|
(defface mlg-breakpoint-face
|
|
|
|
|
(list
|
|
|
|
|
(list t
|
|
|
|
|
(list :background nil
|
|
|
|
|
:foreground nil
|
|
|
|
|
:underline "red1")))
|
|
|
|
|
"*Face to use to highlight breakpoints."
|
|
|
|
|
:group 'matlab-shell)
|
|
|
|
|
|
2019-12-08 19:08:43 +01:00
|
|
|
|
(cl-defmethod mlg-activate ((bp mlg-breakpoint))
|
2019-12-08 01:41:49 +01:00
|
|
|
|
"Activate breakpoint BP if needed."
|
|
|
|
|
;; yes overlay, but inactive
|
|
|
|
|
(when (and (slot-boundp bp 'overlay)
|
|
|
|
|
(oref bp overlay)
|
|
|
|
|
(not (overlay-buffer (oref bp overlay))))
|
|
|
|
|
(oset bp overlay nil))
|
|
|
|
|
|
|
|
|
|
(let ((buff (find-buffer-visiting (oref bp file))))
|
|
|
|
|
;; No overlay, and we can make one.
|
|
|
|
|
(when (and (or (not (slot-boundp bp 'overlay))
|
|
|
|
|
(not (oref bp overlay)))
|
|
|
|
|
buff)
|
|
|
|
|
(with-current-buffer buff
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- (oref bp line)))
|
|
|
|
|
(let ((ol (matlab-make-overlay (save-excursion
|
|
|
|
|
(back-to-indentation)
|
|
|
|
|
(point))
|
|
|
|
|
(point-at-eol) buff nil nil)))
|
|
|
|
|
;; Store it
|
|
|
|
|
(oset bp overlay ol)
|
|
|
|
|
;; Setup cool stuff
|
|
|
|
|
(matlab-overlay-put ol 'face 'mlg-breakpoint-face)
|
|
|
|
|
(matlab-overlay-put ol 'before-string
|
|
|
|
|
(propertize "#"
|
|
|
|
|
'display
|
|
|
|
|
'(left-fringe
|
|
|
|
|
filled-square
|
|
|
|
|
matlab-shell-error-face))
|
|
|
|
|
))))
|
|
|
|
|
))
|
|
|
|
|
|
2019-12-08 19:08:43 +01:00
|
|
|
|
(cl-defmethod mlg-deactivate ((bp mlg-breakpoint))
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Deactivate this breakpoint BP."
|
2019-12-08 01:41:49 +01:00
|
|
|
|
(when (slot-boundp bp 'overlay)
|
|
|
|
|
(with-slots (overlay) bp
|
|
|
|
|
(when (and overlay (overlayp overlay))
|
|
|
|
|
(delete-overlay overlay)
|
|
|
|
|
(setq overlay nil)))))
|
|
|
|
|
|
|
|
|
|
(defun mlg-breakpoint-activate-buffer-opened-hook ()
|
|
|
|
|
"Activate any breakpoints in a buffer when that buffer is read in."
|
|
|
|
|
(if (not (matlab-shell-active-p))
|
|
|
|
|
(mlg-reset-breakpoints)
|
|
|
|
|
|
|
|
|
|
;; Still going, activate.
|
|
|
|
|
(dolist (BP matlab-gud-visible-breakpoints)
|
|
|
|
|
(mlg-activate BP)
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
(defun mlg-breakpoint-flush-and-reactivate ()
|
|
|
|
|
"Flush existing breakpoint markers, and reactivate."
|
|
|
|
|
(interactive)
|
|
|
|
|
(dolist (BP matlab-gud-visible-breakpoints)
|
|
|
|
|
(mlg-deactivate BP)
|
|
|
|
|
(mlg-activate BP))
|
|
|
|
|
)
|
2019-11-29 00:14:25 +01:00
|
|
|
|
|
2019-11-29 02:31:30 +01:00
|
|
|
|
;;; K prompt state and hooks.
|
2019-12-08 01:41:49 +01:00
|
|
|
|
;;
|
2019-11-29 02:31:30 +01:00
|
|
|
|
|
|
|
|
|
(defun gud-matlab-debug-tracker ()
|
|
|
|
|
"Function called when new prompts appear.
|
|
|
|
|
Call debug activate/deactivate features."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((inhibit-field-text-motion t))
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(cond
|
|
|
|
|
((and gud-matlab-debug-active (looking-at gud-matlab-marker-regexp->>))
|
|
|
|
|
(setq gud-matlab-debug-active nil)
|
2019-11-29 15:55:42 +01:00
|
|
|
|
(when (boundp 'tool-bar-map) ; not --without-x
|
|
|
|
|
(with-current-buffer (matlab-shell-active-p) (kill-local-variable 'tool-bar-map)))
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(global-matlab-shell-gud-minor-mode -1)
|
|
|
|
|
(run-hooks 'gud-matlab-debug-deactivate-hook))
|
|
|
|
|
((and (not gud-matlab-debug-active) (looking-at gud-matlab-marker-regexp-K>>))
|
|
|
|
|
(setq gud-matlab-debug-active t)
|
2019-11-29 15:55:42 +01:00
|
|
|
|
(when (boundp 'tool-bar-map) ; not --without-x
|
|
|
|
|
(with-current-buffer (matlab-shell-active-p)
|
|
|
|
|
(setq-local tool-bar-map gud-matlab-tool-bar-map)))
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(global-matlab-shell-gud-minor-mode 1)
|
|
|
|
|
(run-hooks 'gud-matlab-debug-activate-hook))
|
|
|
|
|
(t
|
|
|
|
|
;; All clear
|
|
|
|
|
))))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
;;; MATLAB SHELL GUD Minor Mode
|
|
|
|
|
;;
|
|
|
|
|
;; When K prompt is active, this minor mode is applied to frame buffers so
|
|
|
|
|
;; that GUD commands are easy to get to.
|
|
|
|
|
|
2019-12-13 04:22:09 +01:00
|
|
|
|
(defvar matlab-shell-gud-minor-mode-map
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(let ((km (make-sparse-keymap))
|
|
|
|
|
(key ?\ ))
|
|
|
|
|
(while (<= key ?~)
|
|
|
|
|
(define-key km (string key) 'matlab-shell-gud-mode-help-notice)
|
|
|
|
|
(setq key (1+ key)))
|
|
|
|
|
(define-key km "h" 'matlab-shell-gud-mode-help)
|
|
|
|
|
|
|
|
|
|
;; gud bindings.
|
|
|
|
|
(define-key km "b" 'gud-break)
|
|
|
|
|
(define-key km "r" 'gud-remove)
|
|
|
|
|
(define-key km "c" 'gud-cont)
|
|
|
|
|
(define-key km "s" 'gud-step)
|
|
|
|
|
(define-key km "n" 'gud-next)
|
|
|
|
|
(define-key km "f" 'gud-finish)
|
|
|
|
|
(define-key km "q" 'gud-finish)
|
|
|
|
|
(define-key km "u" 'gud-up)
|
|
|
|
|
(define-key km "d" 'gud-down)
|
|
|
|
|
(define-key km "<" 'gud-up)
|
|
|
|
|
(define-key km ">" 'gud-down)
|
2019-12-15 00:58:10 +01:00
|
|
|
|
(define-key km "v" 'mlg-show-stack)
|
2019-11-29 03:19:37 +01:00
|
|
|
|
(define-key km "p" 'matlab-shell-gud-show-symbol-value)
|
2019-11-29 02:31:30 +01:00
|
|
|
|
;; (define-key km "p" gud-print)
|
|
|
|
|
|
|
|
|
|
(define-key km "e" 'matlab-shell-gud-mode-edit)
|
2019-12-07 19:17:57 +01:00
|
|
|
|
(define-key km "\C-x\C-q" 'matlab-shell-gud-mode-edit) ; like toggle-read-only
|
2019-11-29 02:31:30 +01:00
|
|
|
|
|
|
|
|
|
km)
|
|
|
|
|
"Keymap used by matlab mode maintainers.")
|
|
|
|
|
|
|
|
|
|
(easy-menu-define
|
|
|
|
|
matlab-shell-gud-menu matlab-shell-gud-minor-mode-map "MATLAB Maintainer's Minor Mode"
|
|
|
|
|
'("MATLAB-DEBUG"
|
2019-12-07 19:17:57 +01:00
|
|
|
|
["Edit File (toggle read-only)" matlab-shell-gud-mode-edit
|
2019-11-29 02:31:30 +01:00
|
|
|
|
:help "Exit the MATLAB debug minor mode to edit without exiting MATLAB's K>> prompt."]
|
|
|
|
|
["dbstop in FILE at point" gud-break
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, set break point at current M-file point"]
|
|
|
|
|
["dbclear in FILE at point" gud-remove
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, clear break point at current M-file point"]
|
|
|
|
|
["dbstep in" gud-step
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, step into line"]
|
|
|
|
|
["dbstep" gud-next
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, step one line"]
|
|
|
|
|
["dbup" gud-up
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active and at break point, go up a frame"]
|
|
|
|
|
["dbdown" gud-down
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active and at break point, go down a frame"]
|
|
|
|
|
["dbcont" gud-cont
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, run to next break point or finish"]
|
2019-12-15 00:58:10 +01:00
|
|
|
|
["Show Stack" mlg-show-stack
|
|
|
|
|
:active (matlab-any-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, show value of the symbol under point."]
|
2019-12-07 19:17:57 +01:00
|
|
|
|
["Show symbol value" matlab-shell-gud-show-symbol-value
|
2019-12-15 00:58:10 +01:00
|
|
|
|
:active (matlab-any-shell-active-p)
|
2019-12-07 19:17:57 +01:00
|
|
|
|
:help "When MATLAB debugger is active, show value of the symbol under point."]
|
2019-11-29 02:31:30 +01:00
|
|
|
|
["dbquit" gud-finish
|
|
|
|
|
:active (matlab-shell-active-p)
|
|
|
|
|
:help "When MATLAB debugger is active, stop debugging"]
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(define-minor-mode matlab-shell-gud-minor-mode
|
|
|
|
|
"Minor mode activated when `matlab-shell' K>> prompt is active.
|
|
|
|
|
This minor mode makes MATLAB buffers read only so simple keystrokes
|
2019-11-29 14:52:24 +01:00
|
|
|
|
activate debug commands. It also enables tooltips to appear when the
|
|
|
|
|
mouse hovers over a symbol when debugging.
|
2019-11-29 02:31:30 +01:00
|
|
|
|
\\<matlab-shell-gud-minor-mode-map>
|
|
|
|
|
Debug commands are:
|
|
|
|
|
\\[gud-break] - Set a breakpoint on the current line
|
|
|
|
|
\\[gud-remove] - Clear breakpoint on line
|
|
|
|
|
\\[gud-cont] - Continue till next breakpoint
|
|
|
|
|
\\[gud-step] - Step into next functions
|
|
|
|
|
\\[gud-next] - Next line in current function
|
|
|
|
|
\\[gud-finish] - Exit debug mode
|
|
|
|
|
\\[gud-up] - Navigate up the call stack
|
|
|
|
|
\\[gud-down] - Navigate down the call stack
|
|
|
|
|
\\[matlab-shell-gud-mode-edit] - Exit gud minor mode so you can edit
|
|
|
|
|
you file without causing MATLAB to exit debug mode."
|
|
|
|
|
nil " MGUD" matlab-shell-gud-minor-mode-map
|
|
|
|
|
|
|
|
|
|
;; Make the buffer read only
|
|
|
|
|
(if matlab-shell-gud-minor-mode
|
2019-12-01 02:41:13 +01:00
|
|
|
|
(progn
|
|
|
|
|
;; Enable
|
|
|
|
|
(when (buffer-file-name) (setq buffer-read-only t))
|
|
|
|
|
(when matlab-shell-debug-tooltips-p
|
|
|
|
|
(gud-tooltip-mode 1)
|
|
|
|
|
(add-hook 'tooltip-functions 'gud-matlab-tooltip-tips)
|
2019-12-07 19:17:57 +01:00
|
|
|
|
)
|
|
|
|
|
;; Replace gud's toolbar which keeps stomping
|
|
|
|
|
;; on our toolbar.
|
2019-12-08 01:41:49 +01:00
|
|
|
|
(make-local-variable 'gud-tool-bar-map)
|
2019-12-07 19:17:57 +01:00
|
|
|
|
(setq gud-tool-bar-map gud-matlab-tool-bar-map)
|
|
|
|
|
)
|
2019-11-29 02:31:30 +01:00
|
|
|
|
;; Disable
|
2019-12-01 02:41:13 +01:00
|
|
|
|
(when (buffer-file-name)
|
|
|
|
|
(setq buffer-read-only (not (file-writable-p (buffer-file-name)))))
|
|
|
|
|
|
2019-12-01 02:15:45 +01:00
|
|
|
|
;; Always disable tooltips, in case configured while in the mode.
|
2019-11-29 05:31:00 +01:00
|
|
|
|
(gud-tooltip-mode -1)
|
|
|
|
|
(remove-hook 'tooltip-functions 'gud-matlab-tooltip-tips)
|
2019-12-07 19:17:57 +01:00
|
|
|
|
|
|
|
|
|
;; Disable the debug toolboar
|
|
|
|
|
(when (boundp 'tool-bar-map) ; not --without-x
|
|
|
|
|
(kill-local-variable 'tool-bar-map))
|
|
|
|
|
|
2019-11-29 05:31:00 +01:00
|
|
|
|
)
|
2019-11-29 02:31:30 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(define-global-minor-mode global-matlab-shell-gud-minor-mode
|
|
|
|
|
matlab-shell-gud-minor-mode
|
|
|
|
|
(lambda ()
|
|
|
|
|
"Should we turn on in this buffer? Only if in a MATLAB mode."
|
|
|
|
|
(when (eq major-mode 'matlab-mode)
|
|
|
|
|
(matlab-shell-gud-minor-mode 1)))
|
|
|
|
|
)
|
|
|
|
|
|
2019-11-29 03:19:37 +01:00
|
|
|
|
(defun matlab-shell-gud-show-symbol-value (sym)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
"Show the value of the symbol SYM under point from MATLAB shell."
|
2019-11-29 03:19:37 +01:00
|
|
|
|
(interactive
|
|
|
|
|
(list
|
|
|
|
|
(if (use-region-p)
|
|
|
|
|
;; Don't ask user anything, just take it.
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(buffer-substring-no-properties (region-beginning) (region-end))
|
2019-11-29 03:19:37 +01:00
|
|
|
|
(let ((word (matlab-read-word-at-point)))
|
|
|
|
|
(read-from-minibuffer "MATLAB variable: " (cons word 0))))))
|
|
|
|
|
(let ((txt (matlab-shell-collect-command-output
|
|
|
|
|
(concat "disp(" sym ")"))))
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(if (not (string-match "ERRORTXT" txt))
|
|
|
|
|
(matlab-output-to-temp-buffer "*MATLAB Help*" txt)
|
|
|
|
|
(message "Error evaluationg MATLAB expression"))))
|
2019-11-29 03:19:37 +01:00
|
|
|
|
|
|
|
|
|
|
2019-11-29 02:31:30 +01:00
|
|
|
|
(defun matlab-shell-gud-mode-edit ()
|
|
|
|
|
"Turn off `matlab-shell-gud-minor-mode' so you can edit again."
|
|
|
|
|
(interactive)
|
|
|
|
|
(global-matlab-shell-gud-minor-mode -1))
|
|
|
|
|
|
|
|
|
|
(defun matlab-shell-gud-mode-help-notice ()
|
|
|
|
|
"Default binding for most keys in `matlab-shell-gud-minor-mode'.
|
|
|
|
|
Shows a help message in the mini buffer."
|
|
|
|
|
(interactive)
|
2019-12-13 04:22:09 +01:00
|
|
|
|
(error "MATLAB shell GUD minor-mode: Press 'h' for help, 'e' to go back to editing"))
|
2019-11-29 02:31:30 +01:00
|
|
|
|
|
|
|
|
|
(defun matlab-shell-gud-mode-help ()
|
|
|
|
|
"Show the default binding for most keys in `matlab-shell-gud-minor-mode'."
|
|
|
|
|
(interactive)
|
|
|
|
|
(describe-minor-mode 'matlab-shell-gud-minor-mode)
|
|
|
|
|
)
|
|
|
|
|
|
2019-11-29 05:31:00 +01:00
|
|
|
|
;;; Tooltips
|
|
|
|
|
;;
|
|
|
|
|
;; Using the gud tooltip feature for a bunch of setup, but then
|
|
|
|
|
;; just override the tooltip fcn (see the mode) with this function
|
|
|
|
|
;; as an additional piece.
|
|
|
|
|
(defun gud-matlab-tooltip-tips (event)
|
|
|
|
|
"Implementation of the tooltip feture for MATLAB.
|
|
|
|
|
Much of this was copied from `gud-tooltip-tips'.
|
|
|
|
|
|
|
|
|
|
This function must return nil if it doesn't handle EVENT."
|
2019-12-08 18:33:37 +01:00
|
|
|
|
(when (and (eventp event) (tooltip-event-buffer event))
|
2019-11-29 05:31:00 +01:00
|
|
|
|
(with-current-buffer (tooltip-event-buffer event)
|
|
|
|
|
(when (and gud-tooltip-mode
|
|
|
|
|
matlab-shell-gud-minor-mode
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(buffer-name gud-comint-buffer) ; might be killed
|
2019-11-29 05:31:00 +01:00
|
|
|
|
)
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(let ((expr (matlab-shell-gud-find-tooltip-expression event))
|
2019-11-29 05:31:00 +01:00
|
|
|
|
(txt nil))
|
|
|
|
|
(when expr
|
|
|
|
|
(setq txt (matlab-shell-collect-command-output
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(concat "emacstipstring(" expr ")")))
|
|
|
|
|
|
|
|
|
|
(when (not (string-match "ERRORTXT" txt))
|
2019-11-29 05:31:00 +01:00
|
|
|
|
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(tooltip-show (concat expr "=\n" txt)
|
|
|
|
|
(or gud-tooltip-echo-area
|
|
|
|
|
tooltip-use-echo-area
|
|
|
|
|
(not tooltip-mode)))
|
2019-12-13 04:22:09 +01:00
|
|
|
|
t)))))))
|
2019-11-29 05:31:00 +01:00
|
|
|
|
|
2019-11-29 14:52:24 +01:00
|
|
|
|
(defun matlab-shell-gud-find-tooltip-expression (event)
|
|
|
|
|
"Identify an expression to output in a tooltip at EVENT.
|
|
|
|
|
Unlike `tooltip-expr-to-print', this looks at the symbol, and
|
|
|
|
|
if it looks like a function call, it will return nil."
|
|
|
|
|
(interactive)
|
|
|
|
|
|
|
|
|
|
(with-current-buffer (tooltip-event-buffer event)
|
|
|
|
|
;; Only do this for MATLAB stuff.
|
|
|
|
|
(when matlab-shell-gud-minor-mode
|
|
|
|
|
|
|
|
|
|
(let ((point (posn-point (event-end event))))
|
|
|
|
|
(if (use-region-p)
|
|
|
|
|
(when (and (<= (region-beginning) point) (<= point (region-end)))
|
|
|
|
|
(buffer-substring (region-beginning) (region-end)))
|
|
|
|
|
|
|
|
|
|
;; This snippent copied from tooltip.el, then modified to
|
|
|
|
|
;; detect matlab functions
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char point)
|
|
|
|
|
(let* ((origin (point))
|
|
|
|
|
(start (progn
|
|
|
|
|
(skip-syntax-backward "w_")
|
|
|
|
|
;; find full . expression
|
|
|
|
|
(while (= (preceding-char) ?.)
|
|
|
|
|
(forward-char -1)
|
|
|
|
|
(skip-syntax-backward "w_"))
|
|
|
|
|
(point)))
|
|
|
|
|
(pstate (syntax-ppss)))
|
|
|
|
|
(unless (or (looking-at "[0-9]")
|
|
|
|
|
(nth 3 pstate)
|
|
|
|
|
(nth 4 pstate))
|
|
|
|
|
(goto-char origin)
|
|
|
|
|
(skip-syntax-forward "w_")
|
|
|
|
|
(when (> (point) start)
|
|
|
|
|
;; At this point, look to see we are looking at (. If so
|
|
|
|
|
;; we need to grab that stuff too.
|
|
|
|
|
(if (not (looking-at "\\s-*("))
|
|
|
|
|
(buffer-substring-no-properties start (point))
|
|
|
|
|
;; Also grab the arguments
|
|
|
|
|
(matlab-forward-sexp)
|
|
|
|
|
(buffer-substring-no-properties start (point)))
|
|
|
|
|
)))))))))
|
2019-11-29 05:31:00 +01:00
|
|
|
|
|
2019-11-29 00:14:25 +01:00
|
|
|
|
(provide 'matlab-shell-gud)
|
|
|
|
|
|
|
|
|
|
;;; matlab-shell-gud.el ends here
|