:image/png not :png

This commit is contained in:
dickmao 2020-01-11 16:03:54 -05:00
parent ce4252d2af
commit 702f04064a
20 changed files with 184 additions and 656 deletions

View file

@ -37,9 +37,7 @@
(!cons "memory" ecukes-exclude-tags) (!cons "memory" ecukes-exclude-tags)
(!cons "content" ecukes-exclude-tags) (!cons "content" ecukes-exclude-tags)
(when (eq system-type 'darwin) (when (eq system-type 'darwin)
(!cons "julia" ecukes-exclude-tags))) (!cons "julia" ecukes-exclude-tags))
(unless (fboundp 'libxml-parse-xml-region)
(!cons "svg" ecukes-exclude-tags)) (!cons "svg" ecukes-exclude-tags))
(defvar ein:testing-jupyter-server-root (f-parent (f-dirname load-file-name))) (defvar ein:testing-jupyter-server-root (f-parent (f-dirname load-file-name)))

View file

@ -220,7 +220,8 @@ Scenario: Undo needs to at least work for reopened notebooks
And I press "C-/" And I press "C-/"
And I undo again And I undo again
And I undo again And I undo again
Then the cursor should be at point "106" And I undo again
Then the cursor should be at point "124"
@undo @undo
Scenario: Toggling between markdown and codecell does not break undo Scenario: Toggling between markdown and codecell does not break undo

View file

