From b950bd3a4ca3195cd36b6143364cc75d82d24a08 Mon Sep 17 00:00:00 2001 From: dickmao Date: Sat, 27 Oct 2018 17:52:17 -0400 Subject: [PATCH] Fix "Kernel is busy..." bug One line change to fix header not updating after cell execution (keeps saying "Kernel is busy"). The bug does not manifest when running with `ein:debug` true since EMACS's display loop updates more frequently with debug messages. In tracking this bug, noticed eldoc support isn't quite there. `__import__('ein').print_object_info_for(%s)` appears in `ein-completer` and `ein-pytools`, and is invalid python syntax afaict. Took a few steps to make it whole, but incomplete. --- Makefile | 3 +- features/undo.feature | 1 - lisp/ein-cell-edit.el | 3 -- lisp/ein-classes.el | 2 +- lisp/ein-completer.el | 38 +++++++++++-------------- lisp/ein-connect.el | 2 +- lisp/ein-kernel.el | 61 +++++++++++++++++++--------------------- lisp/ein-notebook.el | 27 ++++++++++++++---- lisp/ein-notification.el | 3 +- 9 files changed, 71 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index 52a83a8..88dfba3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,3 @@ -EMACS ?= $(shell which emacs) SRC=$(shell cask files) ELCFILES = $(SRC:.el=.elc) @@ -23,7 +22,7 @@ env-ipy.%: .PHONY: test-compile test-compile: clean autoloads - ! ( cask build 2>&1 | awk '{if (/^ /) { gsub(/^ +/, " ", $$0); printf "%s", $$0 } else { printf "\n%s", $$0 }}' | egrep "not known|Error|free variable|error for|Use of gv-ref" ) + ! ( cask build 2>&1 | awk '{if (/^ /) { gsub(/^ +/, " ", $$0); printf "%s", $$0 } else { printf "\n%s", $$0 }}' | egrep -a "not known|Error|free variable|error for|Use of gv-ref" ) cask clean-elc .PHONY: quick diff --git a/features/undo.feature b/features/undo.feature index 39646b2..7804169 100644 --- a/features/undo.feature +++ b/features/undo.feature @@ -173,7 +173,6 @@ Scenario: Undo needs to at least work for reopened notebooks Given I start the server configured "\n" Given I enable "ein:worksheet-enable-undo" Given old notebook "undo.ipynb" - When I press "C-" And I type "howdy" And I press "RET" And I press "C-" diff --git a/lisp/ein-cell-edit.el b/lisp/ein-cell-edit.el index 4d75233..fcf6fe2 100644 --- a/lisp/ein-cell-edit.el +++ b/lisp/ein-cell-edit.el @@ -25,9 +25,6 @@ ;;; Code: (require 'ein-cell) -(require 'ess-r-mode nil t) -(require 'org-src nil t) -(require 'markdown-mode nil t) (autoload 'markdown-mode "markdown-mode") (autoload 'R-mode "ess-r-mode") diff --git a/lisp/ein-classes.el b/lisp/ein-classes.el index 0a2af32..5fb2b08 100644 --- a/lisp/ein-classes.el +++ b/lisp/ein-classes.el @@ -348,7 +348,7 @@ auto-execution mode flag in the connected buffer is `t'."))) ((status :initarg :status :initform nil) (message :initarg :message :initform nil) (s2m :initarg :s2m)) - "Hold status and it's string representation (message).") + "Hold status and its string representation (message).") (defclass ein:notification-tab () ((get-list :initarg :get-list :type function) diff --git a/lisp/ein-completer.el b/lisp/ein-completer.el index 4ef69fe..aab4fe5 100644 --- a/lisp/ein-completer.el +++ b/lisp/ein-completer.el @@ -34,6 +34,7 @@ (require 'ein-log) (require 'ein-subpackages) (require 'ein-kernel) +(require 'dash) (defun ein:completer-choose () (require 'ein-ac) @@ -130,23 +131,19 @@ notebook buffers and connected buffers." (defvar *ein:oinfo-cache* (make-hash-table :test #'equal)) (defun ein:completions--get-oinfo (obj) - (let ((d (deferred:new #'identity)) - (kernel (ein:get-kernel))) + (lexical-let ((d (deferred:new #'identity)) + (kernel (ein:get-kernel))) (if (ein:kernel-live-p kernel) (ein:kernel-execute kernel (format "__import__('ein').print_object_info_for(%s)" obj) (list - :output (cons (lambda (d &rest args) (deferred:callback-post d args)) - d))) + :output `(,(lambda (d &rest args) (deferred:callback-post d args)) . ,d))) (deferred:callback-post d (list nil nil))) d)) -(defun ein:clear-oinfo-cache () - (clrhash *ein:oinfo-cache*)) - (defun ein:completions--build-oinfo-cache (objs) - (dolist (o objs) + (dolist (o (-non-nil objs)) (deferred:$ (deferred:next (lambda () @@ -156,26 +153,23 @@ notebook buffers and connected buffers." (ein:completions--prepare-oinfo output o)))))) (defun ein:completions--prepare-oinfo (output obj) - (condition-case _ + (condition-case err (destructuring-bind (msg-type content _) output (ein:case-equal msg-type - (("stream" "display_data") - (let* ((oinfo (ein:json-read-from-string (plist-get content :text)))) - (setf (gethash obj *ein:oinfo-cache*) oinfo))))) - (error (setf (gethash obj *ein:oinfo-cache*) "")))) + (("stream" "display_data" "pyout" "execute_result") + (setf (gethash obj *ein:oinfo-cache*) (plist-get content :text))) + (("error" "pyerr") + (ein:log 'error "ein:completions--prepare-oinfo: %S" (plist-get content :traceback))))) + (error (ein:log 'error "ein:completions--prepare-oinfo: [%s] %s" err obj) + (setf (gethash obj *ein:oinfo-cache*) :json-false)))) ;;; Support for Eldoc (defun ein:completer--get-eldoc-signature () - (let* ((func (ein:function-at-point)) - (oinfo (gethash func *ein:oinfo-cache* nil))) - (if (not oinfo) - (ein:completions--build-oinfo-cache (list func)) - (plist-get oinfo :definition)))) - -(defun ein:notebook--enable-eldoc () - (set (make-local-variable 'eldoc-documentation-function) - #'ein:completer--get-eldoc-signature)) + (let ((func (ein:function-at-point))) + (ein:aif (gethash func *ein:oinfo-cache*) + (ein:kernel-construct-defstring it) + (ein:completions--build-oinfo-cache (list func))))) (provide 'ein-completer) diff --git a/lisp/ein-connect.el b/lisp/ein-connect.el index c364fda..0731473 100644 --- a/lisp/ein-connect.el +++ b/lisp/ein-connect.el @@ -368,7 +368,7 @@ notebook." (define-key map "\C-c\C-l" 'ein:connect-reload-buffer) (define-key map "\C-c\C-r" 'ein:connect-eval-region) (define-key map (kbd "C-:") 'ein:shared-output-eval-string) - (define-key map "\C-c\C-f" 'ein:pytools-request-tooltip-or-help) + (define-key map "\C-c\C-h" 'ein:pytools-request-tooltip-or-help) (define-key map "\C-c\C-i" 'ein:completer-complete) (define-key map "\C-c\C-z" 'ein:connect-pop-to-notebook) (define-key map "\C-c\C-a" 'ein:connect-toggle-autoexec) diff --git a/lisp/ein-kernel.el b/lisp/ein-kernel.el index 7b3c59b..c889854 100644 --- a/lisp/ein-kernel.el +++ b/lisp/ein-kernel.el @@ -464,7 +464,7 @@ When calling this method pass a CALLBACKS structure of the form: :clear_output CLEAR-OUTPUT-CALLBACK :set_next_input SET-NEXT-INPUT) -Objects end with -CALLBACK above must pack a FUNCTION and its +Right hand sides ending -CALLBACK above must pack a FUNCTION and its first ARGUMENT in a `cons':: (FUNCTION . ARGUMENT) @@ -515,27 +515,23 @@ Sample implementations ;; (funcall FUNCTION [ARG ...] CONTENT METADATA) (assert (ein:kernel-live-p kernel) nil "execute_reply: Kernel is not active.") - (if (not (ein:$kernel-stdin-activep kernel)) - (let* ((content (list - :code code - :silent (or silent json-false) - :store_history (or store-history json-false) - :user_expressions user-expressions - :allow_stdin allow-stdin - :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))) - (run-hook-with-args 'ein:pre-kernel-execute-functions msg) - (ein:websocket-send-shell-channel kernel msg) - (unless (plist-get callbacks :execute_reply) - (ein:log 'debug "code: %s" code)) - (ein:kernel-set-callbacks-for-msg kernel msg-id callbacks) - (unless silent - (mapc #'ein:funcall-packed - (ein:$kernel-after-execute-hook kernel))) - msg-id) - (message "[ein]: stdin active, cannot communicate with kernel."))) - + (let* ((content (list + :code code + :silent (or silent json-false) + :store_history (or store-history json-false) + :user_expressions user-expressions + :allow_stdin allow-stdin + :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) + (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) + (unless silent + (mapc #'ein:funcall-packed + (ein:$kernel-after-execute-hook kernel))) + msg-id)) (defun ein:kernel-complete (kernel line cursor-pos callbacks) "Complete code at CURSOR-POS in a string LINE on KERNEL. @@ -736,6 +732,8 @@ Example:: (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" + msg-type msg-id) (cond ((string-equal msg-type "input_request") (if (not (eql password :json-false)) (let* ((passwd (read-passwd (plist-get content :prompt))) @@ -760,12 +758,10 @@ Example:: (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) - (ein:log 'debug "KERNEL--HANDLE-SHELL-REPLY: msg_type = %s" msg-type) - (if cb - (ein:funcall-packed cb content metadata) - (ein:log 'debug "no callback for: msg_type=%s msg_id=%s" - msg-type msg-id)) + (ein:aif cb (ein:funcall-packed it content metadata)) (ein:aif (plist-get content :payload) (ein:kernel--handle-payload kernel callbacks it)) (let ((events (ein:$kernel-events kernel))) @@ -806,10 +802,11 @@ Example:: (&key content metadata parent_header header &allow-other-keys) (ein:json-read-from-string packet) (let* ((msg-type (plist-get header :msg_type)) - (callbacks (ein:kernel-get-callbacks-for-msg - kernel (plist-get parent_header :msg_id))) + (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-type) + (ein:log 'debug "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 "Got message not from this notebook.") (ein:case-equal msg-type @@ -829,8 +826,8 @@ Example:: (ein:log 'verbose (format "Received data_pub message w/content %s" packet))) (("clear_output") (ein:aif (plist-get callbacks :clear_output) - (ein:funcall-packed it content metadata)))))))) - (ein:log 'debug "KERNEL--HANDLE-IOPUB-REPLY: finished")) + (ein:funcall-packed it content metadata))))))))) +(ein:log 'debug "KERNEL--HANDLE-IOPUB-REPLY: finished") ;;; Utility functions diff --git a/lisp/ein-notebook.el b/lisp/ein-notebook.el index 7d79e6b..171c237 100644 --- a/lisp/ein-notebook.el +++ b/lisp/ein-notebook.el @@ -188,6 +188,7 @@ Current buffer for these functions is set to the notebook buffer.") (ein:deflocal ein:%notebook% nil "Buffer local variable to store an instance of `ein:$notebook'.") + (define-obsolete-variable-alias 'ein:notebook 'ein:%notebook% "0.1.2") @@ -299,6 +300,8 @@ will be updated with kernel's cwd." (defun ein:notebook-open--decorate-callback (notebook existing callback) "In addition to CALLBACK, also pop-to-buffer the new notebook, and save to disk the kernelspec metadata." (apply-partially (lambda (notebook* created callback*) + (with-current-buffer (ein:notebook-buffer notebook*) + (ein:worksheet-focus-cell)) (pop-to-buffer (ein:notebook-buffer notebook*)) (ein:aif (ein:$notebook-kernelspec notebook*) (progn @@ -604,7 +607,6 @@ This is equivalent to do ``C-c`` in the console program." ;; Now that major-mode is set, set buffer local variables: (ein:notebook--notification-setup notebook) (ein:notebook-setup-kill-buffer-hook) - (ein:notebook--enable-eldoc) (setq ein:%notebook% notebook))) (defun ein:notebook--notification-setup (notebook) @@ -1286,10 +1288,11 @@ Use simple `python-mode' based notebook mode when MuMaMo is not installed:: (const :tag "Plain" ein:notebook-plain-mode))) :group 'ein) -(defcustom ein:notebook-mode-hook nil +(defcustom ein:notebook-mode-hook + '(ein:worksheet-imenu-setup ein:worksheet-reinstall-which-cell-hook) "Hook for `ein:notebook-mode'. This hook is run regardless the actual major mode used." - :type 'hook + :type '(repeat function) :group 'ein) (defun ein:notebook-choose-mode () @@ -1497,6 +1500,20 @@ This hook is run regardless the actual major mode used." )) map) +(defun ein:notebook-configure-eldoc () + "eldoc comments say: Major modes for other languages may use ElDoc by defining an +appropriate function as the buffer-local value of `eldoc-documentation-function'." + ;; TODO + (when nil + (require 'eldoc nil t) + (if (boundp 'eldoc-documentation-function) + (setq-local eldoc-documentation-function + (apply-partially (lambda (oldfun &rest args) + (or (apply #'ein:completer--get-eldoc-signature args) + (apply oldfun args))) + eldoc-documentation-function)) + (setq-local eldoc-documentation-function #'ein:completer--get-eldoc-signature)))) + (defun ein:notebook-mode () (funcall (ein:notebook-choose-mode)) (case ein:completion-backend @@ -1519,11 +1536,9 @@ This hook is run regardless the actual major mode used." (define-key ein:notebook-mode-map it 'anything-ein-kernel-history)) (ein:notebook-minor-mode +1) (setq indent-tabs-mode nil) ;; Being T causes problems with Python code. + (ein:notebook-configure-eldoc) (run-hooks 'ein:notebook-mode-hook)) -(add-hook 'ein:notebook-mode-hook 'ein:worksheet-imenu-setup) -(add-hook 'ein:notebook-mode-hook 'ein:worksheet-reinstall-which-cell-hook) - (define-minor-mode ein:notebook-minor-mode "Minor mode to install `ein:notebook-mode-map' for `ein:notebook-mode'." :keymap ein:notebook-mode-map diff --git a/lisp/ein-notification.el b/lisp/ein-notification.el index 508b554..3bf8a2c 100644 --- a/lisp/ein-notification.el +++ b/lisp/ein-notification.el @@ -57,7 +57,8 @@ S-mouse-1/3 (Shift + left/right click): move this tab to left/right" (defmethod ein:notification-status-set ((ns ein:notification-status) status) (let* ((message (cdr (assoc status (slot-value ns 's2m))))) (setf (slot-value ns 'status) status) - (setf (slot-value ns 'message) message))) + (setf (slot-value ns 'message) message) + (force-mode-line-update))) (defmethod ein:notification-bind-events ((notification ein:notification) events)