Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
7.5 KiB

  1. ;;; ein-ipdb.el --- Support ipython debugger (ipdb)
  2. ;; Copyright (C) 2015 - John Miller
  3. ;; Author: John Miller <millejoh at mac.com>
  4. ;; This file is NOT part of GNU Emacs.
  5. ;; ein-ipdb.el is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; ein-ipdb.el is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with ein-kernel.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;;; Code:
  18. (defvar *ein:ipdb-sessions* (make-hash-table))
  19. (defconst *ein:ipdb-prompt* "(Pdb) ")
  20. (defvar ein:ipdb-buffer-active-kernel nil)
  21. (defvar ein:ipdb-buffer-prompt nil)
  22. (cl-defstruct ein:$ipdb-session
  23. buffer
  24. notebook-buffer
  25. kernel
  26. current-payload)
  27. (defun ein:ipbd ()
  28. "Convenience function that will launch the ipython debugger,
  29. assuming there is an active kernel associated with the current
  30. buffer. For more information see the %debug magic documentation
  31. in ipython."
  32. (interactive)
  33. (ein:shared-output-eval-string (ein:get-kernel)
  34. "%debug"
  35. nil))
  36. (defun ein:find-or-create-ipdb-session (kernel &optional buffer)
  37. (ein:aif (gethash (ein:$kernel-kernel-id kernel) *ein:ipdb-sessions*)
  38. it
  39. (let ((db-session (make-ein:$ipdb-session
  40. :kernel kernel
  41. :notebook-buffer buffer
  42. :current-payload "Debugger started.")))
  43. (setf (gethash (ein:$kernel-kernel-id kernel) *ein:ipdb-sessions*) db-session)
  44. db-session)))
  45. (defun ein:pdb-session-id (session)
  46. (ein:$kernel-kernel-id (ein:$ipdb-session-kernel session)))
  47. (defun ein:run-ipdb-session (kernel prompt)
  48. (unless (gethash (ein:$kernel-kernel-id kernel) *ein:ipdb-sessions*)
  49. (let ((pdb-session (ein:find-or-create-ipdb-session kernel (current-buffer))))
  50. (setf (ein:$kernel-stdin-activep kernel) t)
  51. (ein:prepare-ipdb-session pdb-session prompt))))
  52. (defun ein:prepare-ipdb-session-old (session prompt)
  53. (with-current-buffer (setf (ein:$ipdb-session-buffer session)
  54. (get-buffer-create (format "*ipdb: %s*" (ein:pdb-session-id session))))
  55. (add-hook 'kill-buffer-hook 'ein:ipdb-on-stop)
  56. (ein:ipdb-mode)
  57. (setq comint-prompt-regexp (concat "^" (regexp-quote prompt)))
  58. (setq comint-input-sender 'ein:ipdb-input-sender)
  59. (unless (comint-check-proc (current-buffer))
  60. ;; Was cat, but on non-Unix platforms that might not exist, so
  61. ;; use hexl instead, which is part of the Emacs distribution.
  62. (let ((fake-proc
  63. (condition-case nil
  64. (start-process "ein:ipdb" (current-buffer) "cat")
  65. (file-error (start-process "ein:ipdb" (current-buffer) "hexl")))))
  66. (set-process-query-on-exit-flag fake-proc nil)
  67. (insert "#ipdb#\n")
  68. (set-marker
  69. (process-mark fake-proc) (point))
  70. (comint-output-filter fake-proc prompt)))
  71. (setq ein:ipdb-buffer-active-kernel (ein:pdb-session-id session))
  72. (setq ein:ipdb-buffer-prompt prompt)
  73. (switch-to-buffer (ein:$ipdb-session-buffer session))))
  74. (defun ein:prepare-ipdb-session (session prompt)
  75. (with-current-buffer (setf (ein:$ipdb-session-buffer session)
  76. (get-buffer-create (format "*ipdb: %s*" (ein:pdb-session-id session))))
  77. (set (make-local-variable 'ein:ipdb-buffer-active-kernel) (ein:pdb-session-id session))
  78. (set (make-local-variable 'ein:ipdb-buffer-prompt) prompt)
  79. (put 'ein:ipdb-buffer-active-kernel 'permanent-local t)
  80. (put 'ein:ipdb-buffer-prompt 'permanent-local t)
  81. (add-hook 'kill-buffer-hook 'ein:ipdb-on-stop)
  82. (ein:ipdb-mode)
  83. (pop-to-buffer (ein:$ipdb-session-buffer session))))
  84. (defun ein:ipdb-on-stop ()
  85. (when ein:ipdb-buffer-active-kernel
  86. (let* ((kernel (ein:$ipdb-session-kernel
  87. (gethash ein:ipdb-buffer-active-kernel *ein:ipdb-sessions*)))
  88. (msg (ein:kernel--get-msg kernel "input_reply" (list :value "q"))))
  89. (ein:websocket-send-stdin-channel kernel msg)
  90. (setf (ein:$kernel-stdin-activep kernel) nil)
  91. (remhash ein:ipdb-buffer-active-kernel *ein:ipdb-sessions*))))
  92. (defun ein:ipdb--handle-iopub-reply (kernel packet)
  93. (cl-destructuring-bind
  94. (&key content metadata parent_header header &allow-other-keys)
  95. (ein:json-read-from-string packet)
  96. (let ((msg-type (plist-get header :msg_type)))
  97. (if (string-equal msg-type "stream")
  98. (let* ((session (ein:find-or-create-ipdb-session kernel))
  99. (buf (ein:$ipdb-session-buffer session))
  100. (text (plist-get content :text)))
  101. (with-current-buffer buf
  102. (setf (ein:$ipdb-session-current-payload session) text)
  103. (let ((buffer-read-only nil)
  104. (proc (get-buffer-process buf)))
  105. (comint-output-filter proc text)
  106. (comint-output-filter proc ein:ipdb-buffer-prompt))
  107. (when ein:ipdb--received-quit-p
  108. (kill-buffer)
  109. (ein:aif (ein:$ipdb-session-notebook-buffer session)
  110. (pop-to-buffer it)))))))))
  111. ;;; Now try with comint
  112. (defun ein:ipdb-input-sender (proc input)
  113. (with-current-buffer (process-buffer proc)
  114. (cl-assert (not (null ein:ipdb-buffer-active-kernel)) t "No active kernel associated with this buffer %s.")
  115. (let* ((session (gethash ein:ipdb-buffer-active-kernel *ein:ipdb-sessions*))
  116. (buffer-read-only nil)
  117. (kernel (ein:$ipdb-session-kernel session))
  118. (content (list :value input))
  119. (msg (ein:kernel--get-msg kernel "input_reply" content)))
  120. (ein:websocket-send-stdin-channel kernel msg)
  121. (when (or (string= input "q")
  122. (string= input "quit"))
  123. (setq ein:ipdb--received-quit-p t)))))
  124. (defun ein:ipdb-buffer-initialize ()
  125. "Helper function to initialize a newly minted ein:ipdb buffer."
  126. (setq comint-use-prompt-regexp t))
  127. (defvar ein:ipdb--received-quit-p nil)
  128. (define-derived-mode ein:ipdb-mode comint-mode "EIN:IPDB"
  129. "Run an EIN debug session.
  130. \\<ein:ipdb-mode-map>"
  131. ;:syntax-table python-mode-syntax-table
  132. (setq comint-prompt-regexp (concat "^" (regexp-quote ein:ipdb-buffer-prompt)))
  133. (setq comint-input-sender 'ein:ipdb-input-sender)
  134. (setq comint-prompt-read-only t)
  135. (set (make-local-variable 'comint-output-filter-functions)
  136. '(ansi-color-process-output
  137. python-pdbtrack-comint-output-filter-function))
  138. (set (make-local-variable 'ein:ipdb--received-quit-p) nil)
  139. (unless (comint-check-proc (current-buffer))
  140. ;; Was cat, but on non-Unix platforms that might not exist, so
  141. ;; use hexl instead, which is part of the Emacs distribution.
  142. (let ((fake-proc
  143. (condition-case nil
  144. (start-process "ein:ipdb" (current-buffer) "cat")
  145. (file-error (start-process "ein:ipdb" (current-buffer) "hexl")))))
  146. (set-process-query-on-exit-flag fake-proc nil)
  147. (insert "=== EIN IPython Debugger ===\n")
  148. (set-marker (process-mark fake-proc) (point))
  149. (comint-output-filter fake-proc ein:ipdb-buffer-prompt))))
  150. (add-hook 'ein:ipdb-mode-hook 'ein:ipdb-buffer-initialize)
  151. (provide 'ein-ipdb)