2012-05-07 14:41:15 +02:00
;;; ein-notebooklist.el --- Notebook list buffer
2018-04-22 10:22:11 -05:00
;; Copyright (C) 2018- John M. Miller
2012-05-07 14:41:15 +02:00
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 )
2018-05-31 13:15:04 -05:00
( require 'cus-edit )
2012-08-28 15:26:32 +02:00
( require 'ein-core )
2012-05-07 14:41:15 +02:00
( require 'ein-notebook )
2018-10-15 16:57:22 -04:00
( require 'ein-junk )
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
2018-10-01 18:40:31 -04:00
( defcustom ein:notebooklist-render-order
2018-03-25 13:30:50 -07:00
' ( render-header
render-opened-notebooks
2018-10-01 18:40:31 -04:00
render-directory )
2018-03-25 13:30:50 -07:00
" Order of notebook list sections.
2018-10-01 18:40:31 -04:00
Must contain render-header, render-opened-notebooks, and render-directory. "
2018-03-25 13:30:50 -07:00
:group 'ein
:type 'list
)
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.
2018-10-01 18:40:31 -04:00
` ein:$notebooklist-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' . " )
2018-04-22 20:57:01 -05:00
2018-09-26 10:25:48 -04:00
( ein:deflocal ein:%notebooklist-new-kernel% nil
" Buffer local variable to store kernel type for newly created notebooks. " )
2018-05-31 13:15:04 -05:00
( defcustom ein:notebooklist-sort-field :name
" The notebook list sort field. "
:type ' ( choice ( const :tag " Name " :name )
( const :tag " Last modified " :last_modified ) )
:group 'ein )
( make-variable-buffer-local 'ein:notebooklist-sort-field )
( put 'ein:notebooklist-sort-field 'permanent-local t )
( defcustom ein:notebooklist-sort-order :ascending
" The notebook list sort order. "
:type ' ( choice ( const :tag " Ascending " :ascending )
( const :tag " Descending " :descending ) )
:group 'ein )
( make-variable-buffer-local 'ein:notebooklist-sort-order )
( put 'ein:notebooklist-sort-order 'permanent-local t )
2018-05-04 15:07:32 -04:00
( defmacro ein:make-sorting-widget ( tag custom-var )
" Create the sorting widget. "
;; assume that custom-var has type `choice' of `const's.
` ( widget-create
'menu-choice :tag , tag
:value , custom-var
:notify ( lambda ( widget &rest ignore )
( run-at-time 1 nil
#' ein:notebooklist-reload
ein:%notebooklist% )
( setq , custom-var ( widget-value widget ) ) )
,@ ( mapcar ( lambda ( const )
` ' ( item :tag , ( third const ) :value , ( fourth const ) ) )
( rest ( custom-variable-type custom-var ) ) ) ) )
2018-04-22 20:57:01 -05:00
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 ) )
2018-10-15 16:57:22 -04:00
( defun ein:notebooklist-list-remove ( url-or-port )
( remhash url-or-port ein:notebooklist-map ) )
2012-06-26 17:45:51 +02:00
( 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 '.
2018-05-27 12:04:13 -04:00
To suppress popup, you can pass ` ignore ' 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 " ) ) ) )
2018-10-15 16:57:22 -04:00
( ein:url url-or-port base-path path ) ) )
2014-10-17 16:44:04 -05:00
2018-10-15 16:57:22 -04:00
( defun ein:notebooklist-proc--sentinel ( url-or-port process event )
" TODO Add this to %ein:jupyter-server-session% "
;; (add-function :before (process-sentinel %ein:jupyter-server-session%")
;; (apply-partially #'ein:notebooklist-proc--sentinel url-or-port))
( when ( not ( string= " open " ( substring event 0 4 ) ) )
( ein:log 'info " Event %s Process %s url-or-port %s "
event ( car ( process-command process ) ) url-or-port )
( ein:notebooklist-list-remove url-or-port ) ) )
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
2018-10-15 16:57:22 -04:00
( defun ein:crib-token ( url-or-port )
( ein:aif ( loop for line in ( process-lines ein:jupyter-default-server-command
" notebook " " list " " --json " )
with token0
with password0
when ( destructuring-bind
( &key password url token &allow-other-keys )
( ein:json-read-from-string line )
( prog1 ( equal ( ein:url url ) url-or-port )
( setq password0 password ) ;; t or :json-false
( setq token0 token ) ) )
return ( list password0 token0 ) )
it ( list nil nil ) ) )
( defun ein:notebooklist-token-or-password ( url-or-port )
2018-10-17 19:34:19 -04:00
" Return token or password (I believe jupyter requires one or the other but not both) for URL-OR-PORT. Empty string token means all authentication disabled. Nil means don't know. "
2018-10-15 16:57:22 -04:00
( multiple-value-bind ( password-p token ) ( ein:crib-token url-or-port )
( cond ( ( eql password-p t ) ( read-passwd " Password: " ) )
2018-10-17 19:34:19 -04:00
( ( and ( stringp token ) ( eql password-p :json-false ) ) token )
2018-10-15 16:57:22 -04:00
( t nil ) ) ) )
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
2018-10-15 14:13:51 -04:00
( completing-read ( format " URL or port number (default %s): " default )
url-or-port-list
nil nil nil nil
default ) ) )
2018-10-11 16:53:02 -04:00
( ein:url url-or-port ) ) )
2012-06-09 01:02:53 +02:00
2018-10-12 15:32:51 -05:00
( defcustom ein:populate-hierarchy-on-notebooklist-open nil
2018-10-12 20:51:32 -05:00
" Prepopulate the content hierarchy cache after calling `ein:notebooklist-open' .
If T, will recursively walk through the notebook hierarchy and
generate a cache of all the entries. Only a couple of
infrequently ( ` ein:notebooklist-open-notebook-global ' being the
only one that comes to mind, actually ) used commands depend on
this cache, so for most users this setting can be left at its
default value. "
2018-10-12 15:32:51 -05:00
:group 'ein
:type 'boolean )
2018-10-15 16:57:22 -04:00
( defun ein:notebooklist-open* ( url-or-port &optional path resync callback )
" The main entry to server at URL-OR-PORT. Users should not directly call this, but instead `ein:notebooklist-login' .
PATH is specifying directory from file navigation. PATH is empty on login. RESYNC is requery server attributes such as ipython version and kernelspecs. CALLBACK takes one argument the resulting buffer.
"
2014-09-12 12:37:24 -05:00
( unless path ( setq path " " ) )
2018-10-11 16:53:02 -04:00
( setq url-or-port ( ein:url url-or-port ) ) ;; should work towards not needing this
2016-02-02 12:12:46 -06:00
( ein:subpackages-load )
2018-10-15 16:57:22 -04:00
( lexical-let* ( ( url-or-port url-or-port )
( path path )
( callback callback )
( success ( apply-partially #' ein:notebooklist-open--finish callback ) ) )
2018-10-01 18:40:31 -04:00
( if ( or resync ( not ( ein:notebooklist-list-get url-or-port ) ) )
( deferred:$
( deferred:parallel
( lexical-let ( ( d ( deferred:new #' identity ) ) )
( ein:query-ipython-version url-or-port ( lambda ( )
( deferred:callback-post d ) ) )
d )
( lexical-let ( ( d ( deferred:new #' identity ) ) )
( ein:query-kernelspecs url-or-port ( lambda ( )
( deferred:callback-post d ) ) )
2018-10-11 16:53:02 -04:00
d ) )
( deferred:nextc it
( lambda ( &rest ignore )
2018-10-12 15:32:51 -05:00
( lexical-let ( ( d ( deferred:new #' identity ) ) )
2018-10-11 16:53:02 -04:00
( if ein:populate-hierarchy-on-notebooklist-open
( ein:content-query-hierarchy url-or-port ( lambda ( tree )
( deferred:callback-post d ) ) )
( deferred:callback-post d ) )
2018-10-12 15:32:51 -05:00
d ) ) )
2018-10-01 18:40:31 -04:00
( deferred:nextc it
( lambda ( &rest ignore )
( ein:content-query-contents url-or-port path success ) ) ) )
2018-10-11 16:53:02 -04:00
( ein:content-query-contents url-or-port path success ) ) ) )
2012-05-07 14:41:15 +02:00
2018-10-01 18:40:31 -04:00
;; point of order (poo): ein:notebooklist-refresh-kernelspecs requeries the kernelspecs and calls ein:notebooklist-reload. ein:notebooklist-reload already requeries the kernelspecs in one of its callbacks, so this function seems redundant.
;; (defun ein:notebooklist-refresh-kernelspecs (&optional url-or-port)
;; (interactive (list (or (and ein:%notebooklist% (ein:$notebooklist-url-or-port ein:%notebooklist%))
;; (ein:notebooklist-ask-url-or-port))))
;; (unless url-or-port
;; (if ein:%notebooklist%
;; (setq url-or-port (ein:$notebooklist-url-or-port ein:%notebooklist%))
;; (setq url-or-port (ein:default-url-or-port))))
;; (ein:query-kernelspecs url-or-port)
;; (when ein:%notebooklist%
;; (ein:notebooklist-reload ein:%notebooklist%))
;; )
2018-05-20 08:42:42 -05: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 )
2018-04-23 17:33:13 -04:00
( defcustom ein:notebooklist-date-format " %x "
" The format spec for date in notebooklist mode.
2018-05-25 11:43:39 -04:00
See ` ein:format-time-string '. "
2018-04-23 17:33:13 -04:00
:type ' ( or string function )
: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
2018-10-01 18:40:31 -04:00
( run-at-time 0.1 refresh-time #' ein:content-query-contents url-or-port " " 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
2018-10-15 16:57:22 -04:00
( defun ein:notebooklist-open--finish ( 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 ) ) )
( 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% )
2018-10-01 18:40:31 -04:00
( ein:notebooklist-render ipy-version )
2015-02-10 14:53:08 -06:00
( goto-char orig-point )
2018-10-01 18:40:31 -04:00
( ein:log 'verbose " Opened notebooklist at %s " ( concat ( file-name-as-directory 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 ) ) )
2018-10-15 16:57:22 -04:00
( when callback
( funcall callback ( current-buffer ) ) )
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
2018-10-01 18:40:31 -04:00
&key error-thrown
2012-06-26 19:30:35 +02:00
&allow-other-keys )
( ein:log 'error
2018-10-01 18:40:31 -04:00
" ein:notebooklist-open-error %s: ERROR %s DATA %s " ( concat ( file-name-as-directory url-or-port ) path ) ( car error-thrown ) ( cdr error-thrown ) ) )
2012-06-26 19:30:35 +02:00
2012-09-01 20:51:55 +02:00
;;;###autoload
2018-10-01 18:40:31 -04:00
( defun ein:notebooklist-reload ( notebooklist &optional resync )
2012-05-13 05:18:38 +02:00
" Reload current Notebook list. "
2018-10-01 18:40:31 -04:00
( interactive ( list ein:%notebooklist% ) )
( when notebooklist
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* ( ein:$notebooklist-url-or-port notebooklist )
( ein:$notebooklist-path notebooklist ) resync ) ) )
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 '. "
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* ( ein:$notebook-url-or-port ein:%notebook% )
( ein:$notebook-notebook-path ein:%notebook% ) ) )
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 )
2018-10-15 16:57:22 -04:00
( ein:file-open url-or-port path ) )
2017-04-20 07:47:05 -05:00
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 )
2018-10-11 16:53:02 -04:00
" Ask server to create a new notebook and open it in a new buffer.
TODO - New and open should be separate, and we should flag an exception if we try to new an existing.
"
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
( 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. " ) )
2018-10-15 16:57:22 -04:00
( let ( ( url ( ein:notebooklist-url url-or-port
version
path ) ) )
2014-09-19 09:17:12 -05:00
( 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
2018-10-11 16:53:02 -04:00
&allow-other-keys )
( let ( ( nbname ( plist-get data :name ) )
( nbpath ( plist-get data :path ) ) )
( when ( = ( ein:need-ipython-version url-or-port ) 2 )
( if ( string= nbpath " " )
( setq nbpath nbname )
( setq nbpath ( format " %s/%s " nbpath nbname ) ) ) )
( ein:notebook-open url-or-port nbpath kernelspec callback cbargs )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* url-or-port path ) ) )
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
( 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)
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* url-or-port nil nil #' pop-to-buffer ) )
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
2018-10-11 16:53:02 -04:00
( defun ein:notebooklist-delete-notebook ( path &optional callback )
( lexical-let* ( ( path path )
( notebooklist ein:%notebooklist% )
( callback callback )
( url-or-port ( ein:$notebooklist-url-or-port notebooklist ) ) )
( unless callback ( setq callback ( lambda ( ) ( ein:notebooklist-reload notebooklist ) ) ) )
( ein:query-singleton-ajax
( list 'notebooklist-delete-notebook ( ein:url url-or-port path ) )
( ein:notebook-url-from-url-and-id
url-or-port ( ein:$notebooklist-api-version notebooklist ) path )
:type " DELETE "
:complete ( apply-partially #' ein:notebooklist-delete-notebook--complete ( ein:url url-or-port path ) callback ) ) ) )
( defun* ein:notebooklist-delete-notebook--complete ( url callback
&key data response symbol-status
&allow-other-keys
&aux ( resp-string ( format " STATUS: %s DATA: %s " ( request-response-status-code response ) data ) ) )
( ein:log 'debug " ein:notebooklist-delete-notebook--complete %s " resp-string )
( when ( and callback ( eq symbol-status 'success ) ) ( funcall callback ) ) )
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 ) ) ) ) ) ) )
2018-04-22 20:57:01 -05:00
( defun* ein:nblist--sort-group ( group by-param order )
2018-04-22 10:22:11 -05:00
( sort group #' ( lambda ( x y )
2018-05-04 15:07:32 -04:00
( cond ( ( eql order :ascending )
2018-04-22 20:57:01 -05:00
( string-lessp ( plist-get x by-param )
( plist-get y by-param ) ) )
2018-05-04 15:07:32 -04:00
( ( eql order :descending )
2018-04-22 20:57:01 -05:00
( string-greaterp ( plist-get x by-param )
( plist-get y by-param ) ) ) ) ) ) )
( defun ein:notebooklist--order-data ( nblist-data sort-param sort-order )
2018-03-27 22:34:19 -07:00
" Try to sanely sort the notebooklist data for the current path. "
( let* ( ( groups ( -group-by #' ( lambda ( x ) ( plist-get x :type ) )
nblist-data ) )
2018-04-22 20:57:01 -05:00
( dirs ( ein:nblist--sort-group ( cdr ( assoc " directory " groups ) )
sort-param
sort-order ) )
( nbs ( ein:nblist--sort-group ( cdr ( assoc " notebook " groups ) )
sort-param
sort-order ) )
2018-04-22 10:22:11 -05:00
( files ( ein:nblist--sort-group ( -flatten-n 1 ( -map #' cdr ( -group-by
#' ( lambda ( x ) ( car ( last ( s-split " \\ . " ( plist-get x :name ) ) ) ) )
2018-04-22 20:57:01 -05:00
( cdr ( assoc " file " groups ) ) ) ) )
sort-param
sort-order ) ) )
2018-03-27 22:34:19 -07:00
( -concat dirs nbs files ) ) )
2018-10-01 18:40:31 -04:00
( defun render-header-ipy2 ( &rest args )
2018-04-22 10:22:11 -05:00
" Render the header (for ipython2). "
2016-04-15 16:08:53 -05:00
;; Create notebook list
( widget-insert ( format " IPython %s Notebook list \n \n " ( ein:$notebooklist-api-version ein:%notebooklist% ) ) )
2018-03-27 22:34:19 -07:00
2016-04-15 16:08:53 -05: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
( ein:$notebooklist-url-or-port ein:%notebooklist% )
path ) )
name ) ) )
( widget-insert " | \n \n " ) )
2018-03-27 22:34:19 -07:00
2016-04-15 16:08:53 -05:00
( 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
2018-10-01 18:40:31 -04:00
:notify ( lambda ( &rest ignore ) ( ein:notebooklist-reload ein:%notebooklist% t ) )
2016-04-15 16:08:53 -05:00
" 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 " )
2018-04-22 10:22:11 -05:00
( widget-insert " \n " ) )
2017-09-12 16:22:19 -05:00
2018-10-01 18:40:31 -04:00
( defun render-header* ( url-or-port &rest args )
2018-04-22 10:22:11 -05:00
" Render the header (for ipython>=3). "
2018-10-01 18:40:31 -04:00
( with-current-buffer ( ein:notebooklist-get-buffer url-or-port )
( widget-insert
( if ( < ( ein:$notebooklist-api-version ein:%notebooklist% ) 4 )
( format " IPython v%s Notebook list (%s) \n \n " ( ein:$notebooklist-api-version ein:%notebooklist% ) url-or-port )
( format " Jupyter v%s Notebook list (%s) \n \n " ( ein:$notebooklist-api-version ein:%notebooklist% ) url-or-port ) ) )
( let ( ( breadcrumbs ( generate-breadcrumbs ( ein:$notebooklist-path ein:%notebooklist% ) ) ) )
( dolist ( p breadcrumbs )
( lexical-let ( ( url-or-port url-or-port )
( name ( car p ) )
( path ( cdr p ) ) )
( widget-insert " | " )
( widget-create
'link
:notify ( lambda ( &rest ignore )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* url-or-port path nil #' pop-to-buffer ) )
2018-10-01 18:40:31 -04:00
name ) ) )
( widget-insert " | \n \n " ) )
( lexical-let* ( ( url-or-port url-or-port )
( kernels ( ein:list-available-kernels url-or-port ) ) )
( if ( null ein:%notebooklist-new-kernel% )
( setq ein:%notebooklist-new-kernel% ( ein:get-kernelspec url-or-port ( caar kernels ) ) ) )
( widget-create
'link
:notify ( lambda ( &rest ignore ) ( ein:notebooklist-new-notebook
url-or-port
ein:%notebooklist-new-kernel% ) )
" New Notebook " )
( widget-insert " " )
( widget-create
'link
:notify ( lambda ( &rest ignore ) ( ein:notebooklist-reload ein:%notebooklist% t ) )
" Resync " )
( widget-insert " " )
( widget-create
'link
:notify ( lambda ( &rest ignore )
( browse-url ( ein:url url-or-port ) ) )
" Open In Browser " )
( widget-insert " \n \n Create New Notebooks Using Kernel: \n " )
( let* ( ( radio-widget ( widget-create 'radio-button-choice
:value ( and ein:%notebooklist-new-kernel% ( ein:$kernelspec-name ein:%notebooklist-new-kernel% ) )
:notify ( lambda ( widget &rest ignore )
( setq ein:%notebooklist-new-kernel%
( ein:get-kernelspec url-or-port ( widget-value widget ) ) )
( message " New notebooks will be started using the %s kernel. "
( ein:$kernelspec-display-name ein:%notebooklist-new-kernel% ) ) ) ) ) )
( if ( null kernels )
( widget-insert " \n No kernels found. " )
( dolist ( k kernels )
( widget-radio-add-item radio-widget ( list 'item :value ( car k )
:format ( format " %s \n " ( cdr k ) ) ) ) )
( widget-insert " \n " ) ) ) ) ) )
( defun render-opened-notebooks ( url-or-port &rest args )
2018-03-25 12:32:31 -07:00
" Render the opened notebooks section (for ipython>=3). "
2018-04-22 10:22:11 -05:00
;; Opened Notebooks Section
2018-10-01 18:40:31 -04:00
( with-current-buffer ( ein:notebooklist-get-buffer url-or-port )
( widget-insert " \n ---------- All Opened Notebooks ---------- \n \n " )
( loop for buffer in ( ein:notebook-opened-buffers )
do ( progn ( widget-create
'link
:notify ( lexical-let ( ( buffer buffer ) )
( lambda ( &rest ignore )
2018-10-15 16:57:22 -04:00
( condition-case err
( switch-to-buffer buffer )
( error
( message " %S " err )
( ein:notebooklist-reload ein:%notebooklist% ) ) ) ) )
2018-10-01 18:40:31 -04:00
" Open " )
( widget-create
'link
:notify ( lexical-let ( ( buffer buffer ) )
( lambda ( &rest ignore )
2018-10-15 16:57:22 -04:00
( if ( buffer-live-p buffer )
( kill-buffer buffer ) )
2018-10-01 18:40:31 -04:00
( run-at-time 1 nil
#' ein:notebooklist-reload
ein:%notebooklist% ) ) )
" Close " )
( widget-insert " : " ( buffer-name buffer ) )
( widget-insert " \n " ) ) ) ) )
2018-03-31 14:38:25 -07:00
2018-04-22 20:57:01 -05:00
( defun ein:format-nbitem-data ( name last-modified )
( let ( ( dt ( date-to-time last-modified ) ) )
2018-04-23 17:33:13 -04:00
( format " %-40s%+20s " name
2018-05-25 11:43:39 -04:00
( ein:format-time-string ein:notebooklist-date-format dt ) ) ) )
2018-04-22 20:57:01 -05:00
2018-10-01 18:40:31 -04:00
( defun render-directory ( url-or-port sessions )
( with-current-buffer ( ein:notebooklist-get-buffer url-or-port )
( widget-insert " \n ------------------------------------------ \n \n " )
2018-05-04 15:07:32 -04:00
( ein:make-sorting-widget " Sort by " ein:notebooklist-sort-field )
( ein:make-sorting-widget " In Order " ein:notebooklist-sort-order )
2018-04-22 20:57:01 -05:00
( widget-insert " \n " )
( loop for note in ( ein:notebooklist--order-data ( ein:$notebooklist-data ein:%notebooklist% )
2018-05-04 15:07:32 -04:00
ein:notebooklist-sort-field
ein:notebooklist-sort-order )
2016-08-31 10:04:06 -05:00
for name = ( plist-get note :name )
for path = ( plist-get note :path )
2018-04-22 20:57:01 -05:00
for last-modified = ( plist-get note :last_modified )
2018-03-31 13:58:01 -07:00
;; (cond ((= 2 api-version)
;; (plist-get note :path))
;; ((= 3 api-version)
;; (ein:get-actual-path (plist-get note :path))))
2016-08-31 10:04:06 -05:00
for type = ( plist-get note :type )
2018-10-01 18:40:31 -04:00
for opened-notebook-maybe = ( ein:notebook-get-opened-notebook url-or-port path )
2016-08-31 10:04:06 -05:00
do ( widget-insert " " )
if ( string= type " directory " )
do ( progn ( widget-create
'link
2018-10-01 18:40:31 -04:00
:notify ( lexical-let ( ( url-or-port url-or-port )
( name name ) )
2016-08-31 10:04:06 -05:00
( lambda ( &rest ignore )
2018-10-01 18:40:31 -04:00
;; each directory creates a whole new notebooklist
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* url-or-port
( concat ( directory-file-name ( ein:$notebooklist-path ein:%notebooklist% ) ) name ) nil #' pop-to-buffer ) ) )
2016-08-31 10:04:06 -05:00
" Dir " )
( widget-insert " : " name )
( widget-insert " \n " ) )
2018-10-01 18:40:31 -04:00
if ( and ( string= type " file " ) ( > ( ein:need-ipython-version url-or-port ) 2 ) )
2018-03-31 13:58:01 -07:00
do ( progn ( widget-create
'link
2018-10-01 18:40:31 -04:00
:notify ( lexical-let ( ( url-or-port url-or-port )
2018-03-31 13:58:01 -07:00
( path path ) )
( lambda ( &rest ignore )
2018-10-01 18:40:31 -04:00
( ein:notebooklist-open-file url-or-port path ) ) )
2018-03-31 13:58:01 -07:00
" Open " )
2018-04-22 20:57:01 -05:00
( widget-insert " ------ " )
2018-03-31 13:58:01 -07:00
( widget-create
'link
:notify ( lexical-let ( ( path path ) )
( lambda ( &rest ignore )
( message " [EIN]: NBlist delete file command. Implement me! " ) ) )
" Delete " )
2018-04-22 20:57:01 -05:00
( widget-insert " : " ( ein:format-nbitem-data name last-modified ) )
2018-03-31 13:58:01 -07:00
( 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 " " )
2018-04-22 20:57:01 -05:00
( if ( 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-insert " " )
2016-08-31 10:04:06 -05:00
( widget-create
'link
:notify ( lexical-let ( ( path path ) )
( lambda ( &rest ignore )
( ein:notebooklist-delete-notebook-ask
path ) ) )
" Delete " )
2018-04-22 20:57:01 -05:00
( widget-insert " : " ( ein:format-nbitem-data name last-modified ) )
2018-04-22 10:22:11 -05:00
( widget-insert " \n " ) ) ) ) )
2018-03-25 12:32:31 -07:00
2018-10-01 18:40:31 -04:00
( defun ein:notebooklist-render ( ipy-version )
2018-03-25 12:32:31 -07:00
" Render notebook list widget.
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 )
2018-10-01 18:40:31 -04:00
( let ( ( url-or-port ( ein:$notebooklist-url-or-port ein:%notebooklist% ) ) )
( ein:content-query-sessions url-or-port
( apply-partially #' ein:notebooklist-render--finish ipy-version url-or-port ) ) ) )
( defun ein:notebooklist-render--finish ( ipy-version url-or-port sessions )
2018-10-12 15:32:51 -05:00
( cl-letf ( ( ( symbol-function 'render-header ) ( if ( < ipy-version 3 )
#' render-header-ipy2
2018-10-01 18:40:31 -04:00
#' render-header* ) ) )
( mapc ( lambda ( x ) ( funcall ( symbol-function x ) url-or-port sessions ) )
ein:notebooklist-render-order ) )
( with-current-buffer ( ein:notebooklist-get-buffer url-or-port )
( ein:notebooklist-mode )
( widget-setup )
( goto-char ( point-min ) ) ) )
2012-05-07 14:41:15 +02:00
2018-03-25 13:42:35 -07:00
;;;###autoload
2018-10-01 18:40:31 -04:00
2012-07-12 01:05:37 +02:00
( defun ein:notebooklist-list-notebooks ( )
2018-03-25 13:42:35 -07:00
" Return a list of notebook path (NBPATH). Each element NBPATH
2018-10-15 16:57:22 -04:00
is a string of the format \"URL-OR-PORT/PATH\". "
2012-07-12 01:05:37 +02:00
( apply #' append
( loop for nblist in ( ein:notebooklist-list )
for url-or-port = ( ein:$notebooklist-url-or-port nblist )
collect
2018-10-01 18:40:31 -04:00
( loop for content in ( ein:content-need-hierarchy url-or-port )
when ( string= ( ein:$content-type content ) " notebook " )
collect ( format " %s/%s " url-or-port
( ein:$content-path content ) ) )
2015-02-12 09:20:34 -06:00
;; (if (= api-version 3)
2018-10-01 18:40:31 -04:00
;; (loop for note in (ein:content-need-hierarchy url-or-port)
2015-02-12 09:20:34 -06:00
;; 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
2018-10-01 18:40:31 -04:00
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
2018-10-01 18:40:31 -04:00
( list ( if noninteractive
2018-10-12 15:32:51 -05:00
( car ( ein:notebooklist-list-notebooks ) )
2018-10-01 18:40:31 -04:00
( completing-read
" Open notebook [URL-OR-PORT/NAME]: "
( ein:notebooklist-list-notebooks ) ) ) ) )
( let* ( ( parsed ( url-generic-parse-url nbpath ) )
( path ( url-filename parsed ) ) )
2018-10-12 15:32:51 -05:00
( ein:notebook-open ( substring nbpath 0 ( - ( length nbpath ) ( length path ) ) )
2018-10-01 18:40:31 -04:00
( substring path 1 ) nil callback cbargs ) ) )
2012-06-05 14:54:18 +02:00
2012-09-01 20:51:55 +02:00
;;;###autoload
2018-10-01 18:40:31 -04:00
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 '. "
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* url-or-port nil ) )
2012-12-17 16:51:31 +01:00
;;; Login
2018-10-17 19:34:19 -04:00
( defun ein:notebooklist-login--iteration ( url-or-port callback errback token iteration response-status )
" Called from `ein:notebooklist-login' . "
( ein:log 'debug " Login attempt #%d in response to %s from "
iteration response-status url-or-port )
2018-10-15 16:57:22 -04:00
( ein:query-singleton-ajax
2018-10-17 19:34:19 -04:00
( list 'notebooklist-login--iteration url-or-port )
2018-10-15 16:57:22 -04:00
( ein:url url-or-port " login " )
2018-10-17 19:34:19 -04:00
;; This was the culprit that necessitated the 405 workaround
;; :type "POST"
;; From the curl manpage:
;; The method string you set with -X will be used for all requests,
;; which if you for example use -L, --location may cause unintended
;; side-effects when curl doesn't change request method according
;; to the HTTP 30x response codes - and similar.
:data ( if token ( concat " password= " ( url-hexify-string token ) ) )
2018-10-15 16:57:22 -04:00
:parser #' ein:notebooklist-login--parser
:complete ( apply-partially #' ein:notebooklist-login--complete url-or-port )
2018-10-17 19:34:19 -04:00
:error ( apply-partially #' ein:notebooklist-login--error url-or-port token callback errback iteration )
:success ( apply-partially #' ein:notebooklist-login--success url-or-port callback errback token iteration ) ) )
2018-10-15 16:57:22 -04:00
2018-10-17 17:13:19 -04:00
;;;###autoload
( defun ein:notebooklist-open ( url-or-port callback )
" This is now an alias for ein:notebooklist-login "
( interactive ` ( , ( ein:notebooklist-ask-url-or-port ) , #' pop-to-buffer ) )
( ein:notebooklist-login url-or-port callback ) )
( make-obsolete 'ein:notebooklist-open 'ein:notebooklist-login " 0.14.2 " )
2018-10-15 16:57:22 -04:00
2016-12-27 12:03:05 -05:00
;;;###autoload
2018-10-15 16:57:22 -04:00
( defalias 'ein:login 'ein:notebooklist-login )
2018-10-01 18:40:31 -04:00
2018-10-15 16:57:22 -04:00
;;;###autoload
( defalias 'ein-login 'ein:notebooklist-login )
;;;###autoload
( defun ein:notebooklist-login ( url-or-port callback )
2018-10-17 17:44:11 -04:00
" Deal with security before main entry of ein:notebooklist-open*.
2018-10-15 16:57:22 -04:00
CALLBACK takes one argument, the buffer created by ein:notebooklist-open--success. "
( interactive ` ( , ( ein:notebooklist-ask-url-or-port ) , #' pop-to-buffer ) )
( unless callback ( setq callback ( lambda ( buffer ) ) ) )
( lexical-let* ( done-p
( done-callback ( lambda ( &rest ignore ) ( setf done-p t ) ) )
( errback ( lambda ( &rest ignore ) ( setf done-p 'error ) ) )
( token ( ein:notebooklist-token-or-password url-or-port ) ) )
( add-function :before callback done-callback )
( ein:message-whir " Establishing session " ( lambda ( ) done-p ) )
2018-10-17 19:34:19 -04:00
( cond ( ( null token ) ;; don't know
( ein:notebooklist-login--iteration url-or-port callback errback nil -1 nil ) )
( ( string= token " " ) ;; all authentication disabled
( ein:log 'verbose " Skipping login %s " url-or-port )
( ein:notebooklist-open* url-or-port nil nil callback ) )
( t ( ein:notebooklist-login--iteration url-or-port callback errback token 0 nil ) ) ) ) )
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
2018-10-11 16:53:02 -04:00
( defun ein:notebooklist-login--success-1 ( url-or-port callback )
2018-10-15 16:57:22 -04:00
( ein:log 'info " Login to %s complete. " url-or-port )
( ein:notebooklist-open* url-or-port nil nil callback ) )
2012-12-17 19:26:13 +01:00
2018-10-15 16:57:22 -04:00
( defun ein:notebooklist-login--error-1 ( url-or-port errback )
( ein:log 'error " Login to %s failed " url-or-port )
( funcall errback ) )
2012-12-17 19:26:13 +01:00
2018-10-11 16:53:02 -04:00
( defun* ein:notebooklist-login--complete ( url-or-port &key data response
2018-10-15 14:13:51 -04:00
&allow-other-keys
2018-10-11 16:53:02 -04:00
&aux ( resp-string ( format " STATUS: %s DATA: %s " ( request-response-status-code response ) data ) ) )
( ein:log 'debug " ein:notebooklist-login--complete %s " resp-string ) )
2018-10-17 19:34:19 -04:00
( defun* ein:notebooklist-login--success ( url-or-port callback errback token iteration
&key data response
&allow-other-keys
&aux
( response-status ( request-response-status-code response ) ) )
2012-12-17 19:26:13 +01:00
( if ( plist-get data :bad-page )
2018-10-17 19:34:19 -04:00
( if ( >= iteration 0 )
( ein:notebooklist-login--error-1 url-or-port errback )
( setq token ( read-passwd " Password: " ) )
( ein:notebooklist-login--iteration url-or-port callback errback token ( 1+ iteration ) response-status ) )
2018-10-11 16:53:02 -04:00
( ein:notebooklist-login--success-1 url-or-port callback ) ) )
2012-12-17 19:26:13 +01:00
2013-01-09 05:51:25 +01:00
( defun* ein:notebooklist-login--error
2018-10-17 19:34:19 -04:00
( url-or-port token callback errback iteration &key
2013-01-09 05:51:25 +01:00
data
symbol-status
response
&allow-other-keys
&aux
( response-status ( request-response-status-code response ) ) )
2018-10-17 19:34:19 -04:00
( cond ( ( < iteration 0 )
( setq token ( read-passwd " Password: " ) )
( ein:notebooklist-login--iteration url-or-port callback errback token ( 1+ iteration ) response-status ) )
( ( and ( eq response-status 403 ) ( < iteration 1 ) )
( ein:notebooklist-login--iteration url-or-port callback errback token ( 1+ iteration ) response-status ) )
( ( and ( eq symbol-status 'timeout ) ;; workaround for url-retrieve backend
( eq response-status 302 )
( request-response-header response " set-cookie " ) )
2018-10-11 16:53:02 -04:00
( ein:notebooklist-login--success-1 url-or-port callback ) )
2018-10-15 16:57:22 -04:00
( t ( ein:notebooklist-login--error-1 url-or-port errback ) ) ) )
2012-12-17 16:51:31 +01:00
2017-05-24 09:53:58 -05:00
;;;###autoload
2018-10-01 18:40:31 -04:00
2017-05-24 09:53:58 -05:00
( 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 ) ) ) ) ) )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* new-url-or-port nil )
2017-11-07 15:14:45 -06:00
( 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 ) )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* new-url-or-port nil nil #' pop-to-buffer ) ) )
2017-11-07 15:14:45 -06:00
( 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 ) )
2018-09-04 11:54:01 -04: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
2018-10-01 18:40:31 -04:00
( lambda ( )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* new-url-or-port nil )
2018-10-01 18:40:31 -04: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
2018-10-01 18:40:31 -04:00
( lambda ( )
( dolist ( nb open-nb )
( ein:notebook-update-url-or-port new-url-or-port nb ) ) ) )
2017-06-03 15:45:28 -05:00
( deferred:nextc it
2018-10-01 18:40:31 -04:00
( lambda ( )
( kill-buffer ( ein:notebooklist-get-buffer old-url ) )
2018-10-15 16:57:22 -04:00
( ein:notebooklist-open* new-url-or-port nil nil #' pop-to-buffer ) ) ) ) ) )
2012-08-13 18:55:32 +02:00
;;; Generic getter
2017-05-24 09:53:58 -05:00
2018-10-01 18:40:31 -04: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
2018-10-01 18:40:31 -04:00
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 )
2018-10-01 18:40:31 -04:00
( ein:notebooklist-reload ein:%notebooklist% ) )
2016-09-19 09:02:35 -05:00
( define-derived-mode ein:notebooklist-mode special-mode " ein:notebooklist "
" IPython notebook list mode.
Commands:
2018-09-04 11:54:37 -04:00
\\{ein:notebooklist-mode-map} "
2016-09-19 09:02:35 -05:00
( 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