;;; ein-notebooklist.el --- Notebook list buffer ;; Copyright (C) 2012- Takafumi Arakaki ;; Author: Takafumi Arakaki ;; 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 . ;;; Commentary: ;; ;;; Code: (eval-when-compile (require 'cl)) (require 'widget) (require 'ein-core) (require 'ein-notebook) (require 'ein-subpackages) (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) (defstruct ein:$notebooklist "Hold notebooklist variables. `ein:$notebooklist-url-or-port' URL or port of IPython server. `ein:$notebooklist-data' JSON data sent from the server." url-or-port data) (ein:deflocal ein:%notebooklist% nil "Buffer local variable to store an instance of `ein:$notebooklist'.") (define-obsolete-variable-alias 'ein:notebooklist 'ein:%notebooklist% "0.1.2") (defvar ein:notebooklist-buffer-name-template "*ein:notebooklist %s*") (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'.") (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)) (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)) (defun ein:notebooklist-open-notebook-by-name (name &optional url-or-port callback cbargs) "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 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." (loop with nblist = (if url-or-port (ein:notebooklist-list-get url-or-port) ein:%notebooklist%) for note in (ein:$notebooklist-data nblist) for notebook-name = (plist-get note :name) for notebook-id = (plist-get note :notebook_id) when (equal notebook-name name) return (ein:notebook-open (ein:$notebooklist-url-or-port nblist) notebook-id callback cbargs))) (defun ein:notebooklist-url (url-or-port) (ein:url url-or-port "notebooks")) (defun ein:notebooklist-new-url (url-or-port) (ein:url url-or-port "new")) (defun ein:notebooklist-get-buffer (url-or-port) (get-buffer-create (format ein:notebooklist-buffer-name-template url-or-port))) (defun ein:notebooklist-ask-url-or-port () (let* ((url-or-port-list (mapcar (lambda (x) (format "%s" x)) ein:url-or-port)) (default (format "%s" (ein:aif (ein:get-notebook) (ein:$notebook-url-or-port it) (ein:aif ein:%notebooklist% (ein:$notebooklist-url-or-port it) (ein:default-url-or-port))))) (url-or-port (completing-read (format "URL or port number (default %s): " default) url-or-port-list nil nil nil nil default))) (if (string-match "^[0-9]+$" url-or-port) (string-to-number url-or-port) url-or-port))) ;;;###autoload (defun ein:notebooklist-open (&optional url-or-port no-popup) "Open notebook list buffer." (interactive (list (ein:notebooklist-ask-url-or-port))) (unless url-or-port (setq url-or-port (ein:default-url-or-port))) (ein:subpackages-load) (let ((success (if no-popup #'ein:notebooklist-url-retrieve-callback (lambda (&rest args) (pop-to-buffer (apply #'ein:notebooklist-url-retrieve-callback args)))))) (ein:query-singleton-ajax (list 'notebooklist-open url-or-port) (ein:notebooklist-url url-or-port) :cache nil :parser #'ein:json-read :error (cons #'ein:notebooklist-open-error url-or-port) :success (cons success url-or-port))) (ein:notebooklist-get-buffer url-or-port)) (defun* ein:notebooklist-url-retrieve-callback (url-or-port &key status data &allow-other-keys) "Called via `ein:notebooklist-open'." (ein:aif (plist-get status :error) (error "Failed to connect to server '%s'. Got: %S" (ein:url url-or-port) it)) (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 :data data)) (ein:notebooklist-list-add ein:%notebooklist%) (ein:notebooklist-render) (goto-char orig-point) (message "Opened notebook list at %s" url-or-port) (unless already-opened-p (run-hooks 'ein:notebooklist-first-open-hook)) (current-buffer)))) (defun* ein:notebooklist-open-error (url-or-port &key symbol-status &allow-other-keys) (ein:log 'error "Error (%s) while opening notebook list at the server %s." symbol-status url-or-port)) ;;;###autoload (defun ein:notebooklist-reload () "Reload current Notebook list." (interactive) (ein:notebooklist-open (ein:$notebooklist-url-or-port ein:%notebooklist%) t)) (defun ein:notebooklist-refresh-related () "Reload notebook list in which current notebook locates. This function is called via `ein:notebook-after-rename-hook'." (ein:notebooklist-open (ein:$notebook-url-or-port ein:%notebook%) t)) (add-hook 'ein:notebook-after-rename-hook 'ein:notebooklist-refresh-related) (defun ein:notebooklist-get-data-in-body-tag (key) "Very ad-hoc parser to get data in body tag." (ignore-errors (save-excursion (goto-char (point-min)) (search-forward "