Update documentation, cleanup comments

* Remove implementation details in documentation strings

* Canonicalize documentation of functions which perform similar operations.
This commit is contained in:
Nathaniel Nicandro 2018-06-14 21:07:22 -05:00
parent 7b3e4f9f84
commit de9f85cc2e
3 changed files with 147 additions and 206 deletions

View file

@ -403,14 +403,6 @@ Any other command sent to the subprocess will be ignored."
;; solution to this is to send the process id of the parent emacs and ;; solution to this is to send the process id of the parent emacs and
;; periodically check if the process is still alive, then exit the subprocess ;; periodically check if the process is still alive, then exit the subprocess
;; if the parent process is dead. ;; if the parent process is dead.
;;
;; FIXME: Fix the problem where lots of display_data messages are coming in,
;; then we send a request, and wait for the request id to come back with
;; `jupyter-request-id'. `jupyter-request-id' will time out. it looks like the
;; poller is not noticing the stdin event in this case.
;;
;; One option would be to have two separate poll commands, one for STDIN and
;; the other for the channels. But first write a test case for this scenario.
(defun jupyter--ioloop (client) (defun jupyter--ioloop (client)
"Return the function used for communicating with CLIENT's kernel." "Return the function used for communicating with CLIENT's kernel."
(let ((sid (jupyter-session-id (oref client session))) (let ((sid (jupyter-session-id (oref client session)))
@ -468,17 +460,6 @@ subprocess."
(defun jupyter--ioloop-push-request (client req) (defun jupyter--ioloop-push-request (client req)
"Insert a request into CLIENT's pending requests. "Insert a request into CLIENT's pending requests.
Pending requests are `jupyter-request's that have a nil
`jupyter-request--id'. The `jupyter-send' method for a
`jupyter-kernel-client' sends a message to the ioloop subprocess
which encodes the message and sends it off to the kernel.
When the subprocess sends the message to the kernel, it sends the
message ID associated with the request back to the parent Emacs
process which is when the `jupyter-request--id' field becomes
non-nil. This acts as a synchronization method so that the parent
Emacs process can guarantee a message has been sent.
Pending requests are stored in a ring located in the Pending requests are stored in a ring located in the
`:pending-requests' property of an ioloop subprocess. REQ is `:pending-requests' property of an ioloop subprocess. REQ is
added as the newest element in this ring." added as the newest element in this ring."
@ -824,11 +805,11 @@ are taken:
(defmacro jupyter-dispatch-message-cases (client req msg cases) (defmacro jupyter-dispatch-message-cases (client req msg cases)
"Dispatch to CLIENT handler's based on REQ and MSG. "Dispatch to CLIENT handler's based on REQ and MSG.
CASES defines the the handlers to dispatch to based on the CASES defines the handlers to dispatch to based on the
`jupyter-message-type' of MSG and should be a list of lists, the `jupyter-message-type' of MSG and should be a list of lists, the
first element of each inner list being the name of the handler, first element of each inner list being the name of the handler,
excluding the `jupyter-handle-' prefix. The rest of the elements excluding the `jupyter-handle-' prefix. The rest of the elements
sin the list are the name of the keys that will be extracted from in the list are the name of the keys that will be extracted from
the `jupyter-message-content' of MSG and passed to the handler in the `jupyter-message-content' of MSG and passed to the handler in
the same order as they appear. For example, the same order as they appear. For example,
@ -841,11 +822,11 @@ will be transformed to
(let ((content (jupyter-message-content msg))) (let ((content (jupyter-message-content msg)))
(pcase (jupyter-message-type msg) (pcase (jupyter-message-type msg)
(:shutdown-reply (:shutdown-reply
(cl-destructuring-bind (&key restart @allow-other-keys) (cl-destructuring-bind (&key restart &allow-other-keys)
content content
(jupyter-handle-shutdown-reply client req restart))) (jupyter-handle-shutdown-reply client req restart)))
(:stream (:stream
(cl-destructuring-bind (&key name text @allow-other-keys) (cl-destructuring-bind (&key name text &allow-other-keys)
content content
(jupyter-handle-stream client req name text))) (jupyter-handle-stream client req name text)))
(_ (warn \"Message type not handled (%s)\" (_ (warn \"Message type not handled (%s)\"

View file

@ -299,8 +299,7 @@ RENDER-PARAM to the PARAMS."
Remove RENDER-PARAM from PARAMS or from the result parameters Remove RENDER-PARAM from PARAMS or from the result parameters
found in PARAMS. If RENDER-PARAM is a cons cell, remove it from found in PARAMS. If RENDER-PARAM is a cons cell, remove it from
the PARAMS list. If RENDER-PARAM is a string, remove it from the the PARAMS list. If RENDER-PARAM is a string, remove it from the
`:result-params' of PARAMS. In all cases, `delq' is used for `:result-params' of PARAMS."
removal."
(cond (cond
((consp render-param) ((consp render-param)
(delq render-param params)) (delq render-param params))
@ -422,7 +421,8 @@ it does not need to be added by the user."
(jupyter-org--clear-render-param render-param params))) (jupyter-org--clear-render-param render-param params)))
(defun jupyter-org-insert-sync-results (client req) (defun jupyter-org-insert-sync-results (client req)
"For CLIENT, insert the results of REQ." "For CLIENT, insert the results of REQ.
Meant to be used as the return value of `org-babel-execute:jupyter'."
(let ((results (nreverse (jupyter-org-request-results req))) (let ((results (nreverse (jupyter-org-request-results req)))
(params (jupyter-org-request-block-params req)) (params (jupyter-org-request-block-params req))
(kernel-lang (jupyter-repl-language client))) (kernel-lang (jupyter-repl-language client)))

View file

@ -30,14 +30,15 @@
;; When called interactively, `run-jupyter-repl' asks for a kernel to start ;; When called interactively, `run-jupyter-repl' asks for a kernel to start
;; (based on the kernels found using `jupyter-available-kernelspecs'), connects ;; (based on the kernels found using `jupyter-available-kernelspecs'), connects
;; a `jupyter-repl-client' to the selected kernel, and pops up a REPL buffer. ;; a `jupyter-repl-client' to the selected kernel, and pops up a REPL buffer.
;; On the other hand, if `connect-jupyter-repl' is called interactively, it ;; The main difference of `connect-jupyter-repl' is that it will obtain the
;; will ask for the JSON file that contains the kernel's connection info. ;; kernel's connection info by asking for the JSON file containing it to start
;; connection to a kernel.
;; ;;
;; Additionally, `jupyter-repl-associate-buffer' associates the ;; Additionally, `jupyter-repl-associate-buffer' associates the
;; `current-buffer' with a REPL client appropriate for the buffer's ;; `current-buffer' with a REPL client appropriate for the buffer's
;; `major-mode'. Associating a buffer with a REPL client enables the minor mode ;; `major-mode'. Associating a buffer with a REPL client enables the minor mode
;; `jupyter-repl-interaction-mode' and, if `company-mode' is installed, enables ;; `jupyter-repl-interaction-mode' and, if `company-mode' is installed, enables
;; auto-completion using the associated REPL client. ;; code completion using the associated REPL client.
;; ;;
;; `jupyter-repl-interaction-mode' adds the following keybindings for ;; `jupyter-repl-interaction-mode' adds the following keybindings for
;; interacing a REPL client: ;; interacing a REPL client:
@ -128,7 +129,8 @@ timeout, the built-in is-complete handler is used."
:documentation "The REPL buffer whose :documentation "The REPL buffer whose
`jupyter-repl-current-client' is this client.") `jupyter-repl-current-client' is this client.")
(wait-to-clear (wait-to-clear
:type boolean :initform nil :type boolean
:initform nil
:documentation "Whether or not we should wait to clear the :documentation "Whether or not we should wait to clear the
current output of the cell. Set when the kernel sends a current output of the cell. Set when the kernel sends a
`:clear-output' message.") `:clear-output' message.")
@ -136,9 +138,7 @@ current output of the cell. Set when the kernel sends a
:type json-plist :type json-plist
:initform nil :initform nil
:documentation "The saved kernel info created when first :documentation "The saved kernel info created when first
initializing this client. This is the plist returned from the the initializing this client.")
call to `jupyter-start-new-kernel' when this client was
created.")
(execution-state (execution-state
:type string :type string
:initform "idle" :initform "idle"
@ -210,16 +210,12 @@ executing BODY."
(defmacro jupyter-repl-do-at-request (client req &rest body) (defmacro jupyter-repl-do-at-request (client req &rest body)
"Switch to CLIENT's buffer, move to the end of REQ, and run BODY. "Switch to CLIENT's buffer, move to the end of REQ, and run BODY.
Switching to CLIENT's buffer is accomplished using REQ is a `jupyter-request' previously made using CLIENT, a
`with-jupyter-repl-buffer'. After switching, `point' is moved to `jupyter-repl-client'.
the `jupyter-repl-cell-beginning-position' of the cell after the
one associated with REQ, where REQ is a `jupyter-request'
previously made using CLIENT. This position is where any output
of REQ should be inserted.
Note that `inhibit-modification-hooks' is set to t when BODY is `point' is moved to the `jupyter-repl-cell-beginning-position' of
run, this prevents any line continuation prompts to be inserted the cell *after* REQ, this position is where any newly generated
for multi-line output." output of REQ should be inserted."
(declare (indent 2) (debug (symbolp &rest form))) (declare (indent 2) (debug (symbolp &rest form)))
`(with-jupyter-repl-buffer ,client `(with-jupyter-repl-buffer ,client
(jupyter-repl-without-continuation-prompts (jupyter-repl-without-continuation-prompts
@ -243,8 +239,7 @@ running BODY."
The cell is narrowed to the region between and including The cell is narrowed to the region between and including
`jupyter-repl-cell-code-beginning-position' and `jupyter-repl-cell-code-beginning-position' and
`jupyter-repl-cell-code-end-position'. When BODY is run, `point' will `jupyter-repl-cell-code-end-position'. When BODY is run, `point' will
be at the `jupyter-repl-cell-code-beginning-position'. Note that be at the `jupyter-repl-cell-code-beginning-position'."
this assumes that the `current-buffer' is a Jupyter REPL buffer."
(declare (indent 0) (debug (&rest form))) (declare (indent 0) (debug (&rest form)))
`(save-excursion `(save-excursion
(save-restriction (save-restriction
@ -262,9 +257,9 @@ A REPL documentation buffer has the following characteristics:
- local keybindings to quit the window (q), and scroll the - local keybindings to quit the window (q), and scroll the
window (SPC and <backtab>). window (SPC and <backtab>).
The buffer returned will have a `buffer-name' with the form The buffer returned will have a `buffer-name' of
\"*jupyter-repl-NAME*\". If a buffer with this name already
\"*jupyter-repl-NAME*\"" exists, it is returned."
(let* ((bname (format "*jupyter-repl-%s*" name)) (let* ((bname (format "*jupyter-repl-%s*" name))
(buffer (get-buffer bname))) (buffer (get-buffer bname)))
(unless buffer (unless buffer
@ -296,12 +291,13 @@ erased."
;;; Convenience functions ;;; Convenience functions
(defsubst jupyter-repl-language-mode (client) (defun jupyter-repl-language-mode (client)
"Get the `major-mode' of CLIENT's kernel language." "Return the `major-mode' of CLIENT's kernel language."
(with-jupyter-repl-buffer client (with-jupyter-repl-buffer client
jupyter-repl-lang-mode)) jupyter-repl-lang-mode))
(cl-defmethod jupyter-repl-language ((client jupyter-repl-client)) (cl-defmethod jupyter-repl-language ((client jupyter-repl-client))
"Return the name of CLIENT's kernel language."
(plist-get (plist-get (oref client kernel-info) :language_info) :name)) (plist-get (plist-get (oref client kernel-info) :language_info) :name))
;;; Text insertion ;;; Text insertion
@ -344,12 +340,9 @@ insertion into the REPL buffer and adding
(throw 'done t)))))) (throw 'done t))))))
(defun jupyter-repl-get-fontify-buffer (mode) (defun jupyter-repl-get-fontify-buffer (mode)
"Get the cached buffer used to fontify text for MODE. "Return the buffer used to fontify text for MODE.
Consult the `jupyter-repl-fontify-buffers' alist for a buffer to Retrieve the buffer for MODE from `jupyter-repl-fontify-buffers'.
use for fontification according to MODE and return the buffer If no buffer for MODE exists, create a new one."
found. If no buffer exists for MODE: create a new buffer, set its
`major-mode' to MODE, add it to `juptyer-repl-fontify-buffers',
and return the buffer."
(let ((buf (alist-get mode jupyter-repl-fontify-buffers))) (let ((buf (alist-get mode jupyter-repl-fontify-buffers)))
(unless buf (unless buf
(setq buf (get-buffer-create (setq buf (get-buffer-create
@ -361,12 +354,6 @@ and return the buffer."
(defun jupyter-repl-fontify-according-to-mode (mode str) (defun jupyter-repl-fontify-according-to-mode (mode str)
"Fontify a string according to MODE. "Fontify a string according to MODE.
MODE has the same meaning as in
`jupyter-repl-get-fontify-buffer'. STR is a string that will be
fontified according to MODE by inserting it into the buffer
returned by `jupyter-repl-get-fontify-buffer' (erasing any
contents of the buffer before insertion).
In addition to fontifying STR, if MODE has a non-default In addition to fontifying STR, if MODE has a non-default
`fill-forward-paragraph-function', STR will be filled using `fill-forward-paragraph-function', STR will be filled using
`fill-region'." `fill-region'."
@ -639,9 +626,8 @@ DATA is displayed as a widget."
;;; Prompt ;;; Prompt
(defun jupyter-repl--prompt-display-value (str face) (defun jupyter-repl--prompt-display-value (str face)
"Return the margin display value for a prompt. "Return the margin display value for a prompt STR.
STR is the string used for the display value and FACE is the FACE is the `font-lock-face' to use for STR."
`font-lock-face' to use for STR."
(list '(margin left-margin) (list '(margin left-margin)
(propertize (propertize
(concat (make-string (concat (make-string
@ -655,10 +641,9 @@ STR is the string used for the display value and FACE is the
(defun jupyter-repl--insert-prompt (str face) (defun jupyter-repl--insert-prompt (str face)
"Insert a new prompt at `point'. "Insert a new prompt at `point'.
STR is the prompt string displayed in the `left-margin' using STR is the prompt string displayed in the `left-margin' using
FACE as the `font-lock-face'. A newline is inserted before adding FACE as the `font-lock-face'. A newline is inserted and the
the prompt. The prompt string is inserted as a `display' text prompt is added as the after-string of an overlay of the
property in the `after-string' property of the overlay and the newline."
overlay is added to the newline character just inserted."
(jupyter-repl-newline) (jupyter-repl-newline)
(overlay-recenter (point)) (overlay-recenter (point))
(let ((ov (make-overlay (1- (point)) (point) nil t)) (let ((ov (make-overlay (1- (point)) (point) nil t))
@ -668,9 +653,9 @@ overlay is added to the newline character just inserted."
ov)) ov))
(defun jupyter-repl-insert-prompt (&optional type) (defun jupyter-repl-insert-prompt (&optional type)
"Insert a REPL promp in CLIENT's buffer according to type. "Insert a REPL prompt according to TYPE.
If TYPE is nil or `in' insert a new input prompt. If TYPE is TYPE can either be `in', `out', or `continuation'. A value of nil
`out' insert a new output prompt." for TYPE is interpreted as `in'."
(setq type (or type 'in)) (setq type (or type 'in))
(unless (memq type '(in out continuation)) (unless (memq type '(in out continuation))
(error "Prompt type can only be (`in', `out', or `continuation')")) (error "Prompt type can only be (`in', `out', or `continuation')"))
@ -729,22 +714,16 @@ STR is the replacement prompt string."
str 'jupyter-repl-input-prompt)))))) str 'jupyter-repl-input-prompt))))))
(defun jupyter-repl-cell-mark-busy () (defun jupyter-repl-cell-mark-busy ()
"Mark the current cell as busy. "Mark the current cell as busy."
The changes the current input prompt to \"In [*] \""
(jupyter-repl-cell-update-prompt "In [*] ")) (jupyter-repl-cell-update-prompt "In [*] "))
(defun jupyter-repl-cell-unmark-busy () (defun jupyter-repl-cell-unmark-busy ()
"Un-mark the current cell as busy. "Un-mark the current cell as busy."
This changes the current input prompt to \"In [N] \" where N is
the execution count of the cell."
(jupyter-repl-cell-update-prompt (jupyter-repl-cell-update-prompt
(format "In [%d] " (jupyter-repl-cell-count)))) (format "In [%d] " (jupyter-repl-cell-count))))
(defun jupyter-repl-cell-count () (defun jupyter-repl-cell-count ()
"Get the cell count of the current cell at `point'. "Return the cell count of the cell at `point'."
If PREVIOUS is non-nil and `point' is already at the beginning of
a cell, return the cell count of the previous cell before the
current one."
(let ((pos (if (jupyter-repl-cell-beginning-p) (point) (let ((pos (if (jupyter-repl-cell-beginning-p) (point)
(save-excursion (save-excursion
(jupyter-repl-previous-cell) (jupyter-repl-previous-cell)
@ -752,7 +731,7 @@ current one."
(nth 1 (get-text-property pos 'jupyter-cell)))) (nth 1 (get-text-property pos 'jupyter-cell))))
(defun jupyter-repl-cell-request () (defun jupyter-repl-cell-request ()
"Get the `jupyter-request' of the current cell." "Return the `jupyter-request' of the current cell."
(get-text-property (jupyter-repl-cell-beginning-position) 'jupyter-request)) (get-text-property (jupyter-repl-cell-beginning-position) 'jupyter-request))
;;; Cell motions ;;; Cell motions
@ -760,11 +739,14 @@ current one."
(defun jupyter-repl-cell-beginning-position () (defun jupyter-repl-cell-beginning-position ()
"Return the cell beginning position of the current cell. "Return the cell beginning position of the current cell.
If `point' is already at the beginning of the current cell, If `point' is already at the beginning of the current cell,
return `point'. Note that if the end of a cell is found before return `point'.
the beginning of a cell, i.e. when `point' is somewhere inside
the output of a cell, raise an error. If the beginning of the If the end of a cell is found before the beginning of one, i.e.
buffer is found before the beginning of a cell, raise a when `point' is somewhere inside the output of a cell, raise an
`beginning-of-buffer' error." error.
If the beginning of the buffer is found before the beginning of a
cell, raise a `beginning-of-buffer' error."
(let ((pos (point))) (let ((pos (point)))
(while (not (jupyter-repl-cell-beginning-p pos)) (while (not (jupyter-repl-cell-beginning-p pos))
(setq pos (previous-single-property-change pos 'jupyter-cell)) (setq pos (previous-single-property-change pos 'jupyter-cell))
@ -780,8 +762,10 @@ buffer is found before the beginning of a cell, raise a
This is similar to `jupyter-repl-cell-beginning-position' except This is similar to `jupyter-repl-cell-beginning-position' except
the position at the end of the current cell is returned and an the position at the end of the current cell is returned and an
error is raised if the beginning of a cell is found before an error is raised if the beginning of a cell is found before an
end. Note that if the current cell is the last cell in the end.
buffer, `point-max' is considered the end of the cell."
Note: If the current cell is the last cell in the buffer,
`point-max' is considered the end of the cell."
(let ((pos (point))) (let ((pos (point)))
(catch 'unfinalized (catch 'unfinalized
(while (not (jupyter-repl-cell-end-p pos)) (while (not (jupyter-repl-cell-end-p pos))
@ -814,9 +798,9 @@ unfinalized cell, the code ending position is `point-max'."
(1- pos)))) (1- pos))))
(defun jupyter-repl-next-cell (&optional N) (defun jupyter-repl-next-cell (&optional N)
"Go to the start of the next cell. "Go to the beginning of the next cell.
Optional argument N is the number of times to move to the next Move N times where N defaults to 1. Return the count of cells
cell. N defaults to 1." left to move."
(or N (setq N 1)) (or N (setq N 1))
(catch 'done (catch 'done
(while (> N 0) (while (> N 0)
@ -829,11 +813,12 @@ cell. N defaults to 1."
N) N)
(defun jupyter-repl-previous-cell (&optional N) (defun jupyter-repl-previous-cell (&optional N)
"Go to the start of the current or previous cell. "Go to the beginning of the previous cell.
If `point' is already at the start of the current cell, go to the Move N times where N defaults to 1. Return the count of cells
start of the previous cell. Otherwise go to the start of the left to move.
current cell. Optional argument N is the number of times to move
to the previous cell. N defaults to 1." Note, if `point' is not at the beginning of the current cell, the
first move is to the beginning of the current cell."
(or N (setq N 1)) (or N (setq N 1))
(catch 'done (catch 'done
(while (> N 0) (while (> N 0)
@ -863,7 +848,7 @@ a Jupyter REPL buffer."
(error "Cell for request not found"))) (error "Cell for request not found")))
(defun jupyter-repl-forward-cell (&optional arg) (defun jupyter-repl-forward-cell (&optional arg)
"Move to the code beginning of the cell after the current one. "Go to the code beginning of the cell after the current one.
ARG is the number of cells to move and defaults to 1." ARG is the number of cells to move and defaults to 1."
(interactive "^p") (interactive "^p")
(or arg (setq arg 1)) (or arg (setq arg 1))
@ -871,7 +856,7 @@ ARG is the number of cells to move and defaults to 1."
(goto-char (jupyter-repl-cell-code-beginning-position))) (goto-char (jupyter-repl-cell-code-beginning-position)))
(defun jupyter-repl-backward-cell (&optional arg) (defun jupyter-repl-backward-cell (&optional arg)
"Move to the code beginning of the cell before the current one. "Go to the code beginning of the cell before the current one.
ARG is the number of cells to move and defaults to 1." ARG is the number of cells to move and defaults to 1."
(interactive "^p") (interactive "^p")
(or arg (setq arg 1)) (or arg (setq arg 1))
@ -911,22 +896,16 @@ POS defaults to `point'."
(jupyter-repl-cell-code-end-position)))))) (jupyter-repl-cell-code-end-position))))))
(defun jupyter-repl-cell-finalized-p () (defun jupyter-repl-cell-finalized-p ()
"Has the current cell been finalized? "Has the current cell been finalized?"
A cell is considered finalized when `jupyter-repl-finalize-cell'
has been previously called for it. `jupyter-repl-finalize-cell'
is responsible for adding the text properties which cause
`jupyter-repl-cell-end-p' to return non-nil."
(jupyter-repl-cell-end-p (jupyter-repl-cell-end-position))) (jupyter-repl-cell-end-p (jupyter-repl-cell-end-position)))
(defun jupyter-repl-client-has-manager-p () (defun jupyter-repl-client-has-manager-p ()
"Does the `jupyter-repl-current-client' have a `jupyter-kernel-manager'? "Does the `jupyter-repl-current-client' have a `jupyter-kernel-manager'?"
Checks to see if the REPL client of the `current-buffer' has a
kernel manager as its manager slot."
(and jupyter-repl-current-client (and jupyter-repl-current-client
(oref jupyter-repl-current-client manager))) (oref jupyter-repl-current-client manager)))
(defun jupyter-repl-connected-p () (defun jupyter-repl-connected-p ()
"Determine if the `jupyter-repl-current-client' is connected to its kernel." "Is the `jupyter-repl-current-client' connected to its kernel?"
(when jupyter-repl-current-client (when jupyter-repl-current-client
(or (and (jupyter-repl-client-has-manager-p) (or (and (jupyter-repl-client-has-manager-p)
;; Check if the kernel is local ;; Check if the kernel is local
@ -952,22 +931,14 @@ kernel manager as its manager slot."
(mapconcat #'identity (nreverse lines) "\n"))))) (mapconcat #'identity (nreverse lines) "\n")))))
(defun jupyter-repl-cell-code-position () (defun jupyter-repl-cell-code-position ()
"Get the position that `point' is at relative to the contents of the cell. "Return the relative position of `point' with respect to the cell code."
The first character of the cell code corresponds to position 1."
(unless (jupyter-repl-cell-line-p) (unless (jupyter-repl-cell-line-p)
(error "Not in code of cell")) (error "Not in code of cell"))
(1+ (- (point) (jupyter-repl-cell-code-beginning-position)))) (1+ (- (point) (jupyter-repl-cell-code-beginning-position))))
(defun jupyter-repl-finalize-cell (req) (defun jupyter-repl-finalize-cell (req)
"Finalize the current cell. "Finalize the current cell.
REQ is the `jupyter-request' to associate with the current cell. REQ is the `jupyter-request' to associate with the current cell."
Finalizing a cell involves the following steps:
- Associate REQ with the cell
- Move `point' to the location where the next input cell can be
inserted
- Add the text property which marks the end of a cell
- Make the cell read-only"
(let ((beg (jupyter-repl-cell-beginning-position)) (let ((beg (jupyter-repl-cell-beginning-position))
(count (jupyter-repl-cell-count))) (count (jupyter-repl-cell-count)))
(goto-char (point-max)) (goto-char (point-max))
@ -994,7 +965,7 @@ Finalizing a cell involves the following steps:
"Truncate the `current-buffer' based on `jupyter-repl-maximum-size'. "Truncate the `current-buffer' based on `jupyter-repl-maximum-size'.
The `current-buffer' is assumed to be a Jupyter REPL buffer. If The `current-buffer' is assumed to be a Jupyter REPL buffer. If
the `current-buffer' is larger than `jupyter-repl-maximum-size' the `current-buffer' is larger than `jupyter-repl-maximum-size'
lines then truncate it to something less than lines, truncate it to something less than
`jupyter-repl-maximum-size' lines." `jupyter-repl-maximum-size' lines."
(save-excursion (save-excursion
(when (= (forward-line (- jupyter-repl-maximum-size)) 0) (when (= (forward-line (- jupyter-repl-maximum-size)) 0)
@ -1098,7 +1069,7 @@ lines then truncate it to something less than
(jupyter-repl-insert-data data metadata)))) (jupyter-repl-insert-data data metadata))))
(defun jupyter-repl-next-display-with-id (id) (defun jupyter-repl-next-display-with-id (id)
"Move `point' to the start of the next display matching ID. "Go to the start of the next display matching ID.
Return non-nil if successful. If no display with ID is found, Return non-nil if successful. If no display with ID is found,
return nil without moving `point'." return nil without moving `point'."
(let ((pos (next-single-property-change (point) 'jupyter-display))) (let ((pos (next-single-property-change (point) 'jupyter-display)))
@ -1109,13 +1080,11 @@ return nil without moving `point'."
(defun jupyter-repl-update-display (id data metadata) (defun jupyter-repl-update-display (id data metadata)
"Update the display with ID using DATA. "Update the display with ID using DATA.
DATA and METADATA have the same meaning as in a `:display-data' DATA and METADATA have the same meaning as in a `:display-data'
message. message."
;; Updating a display involves finding and clearing the data that is
Updating a display involves finding and clearing the data that is ;; currently associated with the ID and inserting DATA at the same location.
currently associated with the ID and inserting DATA at the same ;; If multiple locations have the same display ID, all of them are updated.
location. If multiple locations have the same display ID, all of ;; Raise an error if no display with ID could be found.
them are updated. Raise an error if no display with ID could be
found."
(save-excursion (save-excursion
(goto-char (point-min)) (goto-char (point-min))
(let (str) (let (str)
@ -1385,11 +1354,12 @@ REPL buffer."
"Send the current cell code to the kernel. "Send the current cell code to the kernel.
If `point' is before the last cell in the REPL buffer move to If `point' is before the last cell in the REPL buffer move to
`point-max', i.e. move to the last cell. Otherwise if `point' is `point-max', i.e. move to the last cell. Otherwise if `point' is
at some position within the last cell of the REPL buffer, either at some position within the last cell, either insert a newline or
insert a newline or ask the kernel to execute the cell code ask the kernel to execute the cell code depending on the kernel's
depending on the kernel's response to an `:is-complete-request'. response to an `:is-complete-request'.
If FORCE is non-nil, force the kernel to execute the current cell
code without sending the `:is-complete-request'. See If a prefix argument is given, FORCE the kernel to execute the
current cell code without sending an `:is-complete-request'. See
`jupyter-repl-use-builtin-is-complete' for yet another way to `jupyter-repl-use-builtin-is-complete' for yet another way to
execute the current cell." execute the current cell."
(interactive "P") (interactive "P")
@ -1454,11 +1424,10 @@ Reset `jupyter-repl-use-builtin-is-complete' to nil if this is only temporary.")
(defun jupyter-repl-after-buffer-change (beg end len) (defun jupyter-repl-after-buffer-change (beg end len)
"Insert line continuation prompts in `jupyter-repl-mode' buffers. "Insert line continuation prompts in `jupyter-repl-mode' buffers.
BEG, END, and LEN have the same meaning as for BEG, END, and LEN have the same meaning as in
`after-change-functions'. If the change corresponds to text being `after-change-functions'. For every change that corresponds to
inserted and the beginning of the insertion is on a insertion of text and that text is multi-line, insert line
`jupyter-repl-cell-line-p', insert line continuation prompts if continuation prompts for each line."
the inserted text is multi-line."
(when (eq major-mode 'jupyter-repl-mode) (when (eq major-mode 'jupyter-repl-mode)
(cond (cond
;; Insertions only ;; Insertions only
@ -1518,11 +1487,13 @@ value."
"Return a cons cell, (CODE . POS), for the context around `point'. "Return a cons cell, (CODE . POS), for the context around `point'.
CODE is the required context for TYPE (either `inspect' or CODE is the required context for TYPE (either `inspect' or
`complete') and POS is the relative position of `point' within `complete') and POS is the relative position of `point' within
CODE. The context also depends on the `major-mode' of the CODE.
The returned CODE depends on the `major-mode' of the
`current-buffer'. If the `major-mode' is `jupyter-repl-mode', `current-buffer'. If the `major-mode' is `jupyter-repl-mode',
CODE is the contents of the entire code cell. Otherwise its CODE is the contents of the current cell. Otherwise its either
either the line up to `point' if TYPE is `complete' or the entire the line up to `point' if TYPE is `complete' or the entire line
line if TYPE is `inspect'." if TYPE is `inspect'."
(unless (memq type '(complete inspect)) (unless (memq type '(complete inspect))
(error "Type not `complete' or `inspect' (%s)" type)) (error "Type not `complete' or `inspect' (%s)" type))
(let (code pos) (let (code pos)
@ -1555,8 +1526,8 @@ line if TYPE is `inspect'."
(defun jupyter-repl-completion-prefix () (defun jupyter-repl-completion-prefix ()
"Return the prefix for the current completion context. "Return the prefix for the current completion context.
Note that the prefix returned is not the content sent to the Note that the prefix returned is not the content sent to the
kernel. See `jupyter-repl-code-context-at-point' for what is kernel, but the symbol at `point'. See
actually sent to the kernel." `jupyter-repl-code-context-at-point' for what is actually sent."
(when jupyter-repl-current-client (when jupyter-repl-current-client
(let ((lang-mode (jupyter-repl-language-mode jupyter-repl-current-client))) (let ((lang-mode (jupyter-repl-language-mode jupyter-repl-current-client)))
(and (memq major-mode `(,lang-mode jupyter-repl-mode)) (and (memq major-mode `(,lang-mode jupyter-repl-mode))
@ -1681,20 +1652,16 @@ COMMAND and ARG have the same meaning as the elements of
(defun jupyter-repl--inspect (code pos &optional detail buffer timeout) (defun jupyter-repl--inspect (code pos &optional detail buffer timeout)
"Send an inspect request to a Jupyter kernel. "Send an inspect request to a Jupyter kernel.
CODE and POS are the code to send and the position within the POS is the position of `point' relative to the inspected CODE.
code, respectively.
If DETAIL is non-nil, it is the detail level of the inspect DETAIL is the detail level to use for the request and defaults to
request. Otherwise a detail level of 0 is used. 0.
If BUFFER is non-nil then it should be the buffer in which to If BUFFER is provided, the inspection text returned from the
insert the inspection text returned from the kernel. After the kernel is inserted into BUFFER and BUFFER is returned. Otherwise,
inserting the text into BUFFER, BUFFER is returned. If BUFFER is when BUFFER is nil, the formated inspection string is returned.
nil, just return the inspection text. In both cases the
inspection text is already in a form suitable for display.
TIMEOUT is how long to wait (in seconds) for the kernel to It the kernel doesn't respond within TIMEOUT seconds, return nil."
respond before returning nil."
(let* ((jupyter-inhibit-handlers '(:status)) (let* ((jupyter-inhibit-handlers '(:status))
(msg (jupyter-wait-until-received :inspect-reply (msg (jupyter-wait-until-received :inspect-reply
(jupyter-send-inspect-request jupyter-repl-current-client (jupyter-send-inspect-request jupyter-repl-current-client
@ -1723,38 +1690,39 @@ the `current-buffer' and display the results in a buffer."
(let ((buf (current-buffer))) (let ((buf (current-buffer)))
;; TODO: Reset this to nil when the inspect buffer is closed. ;; TODO: Reset this to nil when the inspect buffer is closed.
(with-jupyter-repl-doc-buffer "inspect" (with-jupyter-repl-doc-buffer "inspect"
(let ((jupyter-repl-current-client ;; Set this in the inspect buffer so that
(buffer-local-value 'jupyter-repl-current-client buf))) ;; `jupyter-repl-markdown-follow-link-at-point' works in the inspect
;; FIXME: Better way of inserting documentation into a buffer. ;; buffer as well.
;; Currently the way text is inserted is by inserting in a temp (setq-local jupyter-repl-current-client
;; buffer and returning the string, but in cases where overlays may (buffer-local-value 'jupyter-repl-current-client buf))
;; be inserted in the buffer (markdown), this fails. A better way ;; FIXME: Better way of inserting documentation into a buffer.
;; would be to supply the buffer in which to insert text like what is ;; Currently the way text is inserted is by inserting in a temp
;; done here, but how to make it more general for all insertion ;; buffer and returning the string, but in cases where overlays may
;; types? ;; be inserted in the buffer (markdown), this fails. A better way
(if (not (jupyter-repl--inspect code pos nil (current-buffer))) ;; would be to supply the buffer in which to insert text like what is
(message "Inspect timed out") ;; done here, but how to make it more general for all insertion
;; TODO: Customizable action ;; types?
(display-buffer (current-buffer)) (if (not (jupyter-repl--inspect code pos nil (current-buffer)))
(set-window-start (get-buffer-window) (point-min))))) (message "Inspect timed out")
;; TODO: Customizable action
(display-buffer (current-buffer))
(set-window-start (get-buffer-window) (point-min))))
(setq other-window-scroll-buffer (get-buffer "*jupyter-repl-inspect*"))))) (setq other-window-scroll-buffer (get-buffer "*jupyter-repl-inspect*")))))
;;; Evaluation ;;; Evaluation
(defun jupyter-repl-eval-string (str &optional silently) (defun jupyter-repl-eval-string (str &optional silently)
"Evaluate STR with the `jupyter-repl-current-client'. "Evaluate STR with the `jupyter-repl-current-client's REPL.
The contents of the last cell in the REPL buffer will be replaced Replaces the contents of the last cell in the REPL buffer with
with STR and the last cell executed with the STR before evaluating.
`juptyer-repl-current-client'. After execution, the execution
result is echoed to the *Message* buffer or a new buffer showing
the result is opened if the result output is larger than 10 lines
long.
If optional argument SILENTLY is non-nil, do not replace the If the result of evaluation is more than 10 lines long, a buffer
contents of the last cell and do not run any of the displaying the results is shown. For results less than 10 lines
`jupyter-repl-client' handlers. All that occurs is that STR is long, the result is displayed in the minibuffer.
sent to the kernel for execution and the results of the execution
displayed without anything showing up in the REPL buffer." If a prefix argument is given, SILENTLY evaluate STR without any
modification to the REPL buffer. Only the results of evaluation
are displayed."
(interactive (list (read-string "Jupyter Eval: ") current-prefix-arg)) (interactive (list (read-string "Jupyter Eval: ") current-prefix-arg))
(unless (buffer-local-value (unless (buffer-local-value
'jupyter-repl-current-client (current-buffer)) 'jupyter-repl-current-client (current-buffer))
@ -1848,7 +1816,7 @@ If the current region is active send the current region using
(defun jupyter-repl-on-kernel-restart (client msg) (defun jupyter-repl-on-kernel-restart (client msg)
"Update the REPL buffer after CLIENT restarts. "Update the REPL buffer after CLIENT restarts.
If MSG is a startup message, insert the banner of the kernel, If MSG is a startup message, insert the banner of the kernel,
syncrhronize the execution state, and insert a new input prompt." synchronize the execution state, and insert a new input prompt."
(prog1 nil (prog1 nil
(with-jupyter-repl-buffer client (with-jupyter-repl-buffer client
(when (jupyter-message-status-starting-p msg) (when (jupyter-message-status-starting-p msg)
@ -2077,10 +2045,7 @@ When the kernel restarts, insert a new prompt."
#'jupyter-repl-on-kernel-restart jupyter-repl-current-client))) #'jupyter-repl-on-kernel-restart jupyter-repl-current-client)))
(defun jupyter-repl-initialize-fontification () (defun jupyter-repl-initialize-fontification ()
"Initialize fontification for the current REPL buffer. "Initialize fontification for the current REPL buffer."
Extract `font-lock-defaults' from the `jupyter-repl-lang-buffer',
set it as the `font-lock-defaults' of the `current-buffer' and
call the function `font-lock-mode'."
(let (fld sff) (let (fld sff)
(with-jupyter-repl-lang-buffer (with-jupyter-repl-lang-buffer
(setq fld font-lock-defaults (setq fld font-lock-defaults
@ -2125,10 +2090,7 @@ it."
(add-text-properties start (point) '(font-lock-face shadow fontified t))))) (add-text-properties start (point) '(font-lock-face shadow fontified t)))))
(defun jupyter-repl-sync-execution-state () (defun jupyter-repl-sync-execution-state ()
"Synchronize the state of the kernel in `jupyter-repl-current-client'. "Synchronize the `jupyter-repl-current-client's kernel state."
Set the execution-count slot of `jupyter-repl-current-client' to
1+ the execution count of the client's kernel. Block until the
kernel goes idle for our request."
(let* ((client jupyter-repl-current-client) (let* ((client jupyter-repl-current-client)
(req (let ((jupyter-inhibit-handlers t)) (req (let ((jupyter-inhibit-handlers t))
(jupyter-send-execute-request client :code "" :silent t)))) (jupyter-send-execute-request client :code "" :silent t))))
@ -2147,9 +2109,7 @@ kernel goes idle for our request."
;;; `jupyter-repl-interaction-mode' ;;; `jupyter-repl-interaction-mode'
(defun jupyter-repl-pop-to-buffer () (defun jupyter-repl-pop-to-buffer ()
"Switch to the REPL buffer associated with the `current-buffer'. "Switch to the REPL buffer of the `jupyter-repl-current-client'."
Switch to the REPL buffer of the `jupyter-repl-current-client'
for the `current-buffer'."
(interactive) (interactive)
(if jupyter-repl-current-client (if jupyter-repl-current-client
(with-jupyter-repl-buffer jupyter-repl-current-client (with-jupyter-repl-buffer jupyter-repl-current-client
@ -2158,10 +2118,9 @@ for the `current-buffer'."
(error "Buffer not associated with a REPL, see `jupyter-repl-associate-buffer'"))) (error "Buffer not associated with a REPL, see `jupyter-repl-associate-buffer'")))
(defun jupyter-repl-available-repl-buffers (&optional mode) (defun jupyter-repl-available-repl-buffers (&optional mode)
"Get a list of REPL buffers that are connected to live kernels. "Return a list of REPL buffers that are connected to live kernels.
If MODE is non-nil, return all REPL buffers whose If MODE is non-nil, return all REPL buffers whose
`jupyter-repl-lang-mode' is MODE. MODE should be the `major-mode' `jupyter-repl-lang-mode' is MODE."
used to edit files of one of the Jupyter kernel languages."
(delq (delq
nil nil
(mapcar (lambda (b) (mapcar (lambda (b)
@ -2175,14 +2134,9 @@ used to edit files of one of the Jupyter kernel languages."
;;;###autoload ;;;###autoload
(defun jupyter-repl-associate-buffer (client) (defun jupyter-repl-associate-buffer (client)
"Associate the `current-buffer' with a REPL CLIENT. "Associate the `current-buffer' with a REPL CLIENT.
The `current-buffer's `major-mode' must be the If the `major-mode' of the `current-buffer' is the
`jupyter-repl-lang-mode' of the CLIENT. CLIENT can either be a `jupyter-repl-lang-mode' of CLIENT, enable
`jupyter-repl-client' or a buffer with a non-nil `jupyter-repl-interaction-mode'."
`jupyter-repl-current-client'.
Associating a buffer with CLIENT involves setting the
buffer-local value of `jupyter-repl-current-client' to CLIENT and
enabling `jupyter-repl-interaction-mode'."
(interactive (interactive
(list (list
(completing-read (completing-read
@ -2211,7 +2165,7 @@ enabling `jupyter-repl-interaction-mode'."
map)) map))
(defun jupyter-repl-propagate-client (orig-fun buffer-or-name &rest args) (defun jupyter-repl-propagate-client (orig-fun buffer-or-name &rest args)
"Propgate the `jupyter-repl-current-client' to other buffers." "Propagate the `jupyter-repl-current-client' to other buffers."
(when jupyter-repl-interaction-mode (when jupyter-repl-interaction-mode
(let ((client jupyter-repl-current-client) (let ((client jupyter-repl-current-client)
(buf (get-buffer buffer-or-name)) (buf (get-buffer buffer-or-name))
@ -2227,9 +2181,15 @@ enabling `jupyter-repl-interaction-mode'."
(define-minor-mode jupyter-repl-interaction-mode (define-minor-mode jupyter-repl-interaction-mode
"Minor mode for interacting with a Jupyter REPL. "Minor mode for interacting with a Jupyter REPL.
Note that for buffers with `jupyter-repl-interaction-mode' When this minor mode is enabled you may evaluate code from the
enabled, any new buffer opened with the same `major-mode' will current buffer using the associated REPL (see
automatically have its buffer associated with the REPL." `jupyter-repl-associate-buffer' to associate a REPL).
In addition any new buffers opened with the same `major-mode' as
the `current-buffer' will automatically have
`jupyter-repl-interaction-mode' enabled for them.
\\{jupyter-repl-interaction-map}"
:group 'jupyter-repl :group 'jupyter-repl
:lighter " JuPy" :lighter " JuPy"
:init-value nil :init-value nil