2012-05-07 14:41:15 +02:00
|
|
|
|
;;; ein-notebooklist.el --- Notebook list buffer
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2012- Takafumi Arakaki
|
|
|
|
|
|
2015-01-31 10:13:49 -06:00
|
|
|
|
;; Authors: Takafumi Arakaki <aka.tkf at gmail.com>
|
|
|
|
|
;; John M. Miller <millejoh at mac.com>
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; ein-notebooklist.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-notebooklist.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-notebooklist.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(eval-when-compile (require 'cl))
|
|
|
|
|
(require 'widget)
|
|
|
|
|
|
2012-08-28 15:26:32 +02:00
|
|
|
|
(require 'ein-core)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(require 'ein-notebook)
|
2018-03-10 07:07:37 -06:00
|
|
|
|
(require 'ein-connect)
|
2017-04-20 07:47:05 -05:00
|
|
|
|
(require 'ein-file)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(require 'ein-contents-api)
|
2012-05-19 15:47:09 +02:00
|
|
|
|
(require 'ein-subpackages)
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(require 'deferred)
|
2017-09-30 14:35:36 +09:00
|
|
|
|
(require 'dash)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-07-05 00:13:12 +02:00
|
|
|
|
(defcustom ein:notebooklist-first-open-hook nil
|
|
|
|
|
"Hooks to run when the notebook list is opened at first time.
|
|
|
|
|
|
|
|
|
|
Example to open a notebook named _scratch_ when the notebook list
|
|
|
|
|
is opened at first time.::
|
|
|
|
|
|
|
|
|
|
(add-hook
|
|
|
|
|
'ein:notebooklist-first-open-hook
|
|
|
|
|
(lambda () (ein:notebooklist-open-notebook-by-name \"_scratch_\")))
|
|
|
|
|
|
|
|
|
|
"
|
|
|
|
|
:type 'hook
|
|
|
|
|
:group 'ein)
|
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(defstruct ein:$notebooklist
|
|
|
|
|
"Hold notebooklist variables.
|
|
|
|
|
|
|
|
|
|
`ein:$notebooklist-url-or-port'
|
|
|
|
|
URL or port of IPython server.
|
|
|
|
|
|
2014-09-12 12:37:24 -05:00
|
|
|
|
`ein:$notbooklist-path'
|
2014-09-11 20:04:00 -05:00
|
|
|
|
The path for the notebooklist.
|
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
`ein:$notebooklist-data'
|
2014-10-17 16:44:04 -05:00
|
|
|
|
JSON data sent from the server.
|
|
|
|
|
`ein:$notebooklist-api-version'
|
|
|
|
|
Major version of the IPython notebook server we are talking to."
|
2012-05-13 02:51:47 +02:00
|
|
|
|
url-or-port
|
2014-09-11 20:04:00 -05:00
|
|
|
|
path
|
2014-10-17 16:44:04 -05:00
|
|
|
|
data
|
|
|
|
|
api-version)
|
2012-05-13 02:51:47 +02:00
|
|
|
|
|
2012-08-18 22:31:15 +02:00
|
|
|
|
(ein:deflocal ein:%notebooklist% nil
|
2012-05-13 02:51:47 +02:00
|
|
|
|
"Buffer local variable to store an instance of `ein:$notebooklist'.")
|
2012-08-18 22:31:15 +02:00
|
|
|
|
(define-obsolete-variable-alias 'ein:notebooklist 'ein:%notebooklist% "0.1.2")
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
|
|
|
|
(defvar ein:notebooklist-buffer-name-template "*ein:notebooklist %s*")
|
|
|
|
|
|
2012-06-26 17:45:51 +02:00
|
|
|
|
(defvar ein:notebooklist-map (make-hash-table :test 'equal)
|
|
|
|
|
"Data store for `ein:notebooklist-list'.
|
|
|
|
|
Mapping from URL-OR-PORT to an instance of `ein:$notebooklist'.")
|
2012-06-05 14:54:18 +02:00
|
|
|
|
|
2012-06-26 17:45:51 +02:00
|
|
|
|
(defun ein:notebooklist-list ()
|
|
|
|
|
"Get a list of opened `ein:$notebooklist'."
|
|
|
|
|
(ein:hash-vals ein:notebooklist-map))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-list-add (nblist)
|
|
|
|
|
"Register notebook list instance NBLIST for global lookup.
|
|
|
|
|
This function adds NBLIST to `ein:notebooklist-map'."
|
|
|
|
|
(puthash (ein:$notebooklist-url-or-port nblist)
|
|
|
|
|
nblist
|
|
|
|
|
ein:notebooklist-map))
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2012-07-05 00:13:12 +02:00
|
|
|
|
(defun ein:notebooklist-list-get (url-or-port)
|
|
|
|
|
"Get an instance of `ein:$notebooklist' by URL-OR-PORT as a key."
|
|
|
|
|
(gethash url-or-port ein:notebooklist-map))
|
|
|
|
|
|
2015-02-10 14:53:08 -06:00
|
|
|
|
;; TODO: FIXME. Use content API.
|
2012-07-12 02:56:23 +02:00
|
|
|
|
(defun ein:notebooklist-open-notebook-by-name (name &optional url-or-port
|
|
|
|
|
callback cbargs)
|
2012-07-05 00:13:12 +02:00
|
|
|
|
"Open notebook named NAME in the server URL-OR-PORT.
|
|
|
|
|
If URL-OR-PORT is not given or `nil', and the current buffer is
|
|
|
|
|
the notebook list buffer, the notebook is searched in the
|
2012-07-12 02:56:23 +02:00
|
|
|
|
notebook list of the current buffer.
|
|
|
|
|
|
|
|
|
|
When used in lisp, CALLBACK and CBARGS are passed to `ein:notebook-open'.
|
|
|
|
|
To suppress popup, you can pass a function `ein:do-nothing' as CALLBACK."
|
2012-07-05 00:13:12 +02:00
|
|
|
|
(loop with nblist = (if url-or-port
|
|
|
|
|
(ein:notebooklist-list-get url-or-port)
|
2012-08-18 22:31:15 +02:00
|
|
|
|
ein:%notebooklist%)
|
2012-07-05 00:13:12 +02:00
|
|
|
|
for note in (ein:$notebooklist-data nblist)
|
|
|
|
|
for notebook-name = (plist-get note :name)
|
2014-09-11 20:04:00 -05:00
|
|
|
|
for notebook-path = (plist-get note :path)
|
2012-07-05 00:13:12 +02:00
|
|
|
|
when (equal notebook-name name)
|
|
|
|
|
return (ein:notebook-open (ein:$notebooklist-url-or-port nblist)
|
2016-01-13 07:52:02 -06:00
|
|
|
|
notebook-path nil callback cbargs)))
|
2012-07-05 00:13:12 +02:00
|
|
|
|
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(defun ein:notebooklist-url (url-or-port version &optional path)
|
|
|
|
|
(let ((base-path (cond ((= version 2) "api/notebooks")
|
2016-11-11 09:48:47 -08:00
|
|
|
|
((>= version 3) "api/contents"))))
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(if path
|
|
|
|
|
(ein:url url-or-port base-path (or path ""))
|
|
|
|
|
(ein:url url-or-port base-path))))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-new-url (url-or-port version &optional path)
|
|
|
|
|
(let ((base-path (cond ((= version 2) "api/notebooks")
|
2016-11-11 09:48:47 -08:00
|
|
|
|
((>= version 3) "api/contents"))))
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(ein:log 'info "New notebook. Port: %s, Path: %s" url-or-port path)
|
|
|
|
|
(if (and path (not (string= path "")))
|
|
|
|
|
(ein:url url-or-port base-path path)
|
|
|
|
|
(ein:url url-or-port base-path))))
|
2012-05-12 00:34:00 +02:00
|
|
|
|
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(defun ein:notebooklist-get-buffer (url-or-port)
|
2012-05-12 00:34:00 +02:00
|
|
|
|
(get-buffer-create
|
2012-05-13 02:51:47 +02:00
|
|
|
|
(format ein:notebooklist-buffer-name-template url-or-port)))
|
2012-05-12 00:34:00 +02:00
|
|
|
|
|
2012-06-09 01:02:53 +02:00
|
|
|
|
(defun ein:notebooklist-ask-url-or-port ()
|
|
|
|
|
(let* ((url-or-port-list (mapcar (lambda (x) (format "%s" x))
|
|
|
|
|
ein:url-or-port))
|
2012-08-13 19:19:23 +02:00
|
|
|
|
(default (format "%s" (ein:aif (ein:get-notebook)
|
2012-06-15 21:55:32 +02:00
|
|
|
|
(ein:$notebook-url-or-port it)
|
2012-08-18 22:31:15 +02:00
|
|
|
|
(ein:aif ein:%notebooklist%
|
2012-06-15 21:55:32 +02:00
|
|
|
|
(ein:$notebooklist-url-or-port it)
|
|
|
|
|
(ein:default-url-or-port)))))
|
2012-06-09 01:02:53 +02:00
|
|
|
|
(url-or-port
|
2012-06-17 09:08:33 +02:00
|
|
|
|
(completing-read (format "URL or port number (default %s): " default)
|
2012-06-09 01:02:53 +02:00
|
|
|
|
url-or-port-list
|
|
|
|
|
nil nil nil nil
|
2012-06-12 18:58:38 +02:00
|
|
|
|
default)))
|
2012-06-09 01:02:53 +02:00
|
|
|
|
(if (string-match "^[0-9]+$" url-or-port)
|
|
|
|
|
(string-to-number url-or-port)
|
|
|
|
|
url-or-port)))
|
|
|
|
|
|
2012-05-14 18:47:08 +02:00
|
|
|
|
;;;###autoload
|
2014-09-11 20:04:00 -05:00
|
|
|
|
(defun ein:notebooklist-open (&optional url-or-port path no-popup)
|
2012-05-12 00:59:15 +02:00
|
|
|
|
"Open notebook list buffer."
|
2012-06-09 01:02:53 +02:00
|
|
|
|
(interactive (list (ein:notebooklist-ask-url-or-port)))
|
2012-06-15 21:46:55 +02:00
|
|
|
|
(unless url-or-port (setq url-or-port (ein:default-url-or-port)))
|
2014-09-12 12:37:24 -05:00
|
|
|
|
(unless path (setq path ""))
|
2017-10-03 10:04:41 -05:00
|
|
|
|
(if (and (stringp url-or-port) (not (string-match-p "^https?" url-or-port)))
|
2015-07-08 16:17:54 -05:00
|
|
|
|
(setq url-or-port (format "http://%s" url-or-port)))
|
2017-10-21 22:07:02 +09:00
|
|
|
|
(ein:log 'debug "NOTEBOOKLIST-OPEN: %s/%s" url-or-port path)
|
2016-02-02 12:12:46 -06:00
|
|
|
|
(ein:subpackages-load)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(let ((success
|
2012-05-26 16:57:42 +02:00
|
|
|
|
(if no-popup
|
|
|
|
|
#'ein:notebooklist-url-retrieve-callback
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(lambda (content)
|
2012-05-26 16:57:42 +02:00
|
|
|
|
(pop-to-buffer
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(funcall #'ein:notebooklist-url-retrieve-callback content))))))
|
2018-01-10 09:36:27 -06:00
|
|
|
|
(ein:query-kernelspecs url-or-port)
|
2017-06-30 21:41:16 -05:00
|
|
|
|
(ein:content-query-contents path url-or-port t success))
|
2017-01-11 07:42:06 -06:00
|
|
|
|
;(ein:notebooklist-get-buffer url-or-port)
|
|
|
|
|
)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
|
2017-01-31 10:44:04 -06:00
|
|
|
|
(defcustom ein:notebooklist-keepalive-refresh-time 1
|
|
|
|
|
"When the notebook keepalive is enabled, the frequency, IN
|
|
|
|
|
HOURS, with which to make calls to the jupyter content API to
|
|
|
|
|
refresh the notebook connection."
|
|
|
|
|
:type 'float
|
|
|
|
|
:group 'ein)
|
|
|
|
|
|
2017-02-15 19:33:35 -06:00
|
|
|
|
(defcustom ein:enable-keepalive nil
|
|
|
|
|
"When non-nil, will cause EIN to automatically call
|
|
|
|
|
`ein:notebooklist-enable-keepalive' after any call to
|
|
|
|
|
`ein:notebooklist-open'."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'ein)
|
|
|
|
|
|
2017-01-31 10:44:04 -06:00
|
|
|
|
(defvar ein:notebooklist--keepalive-timer nil)
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun ein:notebooklist-enable-keepalive (&optional url-or-port)
|
2017-02-01 09:25:47 -06:00
|
|
|
|
"Enable periodic calls to the notebook server to keep long running sessions from expiring.
|
|
|
|
|
By long running we mean sessions to last days, or weeks. The
|
|
|
|
|
frequency of the refresh (which is very similar to a call to
|
|
|
|
|
`ein:notebooklist-open`) is controlled by
|
|
|
|
|
`ein:notebooklist-keepalive-refresh-time`, and is measured in
|
2017-02-16 09:01:14 -06:00
|
|
|
|
terms of hours. If `ein:enable-keepalive' is non-nil this will
|
|
|
|
|
automatically be called during calls to `ein:notebooklist-open`."
|
2017-01-31 10:44:04 -06:00
|
|
|
|
(interactive (list (ein:notebooklist-ask-url-or-port)))
|
2017-02-15 19:33:35 -06:00
|
|
|
|
(unless ein:notebooklist--keepalive-timer
|
|
|
|
|
(message "Enabling notebooklist keepalive...")
|
|
|
|
|
(let ((success
|
|
|
|
|
(lambda (content)
|
|
|
|
|
(ein:log 'info "Refreshing notebooklist connection.")))
|
|
|
|
|
(refresh-time (* ein:notebooklist-keepalive-refresh-time 60 60)))
|
|
|
|
|
(setq ein:notebooklist--keepalive-timer
|
|
|
|
|
(run-at-time 0.1 refresh-time #'ein:content-query-contents "" url-or-port nil success)))))
|
2017-01-31 10:44:04 -06:00
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun ein:notebooklist-disable-keepalive ()
|
2017-02-01 09:25:47 -06:00
|
|
|
|
"Disable the notebooklist keepalive calls to the jupyter notebook server."
|
2017-01-31 10:44:04 -06:00
|
|
|
|
(interactive)
|
2017-02-15 19:33:35 -06:00
|
|
|
|
(message "Disabling notebooklist keepalive...")
|
2017-02-16 09:01:14 -06:00
|
|
|
|
(cancel-timer ein:notebooklist--keepalive-timer)
|
|
|
|
|
(setq ein:notebooklist--keepalive-timer nil))
|
2017-01-31 10:44:04 -06:00
|
|
|
|
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(defun* ein:notebooklist-url-retrieve-callback (content)
|
2012-05-12 00:59:15 +02:00
|
|
|
|
"Called via `ein:notebooklist-open'."
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(let ((url-or-port (ein:$content-url-or-port content))
|
|
|
|
|
(path (ein:$content-path content))
|
|
|
|
|
(ipy-version (ein:$content-ipython-version content))
|
|
|
|
|
(data (ein:$content-raw-content content)))
|
2016-04-15 16:08:53 -05:00
|
|
|
|
(when (>= ipy-version 3)
|
|
|
|
|
(ein:query-kernelspecs url-or-port))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(with-current-buffer (ein:notebooklist-get-buffer url-or-port)
|
|
|
|
|
(let ((already-opened-p (ein:notebooklist-list-get url-or-port))
|
|
|
|
|
(orig-point (point)))
|
|
|
|
|
(setq ein:%notebooklist%
|
|
|
|
|
(make-ein:$notebooklist :url-or-port url-or-port
|
|
|
|
|
:path path
|
|
|
|
|
:data data
|
|
|
|
|
:api-version ipy-version))
|
|
|
|
|
(ein:notebooklist-list-add ein:%notebooklist%)
|
2016-04-15 16:08:53 -05:00
|
|
|
|
(if (< ipy-version 3)
|
|
|
|
|
(ein:notebooklist-render-ipy2)
|
|
|
|
|
(ein:notebooklist-render))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(goto-char orig-point)
|
2017-10-21 22:07:50 +09:00
|
|
|
|
(ein:log 'info "Opened notebook list at %s with path %s." url-or-port path)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(unless already-opened-p
|
|
|
|
|
(run-hooks 'ein:notebooklist-first-open-hook))
|
2017-02-15 19:33:35 -06:00
|
|
|
|
(when ein:enable-keepalive
|
|
|
|
|
(ein:notebooklist-enable-keepalive (ein:$content-url-or-port content)))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(current-buffer)))))
|
2012-05-12 00:34:00 +02:00
|
|
|
|
|
2014-09-11 20:04:00 -05:00
|
|
|
|
(defun* ein:notebooklist-open-error (url-or-port path
|
2012-12-29 17:44:49 +01:00
|
|
|
|
&key symbol-status response
|
2012-06-26 19:30:35 +02:00
|
|
|
|
&allow-other-keys)
|
2012-12-29 17:44:49 +01:00
|
|
|
|
(ein:log 'verbose
|
|
|
|
|
"Error thrown: %S" (request-response-error-thrown response))
|
2012-06-26 19:30:35 +02:00
|
|
|
|
(ein:log 'error
|
2014-09-11 20:04:00 -05:00
|
|
|
|
"Error (%s) while opening notebook list with path %s at the server %s."
|
|
|
|
|
symbol-status path url-or-port))
|
2012-06-26 19:30:35 +02:00
|
|
|
|
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2015-01-13 06:40:15 -06:00
|
|
|
|
(defun ein:notebooklist-reload (&optional notebooklist)
|
2012-05-13 05:18:38 +02:00
|
|
|
|
"Reload current Notebook list."
|
|
|
|
|
(interactive)
|
2015-01-13 06:40:15 -06:00
|
|
|
|
(unless notebooklist
|
|
|
|
|
(setq notebooklist ein:%notebooklist%))
|
|
|
|
|
(ein:notebooklist-open (ein:$notebooklist-url-or-port notebooklist)
|
|
|
|
|
(ein:$notebooklist-path notebooklist) t))
|
2012-05-13 05:18:38 +02:00
|
|
|
|
|
2012-06-26 17:03:36 +02:00
|
|
|
|
(defun ein:notebooklist-refresh-related ()
|
|
|
|
|
"Reload notebook list in which current notebook locates.
|
|
|
|
|
This function is called via `ein:notebook-after-rename-hook'."
|
2015-01-13 06:40:15 -06:00
|
|
|
|
(ein:notebooklist-open (ein:$notebook-url-or-port ein:%notebook%)
|
|
|
|
|
(ein:$notebook-notebook-path ein:%notebook%) t))
|
2012-06-26 17:03:36 +02:00
|
|
|
|
|
|
|
|
|
(add-hook 'ein:notebook-after-rename-hook 'ein:notebooklist-refresh-related)
|
|
|
|
|
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(defun ein:notebooklist-open-notebook (nblist path &optional callback cbargs)
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(ein:notebook-open (ein:$notebooklist-url-or-port nblist)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
path
|
2016-04-15 16:08:53 -05:00
|
|
|
|
nil
|
2015-02-10 14:53:08 -06:00
|
|
|
|
callback
|
|
|
|
|
cbargs))
|
2012-05-18 02:23:30 +02:00
|
|
|
|
|
2017-04-20 07:47:05 -05:00
|
|
|
|
(defun ein:notebooklist-open-file (url-or-port path)
|
|
|
|
|
(ein:file-open url-or-port
|
|
|
|
|
path))
|
|
|
|
|
|
2017-04-02 14:33:56 -05:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun ein:notebooklist-upload-file (upload-path)
|
|
|
|
|
(interactive "fSelect file to upload:")
|
|
|
|
|
(unless ein:%notebooklist%
|
|
|
|
|
(error "Only works when called from an ein:notebooklist buffer."))
|
|
|
|
|
(let ((nb-path (ein:$notebooklist-path ein:%notebooklist%)))
|
|
|
|
|
(ein:content-upload nb-path upload-path)))
|
|
|
|
|
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(defun ein:notebooklist-new-notebook (&optional url-or-port kernelspec path callback cbargs)
|
2012-06-11 14:13:57 +02:00
|
|
|
|
"Ask server to create a new notebook and open it in a new buffer."
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(interactive (list (ein:notebooklist-ask-url-or-port)
|
2017-04-02 14:33:56 -05:00
|
|
|
|
(completing-read
|
|
|
|
|
"Select kernel [default]: "
|
|
|
|
|
(ein:list-available-kernels (ein:$notebooklist-url-or-port ein:%notebooklist%)) nil t nil nil "default" nil)))
|
2014-12-04 13:52:36 -06:00
|
|
|
|
(let ((path (or path (ein:$notebooklist-path (or ein:%notebooklist%
|
|
|
|
|
(ein:notebooklist-list-get url-or-port)))))
|
|
|
|
|
(version (ein:$notebooklist-api-version (or ein:%notebooklist%
|
|
|
|
|
(ein:notebooklist-list-get url-or-port)))))
|
2014-09-19 09:17:12 -05:00
|
|
|
|
(ein:log 'info "Creating a new notebook at %s..." path)
|
|
|
|
|
(unless url-or-port
|
|
|
|
|
(setq url-or-port (ein:$notebooklist-url-or-port ein:%notebooklist%)))
|
|
|
|
|
(assert url-or-port nil
|
|
|
|
|
(concat "URL-OR-PORT is not given and the current buffer "
|
|
|
|
|
"is not the notebook list buffer."))
|
|
|
|
|
(let ((url (ein:notebooklist-new-url url-or-port
|
2014-11-07 19:28:09 -06:00
|
|
|
|
version
|
2014-09-19 09:17:12 -05:00
|
|
|
|
path)))
|
|
|
|
|
(ein:query-singleton-ajax
|
|
|
|
|
(list 'notebooklist-new-notebook url-or-port path)
|
|
|
|
|
url
|
|
|
|
|
:type "POST"
|
2015-01-16 22:01:54 -06:00
|
|
|
|
:data (json-encode '((:type . "notebook")))
|
2014-12-04 13:52:36 -06:00
|
|
|
|
:parser #'ein:json-read
|
|
|
|
|
;; (lambda ()
|
|
|
|
|
;; (ein:html-get-data-in-body-tag "data-notebook-id"))
|
2014-09-19 09:17:12 -05:00
|
|
|
|
:error (apply-partially #'ein:notebooklist-new-notebook-error
|
|
|
|
|
url-or-port path callback cbargs)
|
|
|
|
|
:success (apply-partially #'ein:notebooklist-new-notebook-callback
|
2016-01-13 07:52:02 -06:00
|
|
|
|
url-or-port kernelspec path callback cbargs)))))
|
2012-12-29 17:17:39 +01:00
|
|
|
|
|
|
|
|
|
(defun* ein:notebooklist-new-notebook-callback (url-or-port
|
2016-03-01 16:02:00 -06:00
|
|
|
|
kernelspec
|
2014-09-19 09:17:12 -05:00
|
|
|
|
path
|
2012-12-29 17:17:39 +01:00
|
|
|
|
callback
|
|
|
|
|
cbargs
|
|
|
|
|
&key
|
2012-12-29 17:52:05 +01:00
|
|
|
|
data
|
|
|
|
|
&allow-other-keys
|
|
|
|
|
&aux
|
|
|
|
|
(no-popup t))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(ein:log 'info "Creating a new notebook (%s)... Done." path)
|
2014-12-04 13:52:36 -06:00
|
|
|
|
(if data
|
|
|
|
|
(let ((name (plist-get data :name))
|
|
|
|
|
(path (plist-get data :path)))
|
2015-03-25 09:52:16 -05:00
|
|
|
|
(if (= (ein:query-ipython-version url-or-port) 2)
|
|
|
|
|
(if (string= path "")
|
|
|
|
|
(setq path name)
|
|
|
|
|
(setq path (format "%s/%s" path name))))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(ein:notebook-open url-or-port path kernelspec callback cbargs))
|
2012-12-29 17:52:05 +01:00
|
|
|
|
(ein:log 'info (concat "Oops. EIN failed to open new notebook. "
|
|
|
|
|
"Please find it in the notebook list."))
|
|
|
|
|
(setq no-popup nil))
|
|
|
|
|
;; reload or open notebook list
|
2014-09-19 09:17:12 -05:00
|
|
|
|
(ein:notebooklist-open url-or-port path no-popup))
|
2012-05-26 19:51:57 +02:00
|
|
|
|
|
2012-09-06 23:46:29 +02:00
|
|
|
|
(defun* ein:notebooklist-new-notebook-error
|
2012-12-29 17:44:49 +01:00
|
|
|
|
(url-or-port callback cbargs
|
|
|
|
|
&key response &allow-other-keys
|
|
|
|
|
&aux
|
|
|
|
|
(no-popup t)
|
|
|
|
|
(error (request-response-error-thrown response))
|
2013-01-10 19:10:59 +01:00
|
|
|
|
(dest (request-response-url response)))
|
2012-09-06 23:46:29 +02:00
|
|
|
|
(ein:log 'verbose
|
2013-01-10 19:10:59 +01:00
|
|
|
|
"NOTEBOOKLIST-NEW-NOTEBOOK-ERROR url-or-port: %S; error: %S; dest: %S"
|
|
|
|
|
url-or-port error dest)
|
|
|
|
|
(ein:log 'error
|
|
|
|
|
"Failed to open new notebook (error: %S). \
|
|
|
|
|
You may find the new one in the notebook list." error)
|
|
|
|
|
(setq no-popup nil)
|
2012-12-29 17:52:05 +01:00
|
|
|
|
(ein:notebooklist-open url-or-port no-popup))
|
2012-09-06 23:46:29 +02:00
|
|
|
|
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2016-03-22 07:47:11 -05:00
|
|
|
|
(defun ein:notebooklist-new-notebook-with-name (name kernelspec url-or-port &optional path)
|
2012-06-08 12:59:14 +02:00
|
|
|
|
"Open new notebook and rename the notebook."
|
2012-09-05 14:51:35 +02:00
|
|
|
|
(interactive (let* ((url-or-port (or (ein:get-url-or-port)
|
|
|
|
|
(ein:default-url-or-port)))
|
2016-03-22 07:47:11 -05:00
|
|
|
|
(kernelspec (completing-read
|
|
|
|
|
"Select kernel [default]: "
|
|
|
|
|
(ein:list-available-kernels url-or-port) nil t nil nil "default" nil))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(name (read-from-minibuffer
|
|
|
|
|
(format "Notebook name (at %s): " url-or-port))))
|
|
|
|
|
(list name kernelspec url-or-port)))
|
2015-03-25 09:52:16 -05:00
|
|
|
|
(let ((path (or path (ein:$notebooklist-path
|
|
|
|
|
(or ein:%notebooklist%
|
|
|
|
|
(ein:get-notebook)
|
|
|
|
|
(gethash url-or-port ein:notebooklist-map))))))
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(ein:notebooklist-new-notebook
|
|
|
|
|
url-or-port
|
2016-01-13 07:52:02 -06:00
|
|
|
|
kernelspec
|
2014-10-17 16:44:04 -05:00
|
|
|
|
path
|
|
|
|
|
(lambda (notebook created name)
|
|
|
|
|
(assert created)
|
|
|
|
|
(with-current-buffer (ein:notebook-buffer notebook)
|
|
|
|
|
(ein:notebook-rename-command name)
|
|
|
|
|
;; As `ein:notebook-open' does not call `pop-to-buffer' when
|
|
|
|
|
;; callback is specified, `pop-to-buffer' must be called here:
|
|
|
|
|
(pop-to-buffer (current-buffer))))
|
|
|
|
|
(list name))))
|
2012-06-08 12:59:14 +02:00
|
|
|
|
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(defun ein:notebooklist-delete-notebook-ask (path)
|
|
|
|
|
(when (y-or-n-p (format "Delete notebook %s?" path))
|
|
|
|
|
(ein:notebooklist-delete-notebook path)))
|
2012-05-13 06:51:26 +02:00
|
|
|
|
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(defun ein:notebooklist-delete-notebook (path)
|
|
|
|
|
(ein:log 'info "Deleting notebook %s..." path)
|
2012-06-20 21:01:58 +02:00
|
|
|
|
(ein:query-singleton-ajax
|
|
|
|
|
(list 'notebooklist-delete-notebook
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%) path)
|
2012-05-26 19:01:39 +02:00
|
|
|
|
(ein:notebook-url-from-url-and-id
|
2012-08-18 22:31:15 +02:00
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)
|
2014-10-17 16:44:04 -05:00
|
|
|
|
(ein:$notebooklist-api-version ein:%notebooklist%)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
path)
|
2012-05-26 19:01:39 +02:00
|
|
|
|
:type "DELETE"
|
2017-09-30 21:34:00 +09:00
|
|
|
|
:success (apply-partially (lambda (path notebook-list &rest ignore)
|
2012-12-29 17:17:39 +01:00
|
|
|
|
(ein:log 'info
|
2015-02-10 14:53:08 -06:00
|
|
|
|
"Deleting notebook %s... Done." path)
|
2017-09-30 21:34:00 +09:00
|
|
|
|
(ein:notebooklist-reload notebook-list))
|
|
|
|
|
path ein:%notebooklist%)))
|
2012-05-12 00:59:15 +02:00
|
|
|
|
|
2015-12-04 14:15:41 -06:00
|
|
|
|
;; Because MinRK wants me to suffer (not really, I love MinRK)...
|
2014-11-12 06:38:02 -06:00
|
|
|
|
(defun ein:get-actual-path (path)
|
|
|
|
|
(ein:aif (cl-position ?/ path :from-end t)
|
|
|
|
|
(substring path 0 it)
|
|
|
|
|
""))
|
|
|
|
|
|
2014-12-09 10:41:53 -06:00
|
|
|
|
(defun generate-breadcrumbs (path)
|
|
|
|
|
"Given notebooklist path, generate alist of breadcrumps of form (name . path)."
|
|
|
|
|
(let* ((paths (split-string path "/" t))
|
|
|
|
|
(current-path "/")
|
|
|
|
|
(pairs (list (cons "Home" ""))))
|
|
|
|
|
(dolist (p paths pairs)
|
2015-09-14 13:16:49 -05:00
|
|
|
|
(setf current-path (concat current-path "/" p)
|
2014-12-09 10:41:53 -06:00
|
|
|
|
pairs (append pairs (list (cons p current-path)))))))
|
|
|
|
|
|
2016-04-15 16:08:53 -05:00
|
|
|
|
(defun ein:notebooklist-render-ipy2 ()
|
|
|
|
|
"Render notebook list for IPython 2.x sessions.
|
|
|
|
|
Notebook list data is passed via the buffer local variable
|
|
|
|
|
`ein:notebooklist-data'."
|
|
|
|
|
(kill-all-local-variables)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer))
|
|
|
|
|
(remove-overlays)
|
|
|
|
|
;; Create notebook list
|
|
|
|
|
(widget-insert (format "IPython %s Notebook list\n\n" (ein:$notebooklist-api-version ein:%notebooklist%)))
|
|
|
|
|
(let ((breadcrumbs (generate-breadcrumbs (ein:$notebooklist-path ein:%notebooklist%))))
|
|
|
|
|
(dolist (p breadcrumbs)
|
|
|
|
|
(lexical-let ((name (car p))
|
|
|
|
|
(path (cdr p)))
|
|
|
|
|
(widget-insert " | ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-open
|
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)
|
|
|
|
|
path))
|
|
|
|
|
name)))
|
|
|
|
|
(widget-insert " |\n\n"))
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-new-notebook
|
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)))
|
|
|
|
|
"New Notebook")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-reload))
|
|
|
|
|
"Reload List")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore)
|
|
|
|
|
(browse-url
|
|
|
|
|
(ein:url (ein:$notebooklist-url-or-port ein:%notebooklist%))))
|
|
|
|
|
"Open In Browser")
|
|
|
|
|
(widget-insert "\n")
|
|
|
|
|
(let ((api-version (ein:$notebooklist-api-version ein:%notebooklist%))
|
|
|
|
|
(sessions (make-hash-table :test 'equal)))
|
|
|
|
|
(ein:content-query-sessions sessions (ein:$notebooklist-url-or-port ein:%notebooklist%) t)
|
|
|
|
|
(loop for note in (ein:$notebooklist-data ein:%notebooklist%)
|
|
|
|
|
for urlport = (ein:$notebooklist-url-or-port ein:%notebooklist%)
|
|
|
|
|
for name = (plist-get note :name)
|
|
|
|
|
for path = (plist-get note :path)
|
|
|
|
|
;; (cond ((= 2 api-version)
|
|
|
|
|
;; (plist-get note :path))
|
|
|
|
|
;; ((= 3 api-version)
|
|
|
|
|
;; (ein:get-actual-path (plist-get note :path))))
|
|
|
|
|
for type = (plist-get note :type)
|
|
|
|
|
for opened-notebook-maybe = (ein:notebook-get-opened-notebook urlport path)
|
|
|
|
|
do (widget-insert " ")
|
|
|
|
|
if (string= type "directory")
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((urlport urlport)
|
|
|
|
|
(path name))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(ein:notebooklist-open urlport
|
|
|
|
|
(ein:url (ein:$notebooklist-path ein:%notebooklist%)
|
|
|
|
|
path))))
|
|
|
|
|
"Dir")
|
|
|
|
|
(widget-insert " : " name)
|
|
|
|
|
(widget-insert "\n"))
|
|
|
|
|
if (string= type "notebook")
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((name name)
|
|
|
|
|
(path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(run-at-time 3 nil
|
|
|
|
|
#'ein:notebooklist-reload ein:%notebooklist%) ;; TODO using deferred better?
|
|
|
|
|
(ein:notebooklist-open-notebook
|
|
|
|
|
ein:%notebooklist% path)))
|
|
|
|
|
"Open")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(when (gethash path sessions)
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((session (car (gethash path sessions)))
|
|
|
|
|
(nblist ein:%notebooklist%))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(run-at-time 1 nil
|
|
|
|
|
#'ein:notebooklist-reload
|
|
|
|
|
ein:%notebooklist%)
|
|
|
|
|
(ein:kernel-kill (make-ein:$kernel :url-or-port (ein:$notebooklist-url-or-port nblist)
|
|
|
|
|
:session-id session))))
|
|
|
|
|
"Stop")
|
|
|
|
|
(widget-insert " "))
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(ein:notebooklist-delete-notebook-ask
|
|
|
|
|
path)))
|
|
|
|
|
"Delete")
|
|
|
|
|
(widget-insert " : " name)
|
|
|
|
|
(widget-insert "\n"))))
|
|
|
|
|
(ein:notebooklist-mode)
|
|
|
|
|
(widget-setup))
|
|
|
|
|
|
2017-09-12 16:22:19 -05:00
|
|
|
|
(defun ein:notebooklist--order-data (nblist-data)
|
|
|
|
|
"Try to sanely sort the notebooklist data for the current path."
|
|
|
|
|
(let* ((groups (-group-by #'(lambda (x) (plist-get x :type))
|
|
|
|
|
nblist-data))
|
|
|
|
|
(dirs (cdr (assoc "directory" groups)))
|
|
|
|
|
(nbs (cdr (assoc "notebook" groups)))
|
|
|
|
|
(files (-flatten-n 1 (-map #'cdr (-group-by
|
|
|
|
|
#'(lambda (x) (car (last (s-split "\\." (plist-get x :name)))))
|
|
|
|
|
(cdr (assoc "file" groups)))))))
|
|
|
|
|
(-concat dirs nbs files)))
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(defun ein:notebooklist-render ()
|
2012-05-12 00:59:15 +02:00
|
|
|
|
"Render notebook list widget.
|
|
|
|
|
Notebook list data is passed via the buffer local variable
|
|
|
|
|
`ein:notebooklist-data'."
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(kill-all-local-variables)
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(erase-buffer))
|
|
|
|
|
(remove-overlays)
|
|
|
|
|
;; Create notebook list
|
2016-11-05 17:49:52 -05:00
|
|
|
|
(widget-insert
|
|
|
|
|
(if (< (ein:$notebooklist-api-version ein:%notebooklist%) 4)
|
2017-11-07 15:14:45 -06:00
|
|
|
|
(format "IPython v%s Notebook list (%s)\n\n" (ein:$notebooklist-api-version ein:%notebooklist%) (ein:$notebooklist-url-or-port ein:%notebooklist%))
|
|
|
|
|
(format "Jupyter v%s Notebook list (%s)\n\n" (ein:$notebooklist-api-version ein:%notebooklist%) (ein:$notebooklist-url-or-port ein:%notebooklist%))))
|
2014-12-09 10:41:53 -06:00
|
|
|
|
(let ((breadcrumbs (generate-breadcrumbs (ein:$notebooklist-path ein:%notebooklist%))))
|
|
|
|
|
(dolist (p breadcrumbs)
|
|
|
|
|
(lexical-let ((name (car p))
|
|
|
|
|
(path (cdr p)))
|
|
|
|
|
(widget-insert " | ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-open
|
2015-09-14 13:16:49 -05:00
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)
|
|
|
|
|
path))
|
2014-12-09 10:41:53 -06:00
|
|
|
|
name)))
|
2015-05-28 11:56:40 -05:00
|
|
|
|
(widget-insert " |\n\n"))
|
2016-03-23 12:31:47 -05:00
|
|
|
|
(lexical-let* ((kernels (ein:list-available-kernels (ein:$notebooklist-url-or-port ein:%notebooklist%)))
|
|
|
|
|
(default-kernel (ein:get-kernelspec (ein:$notebooklist-url-or-port ein:%notebooklist%) (first kernels))))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
2016-03-01 16:02:00 -06:00
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-new-notebook
|
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)
|
|
|
|
|
default-kernel))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
"New Notebook")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore) (ein:notebooklist-reload))
|
|
|
|
|
"Reload List")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lambda (&rest ignore)
|
2016-04-15 16:08:53 -05:00
|
|
|
|
(browse-url
|
|
|
|
|
(ein:url (ein:$notebooklist-url-or-port ein:%notebooklist%))))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
"Open In Browser")
|
2016-01-13 08:30:15 -06:00
|
|
|
|
(widget-insert "\n\nCreate New Notebooks Using Kernel: \n")
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(let* ((radio-widget (widget-create 'radio-button-choice
|
2016-12-22 14:04:51 -06:00
|
|
|
|
;; :value (car (first kernels))
|
|
|
|
|
;; :format (format "%s\n" (cdr (first kernels)))
|
|
|
|
|
:notify (lambda (widget &rest ignore)
|
|
|
|
|
(setq default-kernel
|
|
|
|
|
(ein:get-kernelspec (ein:$notebooklist-url-or-port ein:%notebooklist%) (widget-value widget)))
|
|
|
|
|
(message "New notebooks will be started using the %s kernel."
|
|
|
|
|
(widget-value widget))))))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(dolist (k kernels)
|
2016-12-22 14:04:51 -06:00
|
|
|
|
(widget-radio-add-item radio-widget (list 'item :value (car k)
|
|
|
|
|
:format (format "%s\n" (cdr k)))))))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(widget-insert "\n")
|
2017-02-06 20:19:20 -06:00
|
|
|
|
(let ((sessions (make-hash-table :test 'equal)))
|
2015-05-28 11:56:40 -05:00
|
|
|
|
(ein:content-query-sessions sessions (ein:$notebooklist-url-or-port ein:%notebooklist%) t)
|
2017-02-12 22:09:44 -06:00
|
|
|
|
(sit-for 0.2) ;; FIXME: What is the optimum number here?
|
2017-09-12 16:22:19 -05:00
|
|
|
|
(loop for note in (ein:notebooklist--order-data (ein:$notebooklist-data ein:%notebooklist%))
|
2016-08-31 10:04:06 -05:00
|
|
|
|
for urlport = (ein:$notebooklist-url-or-port ein:%notebooklist%)
|
|
|
|
|
for name = (plist-get note :name)
|
|
|
|
|
for path = (plist-get note :path)
|
|
|
|
|
;; (cond ((= 2 api-version)
|
|
|
|
|
;; (plist-get note :path))
|
|
|
|
|
;; ((= 3 api-version)
|
|
|
|
|
;; (ein:get-actual-path (plist-get note :path))))
|
|
|
|
|
for type = (plist-get note :type)
|
|
|
|
|
for opened-notebook-maybe = (ein:notebook-get-opened-notebook urlport path)
|
|
|
|
|
do (widget-insert " ")
|
|
|
|
|
if (string= type "directory")
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((urlport urlport)
|
|
|
|
|
(path name))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(ein:notebooklist-open urlport
|
|
|
|
|
(ein:url (ein:$notebooklist-path ein:%notebooklist%)
|
|
|
|
|
path))))
|
|
|
|
|
"Dir")
|
|
|
|
|
(widget-insert " : " name)
|
|
|
|
|
(widget-insert "\n"))
|
2017-04-20 07:47:05 -05:00
|
|
|
|
if (string= type "file")
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((urlport urlport)
|
|
|
|
|
(path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(ein:notebooklist-open-file urlport path)))
|
|
|
|
|
"Open")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(message "[EIN]: NBlist delete file command. Implement me!")))
|
|
|
|
|
"Delete")
|
|
|
|
|
(widget-insert " : " name)
|
|
|
|
|
(widget-insert "\n"))
|
2016-08-31 10:04:06 -05:00
|
|
|
|
if (string= type "notebook")
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((name name)
|
|
|
|
|
(path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(run-at-time 3 nil
|
|
|
|
|
#'ein:notebooklist-reload ein:%notebooklist%) ;; TODO using deferred better?
|
|
|
|
|
(ein:notebooklist-open-notebook
|
|
|
|
|
ein:%notebooklist% path)))
|
|
|
|
|
"Open")
|
|
|
|
|
(widget-insert " ")
|
|
|
|
|
(when (gethash path sessions)
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((session (car (gethash path sessions)))
|
|
|
|
|
(nblist ein:%notebooklist%))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(run-at-time 1 nil
|
|
|
|
|
#'ein:notebooklist-reload
|
|
|
|
|
ein:%notebooklist%)
|
|
|
|
|
(ein:kernel-kill (make-ein:$kernel :url-or-port (ein:$notebooklist-url-or-port nblist)
|
|
|
|
|
:session-id session))))
|
|
|
|
|
"Stop")
|
|
|
|
|
(widget-insert " "))
|
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((path path))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(ein:notebooklist-delete-notebook-ask
|
|
|
|
|
path)))
|
|
|
|
|
"Delete")
|
|
|
|
|
(widget-insert " : " name)
|
|
|
|
|
(widget-insert "\n")))
|
|
|
|
|
(widget-insert "\n---------- All Opened Notebooks ----------\n\n")
|
|
|
|
|
(loop for buffer in (ein:notebook-opened-buffers)
|
|
|
|
|
do (progn (widget-create
|
|
|
|
|
'link
|
2016-09-04 17:58:55 -05:00
|
|
|
|
:notify (lexical-let ((buffer buffer))
|
|
|
|
|
(lambda (&rest ignore)
|
2016-08-31 10:04:06 -05:00
|
|
|
|
(switch-to-buffer buffer)))
|
|
|
|
|
"Open")
|
2016-09-04 17:58:55 -05:00
|
|
|
|
(widget-create
|
|
|
|
|
'link
|
|
|
|
|
:notify (lexical-let ((buffer buffer))
|
|
|
|
|
(lambda (&rest ignore)
|
|
|
|
|
(kill-buffer buffer)
|
|
|
|
|
(run-at-time 1 nil
|
|
|
|
|
#'ein:notebooklist-reload
|
|
|
|
|
ein:%notebooklist%)))
|
|
|
|
|
"Close")
|
2016-08-31 10:04:06 -05:00
|
|
|
|
(widget-insert " : " (buffer-name buffer))
|
|
|
|
|
(widget-insert "\n"))))
|
2012-05-15 03:41:08 +02:00
|
|
|
|
(ein:notebooklist-mode)
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(widget-setup))
|
|
|
|
|
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2012-07-12 01:05:37 +02:00
|
|
|
|
(defun ein:notebooklist-list-notebooks ()
|
|
|
|
|
"Return a list of notebook path (NBPATH). Each element NBPATH
|
|
|
|
|
is a string of the format \"URL-OR-PORT/NOTEBOOK-NAME\"."
|
|
|
|
|
(apply #'append
|
|
|
|
|
(loop for nblist in (ein:notebooklist-list)
|
|
|
|
|
for url-or-port = (ein:$notebooklist-url-or-port nblist)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
for api-version = (ein:$notebooklist-api-version nblist)
|
2012-07-12 01:05:37 +02:00
|
|
|
|
collect
|
2015-12-04 14:15:41 -06:00
|
|
|
|
(loop for note in (ein:get-content-hierarchy url-or-port)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
collect (format "%s/%s" url-or-port
|
|
|
|
|
(ein:$content-path note)
|
|
|
|
|
))
|
2015-02-12 09:20:34 -06:00
|
|
|
|
;; (if (= api-version 3)
|
|
|
|
|
;; (loop for note in (ein:make-content-hierarchy "" url-or-port)
|
|
|
|
|
;; collect (format "%s/%s" url-or-port
|
|
|
|
|
;; (ein:$content-path note)
|
|
|
|
|
;; ))
|
|
|
|
|
;; (loop for note in (ein:$notebooklist-data nblist)
|
|
|
|
|
;; collect (format "%s/%s"
|
|
|
|
|
;; url-or-port
|
|
|
|
|
;; (plist-get note :name))))
|
|
|
|
|
)))
|
2012-07-12 01:05:37 +02:00
|
|
|
|
|
2014-12-03 19:44:02 -06:00
|
|
|
|
;;FIXME: Code below assumes notebook is in root directory - need to do a better
|
|
|
|
|
;; job listing notebooks in subdirectories and parsing out the path.
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2012-07-12 02:04:50 +02:00
|
|
|
|
(defun ein:notebooklist-open-notebook-global (nbpath &optional callback cbargs)
|
2012-07-12 01:05:37 +02:00
|
|
|
|
"Choose notebook from all opened notebook list and open it.
|
|
|
|
|
Notebook is specified by a string NBPATH whose format is
|
2012-07-12 02:04:50 +02:00
|
|
|
|
\"URL-OR-PORT/NOTEBOOK-NAME\".
|
|
|
|
|
|
|
|
|
|
When used in lisp, CALLBACK and CBARGS are passed to `ein:notebook-open'."
|
2012-06-05 14:54:18 +02:00
|
|
|
|
(interactive
|
|
|
|
|
(list (completing-read
|
|
|
|
|
"Open notebook [URL-OR-PORT/NAME]: "
|
2012-07-12 01:05:37 +02:00
|
|
|
|
(ein:notebooklist-list-notebooks))))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(let* ((url-or-port (substring nbpath 0 (cl-position ?/ nbpath)))
|
|
|
|
|
(path (substring nbpath (1+ (cl-position ?/ nbpath)))))
|
2012-06-05 14:54:18 +02:00
|
|
|
|
(when (and (stringp url-or-port)
|
|
|
|
|
(string-match "^[0-9]+$" url-or-port))
|
|
|
|
|
(setq url-or-port (string-to-number url-or-port)))
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(ein:notebook-open url-or-port path nil callback cbargs)
|
2014-12-03 19:44:02 -06:00
|
|
|
|
(ein:log 'info "Notebook '%s' not found" nbpath)))
|
2012-06-05 14:54:18 +02:00
|
|
|
|
|
2012-09-01 20:51:55 +02:00
|
|
|
|
;;;###autoload
|
2012-08-26 22:12:49 +02:00
|
|
|
|
(defun ein:notebooklist-load (&optional url-or-port)
|
|
|
|
|
"Load notebook list but do not pop-up the notebook list buffer.
|
|
|
|
|
|
|
|
|
|
For example, if you want to load notebook list when Emacs starts,
|
|
|
|
|
add this in the Emacs initialization file::
|
|
|
|
|
|
|
|
|
|
(add-to-hook 'after-init-hook 'ein:notebooklist-load)
|
|
|
|
|
|
|
|
|
|
or even this (if you want fast Emacs start-up)::
|
|
|
|
|
|
|
|
|
|
;; load notebook list if Emacs is idle for 3 sec after start-up
|
|
|
|
|
(run-with-idle-timer 3 nil #'ein:notebooklist-load)
|
|
|
|
|
|
|
|
|
|
You should setup `ein:url-or-port' or `ein:default-url-or-port'
|
|
|
|
|
in order to make this code work.
|
|
|
|
|
|
|
|
|
|
See also:
|
|
|
|
|
`ein:connect-to-default-notebook', `ein:connect-default-notebook'."
|
|
|
|
|
(ein:notebooklist-open url-or-port t))
|
|
|
|
|
|
2012-09-05 17:27:29 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-find-server-by-notebook-name (name)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
"Find a notebook named NAME and return a list (URL-OR-PORT PATH)."
|
2012-09-05 17:27:29 +02:00
|
|
|
|
(loop named outer
|
|
|
|
|
for nblist in (ein:notebooklist-list)
|
|
|
|
|
for url-or-port = (ein:$notebooklist-url-or-port nblist)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
for ipython-version = (ein:$notebooklist-api-version nblist)
|
|
|
|
|
do
|
2018-03-08 14:08:45 -06:00
|
|
|
|
(if (>= ipython-version 3)
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(loop for note in (ein:make-content-hierarchy "" url-or-port)
|
|
|
|
|
when (equal (ein:$content-name note) name)
|
|
|
|
|
do (return-from outer
|
|
|
|
|
(list url-or-port (ein:$content-path note))))
|
|
|
|
|
(loop for note in (ein:$notebooklist-data nblist)
|
|
|
|
|
when (equal (plist-get note :name) name)
|
|
|
|
|
do (return-from outer
|
|
|
|
|
(list url-or-port
|
|
|
|
|
(format "%s/%s" (plist-get note :path) (plist-get note :name))))))))
|
2012-09-05 17:27:29 +02:00
|
|
|
|
|
2012-09-05 17:52:46 +02:00
|
|
|
|
(defun ein:notebooklist-open-notebook-by-file-name
|
2012-09-05 18:02:35 +02:00
|
|
|
|
(&optional filename noerror buffer-callback)
|
2012-09-05 17:27:29 +02:00
|
|
|
|
"Find the notebook named as same as the current file in the servers.
|
|
|
|
|
Open the notebook if found. Note that this command will *not*
|
|
|
|
|
upload the current file to the server.
|
|
|
|
|
|
|
|
|
|
.. When FILENAME is unspecified the variable `buffer-file-name'
|
2012-09-05 17:52:46 +02:00
|
|
|
|
is used instead. Set NOERROR to non-`nil' to suppress errors.
|
2012-09-05 18:02:35 +02:00
|
|
|
|
BUFFER-CALLBACK is called after opening notebook with the
|
|
|
|
|
current buffer as the only one argument."
|
2012-09-05 17:27:29 +02:00
|
|
|
|
(interactive (progn (assert buffer-file-name nil "Not visiting a file.")
|
|
|
|
|
nil))
|
|
|
|
|
(unless filename (setq filename buffer-file-name))
|
|
|
|
|
(assert filename nil "No file found.")
|
|
|
|
|
(let* ((name (file-name-sans-extension
|
|
|
|
|
(file-name-nondirectory (or filename))))
|
2012-09-05 18:02:35 +02:00
|
|
|
|
(found (ein:notebooklist-find-server-by-notebook-name name))
|
|
|
|
|
(callback (lambda (-ignore-1- -ignore-2- buffer buffer-callback)
|
|
|
|
|
(ein:notebook-pop-to-current-buffer) ; default
|
|
|
|
|
(when (buffer-live-p buffer)
|
|
|
|
|
(funcall buffer-callback buffer))))
|
|
|
|
|
(cbargs (list (current-buffer) (or buffer-callback #'ignore))))
|
2012-09-05 20:33:21 +02:00
|
|
|
|
(unless noerror
|
|
|
|
|
(assert found nil "No server has notebook named: %s" name))
|
2015-02-10 14:53:08 -06:00
|
|
|
|
(destructuring-bind (url-or-port path) found
|
2016-01-13 07:52:02 -06:00
|
|
|
|
(ein:notebook-open url-or-port path nil callback cbargs))))
|
2012-09-05 17:52:46 +02:00
|
|
|
|
|
2012-09-05 18:02:35 +02:00
|
|
|
|
(defvar ein:notebooklist-find-file-buffer-callback #'ignore)
|
2012-09-05 17:52:46 +02:00
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-find-file-callback ()
|
2012-09-05 18:02:35 +02:00
|
|
|
|
"A callback function for `find-file-hook' to open notebook.
|
|
|
|
|
|
|
|
|
|
FIMXE: document how to use `ein:notebooklist-find-file-callback'
|
|
|
|
|
when I am convinced with the API."
|
2012-09-05 17:52:46 +02:00
|
|
|
|
(ein:and-let* ((filename buffer-file-name)
|
|
|
|
|
((string-match-p "\\.ipynb$" filename)))
|
2012-09-05 18:02:35 +02:00
|
|
|
|
(ein:notebooklist-open-notebook-by-file-name
|
|
|
|
|
filename t ein:notebooklist-find-file-buffer-callback)))
|
2012-09-05 17:27:29 +02:00
|
|
|
|
|
2012-12-17 16:51:31 +01:00
|
|
|
|
|
|
|
|
|
;;; Login
|
|
|
|
|
|
2016-12-27 12:03:05 -05:00
|
|
|
|
;;;###autoload
|
2017-02-17 15:06:59 -06:00
|
|
|
|
(defun ein:notebooklist-login (url-or-port password &optional retry-p)
|
2012-12-17 16:51:31 +01:00
|
|
|
|
"Login to IPython notebook server."
|
|
|
|
|
(interactive (list (ein:notebooklist-ask-url-or-port)
|
2012-12-17 17:55:30 +01:00
|
|
|
|
(read-passwd "Password: ")))
|
2017-10-21 18:47:38 +09:00
|
|
|
|
(ein:log 'debug "NOTEBOOKLIST-LOGIN: %s:%s" url-or-port password)
|
2012-12-17 16:51:31 +01:00
|
|
|
|
(ein:query-singleton-ajax
|
|
|
|
|
(list 'notebooklist-login url-or-port)
|
|
|
|
|
(ein:url url-or-port "login")
|
|
|
|
|
:type "POST"
|
2012-12-17 23:15:21 +01:00
|
|
|
|
:data (concat "password=" (url-hexify-string password))
|
2012-12-17 19:26:13 +01:00
|
|
|
|
:parser #'ein:notebooklist-login--parser
|
2017-02-17 15:06:59 -06:00
|
|
|
|
:error (apply-partially #'ein:notebooklist-login--error url-or-port password retry-p)
|
2012-12-29 17:17:39 +01:00
|
|
|
|
:success (apply-partially #'ein:notebooklist-login--success url-or-port)))
|
2012-12-17 19:26:13 +01:00
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-login--parser ()
|
|
|
|
|
(goto-char (point-min))
|
2013-01-10 19:25:08 +01:00
|
|
|
|
(list :bad-page (re-search-forward "<input type=.?password" nil t)))
|
2012-12-17 19:26:13 +01:00
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-login--success-1 (url-or-port)
|
|
|
|
|
(ein:log 'info "Login to %s complete. \
|
2012-12-17 16:51:31 +01:00
|
|
|
|
Now you can open notebook list by `ein:notebooklist-open'." url-or-port))
|
2012-12-17 19:26:13 +01:00
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-login--error-1 (url-or-port)
|
|
|
|
|
(ein:log 'info "Failed to login to %s" url-or-port))
|
|
|
|
|
|
|
|
|
|
(defun* ein:notebooklist-login--success (url-or-port &key
|
|
|
|
|
data
|
|
|
|
|
&allow-other-keys)
|
|
|
|
|
(if (plist-get data :bad-page)
|
|
|
|
|
(ein:notebooklist-login--error-1 url-or-port)
|
|
|
|
|
(ein:notebooklist-login--success-1 url-or-port)))
|
|
|
|
|
|
2013-01-09 05:51:25 +01:00
|
|
|
|
(defun* ein:notebooklist-login--error
|
2017-02-17 15:06:59 -06:00
|
|
|
|
(url-or-port password retry-p &key
|
2013-01-09 05:51:25 +01:00
|
|
|
|
data
|
|
|
|
|
symbol-status
|
|
|
|
|
response
|
|
|
|
|
&allow-other-keys
|
|
|
|
|
&aux
|
|
|
|
|
(response-status (request-response-status-code response)))
|
2017-02-17 15:06:59 -06:00
|
|
|
|
(if (and (eq response-status 403)
|
|
|
|
|
(not retry-p))
|
|
|
|
|
(ein:notebooklist-login url-or-port password t))
|
2013-01-10 19:25:08 +01:00
|
|
|
|
(if (or
|
|
|
|
|
;; workaround for url-retrieve backend
|
|
|
|
|
(and (eq symbol-status 'timeout)
|
|
|
|
|
(equal response-status 302)
|
|
|
|
|
(request-response-header response "set-cookie"))
|
|
|
|
|
;; workaround for curl backend
|
|
|
|
|
(and (equal response-status 405)
|
|
|
|
|
(ein:aand (car (request-response-history response))
|
|
|
|
|
(request-response-header it "set-cookie"))))
|
2012-12-17 19:26:13 +01:00
|
|
|
|
(ein:notebooklist-login--success-1 url-or-port)
|
|
|
|
|
(ein:notebooklist-login--error-1 url-or-port)))
|
2012-12-17 16:51:31 +01:00
|
|
|
|
|
2017-05-24 09:53:58 -05:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun ein:notebooklist-change-url-port (new-url-or-port)
|
|
|
|
|
"Update the ipython/jupyter notebook server URL for all the
|
|
|
|
|
notebooks currently opened from the current notebooklist buffer.
|
|
|
|
|
|
|
|
|
|
This function works by calling `ein:notebook-update-url-or-port'
|
|
|
|
|
on all the notebooks opened from the current notebooklist."
|
|
|
|
|
(interactive (list (ein:notebooklist-ask-url-or-port)))
|
|
|
|
|
(unless (eql major-mode 'ein:notebooklist-mode)
|
|
|
|
|
(error "This command needs to be called from within a notebooklist buffer."))
|
2017-11-07 15:14:45 -06:00
|
|
|
|
(let* ((current-nblist ein:%notebooklist%)
|
|
|
|
|
(old-url (ein:$notebooklist-url-or-port current-nblist))
|
|
|
|
|
(new-url-or-port new-url-or-port)
|
|
|
|
|
(open-nb (ein:notebook-opened-notebooks #'(lambda (nb)
|
|
|
|
|
(equal (ein:$notebook-url-or-port nb)
|
|
|
|
|
(ein:$notebooklist-url-or-port current-nblist))))))
|
|
|
|
|
(ein:notebooklist-open new-url-or-port "/" t)
|
|
|
|
|
(loop for x upfrom 0 by 1
|
|
|
|
|
until (or (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port))
|
|
|
|
|
(= x 100))
|
|
|
|
|
do (sit-for 0.1))
|
|
|
|
|
(dolist (nb open-nb)
|
|
|
|
|
(ein:notebook-update-url-or-port new-url-or-port nb))
|
|
|
|
|
(kill-buffer (ein:notebooklist-get-buffer old-url))
|
|
|
|
|
(ein:notebooklist-open new-url-or-port "/" nil)))
|
|
|
|
|
|
|
|
|
|
(defun ein:notebooklist-change-url-port--deferred (new-url-or-port)
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(lexical-let* ((current-nblist ein:%notebooklist%)
|
2017-06-03 15:45:28 -05:00
|
|
|
|
(old-url (ein:$notebooklist-url-or-port current-nblist))
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(new-url-or-port new-url-or-port)
|
|
|
|
|
(open-nb (ein:notebook-opened-notebooks #'(lambda (nb)
|
|
|
|
|
(equal (ein:$notebook-url-or-port nb)
|
|
|
|
|
(ein:$notebooklist-url-or-port current-nblist))))))
|
2017-05-30 14:05:02 -05:00
|
|
|
|
(deferred:$
|
|
|
|
|
(deferred:next
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(lambda ()
|
2017-06-03 15:45:28 -05:00
|
|
|
|
(ein:notebooklist-open new-url-or-port "/" t)
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(loop until (get-buffer (format ein:notebooklist-buffer-name-template new-url-or-port))
|
|
|
|
|
do (sit-for 0.1))))
|
2017-05-30 14:05:02 -05:00
|
|
|
|
(deferred:nextc it
|
2017-05-30 14:33:31 -05:00
|
|
|
|
(lambda ()
|
|
|
|
|
(dolist (nb open-nb)
|
2017-06-03 15:45:28 -05:00
|
|
|
|
(ein:notebook-update-url-or-port new-url-or-port nb))))
|
|
|
|
|
(deferred:nextc it
|
|
|
|
|
(lambda ()
|
|
|
|
|
(kill-buffer (ein:notebooklist-get-buffer old-url))
|
|
|
|
|
(ein:notebooklist-open new-url-or-port "/" nil))))))
|
2012-08-13 18:55:32 +02:00
|
|
|
|
|
|
|
|
|
;;; Generic getter
|
|
|
|
|
|
2017-05-24 09:53:58 -05:00
|
|
|
|
|
2012-08-13 18:55:32 +02:00
|
|
|
|
(defun ein:get-url-or-port--notebooklist ()
|
2012-08-18 22:31:15 +02:00
|
|
|
|
(when (ein:$notebooklist-p ein:%notebooklist%)
|
|
|
|
|
(ein:$notebooklist-url-or-port ein:%notebooklist%)))
|
2012-08-13 18:55:32 +02:00
|
|
|
|
|
2012-05-15 03:41:08 +02:00
|
|
|
|
|
|
|
|
|
;;; Notebook list mode
|
|
|
|
|
|
2012-05-15 04:16:06 +02:00
|
|
|
|
(defun ein:notebooklist-prev-item () (interactive) (move-beginning-of-line 0))
|
|
|
|
|
(defun ein:notebooklist-next-item () (interactive) (move-beginning-of-line 2))
|
|
|
|
|
|
2016-09-23 10:37:48 -05:00
|
|
|
|
(defvar ein:notebooklist-mode-map
|
|
|
|
|
(let ((map (make-sparse-keymap)))
|
|
|
|
|
(set-keymap-parent map (make-composed-keymap widget-keymap
|
|
|
|
|
special-mode-map))
|
|
|
|
|
(define-key map "\C-c\C-r" 'ein:notebooklist-reload)
|
|
|
|
|
(define-key map "p" 'ein:notebooklist-prev-item)
|
|
|
|
|
(define-key map "n" 'ein:notebooklist-next-item)
|
|
|
|
|
map)
|
|
|
|
|
"Keymap for ein:notebooklist-mode.")
|
|
|
|
|
|
|
|
|
|
(easy-menu-define ein:notebooklist-menu ein:notebooklist-mode-map
|
|
|
|
|
"EIN Notebook List Mode Menu"
|
|
|
|
|
`("EIN Notebook List"
|
|
|
|
|
,@(ein:generate-menu
|
|
|
|
|
'(("Reload" ein:notebooklist-reload)
|
|
|
|
|
("New Notebook" ein:notebooklist-new-notebook)
|
|
|
|
|
("New Notebook (with name)"
|
|
|
|
|
ein:notebooklist-new-notebook-with-name)
|
|
|
|
|
("New Junk Notebook" ein:junk-new)))))
|
2012-05-15 03:41:08 +02:00
|
|
|
|
|
2016-09-19 09:02:35 -05:00
|
|
|
|
(defun ein:notebooklist-revert-wrapper (&optional ignore-auto noconfirm preserve-modes)
|
|
|
|
|
(ein:notebooklist-reload))
|
|
|
|
|
|
|
|
|
|
(define-derived-mode ein:notebooklist-mode special-mode "ein:notebooklist"
|
|
|
|
|
"IPython notebook list mode.
|
|
|
|
|
Commands:
|
|
|
|
|
\\{ein:notebooklist-mode-map}}"
|
|
|
|
|
(set (make-local-variable 'revert-buffer-function)
|
|
|
|
|
'ein:notebooklist-revert-wrapper))
|
|
|
|
|
|
|
|
|
|
|
2012-05-07 14:41:15 +02:00
|
|
|
|
(provide 'ein-notebooklist)
|
|
|
|
|
|
|
|
|
|
;;; ein-notebooklist.el ends here
|