@@ -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.
645656Return the result of BODY when it is evaluated, otherwise nil is
646657returned."
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
684719In addition to evaluating BODY with an active Jupyter client set,
685720the `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