Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: infinite loop / org-element--cache corruption during live sync #243

Open
akashpal-21 opened this issue May 4, 2024 · 4 comments
Open

Comments

@akashpal-21
Copy link

org-element-cache is corrupted sometimes during live sync which causes emacs to infinitely generate errors

since #' org-transclusion-content-org-buffer-or-element uses #'org-element-context this causes org-transclusion to go into infinite recursion.

Adding video to show the problem.

untitled.mp4
@nobiot
Copy link
Owner

nobiot commented May 6, 2024

What is the Org version you use? I will need to look at the other (older) infinite loop issue you investigated extensively first, but if the built-in org-element has an influence here, the version info may be relevant.

@nobiot
Copy link
Owner

nobiot commented May 6, 2024

org-element-cache-reset opens the pandora's box for more bugs

Where is this done? I don't recall me doing it explicitly.

@akashpal-21
Copy link
Author

akashpal-21 commented May 7, 2024

One Solution

Improvements are requested.
With async clone propagation org-element--cache-sync works appropriately.

Solution is yet hacky - will require refinement from someone more experienced.

(defun patch/text-clone-live-sync (ol1 after beg end &optional _len)
    (when (and after                                     
	       (not text-clone-live-sync-in-progress)   
	       (overlay-start ol1)                      
	       (<= beg end))                            

      (save-excursion

	;; Now go ahead and update the clones.
	(let ((head (- beg (overlay-start ol1)))       
	      (tail (- (overlay-end ol1) end))         
	      (str (buffer-substring-no-properties beg end))
	      (text-clone-live-sync-in-progress t))   

	  ;; Iterate over each clone of the overlay ol1.
	  (dolist (ol2 (overlay-get ol1 'text-clones))
	    ;; Call the helper function to perform actions within the context of the buffer of the clone ol2.
	    (text-clone-live-sync--async-clone ol1 ol2 head tail str))))))

(defun text-clone-live-sync--async-clone (ol1 ol2 head tail str)
  "Update a clone with synchronized text asynchronously."
  ;; Set `run-clone` to t
  (overlay-put ol2 'run-clone t)
  ;; Initiate timer to run asynchronously
  (run-with-timer 0 nil
		  (lambda (ol1 ol2 head tail str)
		    (with-current-buffer (overlay-buffer ol2)
		      ;; Check if `run-clone` is set to t
		      (when (overlay-get ol2 'run-clone)
			;; Set `run-clone` to nil before proceeding to prevent infinite loop
			(overlay-put ol2 'run-clone nil)
			(save-restriction
			  (widen)
			  (let ((oe (overlay-end ol2)))
			    (unless (or (eq ol1 ol2) (null oe)) 
			      (let ((mod-beg (+ (overlay-start ol2) head)))
				(goto-char (- oe tail))
				(if (not (> mod-beg (point)))  
				    (progn                     
				      (save-excursion (insert str)) 
				      (delete-region mod-beg (point))) 
				  (user-error "No live-sync done.  \
The text strings in the overlays are not identical"))
				)))))))
		  ol1 ol2 head tail str))

(advice-add 'text-clone-live-sync :override #'patch/text-clone-live-sync)

@akashpal-21
Copy link
Author

akashpal-21 commented May 9, 2024

Radchenko also suggested a more trivial way to ensure that before-change-functions run appropriately within the context of the second buffer -- it is to set inhibit-modification-hooks to nil before proceeding -- but we are this way out maneuvering a limitation that has been placed by emacs - I do not know why they have done this, so for the sake of caution I had resorted to using a run-with-timer 0 but also the following works. Please check documentation of before-change-functions for more information about it.

(defun patch/text-clone-live-sync (ol1 after beg end &optional _len)
  (when (and after
             (not text-clone-live-sync-in-progress)
             (overlay-start ol1)
             (<= beg end))
    (save-excursion
      ;; Now go ahead and update the clones.
      (let ((head (- beg (overlay-start ol1)))
            (tail (- (overlay-end ol1) end))
            (str (buffer-substring-no-properties beg end)) ;changed to no-properties
            (text-clone-live-sync-in-progress t)
	    ;; patch: set inhibit-modification-hooks to nil
	    ;; to ensure befre-change-functions run appropriately
	    ;; within the context of clone buffer.
	    (inhibit-modification-hooks nil))
        (dolist (ol2 (overlay-get ol1 'text-clones))
          (with-current-buffer (overlay-buffer ol2)
            (save-restriction
              (widen)
              (let ((oe (overlay-end ol2)))
                (unless (or (eq ol1 ol2) (null oe))
                  (let ((mod-beg (+ (overlay-start ol2) head)))
                    (goto-char (- oe tail))
                    (if (not (> mod-beg (point)))
                        (progn
                          (save-excursion (insert str))
                          (delete-region mod-beg (point)))
                      (user-error "No live-sync done.  \
The text strings in the overlays are not identical"))))))))))))

-- or more simply

(defun patch/text-clone-live-sync (fn &rest args)
  (let ((inhibit-modification-hooks nil))
    (apply fn args)))
(advice-add 'text-clone-live-sync :around #'patch/text-clone-live-sync)

@akashpal-21 akashpal-21 reopened this May 12, 2024
@akashpal-21 akashpal-21 changed the title bug: infinite recursion / org-element corruption during live sync bug: infinite loop / org-element--cache corruption during live sync May 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants