branch: master commit ea5dcba66efc0b752a742437f1044d12121299a2 Author: Sean Farley <s...@farley.io> Commit: Sean Farley <s...@farley.io>
ipdb: add new debugger based on pdb --- THANKS | 2 + configure.ac | 1 + realgud.el | 2 + realgud/debugger/Makefile.am | 2 +- realgud/debugger/ipdb/Makefile.am | 1 + realgud/debugger/ipdb/core.el | 204 +++++++++++++++++++++++++++++++++++ realgud/debugger/ipdb/init.el | 120 +++++++++++++++++++++ realgud/debugger/ipdb/ipdb.el | 109 +++++++++++++++++++ realgud/debugger/ipdb/track-mode.el | 75 +++++++++++++ 9 files changed, 515 insertions(+), 1 deletion(-) diff --git a/THANKS b/THANKS index fba09c8..bacb71c 100644 --- a/THANKS +++ b/THANKS @@ -7,3 +7,5 @@ Nick Roberts - We used some of the code for the fringe icons from gdb-mi. NYC Emacs Meetup and Nicolas Dudebout - straightening me out on set-keymap-parent Kilian Kilger - gdb fixes + +Sean Farley - ipdb support diff --git a/configure.ac b/configure.ac index 0feab78..2a65243 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,7 @@ AC_CONFIG_FILES([Makefile \ realgud/debugger/bashdb/Makefile \ realgud/debugger/gdb/Makefile \ realgud/debugger/gub/Makefile \ + realgud/debugger/ipdb/Makefile \ realgud/debugger/jdb/Makefile \ realgud/debugger/kshdb/Makefile \ realgud/debugger/nodejs/Makefile \ diff --git a/realgud.el b/realgud.el index 0e7eb6f..d60211b 100644 --- a/realgud.el +++ b/realgud.el @@ -57,6 +57,7 @@ ;; kshdb kshdb Korn Shell 93u+ ;; nodejs nodejs node.js javascript debugger ;; pdb realgud:pdb stock C Python debugger +;; ipdb realgud:ipdb iPython debugger ;; perldb realgud:perldb stock Perl5 debugger ;; pydb pydb slighly enhanced pdb for Python 2.x ;; pydbgr pydbgr obsolete trepanning debugger for Python 2.x @@ -111,6 +112,7 @@ "./realgud/debugger/bashdb/bashdb" "./realgud/debugger/gdb/gdb" "./realgud/debugger/gub/gub" + "./realgud/debugger/ipdb/ipdb" "./realgud/debugger/jdb/jdb" "./realgud/debugger/kshdb/kshdb" "./realgud/debugger/nodejs/nodejs" diff --git a/realgud/debugger/Makefile.am b/realgud/debugger/Makefile.am index 69ae438..2d58eed 100644 --- a/realgud/debugger/Makefile.am +++ b/realgud/debugger/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = \ - bashdb gdb gub jdb kshdb nodejs \ + bashdb gdb gub ipdb jdb kshdb nodejs \ pdb perldb pydb pydbgr rdebug remake \ trepan trepan2 trepan3k trepan.pl trepanjs trepanx trepan8 \ zshdb diff --git a/realgud/debugger/ipdb/Makefile.am b/realgud/debugger/ipdb/Makefile.am new file mode 100644 index 0000000..23f8a8f --- /dev/null +++ b/realgud/debugger/ipdb/Makefile.am @@ -0,0 +1 @@ +include ../common.mk diff --git a/realgud/debugger/ipdb/core.el b/realgud/debugger/ipdb/core.el new file mode 100644 index 0000000..fa697f8 --- /dev/null +++ b/realgud/debugger/ipdb/core.el @@ -0,0 +1,204 @@ +;; Copyright (C) 2016 Free Software Foundation, Inc + +;; Author: Rocky Bernstein <ro...@gnu.org> +;; Author: Sean Farley <s...@farley.io> + +;; 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/>. +;;; Copyright (C) 2014-2015 Rocky Bernstein <ro...@gnu.org> + +(eval-when-compile (require 'cl)) + +(require 'load-relative) +(require-relative-list '("../../common/track" + "../../common/core" + "../../common/lang") + "realgud-") +(require-relative-list '("init") "realgud:ipdb-") + + +(declare-function realgud-lang-mode? 'realgud-lang) +(declare-function realgud-parse-command-arg 'realgud-core) +(declare-function realgud-query-cmdline 'realgud-core) +(declare-function realgud-suggest-invocation 'realgud-core) + +;; FIXME: I think the following could be generalized and moved to +;; realgud-... probably via a macro. +(defvar realgud:ipdb-minibuffer-history nil + "minibuffer history list for the command `ipdb'.") + +(defvar realgud:ipdb-remote-minibuffer-history nil + "minibuffer history list for the command `ipdb-remote'.") + +(easy-mmode-defmap ipdb-minibuffer-local-map + '(("\C-i" . comint-dynamic-complete-filename)) + "Keymap for minibuffer prompting of gud startup command." + :inherit minibuffer-local-map) + +;; FIXME: I think this code and the keymaps and history +;; variable chould be generalized, perhaps via a macro. +(defun ipdb-query-cmdline (&optional opt-debugger) + (realgud-query-cmdline + 'ipdb-suggest-invocation + ipdb-minibuffer-local-map + 'realgud:ipdb-minibuffer-history + opt-debugger)) + +(defun ipdb-parse-cmd-args (orig-args) + "Parse command line ORIG-ARGS for the annotate level and name of script to debug. + +ORIG-ARGS should contain a tokenized list of the command line to run. + +We return the a list containing: +* the command processor (e.g. python) and it's arguments if any - a list of strings +* the name of the debugger given (e.g. ipdb) and its arguments - a list of strings +* the script name and its arguments - list of strings +* whether the annotate or emacs option was given ('-A', '--annotate' or '--emacs) - a boolean + +For example for the following input: + (map 'list 'symbol-name + '(python2.6 -O -Qold ./gcd.py a b)) + +we might return: + ((\"python2.6\" \"-O\" \"-Qold\") (\"ipdb\") (\"/tmp/gcd.py\" \"a\" \"b\") nil) + +Note that the script name path has been expanded via `expand-file-name'. +" + + ;; Parse the following kind of pattern: + ;; [python python-options] ipdb ipdb-options script-name script-options + (let ( + (args orig-args) + (pair) ;; temp return from + (python-opt-two-args '()) + ;; Python doesn't have mandatory 2-arg options in our sense, + ;; since the two args can be run together, e.g. "-C/tmp" or "-C /tmp" + ;; + (python-two-args '()) + ;; ipdb doesn't have any arguments + (ipdb-two-args '()) + (ipdb-opt-two-args '()) + (interp-regexp + (if (member system-type (list 'windows-nt 'cygwin 'msdos)) + "^python[-0-9.]*\\(.exe\\)?$" + "^python[-0-9.]*$")) + + ;; Things returned + (annotate-p nil) + (debugger-args '()) + (debugger-name nil) + (interpreter-args '()) + (script-args '()) + (script-name nil) + ) + + (if (not (and args)) + ;; Got nothing: return '(nil, nil) + (list interpreter-args debugger-args script-args annotate-p) + ;; else + ;; Strip off optional "python" or "python182" etc. + (when (string-match interp-regexp + (file-name-sans-extension + (file-name-nondirectory (car args)))) + (setq interpreter-args (list (pop args))) + + ;; Strip off Python-specific options + (while (and args + (string-match "^-" (car args))) + (setq pair (realgud-parse-command-arg + args python-two-args python-opt-two-args)) + (nconc interpreter-args (car pair)) + (setq args (cadr pair)))) + + ;; Remove "ipdb" from "ipdb --ipdb-options script + ;; --script-options" + (setq debugger-name (file-name-sans-extension + (file-name-nondirectory (car args)))) + (unless (string-match "^\\(ipdb\\|cli.py\\)$" debugger-name) + (message + "Expecting debugger name `%s' to be `ipdb' or `cli.py'" + debugger-name)) + (setq debugger-args (list (pop args))) + + ;; Skip to the first non-option argument. + (while (and args (not script-name)) + (let ((arg (car args))) + (cond + ;; Options with arguments. + ((string-match "^-" arg) + (setq pair (realgud-parse-command-arg + args ipdb-two-args ipdb-opt-two-args)) + (nconc debugger-args (car pair)) + (setq args (cadr pair))) + ;; Anything else must be the script to debug. + (t (setq script-name (expand-file-name arg)) + (setq script-args (cons script-name (cdr args)))) + ))) + (list interpreter-args debugger-args script-args annotate-p)))) + +(defun ipdb-parse-remote-cmd-args (orig-args) + "Parse command line ORIG-ARGS +ORIG-ARGS should contain a tokenized list of the command line to run. + +We return the a list containing: +* the command processor (e.g. python) and it's arguments if any - a list of strings +* the name of the debugger given (e.g. ipdb) and its arguments - a list of strings +* the script name and its arguments - list of strings +* nil + +For example for the following input: + (map 'list 'symbol-name + '(telnet localhost 6900)) + +we might return: + ((\"telnet\" \"localhost\" \"6900\") (\"ipdb\") (\"\") nil) + +Note that the script name path has been expanded via `expand-file-name'. +" + (list orig-args '("ipdb") '("") nil) + ) + + ;; To silence Warning: reference to free variable +(defvar realgud:ipdb-command-name) + +(defun ipdb-suggest-invocation (debugger-name) + "Suggest a ipdb command invocation via `realgud-suggest-invocaton'" + (realgud-suggest-invocation realgud:ipdb-command-name + realgud:ipdb-minibuffer-history + "python" "\\.py")) + +(defun ipdb-reset () + "Ipdb cleanup - remove debugger's internal buffers (frame, +breakpoints, etc.)." + (interactive) + ;; (ipdb-breakpoint-remove-all-icons) + (dolist (buffer (buffer-list)) + (when (string-match "\\*ipdb-[a-z]+\\*" (buffer-name buffer)) + (let ((w (get-buffer-window buffer))) + (when w + (delete-window w))) + (kill-buffer buffer)))) + +;; (defun ipdb-reset-keymaps() +;; "This unbinds the special debugger keys of the source buffers." +;; (interactive) +;; (setcdr (assq 'ipdb-debugger-support-minor-mode minor-mode-map-alist) +;; ipdb-debugger-support-minor-mode-map-when-deactive)) + + +(defun realgud:ipdb-customize () + "Use `customize' to edit the settings of the `ipdb' debugger." + (interactive) + (customize-group 'realgud:ipdb)) + +(provide-me "realgud:ipdb-") diff --git a/realgud/debugger/ipdb/init.el b/realgud/debugger/ipdb/init.el new file mode 100644 index 0000000..c854f68 --- /dev/null +++ b/realgud/debugger/ipdb/init.el @@ -0,0 +1,120 @@ +;; Copyright (C) 2016 Free Software Foundation, Inc + +;; Author: Rocky Bernstein <ro...@gnu.org> +;; Author: Sean Farley <s...@farley.io> + +;; 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/>. +;; Stock Python debugger ipdb + +(eval-when-compile (require 'cl)) + +(require 'load-relative) +(require-relative-list '("../../common/regexp" + "../../common/loc" + "../../common/init") + "realgud-") +(require-relative-list '("../../lang/python") "realgud-lang-") + +(defvar realgud-pat-hash) +(declare-function make-realgud-loc-pat (realgud-loc)) + +(defvar realgud:ipdb-pat-hash (make-hash-table :test 'equal) + "Hash key is the what kind of pattern we want to match: +backtrace, prompt, etc. The values of a hash entry is a +realgud-loc-pat struct") + +(declare-function make-realgud-loc "realgud-loc" (a b c d e f)) + +;; Regular expression that describes a ipdb location generally shown +;; before a command prompt. +;; +;; Program-location lines look like this: +;; > /usr/bin/zonetab2pot.py(15)<module>() +;; or MS Windows: +;; > c:\\mydirectory\\gcd.py(10)<module> +(setf (gethash "loc" realgud:ipdb-pat-hash) + (make-realgud-loc-pat + :regexp "^> \\(\\(?:[a-zA-Z]:\\)?[-a-zA-Z0-9_/.\\\\ ]+\\)(\\([0-9]+\\))" + :file-group 1 + :line-group 2)) + +(setf (gethash "prompt" realgud:ipdb-pat-hash) + (make-realgud-loc-pat + :regexp "^ipdb[>] " + )) + +;; Regular expression that describes a Python backtrace line. +(setf (gethash "lang-backtrace" realgud:ipdb-pat-hash) + realgud-python-backtrace-loc-pat) + +;; Regular expression that describes location in a pytest error +(setf (gethash "pytest-error" realgud:ipdb-pat-hash) + realgud-pytest-error-loc-pat) + +;; Regular expression that describes a "breakpoint set" line. For example: +;; Breakpoint 1 at /usr/bin/ipdb:7 +(setf (gethash "brkpt-set" realgud:ipdb-pat-hash) + (make-realgud-loc-pat + :regexp "^Breakpoint \\([0-9]+\\) at[ \t\n]+\\(.+\\):\\([0-9]+\\)\\(\n\\|$\\)" + :num 1 + :file-group 2 + :line-group 3)) + +;; Regular expression that describes a "delete breakpoint" line +(setf (gethash "brkpt-del" realgud:ipdb-pat-hash) + (make-realgud-loc-pat + :regexp "^Deleted breakpoint \\([0-9]+\\)\n" + :num 1)) + +(setf (gethash "font-lock-keywords" realgud:ipdb-pat-hash) + '( + ;; The frame number and first type name, if present. + ("^\\(->\\|##\\)\\([0-9]+\\) \\(<module>\\)? *\\([a-zA-Z_][a-zA-Z0-9_]*\\)(\\(.+\\))?" + (2 realgud-backtrace-number-face) + (4 font-lock-function-name-face nil t)) ; t means optional. + + ;; Parameter sequence, E.g. gcd(a=3, b=5) + ;; ^^^^^^^^^ + ("(\\(.+\\))" + (1 font-lock-variable-name-face)) + + ;; File name. E.g file '/test/gcd.py' + ;; ------^^^^^^^^^^^^- + ("[ \t]+file '\\([^ ]+*\\)'" + (1 realgud-file-name-face)) + + ;; Line number. E.g. at line 28 + ;; ---------^^ + ("[ \t]+at line \\([0-9]+\\)$" + (1 realgud-line-number-face)) + + ;; Function name. + ("\\<\\([a-zA-Z_][a-zA-Z0-9_]*\\)\\.\\([a-zA-Z_][a-zA-Z0-9_]*\\)" + (1 font-lock-type-face) + (2 font-lock-function-name-face)) + ;; (ipdb-frames-match-current-line + ;; (0 ipdb-frames-current-frame-face append)) + )) + +(setf (gethash "ipdb" realgud-pat-hash) realgud:ipdb-pat-hash) + +(defvar realgud:ipdb-command-hash (make-hash-table :test 'equal) + "Hash key is command name like 'shell' and the value is + the ipdb command to use, like 'python'") + +(setf (gethash "shell" realgud:ipdb-command-hash) "python") +(setf (gethash "eval" realgud:ipdb-command-hash) "p %s") +(setf (gethash "ipdb" realgud-command-hash) realgud:ipdb-command-hash) + +(provide-me "realgud:ipdb-") diff --git a/realgud/debugger/ipdb/ipdb.el b/realgud/debugger/ipdb/ipdb.el new file mode 100644 index 0000000..29d0540 --- /dev/null +++ b/realgud/debugger/ipdb/ipdb.el @@ -0,0 +1,109 @@ +;; Copyright (C) 2016 Free Software Foundation, Inc + +;; Author: Rocky Bernstein <ro...@gnu.org> +;; Author: Sean Farley <s...@farley.io> + +;; 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/>. + +;; `ipdb' Main interface to ipdb via Emacs +(require 'load-relative) +(require-relative-list '("core" "track-mode") "realgud:ipdb-") +(require-relative-list '("../../common/run") "realgud:") +(require-relative-list '("core" "track-mode") "realgud:ipdb-") + +;; This is needed, or at least the docstring part of it is needed to +;; get the customization menu to work in Emacs 24. +(defgroup realgud:ipdb nil + "The realgud interface to the Python ipdb debugger" + :group 'realgud + :version "24.1") + +;; ------------------------------------------------------------------- +;; User-definable variables +;; + +(defcustom realgud:ipdb-command-name + "ipdb" + "File name for executing the stock Python debugger and command options. +This should be an executable on your path, or an absolute file name." + :type 'string + :group 'realgud:ipdb) +;; ------------------------------------------------------------------- +;; The end. +;; + +(declare-function ipdb-track-mode 'realgud:ipdb-track) +(declare-function ipdb-query-cmdline 'realgud:ipdb-core) +(declare-function ipdb-parse-cmd-args 'realgud:ipdb-core) +(declare-function realgud:run-debugger 'realgud:run) + +;;;###autoload +(defun realgud:ipdb (&optional opt-cmd-line no-reset) + "Invoke the ipdb Python debugger and start the Emacs user interface. + +String OPT-CMD-LINE specifies how to run ipdb. You will be prompted +for a command line is one isn't supplied. + +OPT-COMMAND-LINE is treated like a shell string; arguments are +tokenized by `split-string-and-unquote'. The tokenized string is +parsed by `ipdb-parse-cmd-args' and path elements found by that +are expanded using `realgud:expand-file-name-if-exists'. + +Normally, command buffers are reused when the same debugger is +reinvoked inside a command buffer with a similar command. If we +discover that the buffer has prior command-buffer information and +NO-RESET is nil, then that information which may point into other +buffers and source buffers which may contain marks and fringe or +marginal icons is reset. See `loc-changes-clear-buffer' to clear +fringe and marginal icons. +" + (interactive) + (realgud:run-debugger "ipdb" 'ipdb-query-cmdline + 'ipdb-parse-cmd-args + 'realgud:ipdb-minibuffer-history + opt-cmd-line no-reset) + ) + + +;;;###autoload +(defun realgud:ipdb-remote (&optional opt-cmd-line no-reset) + "Invoke the ipdb Python debugger and start the Emacs user interface. + +String OPT-CMD-LINE specifies how to run ipdb. You will be prompted +for a command line is one isn't supplied. + +OPT-COMMAND-LINE is treated like a shell string; arguments are +tokenized by `split-string-and-unquote'. The tokenized string is +parsed by `ipdb-parse-remote-cmd-args' and path elements found by that +are expanded using `realgud:expand-file-name-if-exists'. + +Normally, command buffers are reused when the same debugger is +reinvoked inside a command buffer with a similar command. If we +discover that the buffer has prior command-buffer information and +NO-RESET is nil, then that information which may point into other +buffers and source buffers which may contain marks and fringe or +marginal icons is reset. See `loc-changes-clear-buffer' to clear +fringe and marginal icons. +" + (interactive) + (realgud:run-debugger "ipdb" 'ipdb-remote-query-cmdline + 'ipdb-parse-remote-cmd-args + 'realgud:ipdb-remote-minibuffer-history + opt-cmd-line no-reset) + ) + + +(defalias 'ipdb 'realgud:ipdb) + +(provide-me "realgud-") diff --git a/realgud/debugger/ipdb/track-mode.el b/realgud/debugger/ipdb/track-mode.el new file mode 100644 index 0000000..abe8d70 --- /dev/null +++ b/realgud/debugger/ipdb/track-mode.el @@ -0,0 +1,75 @@ +;; Copyright (C) 2016 Free Software Foundation, Inc + +;; Author: Rocky Bernstein <ro...@gnu.org> +;; Author: Sean Farley <s...@farley.io> + +;; 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/>. +;; Python "ipdb" Debugger tracking a comint buffer. + +(eval-when-compile (require 'cl)) +(require 'load-relative) +(require-relative-list '( + "../../common/cmds" + "../../common/menu" + "../../common/track" + "../../common/track-mode" + ) + "realgud-") +(require-relative-list '("core" "init") "realgud:ipdb-") + +(realgud-track-mode-vars "ipdb") + +(declare-function realgud-track-mode 'realgud-track-mode) +(declare-function realgud-track-mode-setup 'realgud-track-mode) +(declare-function realgud:track-set-debugger 'realgud-track-mode) +(declare-function realgud-python-populate-command-keys 'realgud-lang-python) + +(realgud-python-populate-command-keys ipdb-track-mode-map) + +(defun ipdb-track-mode-hook() + (if ipdb-track-mode + (progn + (use-local-map ipdb-track-mode-map) + (message "using ipdb mode map") + ) + (message "ipdb track-mode-hook disable called") + ) +) + +(define-minor-mode ipdb-track-mode + "Minor mode for tracking ipdb source locations inside a process shell via realgud. ipdb is a Python debugger based on ipython. + +If called interactively with no prefix argument, the mode is toggled. A prefix argument, captured as ARG, enables the mode if the argument is positive, and disables it otherwise. + +a process shell. + +\\{ipdb-track-mode-map} +" + :init-value nil + ;; :lighter " ipdb" ;; mode-line indicator from realgud-track is sufficient. + ;; The minor mode bindings. + :global nil + :group 'realgud:ipdb + :keymap ipdb-track-mode-map + (realgud:track-set-debugger "ipdb") + (if ipdb-track-mode + (progn + (realgud-track-mode-setup 't) + (ipdb-track-mode-hook)) + (progn + (setq realgud-track-mode nil) + )) +) + +(provide-me "realgud:ipdb-")