ein-connect.el 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. ;;; ein-connect.el --- Connect external buffers to IPython
  2. ;; Copyright (C) 2012- Takafumi Arakaki
  3. ;; Author: Takafumi Arakaki <aka.tkf at gmail.com>
  4. ;; This file is NOT part of GNU Emacs.
  5. ;; ein-connect.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-connect.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-connect.el. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; FIXME: There is a problem when connected notebook is closed.
  17. ;; This can be fixed in some ways:
  18. ;; * Turn off ein:connect when the command that uses kernel is invoked
  19. ;; but corresponding notebook was closed already.
  20. ;; * Connect directly to ein:kernel and make its destructor to care
  21. ;; about connecting buffers.
  22. ;;; Code:
  23. (require 'eieio)
  24. (eval-when-compile (require 'auto-complete nil t))
  25. (require 'ein-notebook)
  26. (declare-function ein:notebooklist-list-notebooks "ein-notebooklist")
  27. (declare-function ein:notebooklist-open-notebook-global "ein-notebooklist")
  28. ;;; Utils
  29. (defun ein:maybe-save-buffer (option)
  30. "Conditionally save current buffer.
  31. Return `t' if the buffer is unmodified or `nil' otherwise.
  32. If the buffer is modified, buffer is saved depending on the value
  33. of OPTION:
  34. ask : Ask whether the buffer should be saved.
  35. yes : Save buffer always.
  36. no : Do not save buffer."
  37. (if (not (buffer-modified-p))
  38. t
  39. (case option
  40. (ask (when (y-or-n-p "Save buffer? ")
  41. (save-buffer)
  42. t))
  43. (yes (save-buffer)
  44. t)
  45. (t nil))))
  46. ;;; Configuration
  47. (defcustom ein:connect-run-command "%run"
  48. "``%run`` magic command used for `ein:connect-run-buffer'.
  49. Types same as `ein:console-security-dir' are valid."
  50. :type '(choice
  51. (string :tag "command" "%run")
  52. (alist :tag "command mapping"
  53. :key-type (choice :tag "URL or PORT"
  54. (string :tag "URL" "http://127.0.0.1:8888")
  55. (integer :tag "PORT" 8888)
  56. (const default))
  57. :value-type (string :tag "command" "%run"))
  58. (function :tag "command getter"
  59. (lambda (url-or-port) (format "%%run -n -i -t -d"))))
  60. :group 'ein)
  61. (defcustom ein:connect-reload-command "%run -n"
  62. "Setting for `ein:connect-reload-buffer'.
  63. Same as `ein:connect-run-command'."
  64. :type '(choice
  65. (string :tag "command" "%run")
  66. (alist :tag "command mapping"
  67. :key-type (choice :tag "URL or PORT"
  68. (string :tag "URL" "http://127.0.0.1:8888")
  69. (integer :tag "PORT" 8888)
  70. (const default))
  71. :value-type (string :tag "command" "%run"))
  72. (function :tag "command getter"
  73. (lambda (url-or-port) (format "%%run -n -i -t -d"))))
  74. :group 'ein)
  75. (defun ein:connect-run-command-get ()
  76. (ein:choose-setting 'ein:connect-run-command
  77. (ein:$notebook-url-or-port (ein:connect-get-notebook))))
  78. (defcustom ein:connect-save-before-run 'yes
  79. "Whether the buffer should be saved before `ein:connect-run-buffer'."
  80. :type '(choice (const :tag "Always save buffer" yes)
  81. (const :tag "Always do not save buffer" no)
  82. (const :tag "Ask" ask))
  83. :group 'ein)
  84. (defcustom ein:connect-aotoexec-lighter nil
  85. "String appended to the lighter of `ein:connect-mode' (`ein:c')
  86. when auto-execution mode is on. When `nil', use the same string
  87. as `ein:cell-autoexec-prompt'."
  88. :type '(choice (string :tag "String appended to ein:c" "@")
  89. (const :tag "Use `ein:cell-autoexec-prompt'." nil))
  90. :group 'ein)
  91. (defcustom ein:connect-default-notebook nil
  92. "Notebook to be connect when `ein:connect-to-default-notebook' is called.
  93. Example setting to connect to \"My_Notebook\" in the server at
  94. port 8888 when opening any buffer in `python-mode'::
  95. (setq ein:connect-default-notebook \"8888/My_Notebook\")
  96. (add-hook 'python-mode-hook 'ein:connect-to-default-notebook)
  97. `ein:connect-default-notebook' can also be a function without any
  98. argument. This function must return a string (notebook path of
  99. the form \"URL-OR-PORT/NOTEBOOK-NAME\").
  100. As `ein:connect-to-default-notebook' requires notebook list to be
  101. loaded, consider using `ein:notebooklist-load' to load notebook
  102. list if you want to connect to notebook without manually opening
  103. notebook list."
  104. :type '(choice (string :tag "URL-OR-PORT/NOTEBOOK-NAME")
  105. (function :tag "Notebook path getter"))
  106. :group 'ein)
  107. ;;; Class
  108. (ein:deflocal ein:%connect% nil
  109. "Buffer local variable to store an instance of `ein:connect'")
  110. (define-obsolete-variable-alias 'ein:@connect 'ein:%connect% "0.1.2")
  111. (defclass ein:connect ()
  112. ((notebook :initarg :notebook :type ein:$notebook)
  113. (buffer :initarg :buffer :type buffer)
  114. (autoexec :initarg :autoexec :initform nil :type boolean
  115. :document "Auto-execution mode flag.
  116. See also the document of the `autoexec' slot of `ein:codecell'
  117. class.")))
  118. (defun ein:connect-setup (notebook buffer)
  119. (with-current-buffer buffer
  120. (setq ein:%connect%
  121. (ein:connect "Connect" :notebook notebook :buffer buffer))
  122. ein:%connect%))
  123. ;;; Methods
  124. ;; FIXME: Clarify names of these `connect-to-*' functions:
  125. ;;;###autoload
  126. (defun ein:connect-to-notebook-command (&optional not-yet-opened)
  127. "Connect to notebook. When the prefix argument is given,
  128. you can choose any notebook on your server including the ones
  129. not yet opened. Otherwise, already chose from already opened
  130. notebooks."
  131. (interactive "P")
  132. (call-interactively (if not-yet-opened
  133. #'ein:connect-to-notebook
  134. #'ein:connect-to-notebook-buffer)))
  135. ;;;###autoload
  136. (defun ein:connect-to-notebook (nbpath &optional buffer no-reconnection)
  137. "Connect any buffer to notebook and its kernel."
  138. (interactive
  139. (list
  140. (completing-read
  141. "Notebook to connect [URL-OR-PORT/NAME]: "
  142. (ein:notebooklist-list-notebooks))))
  143. (ein:notebooklist-open-notebook-global
  144. nbpath
  145. (lambda (notebook -ignore- buffer no-reconnection)
  146. (ein:connect-buffer-to-notebook notebook buffer no-reconnection))
  147. (list (or buffer (current-buffer)) no-reconnection)))
  148. ;;;###autoload
  149. (defun ein:connect-to-notebook-buffer (buffer-or-name)
  150. "Connect any buffer to opened notebook and its kernel."
  151. (interactive (list (completing-read "Notebook buffer to connect: "
  152. (ein:notebook-opened-buffer-names))))
  153. (let ((notebook
  154. (buffer-local-value 'ein:%notebook% (get-buffer buffer-or-name))))
  155. (ein:connect-buffer-to-notebook notebook)))
  156. ;;;###autoload
  157. (defun ein:connect-buffer-to-notebook (notebook &optional buffer
  158. no-reconnection)
  159. "Connect BUFFER to NOTEBOOK."
  160. (unless buffer
  161. (setq buffer (current-buffer)))
  162. (with-current-buffer buffer
  163. (if (or (not no-reconnection)
  164. (not ein:%connect%))
  165. (let ((connection (ein:connect-setup notebook buffer)))
  166. (when (ein:eval-if-bound 'ac-sources)
  167. (push 'ac-source-ein-async ac-sources))
  168. (ein:connect-mode)
  169. (ein:log 'info "Connected to %s"
  170. (ein:$notebook-notebook-name notebook))
  171. connection)
  172. (ein:log 'info "Buffer is already connected to notebook."))))
  173. (defun ein:connect-get-notebook ()
  174. (oref ein:%connect% :notebook))
  175. (defun ein:connect-get-kernel ()
  176. (ein:$notebook-kernel (ein:connect-get-notebook)))
  177. (defun ein:connect-eval-buffer ()
  178. "Evaluate the whole buffer. Note that this will run the code
  179. inside the ``if __name__ == \"__main__\":`` block."
  180. (interactive)
  181. (ein:shared-output-eval-string (buffer-string) nil nil nil :silent t)
  182. (ein:connect-execute-autoexec-cells)
  183. (ein:log 'info "Whole buffer is sent to the kernel."))
  184. (defun ein:connect-run-buffer (&optional ask-command)
  185. "Run buffer using ``%run``. Ask for command if the prefix ``C-u`` is given.
  186. Variable `ein:connect-run-command' sets the default command."
  187. (interactive "P")
  188. (ein:aif (ein:aand (ein:get-url-or-port)
  189. (ein:filename-to-python it (buffer-file-name)))
  190. (let* ((default-command (ein:connect-run-command-get))
  191. (command (if ask-command
  192. (read-from-minibuffer "Command: " default-command)
  193. default-command))
  194. (cmd (format "%s %s" command it)))
  195. (if (ein:maybe-save-buffer ein:connect-save-before-run)
  196. (progn
  197. (ein:shared-output-eval-string cmd nil nil nil :silent t)
  198. (ein:connect-execute-autoexec-cells)
  199. (ein:log 'info "Command sent to the kernel: %s" cmd))
  200. (ein:log 'info "Buffer must be saved before %%run.")))
  201. (error (concat "This buffer has no associated file. "
  202. "Use `ein:connect-eval-buffer' instead."))))
  203. (defun ein:connect-run-or-eval-buffer (&optional eval)
  204. "Run buffer using the ``%run`` magic command or eval whole
  205. buffer if the prefix ``C-u`` is given.
  206. Variable `ein:connect-run-command' sets the command to run.
  207. You can change the command and/or set the options.
  208. See also: `ein:connect-run-buffer', `ein:connect-eval-buffer'."
  209. (interactive "P")
  210. (if eval
  211. (ein:connect-eval-buffer)
  212. (ein:connect-run-buffer)))
  213. (defun ein:connect-reload-buffer ()
  214. "Reload buffer using the command set by `ein:connect-reload-command'."
  215. (interactive)
  216. (let ((ein:connect-run-command ein:connect-reload-command))
  217. (call-interactively #'ein:connect-run-buffer)))
  218. (defun ein:connect-eval-region (start end)
  219. (interactive "r")
  220. (ein:shared-output-eval-string (buffer-substring start end))
  221. (ein:log 'info "Selected region is sent to the kernel."))
  222. (define-obsolete-function-alias
  223. 'ein:connect-eval-string-internal
  224. 'ein:shared-output-eval-string "0.1.2")
  225. (define-obsolete-function-alias
  226. 'ein:connect-request-tool-tip-or-help-command
  227. 'ein:pytools-request-tooltip-or-help "0.1.2")
  228. (defun ein:connect-pop-to-notebook ()
  229. (interactive)
  230. (ein:connect-assert-connected)
  231. (pop-to-buffer (ein:notebook-buffer (ein:connect-get-notebook))))
  232. ;;; Generic getter
  233. (defun ein:get-url-or-port--connect ()
  234. (ein:aand (ein:get-notebook--connect) (ein:$notebook-url-or-port it)))
  235. (defun ein:get-notebook--connect ()
  236. (when (ein:connect-p ein:%connect%)
  237. (oref ein:%connect% :notebook)))
  238. (defun ein:get-kernel--connect ()
  239. (ein:aand (ein:get-notebook--connect) (ein:$notebook-kernel it)))
  240. (defun ein:get-traceback-data--connect ()
  241. ;; FIXME: Check if the TB in shared-output buffer is originated from
  242. ;; the current buffer.
  243. (ein:aand (ein:shared-output-get-cell) (ein:cell-get-tb-data it)))
  244. (autoload 'ein:shared-output-get-cell "ein-shared-output") ; FIXME: Remove!
  245. ;;; Auto-execution
  246. (defun ein:connect-assert-connected ()
  247. (assert (ein:connect-p ein:%connect%) nil
  248. "Current buffer (%s) is not connected to IPython notebook."
  249. (buffer-name))
  250. (assert (ein:notebook-live-p (oref ein:%connect% :notebook)) nil
  251. "Connected notebook is not live (probably already closed)."))
  252. (defun ein:connect-execute-autoexec-cells ()
  253. "Call `ein:notebook-execute-autoexec-cells' via `after-save-hook'."
  254. (ein:connect-assert-connected)
  255. (when (oref ein:%connect% :autoexec)
  256. (ein:notebook-execute-autoexec-cells (ein:connect-get-notebook))))
  257. (defun ein:connect-toggle-autoexec ()
  258. "Toggle auto-execution mode of the current connected buffer.
  259. When auto-execution mode is on, cells in connected notebook will
  260. be automatically executed whenever run, eval or reload command [#]_
  261. is called in this buffer.
  262. .. [#] Namely, one of
  263. * `ein:connect-run-buffer'
  264. * `ein:connect-eval-buffer'
  265. * `ein:connect-run-or-eval-buffer'
  266. * `ein:connect-reload-buffer'
  267. Note that you need to set cells to run in the connecting buffer
  268. or no cell will be executed.
  269. Use the `ein:worksheet-turn-on-autoexec' command in notebook to
  270. change the cells to run."
  271. (interactive)
  272. (ein:connect-assert-connected)
  273. (let ((autoexec-p (not (oref ein:%connect% :autoexec))))
  274. (oset ein:%connect% :autoexec autoexec-p)
  275. (ein:log 'info "Auto-execution mode is %s."
  276. (if autoexec-p "enabled" "disabled"))))
  277. ;;; Auto-connect
  278. ;;;###autoload
  279. (defun ein:connect-to-default-notebook ()
  280. "Connect to the default notebook specified by
  281. `ein:connect-default-notebook'. Set this to `python-mode-hook'
  282. to automatically connect any python-mode buffer to the
  283. notebook."
  284. (ein:log 'verbose "CONNECT-TO-DEFAULT-NOTEBOOK")
  285. (ein:and-let* ((nbpath ein:connect-default-notebook)
  286. ((not (ein:worksheet-buffer-p))))
  287. (when (functionp nbpath)
  288. (setq nbpath (funcall nbpath)))
  289. (ein:connect-to-notebook nbpath nil t)))
  290. ;;; ein:connect-mode
  291. (defvar ein:connect-mode-map (make-sparse-keymap))
  292. (let ((map ein:connect-mode-map))
  293. (define-key map "\C-c\C-c" 'ein:connect-run-or-eval-buffer)
  294. (define-key map "\C-c\C-l" 'ein:connect-reload-buffer)
  295. (define-key map "\C-c\C-r" 'ein:connect-eval-region)
  296. (define-key map (kbd "C-:") 'ein:shared-output-eval-string)
  297. (define-key map "\C-c\C-f" 'ein:pytools-request-tooltip-or-help)
  298. (define-key map "\C-c\C-i" 'ein:completer-complete)
  299. (define-key map "\C-c\C-z" 'ein:connect-pop-to-notebook)
  300. (define-key map "\C-c\C-a" 'ein:connect-toggle-autoexec)
  301. (define-key map "\C-c\C-o" 'ein:console-open)
  302. (define-key map "\C-c\C-x" 'ein:tb-show)
  303. (define-key map "\M-." 'ein:pytools-jump-to-source-command)
  304. (define-key map (kbd "C-c C-.") 'ein:pytools-jump-to-source-command)
  305. (define-key map "\M-," 'ein:pytools-jump-back-command)
  306. (define-key map (kbd "C-c C-,") 'ein:pytools-jump-back-command)
  307. (define-key map (kbd "C-c C-/") 'ein:notebook-scratchsheet-open)
  308. map)
  309. (defun ein:connect-mode-get-lighter ()
  310. (if (oref ein:%connect% :autoexec)
  311. (format " ein:c%s" (or ein:connect-aotoexec-lighter
  312. ein:cell-autoexec-prompt))
  313. " ein:c"))
  314. (define-minor-mode ein:connect-mode
  315. "Minor mode for communicating with IPython notebook.
  316. \\{ein:connect-mode-map}"
  317. :lighter (:eval (ein:connect-mode-get-lighter))
  318. :keymap ein:connect-mode-map
  319. :group 'ein
  320. (ein:complete-on-dot-install ein:connect-mode-map))
  321. (put 'ein:connect-mode 'permanent-local t)
  322. (provide 'ein-connect)
  323. ;;; ein-connect.el ends here