branch: master commit 924969d35faeed6e809d55e3a2a005d14aa1db3d Author: Sean Farley <s...@farley.io> Commit: Sean Farley <s...@farley.io>
ipdb: add tab completion --- realgud/debugger/ipdb/core.el | 65 +++++++++++++++++++++++++++++++++++++++++ realgud/debugger/ipdb/ipdb.el | 1 + 2 files changed, 66 insertions(+) diff --git a/realgud/debugger/ipdb/core.el b/realgud/debugger/ipdb/core.el index fa697f8..43a42c5 100644 --- a/realgud/debugger/ipdb/core.el +++ b/realgud/debugger/ipdb/core.el @@ -195,6 +195,71 @@ breakpoints, etc.)." ;; (setcdr (assq 'ipdb-debugger-support-minor-mode minor-mode-map-alist) ;; ipdb-debugger-support-minor-mode-map-when-deactive)) +(defun realgud:ipdb-backend-complete () + "Send a command to the ipdb buffer and parse the output. + +The idea here is to rely on the +`comint-redirect-send-command-to-process' function to send a +python command that will return the completions for the given +input. Specifically, here is the python code: + +>>> from IPython import get_ipython +>>> comp = '''%s''' +>>> ';'.join(get_ipython().complete(comp.split()[-1] if len(comp)else '', comp)[1]) + +This returns a list of strings that match the current word (hence +why we need the `bounds-of-thing-at-point')." + (interactive) + (let ((buffer (current-buffer)) + (cmdbuf (realgud-get-cmdbuf)) + (process (get-buffer-process (current-buffer))) + (end-position (point)) + (bounds (bounds-of-thing-at-point 'word)) + ) + + ;; get the input string + (save-excursion + (comint-goto-process-mark) + (let* ((start-position (point)) + (input-str (buffer-substring-no-properties start-position + end-position)) + ) + (when (not (= (length input-str) 0)) + (let* ((python-str (concat + "from IPython import get_ipython; " + "comp = '''%s''';" + "';'.join(get_ipython()" + ".complete(comp.split()[-1] if len(comp)" + "else '', comp)[1])")) + (command-str (format python-str input-str)) + (output-str (with-temp-buffer + (let ((tmpbuf (current-buffer))) + (comint-redirect-send-command-to-process + command-str tmpbuf process nil t) + ;; Wait for the process to complete + (set-buffer (process-buffer process)) + (while (null comint-redirect-completed) + (accept-process-output nil 0 5)) ;; wait 5ms + (set-buffer tmpbuf) + (buffer-substring (1+ (point-min)) + (1- (1- (point-max))))))) + ) + + ;; we need to change the start position to that of the current word + ;; since python returns just the word (and not the whole line) + (setq start-position (car bounds)) + + (list start-position + end-position + (split-string output-str ";")))))))) + +(defun realgud:ipdb-completion-at-point () + (let ((ipdb (realgud:ipdb-backend-complete))) + (when ipdb + (list (nth 0 ipdb) + (nth 1 ipdb) + (nth 2 ipdb) + :exclusive 'yes)))) (defun realgud:ipdb-customize () "Use `customize' to edit the settings of the `ipdb' debugger." diff --git a/realgud/debugger/ipdb/ipdb.el b/realgud/debugger/ipdb/ipdb.el index 29d0540..4f12301 100644 --- a/realgud/debugger/ipdb/ipdb.el +++ b/realgud/debugger/ipdb/ipdb.el @@ -46,6 +46,7 @@ This should be an executable on your path, or an absolute file name." (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:ipdb-completion-at-point 'realgud:ipdb-core) (declare-function realgud:run-debugger 'realgud:run) ;;;###autoload