branch: externals/org-transclusion commit 43c478c6ec9e8a9f713baa3736f5e9bc4e72c8ca Author: Noboru Ota <m...@nobiot.com> Commit: Noboru Ota <m...@nobiot.com>
fix: heuristics to identify & break infinite loop on save Reported in GitHub issues #109 #177. I cannot reproduce the issue myself so far. I am put in place (1) small preventive measure and (2) heuristics to defect and break the infinite loop on save-buffer. (1) Org-transclusion (OT) tries not to save the transcluded buffer content and instead save only the #+transclude keyword line to the file. To achieve this, OT uses 'before-' and 'after-save-hook' to remove-all the transclusions and then add-all them. This operation relies on the returned value of the point from 'org-transclusion-remove' function. In this commit, the point (integer) is changed to marker. This way, any arbitrary buffer change between these remove-all and add-all processes can have less impact on the moving points of reference -- makers automatically move to adopt to the new buffer state. I suspect something like 'whitespace-cleanup` put in 'before-save-buffer' might dislocate the positions in some situations. This preventive measure hopefully preempt the issues. (2) The heuristics is simple but should work if there is an unexpected number loop happens. Since it is simply compare the length of a list, and the 'dolist' loops for the same list, logically this should be redundant; however, since the infinite loop itself to me is anomaly, this heuristics might catch the issue and break the loop. As you can see, both attempts are not based on causal analysis but rather "stabbing in the dark" heuristics. --- org-transclusion.el | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/org-transclusion.el b/org-transclusion.el index c039d0ae48..ede369662c 100644 --- a/org-transclusion.el +++ b/org-transclusion.el @@ -17,7 +17,7 @@ ;; Author: Noboru Ota <m...@nobiot.com> ;; Created: 10 October 2020 -;; Last modified: 09 May 2023 +;; Last modified: 10 May 2023 ;; URL: https://github.com/nobiot/org-transclusion ;; Keywords: org-mode, transclusion, writing @@ -528,7 +528,7 @@ When success, return the beginning point of the keyword re-inserted." (when mkr-at-beg (move-marker mkr-at-beg beg)) ;; Go back to the beginning of the inserted keyword line (goto-char beg)) - beg)) + (move-marker (make-marker) beg))) (message "Nothing done. No transclusion exists here.") nil)) (defun org-transclusion-detach () @@ -558,16 +558,16 @@ function to work only on the narrowed region you are in, leaving the rest of the buffer unchanged." (interactive "P") (save-restriction - (let ((marker (move-marker (make-marker) (point))) - match point list) + (let ((current-marker (move-marker (make-marker) (point))) + match removed-marker list) (unless narrowed (widen)) (goto-char (point-min)) (while (setq match (text-property-search-forward 'org-transclusion-type)) (goto-char (prop-match-beginning match)) - (setq point (org-transclusion-remove)) - (when point (push point list))) - (goto-char marker) - (move-marker marker nil) ; point nowhere for GC + (setq removed-marker (org-transclusion-remove)) + (when removed-marker (push removed-marker list))) + (goto-char current-marker) + (move-marker current-marker nil) ; point nowhere for GC list))) (defun org-transclusion-refresh (&optional detach) @@ -735,18 +735,24 @@ set in `before-save-hook'. It also move the point back to (progn ;; Assume the list is in descending order. ;; pop and do from the bottom of buffer - (dolist (p org-transclusion-remember-transclusions) - (save-excursion - (goto-char p) - (org-transclusion-add))) - ;; After save and adding all transclusions, the modified flag should be - ;; set to nil - (restore-buffer-modified-p nil) - (when org-transclusion-remember-point - (goto-char org-transclusion-remember-point)) + (let ((do-length (length org-transclusion-remember-transclusions)) + (do-count 0)) + (dolist (p org-transclusion-remember-transclusions) + (save-excursion + (goto-char p) + (org-transclusion-add) + (move-marker p nil) + (setq do-count (1+ do-count)) + (when (> do-count do-length) + (error "org-transclusion: Aborting. You may be in an infinite loop")))) + ;; After save and adding all transclusions, the modified flag should be + ;; set to nil + (restore-buffer-modified-p nil) + (when org-transclusion-remember-point + (goto-char org-transclusion-remember-point)))) (progn (setq org-transclusion-remember-point nil) - (setq org-transclusion-remember-transclusions nil))))) + (setq org-transclusion-remember-transclusions nil)))) (defun org-transclusion-before-kill () "Remove transclusions before `kill-buffer' or `kill-emacs'.