Skip to content

Commit f2092cc

Browse files
committed
Refactor source block caching
* jupyter-org-client.el (jupyter-org--src-block-cache): Allow a new form for the cache, to indicate that it is invalid but still keep the information around for potential use as information of the most recently visited source block. (jupyter-org--src-block-beg, jupyter-org--src-block-end): Remove. (jupyter-org--same-src-block-p): Rename to `jupyter-org--at-cached-src-block-p`. (jupyter-org--set-current-src-block): Rename to `jupyter-org--set-src-block-cache`. Handle invalidation semantics. (jupyter-org-with-src-block-bounds): New macro. (jupyter-org-src-block-params): New function. (jupyter-org-when-in-src-block) (jupyter-org--with-src-block-client): Update to use new/modified functions. (jupyter-org-with-src-block-client): Rework documentation. (jupyter-code-context): Remove use of removed functions.
1 parent 5566a1d commit f2092cc

File tree

1 file changed

+115
-81
lines changed

1 file changed

+115
-81
lines changed

jupyter-org-client.el

+115-81
Original file line numberDiff line numberDiff line change
@@ -592,114 +592,148 @@ the request immediately instead of attempting to queue it."
592592
(oset client last-queued-request req)))
593593
(jupyter-return req))))))
594594

595-
;;; Completion in code blocks
595+
;;; Caching the current source block's information
596596

