2012-05-07 14:41:15 +02:00
|
|
|
|
;;; ein-notebook.el --- Notebook module
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2012- Takafumi Arakaki
|
|
|
|
|
|
|
|
|
|
;; Author: Takafumi Arakaki
|
|
|
|
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; ein-notebook.el 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.
|
|
|
|
|
|
|
|
|
|
;; ein-notebook.el 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 ein-notebook.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(eval-when-compile (require 'cl))
|
|
|
|
|
(require 'ewoc)
|
|
|
|
|
|
|
|
|
|
(require 'ein-utils)
|
|
|
|
|
(require 'ein-log)
|
|
|
|
|
(require 'ein-node)
|
|
|
|
|
(require 'ein-kernel)
|
|
|
|
|
(require 'ein-cell)
|
2012-05-15 21:46:17 +02:00
|
|
|
|
(require 'ein-completer)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(require 'ein-pager)
|
|
|
|
|
(require 'ein-events)
|
2012-05-14 23:47:10 +02:00
|
|
|
|
(require 'ein-kill-ring)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-14 17:07:09 +02:00
|
|
|
|
(defvar ein:notebook-pager-buffer-name-template "*ein:pager %s/%s*")
|
2012-05-13 04:29:27 +02:00
|
|
|
|
(defvar ein:notebook-buffer-name-template "*ein: %s/%s*")
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defstruct ein:$notebook
|
2012-05-13 02:51:47 +02:00
|
|
|
|
"Hold notebook variables.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-url-or-port'
|
|
|
|
|
URL or port of IPython server.
|
|
|
|
|
|
2012-05-13 05:46:24 +02:00
|
|
|
|
`ein:$notebook-notebook-id' : string
|
|
|
|
|
uuid string
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-data' - FIXME: remove this!
|
|
|
|
|
Original notebook JSON data sent from server. This slot exists
|
|
|
|
|
first for debugging reason and should be deleted later.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-ewoc' : `ewoc'
|
|
|
|
|
An instance of `ewoc'. Notebook is rendered using `ewoc'.
|
|
|
|
|
Also `ewoc' nodes are used for saving cell data.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-kernel' : `ein:$kernel'
|
|
|
|
|
`ein:$kernel' instance.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-pager'
|
|
|
|
|
Variable for `ein:pager-*' functions. See ein-pager.el.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-dirty' : boolean
|
|
|
|
|
Set to `t' if notebook has unsaved changes. Otherwise `nil'.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-msg-cell-map' : hash
|
|
|
|
|
Hash to hold map from msg-id to cell-id.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-metadata' : plist
|
|
|
|
|
Notebook meta data (e.g., notebook name).
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-name' : string
|
|
|
|
|
Notebook name.
|
|
|
|
|
|
|
|
|
|
`ein:$notebook-nbformat' : integer
|
|
|
|
|
Notebook file format version."
|
2012-05-13 02:51:47 +02:00
|
|
|
|
url-or-port
|
2012-05-13 05:46:24 +02:00
|
|
|
|
notebook-id
|
|
|
|
|
data
|
|
|
|
|
ewoc
|
|
|
|
|
kernel
|
|
|
|
|
pager
|
|
|
|
|
dirty
|
|
|
|
|
msg-cell-map
|
|
|
|
|
metadata
|
|
|
|
|
notebook-name
|
|
|
|
|
nbformat
|
2012-05-07 14:41:15 +02:00
|
|
|
|
)
|
|
|
|
|
|
2012-05-12 22:55:06 +02:00
|
|
|
|
(ein:deflocal ein:notebook nil
|
|
|
|
|
"Buffer local variable to store an instance of `ein:$notebook'.")
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-13 04:32:23 +02:00
|
|
|
|
;; FIMXE: Using buffer local variable directly is not good design.
|
|
|
|
|
;; Remove ein:@notebook macro later
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defmacro ein:@notebook (slot)
|
|
|
|
|
"Quick access to buffer local notebook attributes \(slot of `ein:$notebook').
|
|
|
|
|
|
|
|
|
|
The following two lines are equivalent:
|
|
|
|
|
(ein:@notebook SLOT)
|
|
|
|
|
(ein:$notebook-SLOT ein:notebook)
|
|
|
|
|
Note that SLOT should not be quoted."
|
|
|
|
|
(let ((accessor (intern (format "ein:$notebook-%s" slot))))
|
|
|
|
|
`(,accessor ein:notebook)))
|
|
|
|
|
|
2012-05-11 14:39:41 +02:00
|
|
|
|
(defmacro ein:notebook-with-cell (cell-p &rest body)
|
|
|
|
|
"Execute BODY if in cell with a dynamically bound variable `cell'.
|
|
|
|
|
When CELL-P is non-`nil', it is called with the current cell object
|
|
|
|
|
and BODY will be executed only when it returns non-`nil'. If CELL-P
|
|
|
|
|
is `nil', BODY is executed with any cell types."
|
|
|
|
|
(declare (indent 1))
|
|
|
|
|
`(let ((cell (ein:notebook-get-current-cell)))
|
|
|
|
|
(if ,(if cell-p `(and cell (funcall ,cell-p cell)) 'cell)
|
|
|
|
|
(progn ,@body)
|
|
|
|
|
(ein:log 'warn "Not in cell"))))
|
|
|
|
|
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(defun ein:notebook-new (url-or-port notebook-id &rest args)
|
2012-05-11 05:24:34 +02:00
|
|
|
|
(let ((notebook (apply #'make-ein:$notebook
|
2012-05-13 02:51:47 +02:00
|
|
|
|
:url-or-port url-or-port
|
2012-05-11 05:24:34 +02:00
|
|
|
|
:notebook-id notebook-id
|
|
|
|
|
:msg-cell-map (make-hash-table :test 'equal)
|
|
|
|
|
:nbformat 2
|
|
|
|
|
args)))
|
|
|
|
|
notebook))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(defun ein:notebook-init (notebook data)
|
2012-05-13 04:19:22 +02:00
|
|
|
|
"Initialize NOTEBOOK with DATA from the server."
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(setf (ein:$notebook-data notebook) data)
|
2012-05-13 04:24:30 +02:00
|
|
|
|
(let* ((metadata (plist-get data :metadata))
|
|
|
|
|
(notebook-name (plist-get metadata :name)))
|
|
|
|
|
(setf (ein:$notebook-metadata notebook) metadata)
|
|
|
|
|
(setf (ein:$notebook-notebook-name notebook) notebook-name))
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(setf (ein:$notebook-pager notebook)
|
|
|
|
|
(ein:pager-new
|
2012-05-13 04:29:27 +02:00
|
|
|
|
(format ein:notebook-pager-buffer-name-template
|
|
|
|
|
(ein:$notebook-url-or-port notebook)
|
|
|
|
|
(ein:$notebook-notebook-name notebook)))))
|
2012-05-13 04:07:49 +02:00
|
|
|
|
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(defun ein:notebook-get-buffer-name (notebook)
|
|
|
|
|
(format ein:notebook-buffer-name-template
|
|
|
|
|
(ein:$notebook-url-or-port notebook)
|
|
|
|
|
(ein:$notebook-notebook-name notebook)))
|
|
|
|
|
|
2012-05-13 04:13:29 +02:00
|
|
|
|
(defun ein:notebook-get-buffer (notebook)
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(get-buffer-create (ein:notebook-get-buffer-name notebook)))
|
2012-05-13 04:13:29 +02:00
|
|
|
|
|
2012-05-12 23:13:28 +02:00
|
|
|
|
(defun ein:notebook-url (notebook)
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(ein:notebook-url-from-url-and-id (ein:$notebook-url-or-port notebook)
|
|
|
|
|
(ein:$notebook-notebook-id notebook)))
|
2012-05-12 23:13:28 +02:00
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(defun ein:notebook-url-from-url-and-id (url-or-port notebook-id)
|
|
|
|
|
(ein:url url-or-port "notebooks" notebook-id))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(defun ein:notebook-open (url-or-port notebook-id)
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(let ((url (ein:notebook-url-from-url-and-id url-or-port notebook-id))
|
|
|
|
|
(notebook (ein:notebook-new url-or-port notebook-id)))
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(ein:log 'debug "Opening notebook at %s" url)
|
2012-05-08 07:05:11 +02:00
|
|
|
|
(url-retrieve url
|
|
|
|
|
#'ein:notebook-url-retrieve-callback
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(list notebook))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(defun ein:notebook-url-retrieve-callback (status notebook)
|
2012-05-08 07:05:11 +02:00
|
|
|
|
(ein:log 'debug "URL-RETRIEVE nodtebook-id = %S, status = %S"
|
2012-05-13 04:07:49 +02:00
|
|
|
|
(ein:$notebook-notebook-id notebook)
|
|
|
|
|
status)
|
|
|
|
|
(let ((data (ein:json-read))
|
|
|
|
|
(notebook-id (ein:$notebook-notebook-id notebook)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(kill-buffer (current-buffer))
|
2012-05-13 04:19:22 +02:00
|
|
|
|
(ein:notebook-init notebook data)
|
2012-05-13 04:13:29 +02:00
|
|
|
|
(with-current-buffer (ein:notebook-get-buffer notebook)
|
2012-05-13 04:19:22 +02:00
|
|
|
|
(ein:log-setup (ein:$notebook-notebook-id notebook))
|
|
|
|
|
(setq ein:notebook notebook)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(ein:notebook-render)
|
2012-05-14 15:23:01 +02:00
|
|
|
|
(set-buffer-modified-p nil)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(pop-to-buffer (current-buffer)))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-render ()
|
|
|
|
|
"(Re-)Render the notebook."
|
|
|
|
|
(interactive)
|
2012-05-08 06:41:17 +02:00
|
|
|
|
(assert ein:notebook) ; make sure in a notebook buffer
|
|
|
|
|
(ein:notebook-from-json ein:notebook (ein:@notebook data))
|
2012-05-13 18:59:03 +02:00
|
|
|
|
(setq buffer-undo-list nil) ; clear undo history
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(ein:notebook-mode)
|
|
|
|
|
(ein:notebook-start-kernel)
|
|
|
|
|
(ein:log 'info "Notebook %s is ready" (ein:@notebook notebook-id)))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-pp (ewoc-data)
|
|
|
|
|
(let ((path (ein:$node-path ewoc-data))
|
|
|
|
|
(data (ein:$node-data ewoc-data)))
|
|
|
|
|
(case (car path)
|
|
|
|
|
(cell (ein:cell-pp (cdr path) data)))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Cell indexing, retrieval, etc.
|
|
|
|
|
|
2012-05-10 05:20:52 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-cell-from-json (notebook data &rest args)
|
|
|
|
|
(apply #'ein:cell-from-json
|
|
|
|
|
data :ewoc (ein:$notebook-ewoc notebook) args))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-cell-from-type (notebook type &rest args)
|
|
|
|
|
(apply #'ein:cell-from-type
|
|
|
|
|
(format "%s" type) :ewoc (ein:$notebook-ewoc notebook) args))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-get-cells (notebook)
|
|
|
|
|
(let* ((ewoc (ein:$notebook-ewoc notebook))
|
|
|
|
|
(nodes (ewoc-collect ewoc (lambda (n) (ein:cell-node-p n 'prompt)))))
|
|
|
|
|
(mapcar #'ein:$node-data nodes)))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-cell-for-msg (notebook msg-id)
|
|
|
|
|
(let* ((msg-cell-map (ein:$notebook-msg-cell-map notebook))
|
|
|
|
|
(cell-id (gethash msg-id msg-cell-map)))
|
|
|
|
|
(when cell-id
|
|
|
|
|
(loop for cell in (ein:notebook-get-cells notebook)
|
2012-05-10 05:06:02 +02:00
|
|
|
|
when (equal (oref cell :cell-id) cell-id)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
return cell))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-ncells (notebook)
|
|
|
|
|
(length (ein:notebook-get-cells notebook)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Insertion and deletion of cells
|
|
|
|
|
|
2012-05-11 00:48:22 +02:00
|
|
|
|
(defun ein:notebook-delete-cell (notebook cell)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(apply #'ewoc-delete
|
|
|
|
|
(ein:$notebook-ewoc notebook)
|
|
|
|
|
(ein:cell-all-element cell)))
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-delete-cell-command ()
|
|
|
|
|
(interactive)
|
2012-05-11 14:39:41 +02:00
|
|
|
|
(ein:notebook-with-cell nil
|
2012-05-15 05:03:30 +02:00
|
|
|
|
(ein:notebook-delete-cell ein:notebook cell)
|
|
|
|
|
(ein:aif (ein:notebook-get-current-cell) (ein:cell-goto it))))
|
2012-05-11 00:48:22 +02:00
|
|
|
|
|
2012-05-14 23:47:10 +02:00
|
|
|
|
(defun ein:notebook-kill-cell-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell nil
|
|
|
|
|
(ein:cell-save-text cell)
|
|
|
|
|
(ein:notebook-delete-cell ein:notebook cell)
|
2012-05-15 05:03:30 +02:00
|
|
|
|
(ein:kill-new (ein:cell-deactivate cell))
|
|
|
|
|
(ein:aif (ein:notebook-get-current-cell) (ein:cell-goto it))))
|
2012-05-14 23:47:10 +02:00
|
|
|
|
|
2012-05-15 02:30:27 +02:00
|
|
|
|
(defun ein:notebook-copy-cell-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell nil
|
2012-05-15 05:03:30 +02:00
|
|
|
|
(ein:kill-new (ein:cell-deactivate (ein:cell-copy cell)))
|
|
|
|
|
(ein:aif (ein:notebook-get-current-cell) (ein:cell-goto it))))
|
2012-05-15 02:30:27 +02:00
|
|
|
|
|
2012-05-14 23:47:10 +02:00
|
|
|
|
(defun ein:notebook-yank-cell-command (&optional arg)
|
|
|
|
|
(interactive "*P")
|
2012-05-16 12:08:53 +02:00
|
|
|
|
;; Do not use `ein:notebook-with-cell'.
|
|
|
|
|
;; `ein:notebook-insert-cell-below' handles empty cell.
|
|
|
|
|
(let* ((cell (ein:notebook-get-current-cell))
|
|
|
|
|
(killed (ein:current-kill (cond
|
|
|
|
|
((listp arg) 0)
|
|
|
|
|
((eq arg '-) -2)
|
|
|
|
|
(t (1- arg)))))
|
|
|
|
|
(clone (ein:cell-copy killed)))
|
|
|
|
|
(ein:notebook-insert-cell-below ein:notebook clone cell)
|
|
|
|
|
(ein:cell-goto clone)))
|
2012-05-14 23:47:10 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-insert-cell-below (notebook type-or-cell base-cell)
|
|
|
|
|
(let ((cell (if (ein:basecell-child-p type-or-cell) type-or-cell
|
|
|
|
|
(ein:notebook-cell-from-type notebook type-or-cell))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(when cell
|
|
|
|
|
(cond
|
|
|
|
|
((= (ein:notebook-ncells notebook) 0)
|
|
|
|
|
(ein:cell-enter-last cell))
|
|
|
|
|
(base-cell
|
2012-05-13 07:14:33 +02:00
|
|
|
|
(ein:cell-insert-below base-cell cell))
|
|
|
|
|
(t (error (concat "`base-cell' is `nil' but ncells != 0. "
|
|
|
|
|
"There is something wrong..."))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(ein:cell-goto cell)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t))
|
|
|
|
|
cell))
|
|
|
|
|
|
2012-05-11 00:23:42 +02:00
|
|
|
|
(defun ein:notebook-insert-cell-below-command (&optional markdown)
|
2012-05-14 22:12:08 +02:00
|
|
|
|
"Insert cell below. Insert markdown cell instead of code cell
|
2012-05-11 00:23:42 +02:00
|
|
|
|
when the prefix argument is given."
|
|
|
|
|
(interactive "P")
|
2012-05-13 07:11:04 +02:00
|
|
|
|
(let ((cell (ein:notebook-get-current-cell)))
|
|
|
|
|
;; Do not use `ein:notebook-with-cell'. When there is no cell,
|
|
|
|
|
;; This command should add the first cell. So this clause must be
|
|
|
|
|
;; executed even if `cell' is `nil'.
|
2012-05-15 05:03:30 +02:00
|
|
|
|
(ein:cell-goto
|
|
|
|
|
(ein:notebook-insert-cell-below ein:notebook
|
|
|
|
|
(if markdown 'markdown 'code)
|
|
|
|
|
cell))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-14 22:12:08 +02:00
|
|
|
|
(defun ein:notebook-insert-cell-above (notebook type base-cell)
|
|
|
|
|
(let ((cell (ein:notebook-cell-from-type notebook type)))
|
|
|
|
|
(when cell
|
|
|
|
|
(cond
|
|
|
|
|
((< (ein:notebook-ncells notebook) 2)
|
|
|
|
|
(ein:cell-enter-first cell))
|
|
|
|
|
(base-cell
|
|
|
|
|
(let ((prev-cell (ein:cell-prev base-cell)))
|
|
|
|
|
(if prev-cell
|
|
|
|
|
(ein:cell-insert-below prev-cell cell)
|
|
|
|
|
(ein:cell-enter-first cell))))
|
|
|
|
|
(t (error (concat "`base-cell' is `nil' but ncells > 1. "
|
|
|
|
|
"There is something wrong..."))))
|
|
|
|
|
(ein:cell-goto cell)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t))
|
|
|
|
|
cell))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-insert-cell-above-command (&optional markdown)
|
|
|
|
|
"Insert cell above. Insert markdown cell instead of code cell
|
|
|
|
|
when the prefix argument is given."
|
|
|
|
|
(interactive "P")
|
|
|
|
|
(let ((cell (ein:notebook-get-current-cell)))
|
2012-05-15 05:03:30 +02:00
|
|
|
|
(ein:cell-goto
|
|
|
|
|
(ein:notebook-insert-cell-above ein:notebook
|
|
|
|
|
(if markdown 'markdown 'code)
|
|
|
|
|
cell))))
|
2012-05-14 22:12:08 +02:00
|
|
|
|
|
2012-05-11 23:52:32 +02:00
|
|
|
|
(defun ein:notebook-toggle-cell-type ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell nil
|
|
|
|
|
(let ((type (ein:case-equal (oref cell :cell-type)
|
|
|
|
|
(("code") "markdown")
|
|
|
|
|
(("markdown") "code"))))
|
2012-05-14 22:21:05 +02:00
|
|
|
|
(ein:cell-convert-inplace cell type)
|
|
|
|
|
(ein:cell-goto cell))))
|
2012-05-11 23:52:32 +02:00
|
|
|
|
|
2012-05-11 16:38:29 +02:00
|
|
|
|
|
|
|
|
|
;;; Cell selection.
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-goto-next-cell ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell nil
|
|
|
|
|
(ein:aif (ein:cell-next cell)
|
|
|
|
|
(ein:cell-goto it)
|
|
|
|
|
(ein:log 'warn "No next cell"))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-goto-prev-cell ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell nil
|
|
|
|
|
(ein:aif (ein:cell-prev cell)
|
|
|
|
|
(ein:cell-goto it)
|
|
|
|
|
(ein:log 'warn "No previous cell"))))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
;;; Kernel related things
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-start-kernel ()
|
2012-05-13 15:59:40 +02:00
|
|
|
|
(let ((kernel (ein:kernel-new (ein:@notebook url-or-port))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(setf (ein:@notebook kernel) kernel)
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(ein:kernel-start kernel
|
|
|
|
|
(ein:@notebook notebook-id)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
#'ein:notebook-kernel-started
|
|
|
|
|
(list ein:notebook))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-kernel-started (notebook)
|
|
|
|
|
(let* ((kernel (ein:$notebook-kernel notebook))
|
|
|
|
|
(shell-channel (ein:$kernel-shell-channel kernel))
|
|
|
|
|
(iopub-channel (ein:$kernel-iopub-channel kernel)))
|
|
|
|
|
(lexical-let ((notebook notebook))
|
|
|
|
|
(setf (ein:$websocket-onmessage shell-channel)
|
|
|
|
|
(lambda (packet)
|
|
|
|
|
(ein:notebook-handle-shell-reply notebook packet)))
|
|
|
|
|
(setf (ein:$websocket-onmessage iopub-channel)
|
|
|
|
|
(lambda (packet)
|
|
|
|
|
(ein:notebook-handle-iopub-reply notebook packet)))))
|
|
|
|
|
(ein:log 'info "IPython kernel is started"))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-handle-shell-reply (notebook packet)
|
|
|
|
|
(destructuring-bind
|
|
|
|
|
(&key header content msg_type parent_header &allow-other-keys)
|
|
|
|
|
(ein:json-read-from-string packet)
|
|
|
|
|
(let ((cell (ein:notebook-cell-for-msg
|
|
|
|
|
notebook
|
|
|
|
|
(plist-get parent_header :msg_id))))
|
|
|
|
|
(cond
|
|
|
|
|
((equal msg_type "execute_reply")
|
|
|
|
|
(ein:cell-set-input-prompt cell (plist-get content :execution_count))
|
|
|
|
|
(ein:cell-running-set cell nil)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t))
|
|
|
|
|
((equal msg_type "complete_reply")
|
2012-05-15 21:46:17 +02:00
|
|
|
|
(ein:completer-finish-completing (plist-get content :matched_text)
|
|
|
|
|
(plist-get content :matches)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
((equal msg_type "object_info_reply")
|
|
|
|
|
(when (plist-get content :found)
|
|
|
|
|
(ein:cell-finish-tooltip cell content)))
|
2012-05-13 21:24:18 +02:00
|
|
|
|
(t (ein:log 'info "unknown reply: %s" msg_type)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(when (plist-member content :payload)
|
|
|
|
|
(ein:notebook-handle-payload notebook cell
|
|
|
|
|
(plist-get content :payload))))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-handle-payload (notebook cell payload)
|
|
|
|
|
(loop for p in payload
|
|
|
|
|
for text = (plist-get p :text)
|
|
|
|
|
for source = (plist-get p :source)
|
|
|
|
|
if (equal source "IPython.zmq.page.page")
|
2012-05-11 05:24:34 +02:00
|
|
|
|
when (not (equal (ein:trim text) ""))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
do (let ((pager (ein:$notebook-pager notebook)))
|
|
|
|
|
(ein:pager-clear pager)
|
|
|
|
|
(ein:pager-expand pager)
|
|
|
|
|
(ein:pager-append-text pager text))
|
|
|
|
|
else if
|
|
|
|
|
(equal source "IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input")
|
2012-05-11 14:47:38 +02:00
|
|
|
|
do (let ((new-cell (ein:notebook-insert-cell-below
|
|
|
|
|
notebook 'code cell)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(ein:cell-set-text new-cell text)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-handle-iopub-reply (notebook packet)
|
|
|
|
|
(destructuring-bind
|
|
|
|
|
(&key content msg_type parent_header &allow-other-keys)
|
|
|
|
|
(ein:json-read-from-string packet)
|
|
|
|
|
(let ((cell (ein:notebook-cell-for-msg
|
|
|
|
|
notebook
|
|
|
|
|
(plist-get parent_header :msg_id))))
|
|
|
|
|
(if (and (not (equal msg_type "status")) (null cell))
|
|
|
|
|
(ein:log 'verbose "Got message not from this notebook.")
|
|
|
|
|
(ein:log 'debug "handle-iopub-reply: msg_type = %s" msg_type)
|
|
|
|
|
(ein:case-equal msg_type
|
|
|
|
|
(("stream" "display_data" "pyout" "pyerr")
|
|
|
|
|
(ein:notebook-handle-output notebook cell msg_type content))
|
|
|
|
|
(("status")
|
|
|
|
|
(ein:case-equal (plist-get content :execution_state)
|
|
|
|
|
(("busy")
|
|
|
|
|
(ein:events-trigger 'status_busy.Kernel))
|
|
|
|
|
(("idle")
|
|
|
|
|
(ein:events-trigger 'status_idle.Kernel))
|
|
|
|
|
(("dead"))
|
|
|
|
|
(ein:notebook-handle-status-dead notebook)))
|
|
|
|
|
(("clear_output")
|
|
|
|
|
(ein:cell-clear-output cell
|
|
|
|
|
(plist-get content :stdout)
|
|
|
|
|
(plist-get content :stderr)
|
|
|
|
|
(plist-get content :other))))))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-handle-status-dead (notebook)
|
|
|
|
|
;; FIXME: do something more useful
|
|
|
|
|
(ein:log 'info "The kernel has died."))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-handle-output (notebook cell msg-type content)
|
|
|
|
|
(let* ((json (list :output_type msg-type)))
|
|
|
|
|
(ein:case-equal msg-type
|
|
|
|
|
(("stream")
|
|
|
|
|
(plist-put json :text (plist-get content :data))
|
|
|
|
|
(plist-put json :stream (plist-get content :name)))
|
|
|
|
|
(("display_data" "pyout")
|
|
|
|
|
(when (equal msg-type "pyout")
|
|
|
|
|
(plist-put json :prompt_number (plist-get content :execution_count)))
|
|
|
|
|
(setq json (ein:notebook-convert-mime-types
|
|
|
|
|
json (plist-get content :data))))
|
|
|
|
|
(("pyerr")
|
|
|
|
|
(plist-put json :ename (plist-get content :ename))
|
|
|
|
|
(plist-put json :evalue (plist-get content :evalue))
|
|
|
|
|
(plist-put json :traceback (plist-get content :traceback))))
|
|
|
|
|
(ein:cell-append-output cell json t)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook) t)))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-convert-mime-types (json data)
|
|
|
|
|
(loop for (prop . mime) in '((:text . :text/plain)
|
|
|
|
|
(:html . :text/html)
|
|
|
|
|
(:png . :image/png)
|
|
|
|
|
(:jpeg . :image/jpeg)
|
|
|
|
|
(:latex . :text/latex)
|
|
|
|
|
(:json . :application/json)
|
|
|
|
|
(:javascript . :application/javascript))
|
|
|
|
|
when (plist-member data mime)
|
|
|
|
|
do (plist-put json prop (plist-get data mime)))
|
|
|
|
|
json)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-get-current-ewoc-node (&optional pos)
|
|
|
|
|
(ein:aand ein:notebook (ein:$notebook-ewoc it) (ewoc-locate it pos)))
|
|
|
|
|
|
2012-05-10 21:52:40 +02:00
|
|
|
|
(defun ein:notebook-get-nearest-cell-ewoc-node (&optional pos max cell-p)
|
2012-05-10 19:55:49 +02:00
|
|
|
|
(ein:aif (ein:notebook-get-current-ewoc-node pos)
|
|
|
|
|
(let ((ewoc-node it))
|
|
|
|
|
;; FIXME: can be optimized using the argument `max'
|
|
|
|
|
(while (and ewoc-node
|
2012-05-10 21:52:40 +02:00
|
|
|
|
(not (and (ein:cell-ewoc-node-p ewoc-node)
|
|
|
|
|
(if cell-p
|
|
|
|
|
(funcall cell-p
|
|
|
|
|
(ein:cell-from-ewoc-node ewoc-node))
|
|
|
|
|
t))))
|
2012-05-10 19:55:49 +02:00
|
|
|
|
(setq ewoc-node (ewoc-next (ein:@notebook ewoc) ewoc-node)))
|
|
|
|
|
ewoc-node)))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defun ein:notebook-get-current-cell (&optional pos)
|
2012-05-10 21:08:27 +02:00
|
|
|
|
(let ((cell (ein:cell-from-ewoc-node
|
|
|
|
|
(ein:notebook-get-current-ewoc-node pos))))
|
2012-05-10 13:05:02 +02:00
|
|
|
|
(when (ein:basecell-child-p cell) cell)))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-execute-current-cell ()
|
|
|
|
|
(interactive)
|
|
|
|
|
;; FIXME: implement `add_new' and `terminal' option like
|
|
|
|
|
;; `Notebook.execute_selected_cell'.
|
2012-05-11 14:39:41 +02:00
|
|
|
|
(ein:notebook-with-cell #'ein:codecell-p
|
2012-05-13 16:54:22 +02:00
|
|
|
|
(ein:kernel-if-ready (ein:@notebook kernel)
|
|
|
|
|
(ein:cell-clear-output cell t t t)
|
|
|
|
|
(ein:cell-set-input-prompt cell "*")
|
|
|
|
|
(ein:cell-running-set cell t)
|
|
|
|
|
(let* ((code (ein:cell-get-text cell))
|
|
|
|
|
(msg-id (ein:kernel-execute (ein:@notebook kernel) code)))
|
|
|
|
|
(puthash msg-id (oref cell :cell-id) (ein:@notebook msg-cell-map)))
|
|
|
|
|
(setf (ein:@notebook dirty) t))))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-05-16 04:53:38 +02:00
|
|
|
|
(defun ein:notebook-request-tool-tip (notebook cell func)
|
|
|
|
|
(let ((msg-id (ein:kernel-object-info-request
|
|
|
|
|
(ein:$notebook-kernel notebook) func)))
|
|
|
|
|
(when msg-id
|
|
|
|
|
(puthash msg-id (oref cell :cell-id)
|
|
|
|
|
(ein:$notebook-msg-cell-map notebook)))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-request-tool-tip-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-with-cell #'ein:codecell-p
|
|
|
|
|
(ein:kernel-if-ready (ein:@notebook kernel)
|
2012-05-16 05:04:30 +02:00
|
|
|
|
(let ((func (ein:object-at-point)))
|
2012-05-16 04:53:38 +02:00
|
|
|
|
(ein:notebook-request-tool-tip ein:notebook cell func)))))
|
|
|
|
|
|
2012-05-11 09:59:02 +02:00
|
|
|
|
(defun ein:notebook-complete-cell (notebook cell line-string rel-pos)
|
|
|
|
|
(let ((msg-id (ein:kernel-complete (ein:$notebook-kernel notebook)
|
|
|
|
|
line-string rel-pos)))
|
|
|
|
|
(puthash msg-id (oref cell :cell-id) (ein:@notebook msg-cell-map))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-complete-cell-command ()
|
|
|
|
|
(interactive)
|
2012-05-11 14:39:41 +02:00
|
|
|
|
(ein:notebook-with-cell #'ein:codecell-p
|
2012-05-13 16:54:53 +02:00
|
|
|
|
(ein:kernel-if-ready (ein:@notebook kernel)
|
|
|
|
|
(ein:notebook-complete-cell ein:notebook
|
|
|
|
|
cell
|
|
|
|
|
(thing-at-point 'line)
|
|
|
|
|
(current-column)))))
|
2012-05-11 09:59:02 +02:00
|
|
|
|
|
2012-05-13 07:32:43 +02:00
|
|
|
|
(defun ein:notebook-kernel-interrupt-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:kernel-interrupt (ein:$notebook-kernel ein:notebook)))
|
|
|
|
|
|
2012-05-13 07:37:19 +02:00
|
|
|
|
(defun ein:notebook-kernel-kill-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(when (y-or-n-p "Really kill kernel?")
|
|
|
|
|
(ein:kernel-kill (ein:$notebook-kernel ein:notebook))))
|
|
|
|
|
|
2012-05-08 06:15:23 +02:00
|
|
|
|
|
|
|
|
|
;;; Persistance and loading
|
|
|
|
|
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(defun ein:notebook-set-notebook-name (notebook name)
|
|
|
|
|
"Check NAME and change the name of NOTEBOOK to it."
|
|
|
|
|
(if (ein:notebook-test-notebook-name name)
|
|
|
|
|
(setf (ein:$notebook-notebook-name notebook) name)
|
|
|
|
|
(ein:log 'error "%S is not a good notebook name." name)
|
|
|
|
|
(error "%S is not a good notebook name." name)))
|
|
|
|
|
|
2012-05-13 04:47:41 +02:00
|
|
|
|
(defun ein:notebook-test-notebook-name (name)
|
|
|
|
|
(and (stringp name)
|
|
|
|
|
(> (length name) 0)
|
|
|
|
|
(not (string-match "[\\/\\\\]" name))))
|
|
|
|
|
|
2012-05-08 06:41:17 +02:00
|
|
|
|
(defun ein:notebook-from-json (notebook data)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
;; Enable nonsep for ewoc object (the last argument is non-nil).
|
|
|
|
|
;; This is for putting read-only text properties to the newlines.
|
|
|
|
|
(setf (ein:$notebook-ewoc notebook)
|
|
|
|
|
(ewoc-create 'ein:notebook-pp
|
|
|
|
|
(ein:propertize-read-only "IPython notebook\n\n")
|
|
|
|
|
nil t))
|
|
|
|
|
(mapc (lambda (cell-data)
|
|
|
|
|
(ein:cell-enter-last
|
2012-05-10 05:20:52 +02:00
|
|
|
|
(ein:notebook-cell-from-json ein:notebook cell-data)))
|
2012-05-08 06:41:17 +02:00
|
|
|
|
;; Only handle 1 worksheet for now, as in notebook.js
|
|
|
|
|
(plist-get (nth 0 (plist-get data :worksheets)) :cells))))
|
|
|
|
|
|
2012-05-08 06:15:23 +02:00
|
|
|
|
(defun ein:notebook-to-json (notebook)
|
2012-05-08 08:26:56 +02:00
|
|
|
|
"Return json-ready alist."
|
|
|
|
|
`((worksheets
|
2012-05-08 08:50:09 +02:00
|
|
|
|
. [((cells
|
|
|
|
|
. ,(apply #'vector
|
|
|
|
|
(mapcar #'ein:cell-to-json
|
|
|
|
|
(ein:notebook-get-cells notebook)))))])
|
2012-05-08 08:26:56 +02:00
|
|
|
|
(metadata . ,(ein:$notebook-metadata notebook))))
|
2012-05-08 06:15:23 +02:00
|
|
|
|
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(defun ein:notebook-save-notebook (notebook)
|
|
|
|
|
(let ((data (ein:notebook-to-json notebook)))
|
2012-05-08 08:50:09 +02:00
|
|
|
|
(plist-put (cdr (assq 'metadata data))
|
|
|
|
|
:name (ein:$notebook-notebook-name notebook))
|
2012-05-08 08:26:56 +02:00
|
|
|
|
(push `(nbformat . ,(ein:$notebook-nbformat notebook)) data)
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(ein:events-trigger 'notebook_saving.Notebook)
|
2012-05-13 23:21:32 +02:00
|
|
|
|
(let ((url (ein:url-no-cache (ein:notebook-url notebook)))
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(url-request-method "PUT")
|
|
|
|
|
(url-request-extra-headers '(("Content-Type" . "application/json")))
|
|
|
|
|
(url-request-data (json-encode data)))
|
|
|
|
|
(ein:log 'debug "URL-RETRIEVE url = %s" url)
|
|
|
|
|
(ein:log 'debug "URL-REQUEST-DATA = %s" url-request-data)
|
|
|
|
|
(url-retrieve
|
|
|
|
|
url
|
|
|
|
|
#'ein:notebook-save-notebook-callback
|
|
|
|
|
(list notebook)))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-save-notebook-command ()
|
|
|
|
|
(interactive)
|
|
|
|
|
(ein:notebook-save-notebook ein:notebook))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-save-notebook-callback (status notebook)
|
2012-05-14 01:59:58 +02:00
|
|
|
|
(declare (special url-http-response-status
|
|
|
|
|
url-http-method))
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(ein:log 'debug "SAVE-NOTEBOOK-CALLBACK nodtebook-id = %S, status = %S"
|
|
|
|
|
(ein:$notebook-notebook-id notebook)
|
|
|
|
|
status)
|
2012-05-14 01:11:57 +02:00
|
|
|
|
(ein:log 'debug "url-http-response-status = %s" url-http-response-status)
|
2012-05-14 01:59:58 +02:00
|
|
|
|
(ein:log 'debug "url-request-method = %s" url-request-method)
|
|
|
|
|
(ein:log 'debug "url-http-method = %s" (when (boundp 'url-http-method)
|
|
|
|
|
url-http-method))
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(ein:log 'debug "(buffer-string) = \n%s" (buffer-string))
|
2012-05-14 01:11:57 +02:00
|
|
|
|
(let ((response url-http-response-status))
|
|
|
|
|
;; ^-- "save" local variable before killing buffer.
|
|
|
|
|
(kill-buffer (current-buffer))
|
|
|
|
|
(with-current-buffer (ewoc-buffer (ein:$notebook-ewoc notebook))
|
|
|
|
|
(ein:aif (plist-get status :error)
|
|
|
|
|
(progn
|
|
|
|
|
(ein:log 'debug "ERROR CODE = %S" it)
|
|
|
|
|
(ein:notebook-save-notebook-error notebook status))
|
2012-05-14 01:15:26 +02:00
|
|
|
|
;; IPython server returns 204 only when the notebook URL is
|
|
|
|
|
;; accessed via PUT or DELETE. As it seems Emacs failed to
|
|
|
|
|
;; choose PUT method every two times, let's check the response
|
|
|
|
|
;; here and fail when 204 is not returned.
|
2012-05-14 01:11:57 +02:00
|
|
|
|
(if (eq response 204)
|
|
|
|
|
(ein:notebook-save-notebook-success notebook status)
|
|
|
|
|
(ein:notebook-save-notebook-error notebook status)
|
|
|
|
|
(ein:log 'debug "Status code (=%s) is not 204." response))))))
|
2012-05-08 08:24:20 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-save-notebook-success (notebook status)
|
|
|
|
|
(setf (ein:$notebook-dirty notebook))
|
2012-05-11 00:18:17 +02:00
|
|
|
|
(set-buffer-modified-p nil)
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(ein:events-trigger 'notebook_saved.Notebook))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-save-notebook-error (notebook status)
|
|
|
|
|
(ein:events-trigger 'notebook_save_failed.Notebook))
|
|
|
|
|
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(defun ein:notebook-rename-command (name)
|
|
|
|
|
"Rename current notebook and save it immediately.
|
|
|
|
|
|
|
|
|
|
NAME is any non-empty string that does not contain '/' or '\\'."
|
|
|
|
|
(interactive
|
2012-05-15 04:15:44 +02:00
|
|
|
|
(list (read-string "Rename notebook: "
|
|
|
|
|
(ein:$notebook-notebook-name ein:notebook))))
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(ein:notebook-set-notebook-name ein:notebook name)
|
|
|
|
|
(rename-buffer (ein:notebook-get-buffer-name ein:notebook))
|
|
|
|
|
(ein:notebook-save-notebook ein:notebook))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
;;; Notebook mode
|
|
|
|
|
|
|
|
|
|
(defvar ein:notebook-modes
|
|
|
|
|
'(ein:notebook-mumamo-mode ein:notebook-plain-mode))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-choose-mode ()
|
2012-05-14 18:47:08 +02:00
|
|
|
|
"Return usable (defined) notebook mode."
|
|
|
|
|
;; So try to load extra modules here.
|
|
|
|
|
(when (require 'mumamo nil t)
|
|
|
|
|
(require 'ein-mumamo))
|
|
|
|
|
;; Return first matched mode
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(loop for mode in ein:notebook-modes
|
|
|
|
|
if (functionp mode)
|
|
|
|
|
return mode))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebook-mode ()
|
|
|
|
|
(funcall (ein:notebook-choose-mode)))
|
|
|
|
|
|
|
|
|
|
(defvar ein:notebook-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(define-key map "\C-c\C-r" 'ein:notebook-render)
|
|
|
|
|
(define-key map "\C-c\C-c" 'ein:notebook-execute-current-cell)
|
2012-05-11 00:48:22 +02:00
|
|
|
|
(define-key map "\C-c\C-d" 'ein:notebook-delete-cell-command)
|
2012-05-14 23:47:10 +02:00
|
|
|
|
(define-key map "\C-c\C-k" 'ein:notebook-kill-cell-command)
|
2012-05-15 02:30:27 +02:00
|
|
|
|
(define-key map "\C-c\M-w" 'ein:notebook-copy-cell-command)
|
2012-05-14 23:47:10 +02:00
|
|
|
|
(define-key map "\C-c\C-y" 'ein:notebook-yank-cell-command)
|
2012-05-14 22:12:08 +02:00
|
|
|
|
(define-key map "\C-c\C-a" 'ein:notebook-insert-cell-above-command)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(define-key map "\C-c\C-b" 'ein:notebook-insert-cell-below-command)
|
2012-05-11 23:52:32 +02:00
|
|
|
|
(define-key map "\C-c\C-t" 'ein:notebook-toggle-cell-type)
|
2012-05-11 16:38:29 +02:00
|
|
|
|
(define-key map "\C-c\C-n" 'ein:notebook-goto-next-cell)
|
|
|
|
|
(define-key map "\C-c\C-p" 'ein:notebook-goto-prev-cell)
|
2012-05-16 04:53:38 +02:00
|
|
|
|
(define-key map "\C-c\C-f" 'ein:notebook-request-tool-tip-command)
|
2012-05-11 09:59:02 +02:00
|
|
|
|
(define-key map "\C-c\C-i" 'ein:notebook-complete-cell-command)
|
2012-05-13 07:32:43 +02:00
|
|
|
|
(define-key map "\C-c\C-z" 'ein:notebook-kernel-interrupt-command)
|
2012-05-13 07:37:19 +02:00
|
|
|
|
(define-key map "\C-c\C-q" 'ein:notebook-kernel-kill-command)
|
2012-05-15 01:47:21 +02:00
|
|
|
|
(define-key map "\C-c\C-o" 'ein:notebook-console-open)
|
2012-05-08 08:24:20 +02:00
|
|
|
|
(define-key map "\C-x\C-s" 'ein:notebook-save-notebook-command)
|
2012-05-13 05:06:49 +02:00
|
|
|
|
(define-key map "\C-x\C-w" 'ein:notebook-rename-command)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
map))
|
|
|
|
|
|
|
|
|
|
(define-derived-mode ein:notebook-plain-mode fundamental-mode "ein:notebook"
|
2012-05-13 08:36:21 +02:00
|
|
|
|
"IPython notebook command without fancy coloring."
|
|
|
|
|
(font-lock-mode))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
(setq ein:notebook-plain-mode-map ein:notebook-mode-map)
|
|
|
|
|
|
2012-05-13 18:29:50 +02:00
|
|
|
|
(defun ein:notebook-ask-before-kill ()
|
|
|
|
|
"Return `nil' to prevent killing the notebook buffer.
|
|
|
|
|
Called via `kill-buffer-query-functions'."
|
|
|
|
|
(not (and (ein:$notebook-p ein:notebook)
|
2012-05-15 20:47:54 +02:00
|
|
|
|
(or (ein:$notebook-dirty ein:notebook)
|
|
|
|
|
(buffer-modified-p))
|
2012-05-13 18:29:50 +02:00
|
|
|
|
(not (y-or-n-p "You have unsaved changes. Discard changes?")))))
|
|
|
|
|
|
|
|
|
|
(add-hook 'kill-buffer-query-functions 'ein:notebook-ask-before-kill)
|
|
|
|
|
|
2012-05-14 22:23:43 +02:00
|
|
|
|
|
|
|
|
|
;;; Console integration
|
|
|
|
|
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(defvar ein:notebook-console-security-dir ""
|
2012-05-14 20:32:10 +02:00
|
|
|
|
"Security directory setting.
|
|
|
|
|
|
|
|
|
|
Following type is accepted:
|
|
|
|
|
string : Use this value as a path to security directory.
|
|
|
|
|
Handy when you have only one IPython server.
|
|
|
|
|
alist : An alist whose element is \"(URL-OR-PORT . DIR)\".
|
|
|
|
|
Key (URL-OR-PORT) can be string (URL), integer (port), or
|
|
|
|
|
`default' (symbol). The value of `default' is used when
|
|
|
|
|
other key does not much. Normally you should have this
|
|
|
|
|
entry.
|
|
|
|
|
function : Called with an argument URL-OR-PORT (integer or string).
|
|
|
|
|
You can have complex setting using this.")
|
|
|
|
|
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(defvar ein:notebook-console-args "--profile nbserver"
|
|
|
|
|
"Additional argument when using console.
|
|
|
|
|
Example: \"--ssh HOSTNAME\"
|
|
|
|
|
|
|
|
|
|
Types same as `ein:notebook-console-security-dir' are accepted.")
|
|
|
|
|
|
2012-05-15 03:52:59 +02:00
|
|
|
|
(defun ein:notebook-console-security-dir-get (notebook)
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(let ((dir (ein:choose-setting 'ein:notebook-console-security-dir
|
|
|
|
|
(ein:$notebook-url-or-port notebook))))
|
|
|
|
|
(if (equal dir "")
|
|
|
|
|
dir
|
|
|
|
|
(file-name-as-directory (expand-file-name dir)))))
|
|
|
|
|
|
2012-05-15 03:52:59 +02:00
|
|
|
|
(defun ein:notebook-console-args-get (notebook)
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(ein:choose-setting 'ein:notebook-console-args
|
|
|
|
|
(ein:$notebook-url-or-port notebook)))
|
2012-05-14 20:32:10 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebook-console-open ()
|
|
|
|
|
"Open IPython console.
|
2012-05-14 21:03:23 +02:00
|
|
|
|
To use this function, `ein:notebook-console-security-dir' and
|
|
|
|
|
`ein:notebook-console-args' must be set properly.
|
2012-05-14 20:32:10 +02:00
|
|
|
|
This function requires Fabian Gallina's python.el for now:
|
|
|
|
|
https://github.com/fgallina/python.el"
|
2012-05-14 21:03:23 +02:00
|
|
|
|
;; FIXME: use %connect_info to get connection file, then I can get
|
|
|
|
|
;; rid of `ein:notebook-console-security-dir'.
|
2012-05-14 20:32:10 +02:00
|
|
|
|
(interactive)
|
|
|
|
|
(unless ein:notebook (error "Not in notebook buffer!"))
|
|
|
|
|
(if (fboundp 'python-shell-switch-to-shell)
|
|
|
|
|
(progn
|
2012-05-15 03:52:59 +02:00
|
|
|
|
(let* ((dir (ein:notebook-console-security-dir-get ein:notebook))
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(kid (ein:$kernel-kernel-id
|
|
|
|
|
(ein:$notebook-kernel ein:notebook)))
|
|
|
|
|
(ipy (executable-find "ipython"))
|
2012-05-15 03:52:59 +02:00
|
|
|
|
(args (ein:notebook-console-args-get ein:notebook))
|
2012-05-14 20:32:10 +02:00
|
|
|
|
(python-shell-setup-codes nil)
|
|
|
|
|
(python-shell-interpreter
|
2012-05-14 21:03:23 +02:00
|
|
|
|
(format "python %s console --existing %skernel-%s.json %s"
|
|
|
|
|
ipy dir kid args)))
|
2012-05-14 20:32:10 +02:00
|
|
|
|
(funcall 'python-shell-switch-to-shell)))
|
|
|
|
|
(ein:log 'warn "python.el is not loaded!")))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(provide 'ein-notebook)
|
|
|
|
|
|
|
|
|
|
;;; ein-notebook.el ends here
|