@ -1,78 +0,0 @@
;;; ein-cell-output.el --- Cell module
;; (C) 2015- John M. Miller
;; Author: John M. Miller (millejoh at mac dot com)
;; This file is NOT part of GNU Emacs.
;; ein-cell-output.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-cell-output.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-cell-output.el. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Because wiriting cell outputs to nbformat v4.0 json is complicated
;; enought that it warrants a file all to its own.
;;; Code:
(require 'ein-cell)
(defvar ein:output-type-map
'((:svg . :image/svg+xml) (:png . :image/png) (:jpeg . :image/jpeg)
(:text . :text/plain)
(:html . :text/html) (:latex . :text/latex) (:javascript . :text/javascript)))
(defun ein:output-property (maybe-property)
(cdr (assoc maybe-property ein:output-type-map)))
;;; Dealing with Code cell outputs
(defun ein:cell-stream-output-to-json (output)
`((output_type . "stream")
(name . ,(plist-get output :stream))
(text . ,(plist-get output :text))))
(defun ein:cell-error-output-to-json (output)
`((output_type . "error")
(ename . ,(plist-get output :ename))
(evalue . ,(plist-get output :evalue))
(traceback . ,(plist-get output :traceback))))
(defun ein:cell-execute-result-output-to-json (output)
(let ((data (aif (plist-get output :text)
`("text/plain" . ,it)
(plist-get output :data))))
`((output_type . "execute_result")
(metadata . ,(make-hash-table))
(execution_count . ,(or (plist-get output :prompt_number)
(plist-get output :execution_count)))
(data . (,data)))))
(defun ein:maybe-get-output-mime-data (output)
(cl-loop for type in '(:svg :png :jpeg :html :latex :javascript :text)
if (plist-get output type)
collecting (cons (ein:output-property type) (plist-get output type))))
(defun ein:cell-display-data-output-to-json (output)
(let ((data (or (ein:maybe-get-output-mime-data output)
(plist-get output :data))))
`((output_type . "display_data")
(data . ,data)
(metadata . ,(make-hash-table)))))
(defun ein:find-and-make-outputs (output-plist)
(cl-loop for prop in ein:output-type-map
when (plist-get output-plist (cdr prop))
collect (list (cdr prop) (plist-get output-plist (cdr prop)))))
(provide 'ein-cell-output)

View file

@ -113,36 +113,6 @@ Delete current text first, thus effecting a \"refresh\"."
"Face for cell output area errors" "Face for cell output area errors"
:group 'ein) :group 'ein)
(defface ein:cell-heading-1
'((t :height 1.1 :inherit ein:cell-heading-2))
"Face for level 1 heading."
:group 'ein)
(defface ein:cell-heading-2
'((t :height 1.1 :inherit ein:cell-heading-3))
"Face for level 2 heading."
:group 'ein)
(defface ein:cell-heading-3
'((t :height 1.1 :inherit ein:cell-heading-4))
"Face for level 3 heading."
:group 'ein)
(defface ein:cell-heading-4
'((t :height 1.1 :inherit ein:cell-heading-5))
"Face for level 4 heading."
:group 'ein)
(defface ein:cell-heading-5
'((t :height 1.1 :inherit ein:cell-heading-6))
"Face for level 5 heading."
:group 'ein)
(defface ein:cell-heading-6
'((t :weight bold :inherit (variable-pitch ein:cell-input-area)))
"Face for level 6 heading."
:group 'ein)
(defface ein:cell-output-prompt (defface ein:cell-output-prompt
'((t :inherit header-line)) '((t :inherit header-line))
"Face for cell output prompt" "Face for cell output prompt"
@ -280,7 +250,6 @@ a number will limit the number of lines in a cell output."
(("html") 'ein:htmlcell) (("html") 'ein:htmlcell)
(("markdown") 'ein:markdowncell) (("markdown") 'ein:markdowncell)
(("raw") 'ein:rawcell) (("raw") 'ein:rawcell)
(("heading") 'ein:headingcell)
(("shared-output") 'ein:shared-output-cell) (("shared-output") 'ein:shared-output-cell)
(t (error "No cell type called %S" type)))) (t (error "No cell type called %S" type))))
@ -318,12 +287,6 @@ a number will limit the number of lines in a cell output."
(setf (slot-value cell 'input) it)) (setf (slot-value cell 'input) it))
cell) cell)
(cl-defmethod ein:cell-init ((cell ein:headingcell) data) ;; FIXME: Was :after method
(cl-call-next-method)
(aif (plist-get data :level)
(setf (slot-value cell 'level) it))
cell)
(cl-defmethod ein:cell-convert ((cell ein:basecell) type) (cl-defmethod ein:cell-convert ((cell ein:basecell) type)
(let ((new (ein:cell-from-type type))) (let ((new (ein:cell-from-type type)))
;; copy attributes ;; copy attributes
@ -345,12 +308,6 @@ a number will limit the number of lines in a cell output."
(setf (slot-value new 'kernel) (slot-value cell 'kernel))) (setf (slot-value new 'kernel) (slot-value cell 'kernel)))
new)) new))
(cl-defmethod ein:cell-convert ((cell ein:headingcell) type)
(let ((new (cl-call-next-method)))
(when (ein:headingcell-p new)
(setf (slot-value new 'level) (slot-value cell 'level)))
new))
(cl-defmethod ein:cell-copy ((cell ein:basecell)) (cl-defmethod ein:cell-copy ((cell ein:basecell))
(ein:cell-convert cell (slot-value cell 'cell-type))) (ein:cell-convert cell (slot-value cell 'cell-type)))
@ -385,16 +342,6 @@ a number will limit the number of lines in a cell output."
do (ein:cell--ewoc-invalidate ewoc en))) do (ein:cell--ewoc-invalidate ewoc en)))
new)) new))
(cl-defmethod ein:cell-change-level ((cell ein:headingcell) level)
(assert (integerp level))
(let ((inhibit-read-only t)
(buffer-undo-list t)) ; disable undo recording
(setf (slot-value cell 'level) level)
;; draw ewoc node
(cl-loop with ewoc = (slot-value cell 'ewoc)
for en in (ein:cell-all-element cell)
do (ein:cell--ewoc-invalidate ewoc en))))
;;; Getter/setter ;;; Getter/setter
(cl-defmethod ein:cell-num-outputs ((cell ein:codecell)) (cl-defmethod ein:cell-num-outputs ((cell ein:codecell))
@ -534,11 +481,6 @@ Return language name as a string or `nil' when not defined.
(format "%s:" (slot-value cell 'cell-type)) (format "%s:" (slot-value cell 'cell-type))
'font-lock-face 'ein:cell-input-prompt)) 'font-lock-face 'ein:cell-input-prompt))
(cl-defmethod ein:cell-insert-prompt ((cell ein:headingcell))
(ein:insert-read-only
(format "h%s:" (slot-value cell 'level))
'font-lock-face 'ein:cell-input-prompt))
(cl-defmethod ein:cell-insert-input ((cell ein:basecell)) (cl-defmethod ein:cell-insert-input ((cell ein:basecell))
"Insert input of the CELL in the buffer. "Insert input of the CELL in the buffer.
Called from ewoc pretty printer via `ein:cell-pp'." Called from ewoc pretty printer via `ein:cell-pp'."
@ -557,9 +499,6 @@ Return language name as a string or `nil' when not defined.
"Return the face (symbol) for input area." "Return the face (symbol) for input area."
'ein:cell-input-area) 'ein:cell-input-area)
(cl-defmethod ein:cell-get-input-area-face ((cell ein:headingcell))
(intern (format "ein:cell-heading-%d" (slot-value cell 'level))))
(cl-defmethod ein:cell-get-output-area-face-for-output-type (output-type) (cl-defmethod ein:cell-get-output-area-face-for-output-type (output-type)
"Return the face (symbol) for output area." "Return the face (symbol) for output area."
(ein:case-equal output-type (ein:case-equal output-type
@ -956,27 +895,24 @@ Called from ewoc pretty printer via `ein:cell-insert-output'."
(make-obsolete-variable 'ein:output-type-preference nil "0.17.0") (make-obsolete-variable 'ein:output-type-preference nil "0.17.0")
(defun ein:cell-output-type (mime-type) (defun ein:cell-extract-image-format (mime-type)
"Investigate why :image/svg+xml to :svg and :text/plain to :text" "From :image/svg+xml to \"svg\"."
(let* ((mime-str (if (symbolp mime-type) (symbol-name mime-type) mime-type)) (let* ((mime-str (if (symbolp mime-type) (symbol-name mime-type) mime-type))
(minor-kw (car (nreverse (split-string mime-str "/")))) (minor-kw (car (nreverse (split-string mime-str "/"))))
(minor (car (nreverse (split-string minor-kw ":"))))) (minor (car (nreverse (split-string minor-kw ":")))))
(intern (concat ":" (cl-subseq minor 0 (cl-search "+" minor))))
(cond ((string= minor "plain") "text")
(t (cl-subseq minor 0 (cl-search "+" minor))))))))
(defun ein:cell-append-mime-type (json) (defun ein:cell-append-mime-type (json)
(ein:output-area-case-type (ein:output-area-case-type
json json
(cl-case type (cl-case type
((:html) ((:text/html)
(funcall (ein:output-area-get-html-renderer) value)) (funcall (ein:output-area-get-html-renderer) value))
((:svg :png :jpeg) ((:image/svg+xml :image/png :image/jpeg)
(let ((image (create-image (condition-case nil (let ((image (create-image (condition-case nil
(base64-decode-string value) (base64-decode-string value)
(error value)) (error value))
(intern (car (nreverse (intern-soft (ein:cell-extract-image-format type))
(split-string (symbol-name type) ":"))))
t))) t)))
(if ein:output-area-inlined-images (if ein:output-area-inlined-images
(ein:insert-image image) (ein:insert-image image)
@ -999,124 +935,38 @@ Called from ewoc pretty printer via `ein:cell-insert-output'."
text) text)
(format "Error: %S" err))))) (format "Error: %S" err)))))
(cl-defmethod ein:cell-to-json ((cell ein:codecell) &optional discard-output) (cl-defmethod ein:cell-to-json ((cell ein:codecell))
"Return json-ready alist." "Return json-ready alist."
`((input . ,(ein:cell-get-text cell)) `((input . ,(ein:cell-get-text cell))
(cell_type . "code") (cell_type . "code")
,@(aif (ein:oref-safe cell 'input-prompt-number) ,@(aif (ein:oref-safe cell 'input-prompt-number)
`((prompt_number . ,it))) `((prompt_number . ,it)))
(outputs . ,(if discard-output [] (apply #'vector (slot-value cell 'outputs)))) (outputs . ,(apply #'vector (slot-value cell 'outputs)))
(language . ,(or (ein:cell-language cell) "python")) (language . ,(or (ein:cell-language cell) "python"))
(collapsed . ,(if (slot-value cell 'collapsed) t json-false)))) (collapsed . ,(if (slot-value cell 'collapsed) t json-false))))
(defvar ein:output-type-map (cl-defmethod ein:cell-to-nb4-json ((cell ein:codecell) wsidx)
'((:svg . :image/svg+xml) (:png . :image/png) (:jpeg . :image/jpeg) (let ((execute-count (aif (ein:oref-safe cell 'input-prompt-number)
(:text . :text/plain) (and (numberp it) it)))
(:html . :text/html) (:latex . :text/latex) (:javascript . :text/javascript))) (metadata (slot-value cell 'metadata)))
(defun ein:output-property-p (maybe-property)
(assoc maybe-property ein:output-type-map))
(cl-defmethod ein:cell-to-nb4-json ((cell ein:codecell) wsidx &optional discard-output)
(let* ((metadata (slot-value cell 'metadata))
(outputs (if discard-output []
(slot-value cell 'outputs)))
(renamed-outputs '())
(execute-count (aif (ein:oref-safe cell 'input-prompt-number)
(and (numberp it) it))))
(setq metadata (plist-put metadata :collapsed (if (slot-value cell 'collapsed) t json-false)))
(setq metadata (plist-put metadata :autoscroll json-false))
(setq metadata (plist-put metadata :ein.tags (format "worksheet-%s" wsidx)))
(unless discard-output
(dolist (output outputs)
(let ((otype (plist-get output :output_type)))
(ein:log 'debug "Saving output of type %S" otype)
(if (and (or (equal otype "display_data")
(equal otype "execute_result"))
(null (plist-get output :metadata)))
(plist-put output :metadata (make-hash-table)))
(setq renamed-outputs
(append renamed-outputs
(list (let ((ocopy (cl-copy-list output))
(new-output '()))
(cl-loop while ocopy
do (let ((prop (pop ocopy))
(value (pop ocopy)))
(ein:log 'debug "Checking property %s for output type '%s'"
prop otype)
(cond
((equal prop :stream) (progn (push value new-output)
(push :name new-output)))
((and (or (equal otype "display_data")
(equal otype "execute_result"))
(ein:output-property-p prop))
(let ((new-prop (cdr (ein:output-property-p prop))))
(if (plist-member new-output :data)
(setq new-output (plist-put new-output :data
(append (plist-get new-output :data)
(list new-prop (list value))
)))
(push (list new-prop (list value)) new-output)
(push :data new-output))
))
((and (equal otype "display_data")
(equal prop :text))
(ein:log 'debug "SAVE-NOTEBOOK: Skipping unnecessary :text data."))
;; ((and (equal otype "execute_result")
;; (ein:output-property-p prop)
;; ;; (or (equal prop :text)
;; ;; (equal prop :html)
;; ;; (equal prop :latex))
;; )
;; (ein:log 'debug "Fixing execute_result (%s?)." otype)
;; (let ((new-prop (cdr (ein:output-property-p prop))))
;; (push (list new-prop (list value)) new-output)
;; (push :data new-output)))
((and (equal otype "execute_result")
(equal prop :prompt_number))
(ein:log 'debug "SAVE-NOTEBOOK: Fixing prompt_number property.")
(push value new-output)
(push :execution_count new-output))
(t (progn (push value new-output) (push prop new-output)))))
finally return new-output))))
))))
`((source . ,(ein:cell-get-text cell)) `((source . ,(ein:cell-get-text cell))
(cell_type . "code") (cell_type . "code")
,@(if execute-count ,@(when execute-count
`((execution_count . ,execute-count)) `((execution_count . ,execute-count)))
`((execution_count))) (outputs . ,(apply #'vector (slot-value cell 'outputs)))
(outputs . ,(apply #'vector (or renamed-outputs outputs))) (metadata . ,(plist-put metadata :collapsed (if (slot-value cell 'collapsed) t
(metadata . ,metadata)))) json-false))))))
(cl-defmethod ein:cell-to-json ((cell ein:textcell) &optional discard-output) (cl-defmethod ein:cell-to-json ((cell ein:textcell))
`((cell_type . ,(slot-value cell 'cell-type)) `((cell_type . ,(slot-value cell 'cell-type))
(source . ,(ein:cell-get-text cell)))) (source . ,(ein:cell-get-text cell))))
(cl-defmethod ein:cell-to-nb4-json ((cell ein:textcell) wsidx &optional discard-output) (cl-defmethod ein:cell-to-nb4-json ((cell ein:textcell) wsidx)
(let ((metadata (slot-value cell 'metadata))) (let ((metadata (slot-value cell 'metadata)))
(setq metadata (plist-put metadata :ein.tags (format "worksheet-%s" wsidx)))
`((cell_type . ,(slot-value cell 'cell-type)) `((cell_type . ,(slot-value cell 'cell-type))
(source . ,(ein:cell-get-text cell)) (source . ,(ein:cell-get-text cell))
(metadata . ,metadata)))) (metadata . ,(plist-put metadata :collapsed json-false)))))
(cl-defmethod ein:cell-to-nb4-json ((cell ein:headingcell) wsidx &optional discard-output)
(let ((metadata (slot-value cell 'metadata))
(header (make-string (slot-value cell 'level) ?#)))
(setq metadata (plist-put metadata :ein.tags (format "worksheet-%s" wsidx)))
`((cell_type . "markdown")
(source . ,(format "%s %s" header (ein:cell-get-text cell)))
(metadata . ,metadata))))
(cl-defmethod ein:cell-to-json ((cell ein:headingcell) &optional discard-output)
(let ((json (cl-call-next-method)))
(append json `((level . ,(slot-value cell 'level))))))
(cl-defmethod ein:cell-next ((cell ein:basecell)) (cl-defmethod ein:cell-next ((cell ein:basecell))
"Return next cell of the given CELL or nil if CELL is the last one." "Return next cell of the given CELL or nil if CELL is the last one."
@ -1175,48 +1025,16 @@ Called from ewoc pretty printer via `ein:cell-insert-output'."
(let ((events (slot-value cell 'events))) (let ((events (slot-value cell 'events)))
(ein:events-trigger events 'set_next_input.Worksheet (ein:events-trigger events 'set_next_input.Worksheet
(list :cell cell :text text)) (list :cell cell :text text))
(ein:events-trigger events 'maybe_reset_undo.Worksheet cell) (ein:events-trigger events 'maybe_reset_undo.Worksheet cell)))
))
(cl-defmethod ein:cell--handle-output ((cell ein:codecell) msg-type content meta)
;;; Output area ;; (ein:output-area-convert-mime-types content (plist-get content :data))
(ein:cell-append-output cell
;; These function should go to ein-output-area.el. But as cell and (plist-put
;; EWOC is connected in complicated way, I will leave them in (plist-put content :output_type msg-type)
;; ein-cell.el. :metadata meta))
(cl-defmethod ein:cell--handle-output ((cell ein:codecell) msg-type content
_metadata)
(let* ((json (list :output_type msg-type)))
(ein:case-equal msg-type
(("stream")
(plist-put json :text (or (plist-get content :data)
(plist-get content :text))) ;; Horrible hack to deal with version 5.0 of messaging protocol.
(plist-put json :stream (plist-get content :name)))
(("display_data" "pyout" "execute_result") ;; in v4 nbformat execute_result == pyout
(when (or (equal msg-type "pyout")
(equal msg-type "execute_result"))
(plist-put json :prompt_number (plist-get content :execution_count)))
(setq json (ein:output-area-convert-mime-types json (plist-get content :data))))
(("pyerr" "error")
(plist-put json :ename (plist-get content :ename))
(plist-put json :evalue (plist-get content :evalue))
(plist-put json :traceback (plist-get content :traceback))))
(ein:cell-append-output cell json)
;; (setf (slot-value cell 'dirty) t) ;; (setf (slot-value cell 'dirty) t)
(ein:events-trigger (slot-value cell 'events) 'maybe_reset_undo.Worksheet cell))) (ein:events-trigger (slot-value cell 'events) 'maybe_reset_undo.Worksheet cell))
(defun ein:output-area-convert-mime-types (json data)
(let ((known-mimes (cl-remove-if-not
#'identity
(mapcar (lambda (x) (intern-soft (concat ":" x)))
(mailcap-mime-types)))))
(mapc (lambda (x)
(-when-let* ((mime-val (plist-get data x))
(minor-kw (ein:cell-output-type x)))
(setq json (plist-put json minor-kw mime-val))))
known-mimes)
json))
(cl-defmethod ein:cell--handle-clear-output ((cell ein:codecell) content (cl-defmethod ein:cell--handle-clear-output ((cell ein:codecell) content
_metadata) _metadata)
@ -1230,18 +1048,15 @@ Called from ewoc pretty printer via `ein:cell-insert-output'."
;;; Misc. ;;; Misc.
(cl-defmethod ein:cell-has-image-ouput-p ((cell ein:codecell)) (cl-defmethod ein:cell-has-image-output-p ((cell ein:codecell))
"Return `t' if given cell has image output, `nil' otherwise." "Return `t' if given cell has image output, `nil' otherwise."
(cl-loop for out in (slot-value cell 'outputs) (seq-some (lambda (out)
when (or (plist-member out :svg) (or (plist-member out :image/svg+xml)
(plist-member out :image/svg+xml)
(plist-member out :png)
(plist-member out :image/png) (plist-member out :image/png)
(plist-member out :jpeg) (plist-member out :image/jpeg)))
(plist-member out :image/jpeg)) (slot-value cell 'outputs)))
return t))
(cl-defmethod ein:cell-has-image-ouput-p ((cell ein:textcell)) (cl-defmethod ein:cell-has-image-output-p ((cell ein:textcell))
nil) nil)
(cl-defmethod ein:cell-get-tb-data ((cell ein:codecell)) (cl-defmethod ein:cell-get-tb-data ((cell ein:codecell))

View file

@ -166,10 +166,6 @@
((nbformat :initarg :nbformat :type integer) ((nbformat :initarg :nbformat :type integer)
(get-notebook-name :initarg :get-notebook-name :type cons (get-notebook-name :initarg :get-notebook-name :type cons
:accessor ein:worksheet--notebook-name) :accessor ein:worksheet--notebook-name)
;; This slot introduces too much complexity so therefore must be
;; removed later. This is here only for backward compatible
;; reason.
(discard-output-p :initarg :discard-output-p :accessor ein:worksheet--discard-output-p)
(saved-cells :initarg :saved-cells :initform nil (saved-cells :initarg :saved-cells :initform nil
:accessor ein:worksheet--saved-cells :accessor ein:worksheet--saved-cells
:documentation :documentation
@ -251,7 +247,7 @@
(input :initarg :input :type string (input :initarg :input :type string
:documentation "Place to hold data until it is rendered via `ewoc'.") :documentation "Place to hold data until it is rendered via `ewoc'.")
(outputs :initarg :outputs :initform nil :type list) (outputs :initarg :outputs :initform nil :type list)
(metadata :initarg :metadata :initform nil :type list :accessor ein:cell-metadata) ;; For nbformat >= 4 (metadata :initarg :metadata :initform nil :type list :accessor ein:cell-metadata)
(events :initarg :events :type ein:events) (events :initarg :events :type ein:events)
(cell-id :initarg :cell-id :initform (ein:utils-uuid) :type string (cell-id :initarg :cell-id :initform (ein:utils-uuid) :type string
:accessor ein:cell-id)) :accessor ein:cell-id))
@ -290,11 +286,6 @@ auto-execution mode flag in the connected buffer is `t'.")))
(defclass ein:rawcell (ein:textcell) (defclass ein:rawcell (ein:textcell)
((cell-type :initarg :cell-type :initform "raw"))) ((cell-type :initarg :cell-type :initform "raw")))
(defclass ein:headingcell (ein:textcell)
((cell-type :initarg :cell-type :initform "heading")
(level :initarg :level :initform 1)))
;;; Notifications ;;; Notifications
(defclass ein:notification-status () (defclass ein:notification-status ()
@ -331,9 +322,9 @@ auto-execution mode flag in the connected buffer is `t'.")))
(ein:notification-status (ein:notification-status
"NotebookStatus" "NotebookStatus"
:s2m :s2m
'((notebook_saving.Notebook . "Saving Notebook...") '((notebook_saving.Notebook . "Saving notebook...")
(notebook_saved.Notebook . "Notebook is saved") (notebook_saved.Notebook . "Notebook saved")
(notebook_save_failed.Notebook . "Failed to save Notebook!"))) (notebook_save_failed.Notebook . "Failed saving notebook!")))
:type ein:notification-status) :type ein:notification-status)
(kernel (kernel
:initarg :kernel :initarg :kernel
@ -342,7 +333,7 @@ auto-execution mode flag in the connected buffer is `t'.")))
"KernelStatus" "KernelStatus"
:s2m :s2m
'((status_idle.Kernel . nil) '((status_idle.Kernel . nil)
(status_busy.Kernel . "Kernel is busy...") (status_busy.Kernel . "Kernel busy...")
(status_restarting.Kernel . "Kernel restarting...") (status_restarting.Kernel . "Kernel restarting...")
(status_restarted.Kernel . "Kernel restarted") (status_restarted.Kernel . "Kernel restarted")
(status_dead.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km]") (status_dead.Kernel . "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km]")
@ -352,7 +343,6 @@ auto-execution mode flag in the connected buffer is `t'.")))
:type ein:notification-status)) :type ein:notification-status))
"Notification widget for Notebook.") "Notification widget for Notebook.")
;;; Events ;;; Events
(defclass ein:events () (defclass ein:events ()

View file

@ -296,9 +296,11 @@ global setting. For global setting and more information, see
:type "PUT" :type "PUT"
:headers '(("Content-Type" . "application/json")) :headers '(("Content-Type" . "application/json"))
:timeout ein:content-query-timeout :timeout ein:content-query-timeout
:data (encode-coding-string (ein:content-to-json content) buffer-file-coding-system) :data (encode-coding-string (ein:content-to-json content)
buffer-file-coding-system)
:success (apply-partially #'ein:content-save-success callback cbargs) :success (apply-partially #'ein:content-save-success callback cbargs)
:error (apply-partially #'ein:content-save-error (ein:content-url content) errcb errcbargs)) :error (apply-partially #'ein:content-save-error
(ein:content-url content) errcb errcbargs))
(ein:content-save-legacy content callback cbargs))) (ein:content-save-legacy content callback cbargs)))
(cl-defun ein:content-save-success (callback cbargs &key status response &allow-other-keys) (cl-defun ein:content-save-success (callback cbargs &key status response &allow-other-keys)

View file

@ -211,7 +211,7 @@ CALLBACK of arity 1, the kernel.
(let ((session-id (plist-get data :id))) (let ((session-id (plist-get data :id)))
(if (plist-get data :kernel) (if (plist-get data :kernel)
(setq data (plist-get data :kernel))) (setq data (plist-get data :kernel)))
(destructuring-bind (&key id &allow-other-keys) data (cl-destructuring-bind (&key id &allow-other-keys) data
(ein:log 'verbose "ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s" (ein:log 'verbose "ein:kernel-retrieve-session--success: kernel-id=%s session-id=%s"
id session-id) id session-id)
(setf (ein:$kernel-kernel-id kernel) id) (setf (ein:$kernel-kernel-id kernel) id)
@ -308,7 +308,6 @@ See https://github.com/ipython/ipython/pull/3307"
(ein:log 'info "Kernel connect_request_reply received.")) (ein:log 'info "Kernel connect_request_reply received."))
(defun ein:kernel-run-after-start-hook (kernel) (defun ein:kernel-run-after-start-hook (kernel)
(ein:log 'debug "EIN:KERNEL-RUN-AFTER-START-HOOK")
(mapc #'ein:funcall-packed (mapc #'ein:funcall-packed
(ein:$kernel-after-start-hook kernel))) (ein:$kernel-after-start-hook kernel)))
@ -331,15 +330,6 @@ delete the kernel on the server side"
(ein:log 'verbose "Kernel %s unavailable" (ein:$kernel-kernel-id kernel)) (ein:log 'verbose "Kernel %s unavailable" (ein:$kernel-kernel-id kernel))
(ein:kernel-reconnect-session kernel callback))) (ein:kernel-reconnect-session kernel callback)))
;;; Main public methods
;; NOTE: The argument CALLBACKS for the following functions is almost
;; same as the JS implementation in IPython. However, as Emacs
;; lisp does not support closure, value is "packed" using
;; `cons': `car' is the actual callback function and `cdr' is
;; its first argument. It's like using `cons' instead of
;; `$.proxy'.
(defun ein:kernel-object-info-request (kernel objname callbacks &optional cursor-pos detail-level) (defun ein:kernel-object-info-request (kernel objname callbacks &optional cursor-pos detail-level)
"Send object info request of OBJNAME to KERNEL. "Send object info request of OBJNAME to KERNEL.
@ -385,63 +375,18 @@ http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
(stop-on-error nil)) (stop-on-error nil))
"Execute CODE on KERNEL. "Execute CODE on KERNEL.
When calling this method pass a CALLBACKS structure of the form: The CALLBACKS plist looks like:
(:execute_reply EXECUTE-REPLY-CALLBACK (:execute_reply EXECUTE-REPLY-CALLBACK
:output OUTPUT-CALLBACK :output OUTPUT-CALLBACK
:clear_output CLEAR-OUTPUT-CALLBACK :clear_output CLEAR-OUTPUT-CALLBACK
:set_next_input SET-NEXT-INPUT) :set_next_input SET-NEXT-INPUT)
Right hand sides ending -CALLBACK above must cons a FUNCTION and its Right hand sides ending -CALLBACK above are of the form (FUNCTION ARG1 ... ARGN).
`packed' ARGUMENT which is a sublist of args: (Hindsight: this was all much better implemented using `apply-partially')
(FUNCTION . ARGUMENT) Return randomly generated MSG-ID tag uniquely identifying expectation of a kernel response.
Call signature
--------------
::
(`funcall' EXECUTE-REPLY-CALLBACK ARGUMENT CONTENT METADATA)
(`funcall' OUTPUT-CALLBACK ARGUMENT MSG-TYPE CONTENT METADATA)
(`funcall' CLEAR-OUTPUT-CALLBACK ARGUMENT CONTENT METADATA)
(`funcall' SET-NEXT-INPUT ARGUMENT TEXT)
* Both CONTENT and METADATA objects are plist.
* The MSG-TYPE argument for OUTPUT-CALLBACK is a string
(one of `stream', `display_data', `pyout' and `pyerr').
* The CONTENT object for CLEAR-OUTPUT-CALLBACK has
`stdout', `stderr' and `other' fields that are booleans.
* The SET-NEXT-INPUT callback will be passed the `set_next_input' payload,
which is a string.
See `ein:kernel--handle-shell-reply' for how the callbacks are called.
Links
-----
* For general description of CONTENT and METADATA:
http://ipython.org/ipython-doc/dev/development/messaging.html#general-message-format
* `execute_reply' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#execute
* Output type messages is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
Sample implementations
----------------------
* `ein:cell--handle-execute-reply'
* `ein:cell--handle-output'
* `ein:cell--handle-clear-output'
* `ein:cell--handle-set-next-input'
" "
;; FIXME: Consider changing callback to use `&key'.
;; Otherwise, adding new arguments to callback requires
;; backward incompatible changes (hence a big diff), unlike
;; Javascript. Downside of this is that there is no short way
;; to write anonymous callback because there is no `lambda*'.
;; You can use `function*', but that's bit long...
;; FIXME: Consider allowing a list of fixed argument so that the
;; call signature becomes something like:
;; (funcall FUNCTION [ARG ...] CONTENT METADATA)
(assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.") (assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.")
(let* ((content (list (let* ((content (list
:code code :code code
@ -452,7 +397,7 @@ Sample implementations
:stop_on_error (or stop-on-error json-false))) :stop_on_error (or stop-on-error json-false)))
(msg (ein:kernel--get-msg kernel "execute_request" content)) (msg (ein:kernel--get-msg kernel "execute_request" content))
(msg-id (plist-get (plist-get msg :header) :msg_id))) (msg-id (plist-get (plist-get msg :header) :msg_id)))
(ein:log 'debug "KERNEL-EXECUTE: code=%s msg_id=%s" code msg-id) (ein:log 'debug "ein:kernel-execute: code=%s msg_id=%s" code msg-id)
(run-hook-with-args 'ein:pre-kernel-execute-functions msg) (run-hook-with-args 'ein:pre-kernel-execute-functions msg)
(ein:websocket-send-shell-channel kernel msg) (ein:websocket-send-shell-channel kernel msg)
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks) (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
@ -577,36 +522,6 @@ Example::
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks) (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
msg-id)) msg-id))
(defun ein:kernel-kernel-info-request (kernel callbacks)
"Request core information of KERNEL.
When calling this method pass a CALLBACKS structure of the form::
(:kernel_info_reply (FUNCTION . ARGUMENT))
Call signature::
(`funcall' FUNCTION ARGUMENT CONTENT METADATA)
CONTENT and METADATA are given by `kernel_info_reply' message.
`kernel_info_reply' message is documented here:
http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info
Example::
(ein:kernel-kernel-info-request
(ein:get-kernel)
'(:kernel_info_reply (message . \"CONTENT: %S\\nMETADATA: %S\")))
"
(assert (ein:kernel-live-p kernel) nil "kernel_info_reply: Kernel is not active.")
(ein:log 'debug "EIN:KERNEL-KERNEL-INFO-REQUEST: Sending request.")
(let* ((msg (ein:kernel--get-msg kernel "kernel_info_request" nil))
(msg-id (plist-get (plist-get msg :header) :msg_id)))
(ein:websocket-send-shell-channel kernel msg)
(ein:kernel-set-callbacks-for-msg kernel msg-id callbacks)
msg-id))
(defun ein:kernel-interrupt (kernel) (defun ein:kernel-interrupt (kernel)
(when (ein:kernel-live-p kernel) (when (ein:kernel-live-p kernel)
(ein:log 'info "Interrupting kernel") (ein:log 'info "Interrupting kernel")
@ -670,17 +585,18 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl
(gethash msg-id (ein:$kernel-msg-callbacks kernel))) (gethash msg-id (ein:$kernel-msg-callbacks kernel)))
(defun ein:kernel-set-callbacks-for-msg (kernel msg-id callbacks) (defun ein:kernel-set-callbacks-for-msg (kernel msg-id callbacks)
"Set up promise for MSG-ID."
(puthash msg-id callbacks (ein:$kernel-msg-callbacks kernel))) (puthash msg-id callbacks (ein:$kernel-msg-callbacks kernel)))
(defun ein:kernel--handle-stdin-reply (kernel packet) (defun ein:kernel--handle-stdin-reply (kernel packet)
(setf (ein:$kernel-stdin-activep kernel) t) (setf (ein:$kernel-stdin-activep kernel) t)
(destructuring-bind (cl-destructuring-bind
(&key header parent_header metadata content &allow-other-keys) (&key header parent_header metadata content &allow-other-keys)
(ein:json-read-from-string packet) (ein:json-read-from-string packet)
(let ((msg-type (plist-get header :msg_type)) (let ((msg-type (plist-get header :msg_type))
(msg-id (plist-get header :msg_id)) (msg-id (plist-get header :msg_id))
(password (plist-get content :password))) (password (plist-get content :password)))
(ein:log 'debug "KERNEL--HANDLE-STDIN-REPLY: msg_type=%s msg_id=%s" (ein:log 'debug "ein:kernel--handle-stdin-reply: msg_type=%s msg_id=%s"
msg-type msg-id) msg-type msg-id)
(cond ((string-equal msg-type "input_request") (cond ((string-equal msg-type "input_request")
(if (not (eql password :json-false)) (if (not (eql password :json-false))
@ -697,33 +613,10 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl
(ein:websocket-send-stdin-channel kernel msg) (ein:websocket-send-stdin-channel kernel msg)
(setf (ein:$kernel-stdin-activep kernel) nil)))))))))) (setf (ein:$kernel-stdin-activep kernel) nil))))))))))
(defun ein:kernel--handle-shell-reply (kernel packet)
(destructuring-bind
(&key header content metadata parent_header &allow-other-keys)
(ein:json-read-from-string packet)
(let* ((msg-type (plist-get header :msg_type))
(msg-id (plist-get parent_header :msg_id))
(callbacks (ein:kernel-get-callbacks-for-msg kernel msg-id))
(cb (plist-get callbacks (intern (format ":%s" msg-type)))))
(ein:log 'debug "KERNEL--HANDLE-SHELL-REPLY: msg_type=%s msg_id=%s"
msg-type msg-id)
(run-hook-with-args 'ein:on-shell-reply-functions msg-type header content metadata)
(aif cb (ein:funcall-packed it content metadata))
(aif (plist-get content :payload)
(ein:kernel--handle-payload kernel callbacks it))
(let ((events (ein:$kernel-events kernel)))
(ein:case-equal msg-type
(("execute_reply")
(aif (plist-get content :execution_count)
;; It can be `nil' for silent execution
(ein:events-trigger events 'execution_count.Kernel it))))))))
(defun ein:kernel--handle-payload (kernel callbacks payload) (defun ein:kernel--handle-payload (kernel callbacks payload)
(cl-loop with events = (ein:$kernel-events kernel) (cl-loop with events = (ein:$kernel-events kernel)
for p in payload for p in payload
for text = (or (plist-get p :text) for text = (or (plist-get p :text) (plist-get (plist-get p :data) :text/plain))
(plist-get (plist-get p :data)
:text/plain))
for source = (plist-get p :source) for source = (plist-get p :source)
if (member source '("IPython.kernel.zmq.page.page" if (member source '("IPython.kernel.zmq.page.page"
"IPython.zmq.page.page" "IPython.zmq.page.page"
@ -740,24 +633,47 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl
do (let ((cb (plist-get callbacks :set_next_input))) do (let ((cb (plist-get callbacks :set_next_input)))
(when cb (ein:funcall-packed cb text))))) (when cb (ein:funcall-packed cb text)))))
(defun ein:kernel--handle-shell-reply (kernel packet)
(cl-destructuring-bind
(&key header content metadata parent_header &allow-other-keys)
(ein:json-read-from-string packet)
(let* ((msg-type (plist-get header :msg_type))
(msg-id (plist-get parent_header :msg_id))
(callbacks (ein:kernel-get-callbacks-for-msg kernel msg-id)))
(ein:log 'debug "ein:kernel--handle-shell-reply: msg_type=%s msg_id=%s"
msg-type msg-id)
(run-hook-with-args 'ein:on-shell-reply-functions msg-type header content metadata)
(aif (plist-get callbacks (intern-soft (format ":%s" msg-type)))
(ein:funcall-packed it content metadata)
(ein:log 'info "ein:kernel--handle-shell-reply: No :%s callback for msg_id=%s"
msg-type msg-id))
(aif (plist-get content :payload)
(ein:kernel--handle-payload kernel callbacks it))
(let ((events (ein:$kernel-events kernel)))
(ein:case-equal msg-type
(("execute_reply")
(aif (plist-get content :execution_count)
(ein:events-trigger events 'execution_count.Kernel it))))))))
(defun ein:kernel--handle-iopub-reply (kernel packet) (defun ein:kernel--handle-iopub-reply (kernel packet)
(if (ein:$kernel-stdin-activep kernel) (if (ein:$kernel-stdin-activep kernel)
(ein:ipdb--handle-iopub-reply kernel packet) (ein:ipdb--handle-iopub-reply kernel packet)
(destructuring-bind (cl-destructuring-bind
(&key content metadata parent_header header &allow-other-keys) (&key content metadata parent_header header &allow-other-keys)
(ein:json-read-from-string packet) (ein:json-read-from-string packet)
(let* ((msg-type (plist-get header :msg_type)) (let* ((msg-type (plist-get header :msg_type))
(msg-id (plist-get parent_header :msg_id)) (msg-id (plist-get parent_header :msg_id))
(callbacks (ein:kernel-get-callbacks-for-msg kernel msg-id)) (callbacks (ein:kernel-get-callbacks-for-msg kernel msg-id))
(events (ein:$kernel-events kernel))) (events (ein:$kernel-events kernel)))
(ein:log 'debug "KERNEL--HANDLE-IOPUB-REPLY: msg_type=%s msg_id=%s" (ein:log 'debug "ein:kernel--handle-iopub-reply: msg_type=%s msg_id=%s"
msg-type msg-id) msg-type msg-id)
(if (and (not (equal msg-type "status")) (null callbacks))
(ein:log 'verbose "Not processing msg_type=%s msg_id=%s" msg-type msg-id)
(ein:case-equal msg-type (ein:case-equal msg-type
(("stream" "display_data" "pyout" "pyerr" "error" "execute_result") (("stream" "display_data" "pyout" "pyerr" "error" "execute_result")
(aif (plist-get callbacks :output) (aif (plist-get callbacks :output)
(ein:funcall-packed it msg-type content metadata))) (ein:funcall-packed it msg-type content metadata)
(ein:log 'warn (concat "ein:kernel--handle-iopub-reply: "
"No :output callback for msg_id=%s")
msg-id)))
(("status") (("status")
(ein:case-equal (plist-get content :execution_state) (ein:case-equal (plist-get content :execution_state)
(("busy") (("busy")
@ -767,12 +683,13 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl
(("dead") (("dead")
(ein:kernel-disconnect kernel)))) (ein:kernel-disconnect kernel))))
(("data_pub") (("data_pub")
(ein:log 'verbose (format "Received data_pub message w/content %s" packet))) (ein:log 'verbose "ein:kernel--handle-iopub-reply: data_pub %S" packet))
(("clear_output") (("clear_output")
(aif (plist-get callbacks :clear_output) (aif (plist-get callbacks :clear_output)
(ein:funcall-packed it content metadata))))))))) (ein:funcall-packed it content metadata)
(ein:log 'info (concat "ein:kernel--handle-iopub-reply: "
;;; Utility functions "No :clear_output callback for msg_id=%s")
msg-id))))))))
(defun ein:kernel-filename-to-python (kernel filename) (defun ein:kernel-filename-to-python (kernel filename)
"See: `ein:filename-to-python'." "See: `ein:filename-to-python'."
@ -792,7 +709,6 @@ Used in `ein:pytools-finish-tooltip', etc."
(defun ein:kernel-construct-help-string (content) (defun ein:kernel-construct-help-string (content)
"Construct help string from CONTENT of ``:object_info_reply``. "Construct help string from CONTENT of ``:object_info_reply``.
Used in `ein:pytools-finish-tooltip', etc." Used in `ein:pytools-finish-tooltip', etc."
(ein:log 'debug "KERNEL-CONSTRUCT-HELP-STRING")
(let* ((defstring (ein:aand (let* ((defstring (ein:aand
(ein:kernel-construct-defstring content) (ein:kernel-construct-defstring content)
(ansi-color-apply it) (ansi-color-apply it)
@ -807,7 +723,6 @@ Used in `ein:pytools-finish-tooltip', etc."
(help (ein:aand (help (ein:aand
(delete nil (list defstring docstring)) (delete nil (list defstring docstring))
(ein:join-str "\n" it)))) (ein:join-str "\n" it))))
(ein:log 'debug "KERNEL-CONSTRUCT-HELP-STRING: help=%s" help)
help)) help))
(defun ein:kernel-request-stream (kernel code func &optional args) (defun ein:kernel-request-stream (kernel code func &optional args)

View file

@ -44,7 +44,6 @@
(require 'ein-kernel) (require 'ein-kernel)
(require 'ein-kernelinfo) (require 'ein-kernelinfo)
(require 'ein-cell) (require 'ein-cell)
(require 'ein-cell-output)
(require 'ein-worksheet) (require 'ein-worksheet)
(require 'ein-iexec) (require 'ein-iexec)
(require 'ein-scratchsheet) (require 'ein-scratchsheet)
@ -65,47 +64,19 @@
;;; Configuration ;;; Configuration
(defcustom ein:notebook-discard-output-on-save 'no
"Configure if the output part of the cell should be saved or not.
.. warning:: This configuration is obsolete now.
Use nbconvert (https://github.com/ipython/nbconvert) to
strip output.
`no' : symbol
Save output. This is the default.
`yes' : symbol
Always discard output.
a function
This function takes two arguments, notebook and cell. Return
`t' to discard output and return `nil' to save. For example,
if you don't want to save image output but other kind of
output, use `ein:notebook-cell-has-image-output-p'.
"
:type '(choice (const :tag "No" 'no)
(const :tag "Yes" 'yes)
)
:group 'ein)
(make-obsolete-variable 'ein:use-smartrep nil "0.17.0") (make-obsolete-variable 'ein:use-smartrep nil "0.17.0")
(defvar *ein:notebook--pending-query* (make-hash-table :test 'equal)
"A map: (URL-OR-PORT . PATH) => t/nil")
(make-obsolete-variable 'ein:notebook-autosave-frequency nil "0.17.0") (make-obsolete-variable 'ein:notebook-autosave-frequency nil "0.17.0")
(make-obsolete-variable 'ein:notebook-create-checkpoint-on-save nil "0.17.0") (make-obsolete-variable 'ein:notebook-create-checkpoint-on-save nil "0.17.0")
(make-obsolete-variable 'ein:notebook-discard-output-on-save nil "0.17.0") (make-obsolete-variable 'ein:notebook-discard-output-on-save nil "0.17.0")
(defun ein:notebook-cell-has-image-output-p (-ignore- cell) (defvar *ein:notebook--pending-query* (make-hash-table :test 'equal)
(ein:cell-has-image-ouput-p cell)) "A map: (URL-OR-PORT . PATH) => t/nil")
(defun ein:notebook-discard-output-p (notebook cell) (defun ein:notebook-cell-has-image-output-p (_ignore cell)
"Return non-`nil' if the output must be discarded, otherwise save." (ein:cell-has-image-output-p cell))
(case ein:notebook-discard-output-on-save
(no nil)
(yes t)
(t (funcall ein:notebook-discard-output-on-save notebook cell))))
;; As opening/saving notebook treats possibly huge data, define these ;; As opening/saving notebook treats possibly huge data, define these
;; timeouts separately: ;; timeouts separately:
@ -548,9 +519,6 @@ This is equivalent to do ``C-c`` in the console program."
(funcall func (funcall func
(ein:$notebook-nbformat notebook) (ein:$notebook-nbformat notebook)
(ein:notebook-name-getter notebook) (ein:notebook-name-getter notebook)
(cons (lambda (notebook cell)
(ein:notebook-discard-output-p notebook cell))
notebook)
(ein:$notebook-kernel notebook) (ein:$notebook-kernel notebook)
(ein:$notebook-events notebook))) (ein:$notebook-events notebook)))
@ -761,8 +729,8 @@ This is equivalent to do ``C-c`` in the console program."
(cl-defun ein:notebook-save-notebook-error (notebook &key symbol-status (cl-defun ein:notebook-save-notebook-error (notebook &key symbol-status
&allow-other-keys) &allow-other-keys)
(if (eq symbol-status 'user-cancel) (if (eq symbol-status 'user-cancel)
(ein:log 'info "Cancel saving notebook.") (ein:log 'info "Cancelled save.")
(ein:log 'info "Failed to save notebook!") (ein:log 'warn "Failed saving notebook!")
(ein:events-trigger (ein:$notebook-events notebook) (ein:events-trigger (ein:$notebook-events notebook)
'notebook_save_failed.Notebook))) 'notebook_save_failed.Notebook)))
@ -1437,19 +1405,12 @@ watch the fireworks!"
(aif ein:anything-kernel-history-search-key (aif ein:anything-kernel-history-search-key
(ein:notebook--define-key ein:notebook-mode-map it anything-ein-kernel-history)) (ein:notebook--define-key ein:notebook-mode-map it anything-ein-kernel-history))
(setq indent-tabs-mode nil) ;; Being T causes problems with Python code. (setq indent-tabs-mode nil) ;; Being T causes problems with Python code.
(ein:worksheet-imenu-setup))) ))
;; To avoid MuMaMo to discard `ein:notebook-mode', make it ;; To avoid MuMaMo to discard `ein:notebook-mode', make it
;; permanent local. ;; permanent local.
(put 'ein:notebook-mode 'permanent-local t) (put 'ein:notebook-mode 'permanent-local t)
(define-derived-mode ein:notebook-plain-mode fundamental-mode "EIN[plain]"
"IPython notebook mode without fancy coloring."
(font-lock-mode))
(define-derived-mode ein:notebook-python-mode python-mode "EIN[python]"
"Use `python-mode' for whole notebook buffer.")
(defun ein:notebook-open-in-browser (&optional print) (defun ein:notebook-open-in-browser (&optional print)
"Open current notebook in web browser. "Open current notebook in web browser.
When the prefix argument (``C-u``) is given, print page is opened. When the prefix argument (``C-u``) is given, print page is opened.

View file

@ -125,13 +125,34 @@ Usage::
(ein:xml-replace-attributes dom 'a 'href replace-p replacer) (ein:xml-replace-attributes dom 'a 'href replace-p replacer)
(ein:xml-replace-attributes dom 'img 'src replace-p replacer))) (ein:xml-replace-attributes dom 'img 'src replace-p replacer)))
(defun ein:output-area-type (mime-type)
"Investigate why :image/svg+xml to :svg and :text/plain to :text"
(let* ((mime-str (if (symbolp mime-type) (symbol-name mime-type) mime-type))
(minor-kw (car (nreverse (split-string mime-str "/"))))
(minor (car (nreverse (split-string minor-kw ":")))))
(intern (concat ":"
(cond ((string= minor "plain") "text")
(t (cl-subseq minor 0 (cl-search "+" minor))))))))
(defun ein:output-area-convert-mime-types (json data)
(let ((known-mimes (cl-remove-if-not
#'identity
(mapcar (lambda (x) (intern-soft (concat ":" x)))
(mailcap-mime-types)))))
(mapc (lambda (x)
(-when-let* ((mime-val (plist-get data x))
(minor-kw (ein:output-area-type x)))
(setq json (plist-put json minor-kw mime-val))))
known-mimes)
json))
(defmacro ein:output-area-case-type (json &rest case-body) (defmacro ein:output-area-case-type (json &rest case-body)
`(progn (aif (plist-get ,json :data) (setq ,json it)) ;; nbformat v4 ??? `(progn (aif (plist-get ,json :data) (setq ,json it))
(seq-some (lambda (type) (seq-some (lambda (type)
(when-let ((value (plist-get ,json type))) (when-let ((value (plist-get ,json type)))
,@case-body ,@case-body
t)) t))
(list :svg :png :jpeg :text :html :latex :javascript)))) (list :image/svg+xml :image/png :image/jpeg :text/plain :text/html :application/latex :application/tex :application/javascript))))
(provide 'ein-output-area) (provide 'ein-output-area)

View file

@ -117,7 +117,7 @@ working."
(lambda (name msg-type content -metadata-not-used-) (lambda (name msg-type content -metadata-not-used-)
(ein:case-equal msg-type (ein:case-equal msg-type
(("stream" "display_data") (("stream" "display_data")
(ein:pytools-finish-tooltip name (ein:json-read-from-string (plist-get content :text)) nil)))) (ein:pytools-finish-tooltip name (ein:json-read-from-string (or (plist-get content :text) (plist-get (plist-get content :data) :text/plain))) nil))))
func))) func)))
(ein:kernel-object-info-request (ein:kernel-object-info-request
kernel func (list :object_info_reply kernel func (list :object_info_reply
@ -178,7 +178,7 @@ pager buffer. You can explicitly specify the object by selecting it."
(ein:log 'debug "object[[%s]] other-window[[%s]]" object other-window) (ein:log 'debug "object[[%s]] other-window[[%s]]" object other-window)
(ein:case-equal msg-type (ein:case-equal msg-type
(("stream" "display_data") (("stream" "display_data")
(aif (or (plist-get content :text) (plist-get content :data)) (aif (or (plist-get content :text) (plist-get (plist-get content :data) :text/plain))
(if (string-match ein:pytools-jump-to-source-not-found-regexp it) (if (string-match ein:pytools-jump-to-source-not-found-regexp it)
(ein:log 'info (ein:log 'info
"Jumping to the source of %s...Not found" object) "Jumping to the source of %s...Not found" object)
@ -231,7 +231,7 @@ is defined."
(destructuring-bind (kernel object callback) packed (destructuring-bind (kernel object callback) packed
(if (or (string= msg-type "stream") (if (or (string= msg-type "stream")
(string= msg-type "display_data")) (string= msg-type "display_data"))
(aif (or (plist-get content :text) (plist-get content :data)) (aif (or (plist-get content :text) (plist-get (plist-get content :data) :text/plain))
(if (string-match ein:pytools-jump-to-source-not-found-regexp it) (if (string-match ein:pytools-jump-to-source-not-found-regexp it)
(ein:log 'info (ein:log 'info
"Source of %s not found" object) "Source of %s not found" object)

View file

@ -37,11 +37,10 @@
:documentation :documentation
"Worksheet without needs for saving.") "Worksheet without needs for saving.")
(defun ein:scratchsheet-new (nbformat get-notebook-name discard-output-p (defun ein:scratchsheet-new (nbformat get-notebook-name kernel events &rest args)
kernel events &rest args)
(apply #'make-instance 'ein:scratchsheet (apply #'make-instance 'ein:scratchsheet
:nbformat nbformat :get-notebook-name get-notebook-name :nbformat nbformat :get-notebook-name get-notebook-name
:discard-output-p discard-output-p :kernel kernel :events events :kernel kernel :events events
args)) args))
(cl-defmethod ein:worksheet--buffer-name ((ws ein:scratchsheet)) (cl-defmethod ein:worksheet--buffer-name ((ws ein:scratchsheet))

View file

@ -293,11 +293,10 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein:
;;; Initialization of object and buffer ;;; Initialization of object and buffer
(defun ein:worksheet-new (nbformat get-notebook-name discard-output-p (defun ein:worksheet-new (nbformat get-notebook-name kernel events &rest args)
kernel events &rest args)
(apply #'make-instance 'ein:worksheet (apply #'make-instance 'ein:worksheet
:nbformat nbformat :get-notebook-name get-notebook-name :nbformat nbformat :get-notebook-name get-notebook-name
:discard-output-p discard-output-p :kernel kernel :events events :kernel kernel :events events
args)) args))
(cl-defmethod ein:worksheet-bind-events ((ws ein:worksheet)) (cl-defmethod ein:worksheet-bind-events ((ws ein:worksheet))
@ -463,21 +462,17 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein:
"Convert worksheet WS into JSON ready alist. "Convert worksheet WS into JSON ready alist.
It sets buffer internally so that caller doesn not have to set It sets buffer internally so that caller doesn not have to set
current buffer." current buffer."
(let* ((discard-output-p (ein:worksheet--discard-output-p ws)) (let* ((cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws)
(cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws)
(mapcar (lambda (c) (mapcar (lambda (c)
(ein:cell-to-json (ein:cell-to-json c))
c (ein:funcall-packed discard-output-p c)))
(ein:worksheet-get-cells ws))))) (ein:worksheet-get-cells ws)))))
`((cells . ,(apply #'vector cells)) `((cells . ,(apply #'vector cells))
,@(ein:aand (ein:worksheet--metadata ws) `((metadata . ,it)))))) ,@(ein:aand (ein:worksheet--metadata ws) `((metadata . ,it))))))
(cl-defmethod ein:worksheet-to-nb4-json ((ws ein:worksheet) wsidx) (cl-defmethod ein:worksheet-to-nb4-json ((ws ein:worksheet) wsidx)
(let* ((discard-output-p (slot-value ws 'discard-output-p)) (let* ((cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws)
(cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws)
(mapcar (lambda (c) (mapcar (lambda (c)
(ein:cell-to-nb4-json (ein:cell-to-nb4-json c wsidx))
c wsidx (ein:funcall-packed discard-output-p c)))
(ein:worksheet-get-cells ws))))) (ein:worksheet-get-cells ws)))))
cells)) cells))
@ -613,7 +608,7 @@ If you really want use this command, you can do something like this
t)) t))
(ein:worksheet--shift-undo-list cell) (ein:worksheet--shift-undo-list cell)
(let ((inhibit-read-only t) (let ((inhibit-read-only t)
(buffer-undo-list t)) ; disable undo recording (buffer-undo-list t))
(apply #'ewoc-delete (apply #'ewoc-delete
(slot-value ws 'ewoc) (slot-value ws 'ewoc)
(ein:cell-all-element cell))) (ein:cell-all-element cell)))
@ -772,13 +767,12 @@ directly."
(when focus (ein:cell-goto new relpos)) (when focus (ein:cell-goto new relpos))
(ein:worksheet--unshift-undo-list new nil cell)))) (ein:worksheet--unshift-undo-list new nil cell))))
(defun ein:worksheet-change-cell-type (ws cell type &optional level focus) (defun ein:worksheet-change-cell-type (ws cell type &optional focus)
"Change the cell type of the current cell. "Change the cell type of the current cell.
Prompt will appear in the minibuffer. Prompt will appear in the minibuffer.
When used in as a Lisp function, TYPE (string) should be chose When used in as a Lisp function, TYPE (string) should be chose
from \"code\", \"markdown\", \"raw\" and \"heading\". LEVEL is from \"code\", \"markdown\", \"raw\" and \"heading\"."
an integer used only when the TYPE is \"heading\"."
(interactive (interactive
(let* ((ws (ein:worksheet--get-ws-or-error)) (let* ((ws (ein:worksheet--get-ws-or-error))
(cell (ein:worksheet-get-current-cell)) (cell (ein:worksheet-get-current-cell))
@ -791,18 +785,12 @@ an integer used only when the TYPE is \"heading\"."
(type (case key (type (case key
(?c "code") (?c "code")
(?m "markdown") (?m "markdown")
(?r "raw") (?r "raw"))))
(t "heading"))) (list ws cell type t)))
(level (when (equal type "heading")
(string-to-number (char-to-string key)))))
(list ws cell type level t)))
(let ((relpos (ein:cell-relative-point cell)) (let ((relpos (ein:cell-relative-point cell))
(new (ein:cell-convert-inplace cell type))) (new (ein:cell-convert-inplace cell type)))
(when (ein:codecell-p new) (when (ein:codecell-p new)
(setf (slot-value new 'kernel) (slot-value ws 'kernel))) (setf (slot-value new 'kernel) (slot-value ws 'kernel)))
(when level
(ein:cell-change-level new level))
(ein:worksheet--unshift-undo-list cell) (ein:worksheet--unshift-undo-list cell)
(when focus (ein:cell-goto new relpos)))) (when focus (ein:cell-goto new relpos))))
@ -819,10 +807,7 @@ argument \(C-u)."
(head (buffer-substring beg pos)) (head (buffer-substring beg pos))
(new (ein:worksheet-insert-cell-above ws (new (ein:worksheet-insert-cell-above ws
(slot-value cell 'cell-type) (slot-value cell 'cell-type)
cell)) cell)))
)
(when (ein:headingcell-p cell)
(ein:cell-change-level new (slot-value cell 'level)))
(undo-boundary) (undo-boundary)
(delete-region beg pos) (delete-region beg pos)
(unless no-trim (unless no-trim
@ -1253,26 +1238,6 @@ function."
(ein:worksheet-get-cells ws)))))) (ein:worksheet-get-cells ws))))))
ws (current-buffer))))) ws (current-buffer)))))
;;; Imenu
(defun ein:worksheet-imenu-create-index ()
"`imenu-create-index-function' for notebook buffer."
;; As Imenu does not provide the way to represent level *and*
;; position, use #'s to do that.
(cl-loop for cell in (when (ein:worksheet-p ein:%worksheet%)
(seq-filter #'ein:headingcell-p
(ein:worksheet-get-cells ein:%worksheet%)))
for sharps = (cl-loop repeat (slot-value cell 'level) collect "#")
for text = (ein:cell-get-text cell)
for name = (ein:join-str "" (append sharps (list " " text)))
collect (cons name (ein:cell-input-pos-min cell))))
(defun ein:worksheet-imenu-setup ()
"Called via notebook mode hooks."
(setq imenu-create-index-function #'ein:worksheet-imenu-create-index))
;;; Workarounds ;;; Workarounds
(defadvice fill-paragraph (around ein:worksheet-fill-paragraph activate) (defadvice fill-paragraph (around ein:worksheet-fill-paragraph activate)

View file

@ -120,8 +120,7 @@
(ein:output-area-case-type (ein:output-area-case-type
json json
(cl-case type (cl-case type
((:svg :png :jpeg) ((:image/svg+xml :image/png :image/jpeg)
(message "got here %s" type)
(let ((file (or explicit-file (ob-ein--inline-image-info value)))) (let ((file (or explicit-file (ob-ein--inline-image-info value))))
(ob-ein--write-base64-image value file) (ob-ein--write-base64-image value file)
(setq result (format "[[file:%s]]" file)))) (setq result (format "[[file:%s]]" file))))

View file

@ -67,10 +67,6 @@ To make OUTPUTS data, use `ein:testing-codecell-pyout-data'."
(defun ein:testing-htmlcell-data (&optional source) (defun ein:testing-htmlcell-data (&optional source)
(ein:testing-textcell-data source "html")) (ein:testing-textcell-data source "html"))
(defun ein:testing-headingcell-data (&optional source level)
(append (ein:testing-textcell-data source "heading")
(list :level (or level 1))))
(provide 'ein-testing-cell) (provide 'ein-testing-cell)
;;; ein-testing-cell.el ends here ;;; ein-testing-cell.el ends here

View file

@ -128,47 +128,47 @@ some input
,regexp-display-data))))) ,regexp-display-data)))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
text ("some output") ((:data (:text "some output")))) text ("some output") ((:data (:text/plain "some output"))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
latex latex
("some output \\\\LaTeX") ("some output \\\\LaTeX")
((:data (:latex "some output \\LaTeX")))) ((:data (:application/latex "some output \\LaTeX"))))
(when (image-type-available-p 'svg) (when (image-type-available-p 'svg)
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
svg svg
("\\.") ("\\.")
((:data (:text "some output text" :svg ein:testing-example-svg))))) ((:data (:text/plain "some output text" :image/svg+xml ein:testing-example-svg)))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
html html
("some output text") ("some output text")
((:data (:text "some output text" :html "<b>not shown</b>")))) ((:data (:text/plain "some output text" :text/html "<b>not shown</b>"))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
javascript javascript
("some output text") ("some output text")
((:data (:text "some output text" :javascript "$.do.something()")))) ((:data (:text/plain "some output text" :application/javascript "$.do.something()"))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
text-two text-two
("first output text" "second output text") ("first output text" "second output text")
((:data (:text "first output text")) (:data (:text "second output text")))) ((:data (:text/plain "first output text")) (:data (:text/plain "second output text"))))
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
text-javascript text-javascript
("first output text" "second output text") ("first output text" "second output text")
((:data (:text "first output text")) ((:data (:text/plain "first output text"))
(:data (:text "second output text" :javascript "$.do.something()")))) (:data (:text/plain "second output text" :application/javascript "$.do.something()"))))
(when (image-type-available-p 'svg) (when (image-type-available-p 'svg)
(eintest:gene-test-cell-insert-output-pyout-and-display-data (eintest:gene-test-cell-insert-output-pyout-and-display-data
text-latex-svg text-latex-svg
("first output text" "second output \\\\LaTeX") ("first output text" "second output \\\\LaTeX")
((:data (:text "first output text")) ((:data (:text/plain "first output text"))
(:data (:latex "second output \\LaTeX")) (:data (:application/latex "second output \\LaTeX"))
(:data (:text "some output text" :svg ein:testing-example-svg))))) (:data (:text/plain "some output text" :image/svg+xml ein:testing-example-svg)))))
;; Insert pyerr ;; Insert pyerr

View file

@ -23,8 +23,7 @@
(input (ein:join-str "\n" '("first input" "second input"))) (input (ein:join-str "\n" '("first input" "second input")))
(output-0 (list :output_type "execute_result" (output-0 (list :output_type "execute_result"
:prompt_number output-prompt-number :prompt_number output-prompt-number
:text (list "first output" :text (list "first output" "second output")))
"second output")))
(data (ein:testing-codecell-data (data (ein:testing-codecell-data
input input-prompt-number (list output-0))) input input-prompt-number (list output-0)))
(cell (eintest:cell-from-json data))) (cell (eintest:cell-from-json data)))
@ -62,21 +61,13 @@
(should (ein:rawcell-p cell)) (should (ein:rawcell-p cell))
(should (equal (oref cell :input) input)))) (should (equal (oref cell :input) input))))
(ert-deftest ein:cell-from-json-heading ()
(let* ((input (ein:join-str "\n" '("first input" "second input")))
(data (list :cell_type "heading" :source input))
(cell (eintest:cell-from-json data)))
(should (ein:headingcell-p cell))
(should (equal (oref cell :input) input))))
;; ein:cell-to-json ;; ein:cell-to-json
(defun eintest:cell-to-json (cell input &optional discard-output) (defun eintest:cell-to-json (cell input)
(mocker-let ((ein:cell-get-text (mocker-let ((ein:cell-get-text
(cell) (cell)
((:input (list cell) :output input)))) ((:input (list cell) :output input))))
(ein:cell-to-json cell discard-output))) (ein:cell-to-json cell)))
(ert-deftest ein:cell-to-json-code () (ert-deftest ein:cell-to-json-code ()
(let* ((input-prompt-number 111) (let* ((input-prompt-number 111)
@ -96,24 +87,6 @@
(should (equal (cdr (assq 'language alist)) "python")) (should (equal (cdr (assq 'language alist)) "python"))
(should (equal (cdr (assq 'collapsed alist)) json-false)))) (should (equal (cdr (assq 'collapsed alist)) json-false))))
(ert-deftest ein:cell-to-json-code-discard-output ()
(let* ((input-prompt-number 111)
(output-prompt-number 222)
(input (ein:join-str "\n" '("first input" "second input")))
(output-0 (list :output_type "execute_result"
:prompt_number output-prompt-number
:text (list "first output"
"second output")))
(data (ein:testing-codecell-data
input input-prompt-number (list output-0)))
(cell (eintest:cell-from-json data))
(alist (eintest:cell-to-json cell input t)))
(should (equal (cdr (assq 'input alist)) "first input\nsecond input"))
(should (equal (cdr (assq 'cell_type alist)) "code"))
(should (equal (cdr (assq 'outputs alist)) []))
(should (equal (cdr (assq 'language alist)) "python"))
(should (equal (cdr (assq 'collapsed alist)) json-false))))
(ert-deftest ein:cell-to-json-text () (ert-deftest ein:cell-to-json-text ()
(let* ((input (ein:join-str "\n" '("first input" "second input"))) (let* ((input (ein:join-str "\n" '("first input" "second input")))
(data (list :cell_type "text" :source input)) (data (list :cell_type "text" :source input))
@ -146,16 +119,6 @@
(should (equal (cdr (assq 'cell_type alist)) "raw")) (should (equal (cdr (assq 'cell_type alist)) "raw"))
(should (equal (cdr (assq 'source alist)) "first input\nsecond input")))) (should (equal (cdr (assq 'source alist)) "first input\nsecond input"))))
(ert-deftest ein:cell-to-json-heading ()
(let* ((input (ein:join-str "\n" '("first input" "second input")))
(data (list :cell_type "heading" :source input))
(cell (eintest:cell-from-json data))
(alist (eintest:cell-to-json cell input)))
(should (equal (cdr (assq 'cell_type alist)) "heading"))
(should (equal (cdr (assq 'source alist)) "first input\nsecond input"))
(should (equal (cdr (assq 'level alist)) 1))))
;;; ein:cell-convert/copy ;;; ein:cell-convert/copy
(ert-deftest ein:cell-convert-code-to-markdown () (ert-deftest ein:cell-convert-code-to-markdown ()
@ -206,7 +169,7 @@
(should (equal (oref new :input) input)))) (should (equal (oref new :input) input))))
(ert-deftest ein:cell-copy-text-types () (ert-deftest ein:cell-copy-text-types ()
(cl-loop for cell-type in '("text" "html" "markdown" "raw" "heading") (cl-loop for cell-type in '("text" "html" "markdown" "raw")
for cell-p = (intern (format "ein:%scell-p" cell-type)) for cell-p = (intern (format "ein:%scell-p" cell-type))
do do
(let* ((input (ein:join-str "\n" '("first input" "second input"))) (let* ((input (ein:join-str "\n" '("first input" "second input")))

View file

@ -86,7 +86,7 @@
(ein:kernel--handle-shell-reply kernel (json-encode packet)))) (ein:kernel--handle-shell-reply kernel (json-encode packet))))
(defun eintest:kernel-fake-stream (kernel msg-id data) (defun eintest:kernel-fake-stream (kernel msg-id data)
(let* ((content (list :data data (let* ((content (list :text data
:name "stdout")) :name "stdout"))
(packet (list :header (list :msg_type "stream") (packet (list :header (list :msg_type "stream")
:parent_header (list :msg_id msg-id) :parent_header (list :msg_id msg-id)
@ -143,16 +143,11 @@ is not found."
(list (ein:testing-codecell-data "import numpy") (list (ein:testing-codecell-data "import numpy")
(ein:testing-markdowncell-data "*markdown* text") (ein:testing-markdowncell-data "*markdown* text")
(ein:testing-rawcell-data "`raw` cell text") (ein:testing-rawcell-data "`raw` cell text")
(ein:testing-htmlcell-data "<b>HTML</b> text") (ein:testing-htmlcell-data "<b>HTML</b> text")))
(ein:testing-headingcell-data "Heading 1" 1)
(ein:testing-headingcell-data "Heading 2" 2)
(ein:testing-headingcell-data "Heading 3" 3)
(ein:testing-headingcell-data "Heading 4" 4)
(ein:testing-headingcell-data "Heading 5" 5)
(ein:testing-headingcell-data "Heading 6" 6)))
(should (ein:$notebook-p ein:%notebook%)) (should (ein:$notebook-p ein:%notebook%))
(should (equal (ein:$notebook-notebook-name ein:%notebook%) ein:testing-notebook-dummy-name)) (should (equal (ein:$notebook-notebook-name ein:%notebook%)
(should (equal (ein:worksheet-ncells ein:%worksheet%) 10)) ein:testing-notebook-dummy-name))
(should (equal (ein:worksheet-ncells ein:%worksheet%) 4))
(let ((cells (ein:worksheet-get-cells ein:%worksheet%))) (let ((cells (ein:worksheet-get-cells ein:%worksheet%)))
(should (ein:codecell-p (nth 0 cells))) (should (ein:codecell-p (nth 0 cells)))
(should (ein:markdowncell-p (nth 1 cells))) (should (ein:markdowncell-p (nth 1 cells)))
@ -161,16 +156,8 @@ is not found."
(should (equal (ein:cell-get-text (nth 0 cells)) "import numpy")) (should (equal (ein:cell-get-text (nth 0 cells)) "import numpy"))
(should (equal (ein:cell-get-text (nth 1 cells)) "*markdown* text")) (should (equal (ein:cell-get-text (nth 1 cells)) "*markdown* text"))
(should (equal (ein:cell-get-text (nth 2 cells)) "`raw` cell text")) (should (equal (ein:cell-get-text (nth 2 cells)) "`raw` cell text"))
(should (equal (ein:cell-get-text (nth 3 cells)) "<b>HTML</b> text")) (should (equal (ein:cell-get-text (nth 3 cells)) "<b>HTML</b> text")))))
(cl-loop for i from 4 to 9
for level from 1
for cell = (nth i cells)
do (should (ein:headingcell-p cell))
do (should (equal (ein:cell-get-text cell)
(format "Heading %s" level)))
do (should (= (oref cell :level) level))))))
;;; Destructor ;;; Destructor
(defvar ein:testing-notebook-del-args-log 'nolog) (defvar ein:testing-notebook-del-args-log 'nolog)
@ -340,7 +327,6 @@ some text
(when (< (ein:$notebook-nbformat ein:%notebook%) 4) (when (< (ein:$notebook-nbformat ein:%notebook%) 4)
;; toggle to heading ;; toggle to heading
(call-interactively #'ein:worksheet-toggle-cell-type) (call-interactively #'ein:worksheet-toggle-cell-type)
(should (ein:headingcell-p (ein:worksheet-get-current-cell)))
(should (looking-back "some text")) (should (looking-back "some text"))
;; toggle to code ;; toggle to code
(call-interactively #'ein:worksheet-toggle-cell-type) (call-interactively #'ein:worksheet-toggle-cell-type)
@ -356,11 +342,11 @@ some text
(should (ein:codecell-p (ein:worksheet-get-current-cell))) (should (ein:codecell-p (ein:worksheet-get-current-cell)))
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel)) (should (slot-boundp (ein:worksheet-get-current-cell) :kernel))
(let ((check (let ((check
(lambda (type &optional level) (lambda (type)
(let ((cell-p (intern (format "ein:%scell-p" type))) (let ((cell-p (intern (format "ein:%scell-p" type)))
(cell (ein:worksheet-get-current-cell))) (cell (ein:worksheet-get-current-cell)))
(ein:worksheet-change-cell-type ein:%worksheet% cell (ein:worksheet-change-cell-type ein:%worksheet% cell
type level t) type t)
(let ((new (ein:worksheet-get-current-cell))) (let ((new (ein:worksheet-get-current-cell)))
(should-not (eq new cell)) (should-not (eq new cell))
(should (funcall cell-p new))) (should (funcall cell-p new)))
@ -368,9 +354,6 @@ some text
;; change type: code (no change) -> markdown -> raw ;; change type: code (no change) -> markdown -> raw
(cl-loop for type in '("code" "markdown" "raw") (cl-loop for type in '("code" "markdown" "raw")
do (funcall check type)) do (funcall check type))
;; change level: 1 to 6
(cl-loop for level from 1 to 6
do (funcall check "heading" level))
;; back to code ;; back to code
(funcall check "code") (funcall check "code")
(should (slot-boundp (ein:worksheet-get-current-cell) :kernel))))) (should (slot-boundp (ein:worksheet-get-current-cell) :kernel)))))

View file

@ -21,7 +21,7 @@
(oset ein:%notification% :tab (ein:testing-notification-tab-mock)) (oset ein:%notification% :tab (ein:testing-notification-tab-mock))
(ein:notification-status-set kernel (ein:notification-status-set kernel
'status_busy.Kernel) 'status_busy.Kernel)
(should (string-prefix-p "IP[y]: Kernel is busy... | /1\\ /2\\ /3\\ [+]" (should (string-prefix-p "IP[y]: Kernel busy... | /1\\ /2\\ /3\\ [+]"
(ein:header-line))))) (ein:header-line)))))
(ert-deftest ein:header-line-notebook-status-busy () (ert-deftest ein:header-line-notebook-status-busy ()
@ -30,7 +30,7 @@
(oset ein:%notification% :tab (ein:testing-notification-tab-mock)) (oset ein:%notification% :tab (ein:testing-notification-tab-mock))
(ein:notification-status-set notebook (ein:notification-status-set notebook
'notebook_saved.Notebook) 'notebook_saved.Notebook)
(should (string-prefix-p "IP[y]: Notebook is saved | /1\\ /2\\ /3\\ [+]" (should (string-prefix-p "IP[y]: Notebook saved | /1\\ /2\\ /3\\ [+]"
(ein:header-line))))) (ein:header-line)))))
(ert-deftest ein:header-line-notebook-complex () (ert-deftest ein:header-line-notebook-complex ()
@ -43,7 +43,7 @@
(ein:notification-status-set notebook (ein:notification-status-set notebook
'notebook_saving.Notebook) 'notebook_saving.Notebook)
(should (string-prefix-p (should (string-prefix-p
(concat "IP[y]: Saving Notebook... | " (concat "IP[y]: Saving notebook... | "
(substitute-command-keys "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km] | ") (substitute-command-keys "Kernel requires restart \\<ein:notebook-mode-map>\\[ein:notebook-restart-session-command-km] | ")
;;"Kernel requires restart C-c C-x C-r | " ;;"Kernel requires restart C-c C-x C-r | "
"/1\\ /2\\ /3\\ [+]") (ein:header-line))))) "/1\\ /2\\ /3\\ [+]") (ein:header-line)))))

View file

@ -7,12 +7,10 @@
(list (ein:testing-codecell-data "code example input") (list (ein:testing-codecell-data "code example input")
(ein:testing-markdowncell-data "markdown example input") (ein:testing-markdowncell-data "markdown example input")
(ein:testing-rawcell-data "raw example input") (ein:testing-rawcell-data "raw example input")
(ein:testing-htmlcell-data "html example input") (ein:testing-htmlcell-data "html example input")))
(ein:testing-headingcell-data "heading example input")))
(defun ein:testing-worksheet-new () (defun ein:testing-worksheet-new ()
(make-instance 'ein:worksheet (make-instance 'ein:worksheet))
:discard-output-p (cons #'ignore nil)))
(defun ein:testing-worksheet-to-json (cells &optional metadata) (defun ein:testing-worksheet-to-json (cells &optional metadata)
(let* ((ws-0 (ein:worksheet-from-json (ein:testing-worksheet-new) (let* ((ws-0 (ein:worksheet-from-json (ein:testing-worksheet-new)