597597
(defvar jupyter-org--src-block-cache nil
598-
"A list of three elements (SESSION BEG END).
599-
SESSION is the Jupyter session to use for completion requests for
600-
a code block between BEG and END.
601-
602-
BEG and END are the bounds of the source block which made the
603-
most recent completion request.")
604-
605-
(defsubst jupyter-org--src-block-beg ()
606-
(nth 1 jupyter-org--src-block-cache))
607-
608-
(defsubst jupyter-org--src-block-end ()
609-
(nth 2 jupyter-org--src-block-cache))
610-
611-
(defun jupyter-org--same-src-block-p ()
612-
(when jupyter-org--src-block-cache
613-
(cl-destructuring-bind (_ beg end)
614-
jupyter-org--src-block-cache
615-
(and
616-
(marker-position beg)
617-
(marker-position end)
618-
(<= beg (point) end)))))
619-
620-
(defun jupyter-org--set-current-src-block ()
621-
(unless (jupyter-org--same-src-block-p)
622-
(let* ((el (org-element-at-point))
623-
(lang (org-element-property :language el)))
624-
(when (org-babel-jupyter-language-p lang)
625-
(let* ((info (org-babel-get-src-block-info t el))
626-
(params (nth 2 info))
627-
(beg (save-excursion
628-
(goto-char (org-element-property :post-affiliated el))
629-
(line-beginning-position 2)))
630-
(end (save-excursion
631-
(goto-char (org-element-property :end el))
632-
(skip-chars-backward "\r\n")
633-
(line-beginning-position))))
634-
(unless jupyter-org--src-block-cache
635-
(setq jupyter-org--src-block-cache
636-
(list nil (point-marker) (point-marker)))
637-
;; Move the end marker when text is inserted
638-
(set-marker-insertion-type (nth 2 jupyter-org--src-block-cache) t))
639-
(setf (nth 0 jupyter-org--src-block-cache) params)
640-
(cl-callf move-marker (nth 1 jupyter-org--src-block-cache) beg)
641-
(cl-callf move-marker (nth 2 jupyter-org--src-block-cache) end))))))
598+
"A list (PARAMS BEG END) of most recently visited source block.
599+
PARAMS is the source block parameters of the Jupyter source block
600+
between BEG and END. BEG and END are markers.
601+
602+
Can also take the form (invalid PARAMS BEG END) which means that the
603+
cache may need to be recomputed.")
604+
605+
(defun jupyter-org--at-cached-src-block-p ()
606+
(pcase jupyter-org--src-block-cache
607+
(`(invalid . ,_) nil)
608+
(`(,_ ,beg ,end)
609+
(and
610+
(marker-position beg)
611+
(marker-position end)
612+
(<= beg (point) end)))))
613+
614+
(defun jupyter-org--set-src-block-cache ()
615+
"Set the src-block cache.
616+
If set successfully or if `point' is already inside the cached
617+
source block, return non-nil. Otherwise, when `point' is not
618+
inside a Jupyter src-block, return nil."
619+
(unless jupyter-org--src-block-cache
620+
(setq jupyter-org--src-block-cache
621+
(list (list 'invalid nil (make-marker)
622+
(let ((end (make-marker)))
623+
;; Move the end marker when text is inserted
624+
(set-marker-insertion-type end t)
625+
end)))))
626+
(if (org-in-src-block-p 'inside)
627+
(or (jupyter-org--at-cached-src-block-p)
628+
(when-let* ((el (org-element-at-point))
629+
(info (and (eq (org-element-type el) 'src-block)
630+
(org-babel-jupyter-language-p
631+
(org-element-property :language el))
632+
(org-babel-get-src-block-info t el)))
633+
(params (nth 2 info)))
634+
(when (eq (car jupyter-org--src-block-cache) 'invalid)
635+
(pop jupyter-org--src-block-cache))
636+
(pcase-let (((and cache `(,_ ,beg ,end))
637+
jupyter-org--src-block-cache))
638+
(setcar cache params)
639+
(save-excursion
640+
(goto-char (org-element-property :post-affiliated el))
641+
(move-marker beg (line-beginning-position 2))
642+
(goto-char (org-element-property :end el))
643+
(skip-chars-backward "\r\n")
644+
(move-marker end (line-beginning-position))))
645+
t))
646+
;; Invalidate cache when going outside of a source block. This
647+
;; way if the language of the block changes we don't end up using
648+
;; the cache since it is only used for Jupyter blocks.
649+
(pcase jupyter-org--src-block-cache
650+
((and `(,x . ,_) (guard (not (eq x 'invalid))))
651+
(push 'invalid jupyter-org--src-block-cache)))
652+
nil))
642653

643654
(defmacro jupyter-org-when-in-src-block (&rest body)
644655
"Evaluate BODY when inside a Jupyter source block.
645656
Return the result of BODY when it is evaluated, otherwise nil is
646657
returned."
647658
(declare (debug (body)))
648-
`(if (not (org-in-src-block-p 'inside))
649-
;; Invalidate cache when going outside of a source block. This way if
650-
;; the language of the block changes we don't end up using the cache
651-
;; since it is only used for Jupyter blocks.
652-
(when jupyter-org--src-block-cache
653-
(set-marker (nth 1 jupyter-org--src-block-cache) nil)
654-
(set-marker (nth 2 jupyter-org--src-block-cache) nil)
655-
(setq jupyter-org--src-block-cache nil))
656-
(jupyter-org--set-current-src-block)
657-
(when (jupyter-org--same-src-block-p)
658-
,@body)))
659+
`(when (jupyter-org--set-src-block-cache)
660+
,@body))
661+
662+
(defmacro jupyter-org-with-src-block-bounds (beg end &rest body)
663+
"With BEG and END set to the bounds of the current src-block evaluate BODY.
664+
BODY is only evaluated when the current source block is a Jupyter
665+
source block and `point' is within its contents. Returns the
666+
result of BODY or nil when it isn't evaluated."
667+
(declare (indent 2) (debug (body)))
668+
`(jupyter-org-when-in-src-block
669+
(pcase-let ((`(,_ ,,beg ,,end) jupyter-org--src-block-cache))
670+
,@body)))
671+
672+
(defun jupyter-org-src-block-params (&optional previous)
673+
"Return the src-block parameters for the current Jupyter src-block.
674+
If PREVIOUS is non-nil and `point' is not in a Jupyter source
675+
block, return the parameters of the most recently visited source
676+
block, but only if it was in the same buffer. Otherwise return
677+
nil."
678+
(jupyter-org--set-src-block-cache)
679+
(pcase jupyter-org--src-block-cache
680+
((and (and (guard (and previous
681+
(not (jupyter-org--at-cached-src-block-p)))))
682+
`(invalid ,params ,beg . ,_)
683+
(guard (eq (marker-buffer beg) (current-buffer))))
684+
;; NOTE There are probably cases where the parameters could no
685+
;; longer be valid, hence the invalid tag. This is mainly for
686+
;; the purposes of creating a mode line according to
687+
;; `jupyter-org-interaction-mode-line-display-most-recent'.
688+
params)
689+
(`(invalid . ,_) nil)
690+
(`(,params . ,_) params)))
659691

660692
(defun jupyter-org--with-src-block-client (thunk)
661-
(jupyter-org-when-in-src-block
662-
(let ((params (car jupyter-org--src-block-cache)))
663-
(when (or jupyter-org-auto-connect
664-
(org-babel-jupyter-session-initiated-p params 'noerror))
665-
(let* ((buffer (org-babel-jupyter-initiate-session
666-
(alist-get :session params) params))
667-
(jupyter-current-client
668-
(buffer-local-value 'jupyter-current-client buffer))
669-
(syntax (jupyter-kernel-language-syntax-table
670-
jupyter-current-client)))
671-
(with-syntax-table syntax
672-
(funcall thunk)))))))
693+
(when-let* ((params (jupyter-org-src-block-params))
694+
(buffer
695+
(and (or jupyter-org-auto-connect
696+
(org-babel-jupyter-session-initiated-p
697+
params 'noerror))
698+
(org-babel-jupyter-initiate-session
699+
(alist-get :session params) params)))
700+
(client (or (buffer-local-value
701+
'jupyter-current-client buffer)
702+
(error "No client in session buffer!")))
703+
(syntax (jupyter-kernel-language-syntax-table client)))
704+
(let ((jupyter-current-client client))
705+
(with-syntax-table syntax
706+
(funcall thunk)))))
673707

674708
(defmacro jupyter-org-with-src-block-client (&rest body)
675709
"Evaluate BODY with `jupyter-current-client' set to the session's client.
676-
A client is initialized if needed when `jupyter-org-auto-connect'
677-
is non-nil. When that variable is nil and no client is present
678-
for the source block, don't evaluate BODY and return nil.
679-
680-
If `point' is not inside the code of a Jupyter source block, BODY
681-
is not evaluated and nil is returned. Return the result of BODY
682-
when it is evaluated.
710+
BODY is evaluate and its result returned only when the client
711+
associated with the source block is connected to its kernel and
712+
`point' is within the contents of the source block. If no client
713+
exists for the session yet and `jupyter-org-auto-connect' is
714+
non-nil, a new client is initiated for the session before
715+
evaluating BODY. When `jupyter-org-auto-connect' is nil and
716+
there is no client or when `point' is not in a Jupyter source
717+
block, don't evaluate BODY and return nil.
683718
684719
In addition to evaluating BODY with an active Jupyter client set,
685720
the `syntax-table' will be set to that of the REPL buffer's."
686721
(declare (debug (body)))
687722
`(jupyter-org--with-src-block-client
688723
(lambda () ,@body)))
689724

725+
;;; Completion in code blocks
726+
690727
(cl-defmethod jupyter-code-context ((_type (eql inspect))
691728
&context (major-mode org-mode))
692729
(when (org-in-src-block-p 'inside)
693730
(jupyter-line-context)))
694731

695732
(cl-defmethod jupyter-code-context ((_type (eql completion))
696733
&context (major-mode org-mode))
697-
;; Always called from within a valid code block. See
698-
;; `jupyter-org-completion-at-point'.
699-
(list (buffer-substring-no-properties
700-
(jupyter-org--src-block-beg)
701-
(jupyter-org--src-block-end))
702-
(- (point) (jupyter-org--src-block-beg))))
734+
(jupyter-org-with-src-block-bounds beg end
735+
(list (buffer-substring-no-properties beg end)
736+
(- (point) beg))))
703737

704738
(defun jupyter-org-completion-at-point ()
705739
(jupyter-org-with-src-block-client

0 commit comments

Comments
 (0)