@@ -592,114 +592,148 @@ the request immediately instead of attempting to queue it."
592
592
(oset client last-queued-request req)))
593
593
(jupyter-return req))))))
594
594
595
- ; ;; Completion in code blocks
595
+ ; ;; Caching the current source block's information
596
596
597
597
(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 ))
642
653
643
654
(defmacro jupyter-org-when-in-src-block (&rest body )
644
655
" Evaluate BODY when inside a Jupyter source block.
645
656
Return the result of BODY when it is evaluated, otherwise nil is
646
657
returned."
647
658
(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)))
659
691
660
692
(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)))))
673
707
674
708
(defmacro jupyter-org-with-src-block-client (&rest body )
675
709
" 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.
683
718
684
719
In addition to evaluating BODY with an active Jupyter client set,
685
720
the `syntax-table' will be set to that of the REPL buffer's."
686
721
(declare (debug (body)))
687
722
`(jupyter-org--with-src-block-client
688
723
(lambda () ,@body )))
689
724
725
+ ; ;; Completion in code blocks
726
+
690
727
(cl-defmethod jupyter-code-context ((_type (eql inspect))
691
728
&context (major-mode org-mode))
692
729
(when (org-in-src-block-p 'inside )
693
730
(jupyter-line-context)))
694
731
695
732
(cl-defmethod jupyter-code-context ((_type (eql completion))
696
733
&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))))
703
737
704
738
(defun jupyter-org-completion-at-point ()
705
739
(jupyter-org-with-src-block-client
0 commit comments