From 702f04064affd7439b22a639a3679c970728e132 Mon Sep 17 00:00:00 2001 From: dickmao Date: Sat, 11 Jan 2020 16:03:54 -0500 Subject: [PATCH] :image/png not :png --- features/support/env.el | 4 +- features/undo.feature | 3 +- lisp/ein-cell-output.el | 78 ---------- lisp/ein-cell.el | 257 +++++---------------------------- lisp/ein-classes.el | 20 +-- lisp/ein-contents-api.el | 6 +- lisp/ein-kernel.el | 199 ++++++++----------------- lisp/ein-notebook.el | 53 +------ lisp/ein-notebooklist.el | 4 +- lisp/ein-output-area.el | 25 +++- lisp/ein-pytools.el | 6 +- lisp/ein-scratchsheet.el | 5 +- lisp/ein-worksheet.el | 59 ++------ lisp/ob-ein.el | 3 +- test/ein-testing-cell.el | 4 - test/test-ein-cell-notebook.el | 22 +-- test/test-ein-cell.el | 45 +----- test/test-ein-notebook.el | 35 ++--- test/test-ein-notification.el | 6 +- test/test-ein-worksheet.el | 6 +- 20 files changed, 184 insertions(+), 656 deletions(-) delete mode 100644 lisp/ein-cell-output.el diff --git a/features/support/env.el b/features/support/env.el index ad4bde4..18089be 100644 --- a/features/support/env.el +++ b/features/support/env.el @@ -37,9 +37,7 @@ (!cons "memory" ecukes-exclude-tags) (!cons "content" ecukes-exclude-tags) (when (eq system-type 'darwin) - (!cons "julia" ecukes-exclude-tags))) - -(unless (fboundp 'libxml-parse-xml-region) + (!cons "julia" ecukes-exclude-tags)) (!cons "svg" ecukes-exclude-tags)) (defvar ein:testing-jupyter-server-root (f-parent (f-dirname load-file-name))) diff --git a/features/undo.feature b/features/undo.feature index 71e0b95..62769f1 100644 --- a/features/undo.feature +++ b/features/undo.feature @@ -220,7 +220,8 @@ Scenario: Undo needs to at least work for reopened notebooks And I press "C-/" 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 Scenario: Toggling between markdown and codecell does not break undo diff --git a/lisp/ein-cell-output.el b/lisp/ein-cell-output.el deleted file mode 100644 index 16038fd..0000000 --- a/lisp/ein-cell-output.el +++ /dev/null @@ -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 . - -;;; 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) diff --git a/lisp/ein-cell.el b/lisp/ein-cell.el index db42966..c42785d 100644 --- a/lisp/ein-cell.el +++ b/lisp/ein-cell.el @@ -113,36 +113,6 @@ Delete current text first, thus effecting a \"refresh\"." "Face for cell output area errors" :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 '((t :inherit header-line)) "Face for cell output prompt" @@ -280,7 +250,6 @@ a number will limit the number of lines in a cell output." (("html") 'ein:htmlcell) (("markdown") 'ein:markdowncell) (("raw") 'ein:rawcell) - (("heading") 'ein:headingcell) (("shared-output") 'ein:shared-output-cell) (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)) 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) (let ((new (ein:cell-from-type type))) ;; 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))) 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)) (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))) 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 (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)) '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)) "Insert input of the CELL in the buffer. 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." '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) "Return the face (symbol) for output area." (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") -(defun ein:cell-output-type (mime-type) - "Investigate why :image/svg+xml to :svg and :text/plain to :text" +(defun ein:cell-extract-image-format (mime-type) + "From :image/svg+xml to \"svg\"." (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)))))))) + (cl-subseq minor 0 (cl-search "+" minor)))) (defun ein:cell-append-mime-type (json) (ein:output-area-case-type json (cl-case type - ((:html) + ((:text/html) (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 (base64-decode-string value) (error value)) - (intern (car (nreverse - (split-string (symbol-name type) ":")))) + (intern-soft (ein:cell-extract-image-format type)) t))) (if ein:output-area-inlined-images (ein:insert-image image) @@ -999,124 +935,38 @@ Called from ewoc pretty printer via `ein:cell-insert-output'." text) (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." `((input . ,(ein:cell-get-text cell)) (cell_type . "code") ,@(aif (ein:oref-safe cell 'input-prompt-number) `((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")) (collapsed . ,(if (slot-value cell 'collapsed) t json-false)))) -(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-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)))) - )))) +(cl-defmethod ein:cell-to-nb4-json ((cell ein:codecell) wsidx) + (let ((execute-count (aif (ein:oref-safe cell 'input-prompt-number) + (and (numberp it) it))) + (metadata (slot-value cell 'metadata))) `((source . ,(ein:cell-get-text cell)) (cell_type . "code") - ,@(if execute-count - `((execution_count . ,execute-count)) - `((execution_count))) - (outputs . ,(apply #'vector (or renamed-outputs outputs))) - (metadata . ,metadata)))) + ,@(when execute-count + `((execution_count . ,execute-count))) + (outputs . ,(apply #'vector (slot-value cell 'outputs))) + (metadata . ,(plist-put metadata :collapsed (if (slot-value cell 'collapsed) t + 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)) (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))) - (setq metadata (plist-put metadata :ein.tags (format "worksheet-%s" wsidx))) `((cell_type . ,(slot-value cell 'cell-type)) (source . ,(ein:cell-get-text cell)) - (metadata . ,metadata)))) - -(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)))))) + (metadata . ,(plist-put metadata :collapsed json-false))))) (cl-defmethod ein:cell-next ((cell ein:basecell)) "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))) (ein:events-trigger events 'set_next_input.Worksheet (list :cell cell :text text)) - (ein:events-trigger events 'maybe_reset_undo.Worksheet cell) - )) + (ein:events-trigger events 'maybe_reset_undo.Worksheet cell))) - -;;; Output area - -;; These function should go to ein-output-area.el. But as cell and -;; EWOC is connected in complicated way, I will leave them in -;; ein-cell.el. - -(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) - (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-output ((cell ein:codecell) msg-type content meta) + ;; (ein:output-area-convert-mime-types content (plist-get content :data)) + (ein:cell-append-output cell + (plist-put + (plist-put content :output_type msg-type) + :metadata meta)) + ;; (setf (slot-value cell 'dirty) t) + (ein:events-trigger (slot-value cell 'events) 'maybe_reset_undo.Worksheet cell)) (cl-defmethod ein:cell--handle-clear-output ((cell ein:codecell) content _metadata) @@ -1230,18 +1048,15 @@ Called from ewoc pretty printer via `ein:cell-insert-output'." ;;; 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." - (cl-loop for out in (slot-value cell 'outputs) - when (or (plist-member out :svg) - (plist-member out :image/svg+xml) - (plist-member out :png) - (plist-member out :image/png) - (plist-member out :jpeg) - (plist-member out :image/jpeg)) - return t)) + (seq-some (lambda (out) + (or (plist-member out :image/svg+xml) + (plist-member out :image/png) + (plist-member out :image/jpeg))) + (slot-value cell 'outputs))) -(cl-defmethod ein:cell-has-image-ouput-p ((cell ein:textcell)) +(cl-defmethod ein:cell-has-image-output-p ((cell ein:textcell)) nil) (cl-defmethod ein:cell-get-tb-data ((cell ein:codecell)) diff --git a/lisp/ein-classes.el b/lisp/ein-classes.el index 83fdfc6..9660bfe 100644 --- a/lisp/ein-classes.el +++ b/lisp/ein-classes.el @@ -166,10 +166,6 @@ ((nbformat :initarg :nbformat :type integer) (get-notebook-name :initarg :get-notebook-name :type cons :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 :accessor ein:worksheet--saved-cells :documentation @@ -251,7 +247,7 @@ (input :initarg :input :type string :documentation "Place to hold data until it is rendered via `ewoc'.") (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) (cell-id :initarg :cell-id :initform (ein:utils-uuid) :type string :accessor ein:cell-id)) @@ -290,11 +286,6 @@ auto-execution mode flag in the connected buffer is `t'."))) (defclass ein:rawcell (ein:textcell) ((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 (defclass ein:notification-status () @@ -331,9 +322,9 @@ auto-execution mode flag in the connected buffer is `t'."))) (ein:notification-status "NotebookStatus" :s2m - '((notebook_saving.Notebook . "Saving Notebook...") - (notebook_saved.Notebook . "Notebook is saved") - (notebook_save_failed.Notebook . "Failed to save Notebook!"))) + '((notebook_saving.Notebook . "Saving notebook...") + (notebook_saved.Notebook . "Notebook saved") + (notebook_save_failed.Notebook . "Failed saving notebook!"))) :type ein:notification-status) (kernel :initarg :kernel @@ -342,7 +333,7 @@ auto-execution mode flag in the connected buffer is `t'."))) "KernelStatus" :s2m '((status_idle.Kernel . nil) - (status_busy.Kernel . "Kernel is busy...") + (status_busy.Kernel . "Kernel busy...") (status_restarting.Kernel . "Kernel restarting...") (status_restarted.Kernel . "Kernel restarted") (status_dead.Kernel . "Kernel requires restart \\\\[ein:notebook-restart-session-command-km]") @@ -352,7 +343,6 @@ auto-execution mode flag in the connected buffer is `t'."))) :type ein:notification-status)) "Notification widget for Notebook.") - ;;; Events (defclass ein:events () diff --git a/lisp/ein-contents-api.el b/lisp/ein-contents-api.el index 325a27e..b9086cf 100644 --- a/lisp/ein-contents-api.el +++ b/lisp/ein-contents-api.el @@ -296,9 +296,11 @@ global setting. For global setting and more information, see :type "PUT" :headers '(("Content-Type" . "application/json")) :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) - :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))) (cl-defun ein:content-save-success (callback cbargs &key status response &allow-other-keys) diff --git a/lisp/ein-kernel.el b/lisp/ein-kernel.el index 43d9b2c..ad0c583 100644 --- a/lisp/ein-kernel.el +++ b/lisp/ein-kernel.el @@ -211,7 +211,7 @@ CALLBACK of arity 1, the kernel. (let ((session-id (plist-get data :id))) (if (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" id session-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.")) (defun ein:kernel-run-after-start-hook (kernel) - (ein:log 'debug "EIN:KERNEL-RUN-AFTER-START-HOOK") (mapc #'ein:funcall-packed (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: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) "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)) "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 :output OUTPUT-CALLBACK :clear_output CLEAR-OUTPUT-CALLBACK :set_next_input SET-NEXT-INPUT) -Right hand sides ending -CALLBACK above must cons a FUNCTION and its -`packed' ARGUMENT which is a sublist of args: +Right hand sides ending -CALLBACK above are of the form (FUNCTION ARG1 ... ARGN). +(Hindsight: this was all much better implemented using `apply-partially') - (FUNCTION . ARGUMENT) - -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' +Return randomly generated MSG-ID tag uniquely identifying expectation of a kernel response. " - ;; 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.") (let* ((content (list :code code @@ -452,7 +397,7 @@ Sample implementations :stop_on_error (or stop-on-error json-false))) (msg (ein:kernel--get-msg kernel "execute_request" content)) (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) (ein:websocket-send-shell-channel kernel msg) (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) 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) (when (ein:kernel-live-p 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))) (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))) (defun ein:kernel--handle-stdin-reply (kernel packet) (setf (ein:$kernel-stdin-activep kernel) t) - (destructuring-bind + (cl-destructuring-bind (&key header parent_header metadata content &allow-other-keys) (ein:json-read-from-string packet) (let ((msg-type (plist-get header :msg_type)) (msg-id (plist-get header :msg_id)) (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) (cond ((string-equal msg-type "input_request") (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) (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) (cl-loop with events = (ein:$kernel-events kernel) for p in payload - for text = (or (plist-get p :text) - (plist-get (plist-get p :data) - :text/plain)) + for text = (or (plist-get p :text) (plist-get (plist-get p :data) :text/plain)) for source = (plist-get p :source) if (member source '("IPython.kernel.zmq.page.page" "IPython.zmq.page.page" @@ -740,39 +633,63 @@ We need this to have proper behavior for the 'Stop' command in the ein:notebookl do (let ((cb (plist-get callbacks :set_next_input))) (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) (if (ein:$kernel-stdin-activep kernel) (ein:ipdb--handle-iopub-reply kernel packet) - (destructuring-bind + (cl-destructuring-bind (&key content metadata parent_header 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)) (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) - (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 - (("stream" "display_data" "pyout" "pyerr" "error" "execute_result") - (aif (plist-get callbacks :output) - (ein:funcall-packed it msg-type content metadata))) - (("status") - (ein:case-equal (plist-get content :execution_state) - (("busy") - (ein:events-trigger events 'status_busy.Kernel)) - (("idle") - (ein:events-trigger events 'status_idle.Kernel)) - (("dead") - (ein:kernel-disconnect kernel)))) - (("data_pub") - (ein:log 'verbose (format "Received data_pub message w/content %s" packet))) - (("clear_output") - (aif (plist-get callbacks :clear_output) - (ein:funcall-packed it content metadata))))))))) - -;;; Utility functions + (ein:case-equal msg-type + (("stream" "display_data" "pyout" "pyerr" "error" "execute_result") + (aif (plist-get callbacks :output) + (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") + (ein:case-equal (plist-get content :execution_state) + (("busy") + (ein:events-trigger events 'status_busy.Kernel)) + (("idle") + (ein:events-trigger events 'status_idle.Kernel)) + (("dead") + (ein:kernel-disconnect kernel)))) + (("data_pub") + (ein:log 'verbose "ein:kernel--handle-iopub-reply: data_pub %S" packet)) + (("clear_output") + (aif (plist-get callbacks :clear_output) + (ein:funcall-packed it content metadata) + (ein:log 'info (concat "ein:kernel--handle-iopub-reply: " + "No :clear_output callback for msg_id=%s") + msg-id)))))))) (defun ein:kernel-filename-to-python (kernel filename) "See: `ein:filename-to-python'." @@ -792,7 +709,6 @@ Used in `ein:pytools-finish-tooltip', etc." (defun ein:kernel-construct-help-string (content) "Construct help string from CONTENT of ``:object_info_reply``. Used in `ein:pytools-finish-tooltip', etc." - (ein:log 'debug "KERNEL-CONSTRUCT-HELP-STRING") (let* ((defstring (ein:aand (ein:kernel-construct-defstring content) (ansi-color-apply it) @@ -807,7 +723,6 @@ Used in `ein:pytools-finish-tooltip', etc." (help (ein:aand (delete nil (list defstring docstring)) (ein:join-str "\n" it)))) - (ein:log 'debug "KERNEL-CONSTRUCT-HELP-STRING: help=%s" help) help)) (defun ein:kernel-request-stream (kernel code func &optional args) diff --git a/lisp/ein-notebook.el b/lisp/ein-notebook.el index be1215d..85b23c2 100644 --- a/lisp/ein-notebook.el +++ b/lisp/ein-notebook.el @@ -44,7 +44,6 @@ (require 'ein-kernel) (require 'ein-kernelinfo) (require 'ein-cell) -(require 'ein-cell-output) (require 'ein-worksheet) (require 'ein-iexec) (require 'ein-scratchsheet) @@ -65,47 +64,19 @@ ;;; 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") -(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-create-checkpoint-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) - (ein:cell-has-image-ouput-p cell)) +(defvar *ein:notebook--pending-query* (make-hash-table :test 'equal) + "A map: (URL-OR-PORT . PATH) => t/nil") -(defun ein:notebook-discard-output-p (notebook cell) - "Return non-`nil' if the output must be discarded, otherwise save." - (case ein:notebook-discard-output-on-save - (no nil) - (yes t) - (t (funcall ein:notebook-discard-output-on-save notebook cell)))) +(defun ein:notebook-cell-has-image-output-p (_ignore cell) + (ein:cell-has-image-output-p cell)) ;; As opening/saving notebook treats possibly huge data, define these ;; timeouts separately: @@ -548,9 +519,6 @@ This is equivalent to do ``C-c`` in the console program." (funcall func (ein:$notebook-nbformat notebook) (ein:notebook-name-getter notebook) - (cons (lambda (notebook cell) - (ein:notebook-discard-output-p notebook cell)) - notebook) (ein:$notebook-kernel 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 &allow-other-keys) (if (eq symbol-status 'user-cancel) - (ein:log 'info "Cancel saving notebook.") - (ein:log 'info "Failed to save notebook!") + (ein:log 'info "Cancelled save.") + (ein:log 'warn "Failed saving notebook!") (ein:events-trigger (ein:$notebook-events notebook) 'notebook_save_failed.Notebook))) @@ -1437,19 +1405,12 @@ watch the fireworks!" (aif ein:anything-kernel-history-search-key (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. - (ein:worksheet-imenu-setup))) + )) ;; To avoid MuMaMo to discard `ein:notebook-mode', make it ;; permanent local. (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) "Open current notebook in web browser. When the prefix argument (``C-u``) is given, print page is opened. diff --git a/lisp/ein-notebooklist.el b/lisp/ein-notebooklist.el index 8f0f7ac..f61936e 100644 --- a/lisp/ein-notebooklist.el +++ b/lisp/ein-notebooklist.el @@ -752,8 +752,8 @@ and the url-or-port argument of ein:notebooklist-open*." (domain (url-host parsed-url)) (securep (string-match "^wss://" url-or-port))) (cl-loop for (name content) on cookie-plist by (function cddr) - for line = (mapconcat #'identity (list domain "FALSE" (car (url-path-and-query parsed-url)) (if securep "TRUE" "FALSE") "0" (symbol-name name) (concat content "\n")) "\t") - do (write-region line nil (request--curl-cookie-jar) 'append)))) + for line = (mapconcat #'identity (list domain "FALSE" (car (url-path-and-query parsed-url)) (if securep "TRUE" "FALSE") "0" (symbol-name name) (concat content "\n")) "\t") + do (write-region line nil (request--curl-cookie-jar) 'append)))) (let ((token (ein:notebooklist-token-or-password url-or-port))) (cond ((null token) ;; don't know (ein:notebooklist-login--iteration url-or-port callback nil nil -1 nil)) diff --git a/lisp/ein-output-area.el b/lisp/ein-output-area.el index 9529e3a..72e1842 100644 --- a/lisp/ein-output-area.el +++ b/lisp/ein-output-area.el @@ -125,13 +125,34 @@ Usage:: (ein:xml-replace-attributes dom 'a 'href 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) - `(progn (aif (plist-get ,json :data) (setq ,json it)) ;; nbformat v4 ??? + `(progn (aif (plist-get ,json :data) (setq ,json it)) (seq-some (lambda (type) (when-let ((value (plist-get ,json type))) ,@case-body 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) diff --git a/lisp/ein-pytools.el b/lisp/ein-pytools.el index b2fd3f0..2dab141 100644 --- a/lisp/ein-pytools.el +++ b/lisp/ein-pytools.el @@ -117,7 +117,7 @@ working." (lambda (name msg-type content -metadata-not-used-) (ein:case-equal msg-type (("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))) (ein:kernel-object-info-request 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:case-equal msg-type (("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) (ein:log 'info "Jumping to the source of %s...Not found" object) @@ -231,7 +231,7 @@ is defined." (destructuring-bind (kernel object callback) packed (if (or (string= msg-type "stream") (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) (ein:log 'info "Source of %s not found" object) diff --git a/lisp/ein-scratchsheet.el b/lisp/ein-scratchsheet.el index c0225f2..d094623 100644 --- a/lisp/ein-scratchsheet.el +++ b/lisp/ein-scratchsheet.el @@ -37,11 +37,10 @@ :documentation "Worksheet without needs for saving.") -(defun ein:scratchsheet-new (nbformat get-notebook-name discard-output-p - kernel events &rest args) +(defun ein:scratchsheet-new (nbformat get-notebook-name kernel events &rest args) (apply #'make-instance 'ein:scratchsheet :nbformat nbformat :get-notebook-name get-notebook-name - :discard-output-p discard-output-p :kernel kernel :events events + :kernel kernel :events events args)) (cl-defmethod ein:worksheet--buffer-name ((ws ein:scratchsheet)) diff --git a/lisp/ein-worksheet.el b/lisp/ein-worksheet.el index 94dc02d..9b23cdd 100644 --- a/lisp/ein-worksheet.el +++ b/lisp/ein-worksheet.el @@ -293,11 +293,10 @@ Normalize `buffer-undo-list' by removing extraneous details, and update the ein: ;;; Initialization of object and buffer -(defun ein:worksheet-new (nbformat get-notebook-name discard-output-p - kernel events &rest args) +(defun ein:worksheet-new (nbformat get-notebook-name kernel events &rest args) (apply #'make-instance 'ein:worksheet :nbformat nbformat :get-notebook-name get-notebook-name - :discard-output-p discard-output-p :kernel kernel :events events + :kernel kernel :events events args)) (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. It sets buffer internally so that caller doesn not have to set current buffer." - (let* ((discard-output-p (ein:worksheet--discard-output-p ws)) - (cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws) + (let* ((cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws) (mapcar (lambda (c) - (ein:cell-to-json - c (ein:funcall-packed discard-output-p c))) + (ein:cell-to-json c)) (ein:worksheet-get-cells ws))))) `((cells . ,(apply #'vector cells)) ,@(ein:aand (ein:worksheet--metadata ws) `((metadata . ,it)))))) (cl-defmethod ein:worksheet-to-nb4-json ((ws ein:worksheet) wsidx) - (let* ((discard-output-p (slot-value ws 'discard-output-p)) - (cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws) + (let* ((cells (ein:with-possibly-killed-buffer (ein:worksheet-buffer ws) (mapcar (lambda (c) - (ein:cell-to-nb4-json - c wsidx (ein:funcall-packed discard-output-p c))) + (ein:cell-to-nb4-json c wsidx)) (ein:worksheet-get-cells ws))))) cells)) @@ -613,7 +608,7 @@ If you really want use this command, you can do something like this t)) (ein:worksheet--shift-undo-list cell) (let ((inhibit-read-only t) - (buffer-undo-list t)) ; disable undo recording + (buffer-undo-list t)) (apply #'ewoc-delete (slot-value ws 'ewoc) (ein:cell-all-element cell))) @@ -772,13 +767,12 @@ directly." (when focus (ein:cell-goto new relpos)) (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. Prompt will appear in the minibuffer. When used in as a Lisp function, TYPE (string) should be chose -from \"code\", \"markdown\", \"raw\" and \"heading\". LEVEL is -an integer used only when the TYPE is \"heading\"." +from \"code\", \"markdown\", \"raw\" and \"heading\"." (interactive (let* ((ws (ein:worksheet--get-ws-or-error)) (cell (ein:worksheet-get-current-cell)) @@ -791,18 +785,12 @@ an integer used only when the TYPE is \"heading\"." (type (case key (?c "code") (?m "markdown") - (?r "raw") - (t "heading"))) - (level (when (equal type "heading") - (string-to-number (char-to-string key))))) - (list ws cell type level t))) - + (?r "raw")))) + (list ws cell type t))) (let ((relpos (ein:cell-relative-point cell)) (new (ein:cell-convert-inplace cell type))) (when (ein:codecell-p new) (setf (slot-value new 'kernel) (slot-value ws 'kernel))) - (when level - (ein:cell-change-level new level)) (ein:worksheet--unshift-undo-list cell) (when focus (ein:cell-goto new relpos)))) @@ -819,10 +807,7 @@ argument \(C-u)." (head (buffer-substring beg pos)) (new (ein:worksheet-insert-cell-above ws (slot-value cell 'cell-type) - cell)) - ) - (when (ein:headingcell-p cell) - (ein:cell-change-level new (slot-value cell 'level))) + cell))) (undo-boundary) (delete-region beg pos) (unless no-trim @@ -1253,26 +1238,6 @@ function." (ein:worksheet-get-cells ws)))))) 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 (defadvice fill-paragraph (around ein:worksheet-fill-paragraph activate) diff --git a/lisp/ob-ein.el b/lisp/ob-ein.el index 76191fd..7587308 100644 --- a/lisp/ob-ein.el +++ b/lisp/ob-ein.el @@ -120,8 +120,7 @@ (ein:output-area-case-type json (cl-case type - ((:svg :png :jpeg) - (message "got here %s" type) + ((:image/svg+xml :image/png :image/jpeg) (let ((file (or explicit-file (ob-ein--inline-image-info value)))) (ob-ein--write-base64-image value file) (setq result (format "[[file:%s]]" file)))) diff --git a/test/ein-testing-cell.el b/test/ein-testing-cell.el index 9194c8c..fa2465c 100644 --- a/test/ein-testing-cell.el +++ b/test/ein-testing-cell.el @@ -67,10 +67,6 @@ To make OUTPUTS data, use `ein:testing-codecell-pyout-data'." (defun ein:testing-htmlcell-data (&optional source) (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) ;;; ein-testing-cell.el ends here diff --git a/test/test-ein-cell-notebook.el b/test/test-ein-cell-notebook.el index 5546ea1..1147af4 100644 --- a/test/test-ein-cell-notebook.el +++ b/test/test-ein-cell-notebook.el @@ -128,47 +128,47 @@ some input ,regexp-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 latex ("some output \\\\LaTeX") - ((:data (:latex "some output \\LaTeX")))) + ((:data (:application/latex "some output \\LaTeX")))) (when (image-type-available-p 'svg) (eintest:gene-test-cell-insert-output-pyout-and-display-data 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 html ("some output text") - ((:data (:text "some output text" :html "not shown")))) + ((:data (:text/plain "some output text" :text/html "not shown")))) (eintest:gene-test-cell-insert-output-pyout-and-display-data javascript ("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 text-two ("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 text-javascript ("first output text" "second output text") - ((:data (:text "first output text")) - (:data (:text "second output text" :javascript "$.do.something()")))) + ((:data (:text/plain "first output text")) + (:data (:text/plain "second output text" :application/javascript "$.do.something()")))) (when (image-type-available-p 'svg) (eintest:gene-test-cell-insert-output-pyout-and-display-data text-latex-svg ("first output text" "second output \\\\LaTeX") - ((:data (:text "first output text")) - (:data (:latex "second output \\LaTeX")) - (:data (:text "some output text" :svg ein:testing-example-svg))))) + ((:data (:text/plain "first output text")) + (:data (:application/latex "second output \\LaTeX")) + (:data (:text/plain "some output text" :image/svg+xml ein:testing-example-svg))))) ;; Insert pyerr diff --git a/test/test-ein-cell.el b/test/test-ein-cell.el index f58fa6e..7392420 100644 --- a/test/test-ein-cell.el +++ b/test/test-ein-cell.el @@ -23,8 +23,7 @@ (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"))) + :text (list "first output" "second output"))) (data (ein:testing-codecell-data input input-prompt-number (list output-0))) (cell (eintest:cell-from-json data))) @@ -62,21 +61,13 @@ (should (ein:rawcell-p cell)) (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 -(defun eintest:cell-to-json (cell input &optional discard-output) +(defun eintest:cell-to-json (cell input) (mocker-let ((ein:cell-get-text (cell) ((:input (list cell) :output input)))) - (ein:cell-to-json cell discard-output))) + (ein:cell-to-json cell))) (ert-deftest ein:cell-to-json-code () (let* ((input-prompt-number 111) @@ -96,24 +87,6 @@ (should (equal (cdr (assq 'language alist)) "python")) (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 () (let* ((input (ein:join-str "\n" '("first input" "second input"))) (data (list :cell_type "text" :source input)) @@ -146,16 +119,6 @@ (should (equal (cdr (assq 'cell_type alist)) "raw")) (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 (ert-deftest ein:cell-convert-code-to-markdown () @@ -206,7 +169,7 @@ (should (equal (oref new :input) input)))) (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)) do (let* ((input (ein:join-str "\n" '("first input" "second input"))) diff --git a/test/test-ein-notebook.el b/test/test-ein-notebook.el index 1bdc421..f4364fe 100644 --- a/test/test-ein-notebook.el +++ b/test/test-ein-notebook.el @@ -86,7 +86,7 @@ (ein:kernel--handle-shell-reply kernel (json-encode packet)))) (defun eintest:kernel-fake-stream (kernel msg-id data) - (let* ((content (list :data data + (let* ((content (list :text data :name "stdout")) (packet (list :header (list :msg_type "stream") :parent_header (list :msg_id msg-id) @@ -143,16 +143,11 @@ is not found." (list (ein:testing-codecell-data "import numpy") (ein:testing-markdowncell-data "*markdown* text") (ein:testing-rawcell-data "`raw` cell text") - (ein:testing-htmlcell-data "HTML 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))) + (ein:testing-htmlcell-data "HTML text"))) (should (ein:$notebook-p ein:%notebook%)) - (should (equal (ein:$notebook-notebook-name ein:%notebook%) ein:testing-notebook-dummy-name)) - (should (equal (ein:worksheet-ncells ein:%worksheet%) 10)) + (should (equal (ein:$notebook-notebook-name ein:%notebook%) + ein:testing-notebook-dummy-name)) + (should (equal (ein:worksheet-ncells ein:%worksheet%) 4)) (let ((cells (ein:worksheet-get-cells ein:%worksheet%))) (should (ein:codecell-p (nth 0 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 1 cells)) "*markdown* text")) (should (equal (ein:cell-get-text (nth 2 cells)) "`raw` cell text")) - (should (equal (ein:cell-get-text (nth 3 cells)) "HTML 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)))))) + (should (equal (ein:cell-get-text (nth 3 cells)) "HTML text"))))) - ;;; Destructor (defvar ein:testing-notebook-del-args-log 'nolog) @@ -340,7 +327,6 @@ some text (when (< (ein:$notebook-nbformat ein:%notebook%) 4) ;; toggle to heading (call-interactively #'ein:worksheet-toggle-cell-type) - (should (ein:headingcell-p (ein:worksheet-get-current-cell))) (should (looking-back "some text")) ;; toggle to code (call-interactively #'ein:worksheet-toggle-cell-type) @@ -356,21 +342,18 @@ some text (should (ein:codecell-p (ein:worksheet-get-current-cell))) (should (slot-boundp (ein:worksheet-get-current-cell) :kernel)) (let ((check - (lambda (type &optional level) + (lambda (type) (let ((cell-p (intern (format "ein:%scell-p" type))) (cell (ein:worksheet-get-current-cell))) (ein:worksheet-change-cell-type ein:%worksheet% cell - type level t) + type t) (let ((new (ein:worksheet-get-current-cell))) (should-not (eq new cell)) (should (funcall cell-p new))) (should (looking-back "some text")))))) ;; change type: code (no change) -> markdown -> raw (cl-loop for type in '("code" "markdown" "raw") - do (funcall check type)) - ;; change level: 1 to 6 - (cl-loop for level from 1 to 6 - do (funcall check "heading" level)) + do (funcall check type)) ;; back to code (funcall check "code") (should (slot-boundp (ein:worksheet-get-current-cell) :kernel))))) diff --git a/test/test-ein-notification.el b/test/test-ein-notification.el index 79218f8..837b1b7 100644 --- a/test/test-ein-notification.el +++ b/test/test-ein-notification.el @@ -21,7 +21,7 @@ (oset ein:%notification% :tab (ein:testing-notification-tab-mock)) (ein:notification-status-set 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))))) (ert-deftest ein:header-line-notebook-status-busy () @@ -30,7 +30,7 @@ (oset ein:%notification% :tab (ein:testing-notification-tab-mock)) (ein:notification-status-set 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))))) (ert-deftest ein:header-line-notebook-complex () @@ -43,7 +43,7 @@ (ein:notification-status-set notebook 'notebook_saving.Notebook) (should (string-prefix-p - (concat "IP[y]: Saving Notebook... | " + (concat "IP[y]: Saving notebook... | " (substitute-command-keys "Kernel requires restart \\\\[ein:notebook-restart-session-command-km] | ") ;;"Kernel requires restart C-c C-x C-r | " "/1\\ /2\\ /3\\ [+]") (ein:header-line))))) diff --git a/test/test-ein-worksheet.el b/test/test-ein-worksheet.el index 5065cbe..c501dfc 100644 --- a/test/test-ein-worksheet.el +++ b/test/test-ein-worksheet.el @@ -7,12 +7,10 @@ (list (ein:testing-codecell-data "code example input") (ein:testing-markdowncell-data "markdown example input") (ein:testing-rawcell-data "raw example input") - (ein:testing-htmlcell-data "html example input") - (ein:testing-headingcell-data "heading example input"))) + (ein:testing-htmlcell-data "html example input"))) (defun ein:testing-worksheet-new () - (make-instance 'ein:worksheet - :discard-output-p (cons #'ignore nil))) + (make-instance 'ein:worksheet)) (defun ein:testing-worksheet-to-json (cells &optional metadata) (let* ((ws-0 (ein:worksheet-from-json (ein:testing-worksheet-new)