emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/org 4426d80 3/3: org-element-cache: Fix Phase 1 when ne


From: ELPA Syncer
Subject: [elpa] externals/org 4426d80 3/3: org-element-cache: Fix Phase 1 when new parent overlaps future edits
Date: Fri, 17 Dec 2021 23:57:31 -0500 (EST)

branch: externals/org
commit 4426d8009f14bcb48e91ce5517af3af4f762ab22
Author: Ihor Radchenko <yantar92@gmail.com>
Commit: Ihor Radchenko <yantar92@gmail.com>

    org-element-cache: Fix Phase 1 when new parent overlaps future edits
    
    * lisp/org-element.el (org-element--cache-process-request): New OFFSET
    argument used to correct newly added parents during Phase 1.  The
    `org-element--parse-to' call inside Phase 1 may add new elements to
    cache that intersect with future edits.  Boundaries of these elements
    may be shifted twice, so we have to offset the future shift.
    
    (org-element--cache-sync): New OFFSET argument providing future change
    info to `org-element--cache-process-request'.
    
    (org-element--cache-submit-request): Provide offset value in
    `org-elemnt--cache-sync' call.
    
    (org-element--cache-submit-request):
    (org-element--cache-process-request):
    (org-element--cache-sync): Never use %d format for region boundaries.
    It may be a marker and cause error.  Use %S instead.
    
    (org-element--cache-process-request): Use unique symbols for
    catch-throw.
    
    Fixes 
https://list.orgmode.org/CAFyQvY3Qv5xn-ET83L6Rzg-V1zOVu4y1gt+-_CpfaWNAdt87xA@mail.gmail.com/T/#t
---
 lisp/org-element.el | 111 ++++++++++++++++++++++++++++++++++------------------
 1 file changed, 73 insertions(+), 38 deletions(-)

diff --git a/lisp/org-element.el b/lisp/org-element.el
index 112e335..a617c58 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -5840,7 +5840,7 @@ It is a symbol among nil, t, or a number representing 
smallest level of
 modified headline.  The level considers headline levels both before
 and after the modification.")
 
-(defun org-element--cache-sync (buffer &optional threshold future-change)
+(defun org-element--cache-sync (buffer &optional threshold future-change 
offset)
   "Synchronize cache with recent modification in BUFFER.
 
 When optional argument THRESHOLD is non-nil, do the
@@ -5850,9 +5850,10 @@ then exit.  Otherwise, synchronize cache for as long as
 state.
 
 FUTURE-CHANGE, when non-nil, is a buffer position where changes
-not registered yet in the cache are going to happen.  It is used
-in `org-element--cache-submit-request', where cache is partially
-updated before current modification are actually submitted."
+not registered yet in the cache are going to happen.  OFFSET is the
+change offset.  It is used in `org-element--cache-submit-request',
+where cache is partially updated before current modification are
+actually submitted."
   (when (buffer-live-p buffer)
     (with-current-buffer (or (buffer-base-buffer buffer) buffer)
       ;; Check if the buffer have been changed outside visibility of
@@ -5911,7 +5912,8 @@ The buffer is: %s\n Current command: %S\n Chars modified: 
%S\n Buffer modified:
                 (when next (org-element--request-key next))
                 threshold
                 (unless threshold time-limit)
-                future-change)
+                future-change
+                 offset)
                 ;; Re-assign current and next requests.  It could have
                 ;; been altered during phase 1.
                 (setq request (car org-element--cache-sync-requests)
@@ -5923,7 +5925,7 @@ The buffer is: %s\n Current command: %S\n Chars modified: 
%S\n Buffer modified:
                   ;; or phase 2 requests.  We need to let them know
                   ;; that additional shifting happened ahead of them.
                  (cl-incf (org-element--request-offset next) 
(org-element--request-offset request))
-                  (org-element--cache-log-message "Updating next request 
offset to %d: %s"
+                  (org-element--cache-log-message "Updating next request 
offset to %S: %s"
                                        (org-element--request-offset next)
                                        (let ((print-length 10) (print-level 
3)) (prin1-to-string next)))
                   ;; FIXME: END part of the request only matters for
@@ -5942,7 +5944,7 @@ The buffer is: %s\n Current command: %S\n Chars modified: 
%S\n Buffer modified:
             (setq org-element--cache-sync-keys-value (1+ 
org-element--cache-sync-keys-value))))))))
 
 (defun org-element--cache-process-request
-    (request next-request-key threshold time-limit future-change)
+    (request next-request-key threshold time-limit future-change offset)
   "Process synchronization REQUEST for all entries before NEXT.
 
 REQUEST is a vector, built by `org-element--cache-submit-request'.
@@ -5956,9 +5958,10 @@ stops as soon as a shifted element begins after it.
 When non-nil, TIME-LIMIT is a time value.  Synchronization stops
 after this time or when Emacs exits idle state.
 
-When non-nil, FUTURE-CHANGE is a buffer position where changes
-not registered yet in the cache are going to happen.  See
-`org-element--cache-submit-request' for more information.
+When non-nil, FUTURE-CHANGE is a buffer position where changes not
+registered yet in the cache are going to happen.  OFFSET is the
+changed text length.  See `org-element--cache-submit-request' for more
+information.
 
 Throw `org-element--cache-interrupt' if the process stops before
 completing the request."
@@ -5967,7 +5970,7 @@ completing the request."
                        future-change
                        threshold
                        next-request-key)
-  (catch 'quit
+  (catch 'org-element--cache-quit
     (when (= (org-element--request-phase request) 0)
       ;; Phase 0.
       ;;
@@ -5977,7 +5980,7 @@ completing the request."
       ;; At each iteration, we start again at tree root since
       ;; a deletion modifies structure of the balanced tree.
       (org-element--cache-log-message "Phase 0")
-      (catch 'end-phase
+      (catch 'org-element--cache-end-phase
         (let ((deletion-count 0))
           (while t
            (when (org-element--cache-interrupt-p time-limit)
@@ -6022,23 +6025,23 @@ completing the request."
                                                             
org-element--cache-size
                                                             (log 
org-element--cache-size 2))
                             (org-element-cache-reset)
-                            (throw 'quit t)))
+                            (throw 'org-element--cache-quit t)))
                       ;; Done deleting everthing starting before END.
                       ;; DATA-KEY is the first known element after END.
                       ;; Move on to phase 1.
-                      (org-element--cache-log-message "found element after %d: 
%S::%S"
-                                                      end
-                                                      (org-element-property 
:org-element--cache-sync-key data)
-                                                      
(org-element--format-element data))
+                      (org-element--cache-log-message "found element after %S: 
%S::%S"
+                                           end
+                                           (org-element-property 
:org-element--cache-sync-key data)
+                                           (org-element--format-element data))
                       (setf (org-element--request-key request) data-key)
                       (setf (org-element--request-beg request) pos)
                       (setf (org-element--request-phase request) 1)
-                     (throw 'end-phase nil)))
+                     (throw 'org-element--cache-end-phase nil)))
                ;; No element starting after modifications left in
                ;; cache: further processing is futile.
                 (org-element--cache-log-message "Phase 0 deleted all elements 
in cache after %S!"
-                                                request-key)
-               (throw 'quit t)))))))
+                                     request-key)
+               (throw 'org-element--cache-quit t)))))))
     (when (= (org-element--request-phase request) 1)
       ;; Phase 1.
       ;;
@@ -6087,14 +6090,15 @@ completing the request."
            (setf (org-element--request-key next-request) key)
             (setf (org-element--request-beg next-request) 
(org-element--request-beg request))
            (setf (org-element--request-phase next-request) 1)
-            (throw 'quit t))))
+            (throw 'org-element--cache-quit t))))
       ;; Next element will start at its beginning position plus
       ;; offset, since it hasn't been shifted yet.  Therefore, LIMIT
       ;; contains the real beginning position of the first element to
       ;; shift and re-parent.
-      (let ((limit (+ (org-element--request-beg request) 
(org-element--request-offset request))))
+      (let ((limit (+ (org-element--request-beg request) 
(org-element--request-offset request)))
+            cached-before)
        (cond ((and threshold (> limit threshold))
-               (org-element--cache-log-message "Interrupt: position %d after 
threshold %d" limit threshold)
+               (org-element--cache-log-message "Interrupt: position %S after 
threshold %S" limit threshold)
                (throw 'org-element--cache-interrupt nil))
              ((and future-change (>= limit future-change))
               ;; Changes happened around this element and they will
@@ -6102,18 +6106,49 @@ completing the request."
               ;; and simply proceed with shifting (phase 2) to make
               ;; sure that followup phase 0 request for the recent
               ;; changes can operate on the correctly shifted cache.
-               (org-element--cache-log-message "position %d after future 
change %d" limit future-change)
+               (org-element--cache-log-message "position %S after future 
change %S" limit future-change)
                (setf (org-element--request-parent request) nil)
                (setf (org-element--request-phase request) 2))
              (t
+               (when future-change
+                 ;; Changes happened, but not yet registered after
+                 ;; this element.  However, we a not yet safe to look
+                 ;; at the buffer and parse elements in the cache gap.
+                 ;; Some of the parents to be added to cache may end
+                 ;; after the changes.  Parsing this parents will
+                 ;; assign the :end correct value for cache state
+                 ;; after future-change.  Then, when the future change
+                 ;; is going to be processed, such parent boundary
+                 ;; will be altered unnecessarily.  To avoid this,
+                 ;; we alter the new parents by -OFFSET.
+                 ;; For now, just save last known cached element and
+                 ;; then check all the parents below.
+                 (setq cached-before (org-element--cache-find (1- limit) nil)))
                ;; No relevant changes happened after submitting this
                ;; request.  We are safe to look at the actual Org
                ;; buffer and calculate the new parent.
               (let ((parent (org-element--parse-to (1- limit) nil time-limit)))
-                 (org-element--cache-log-message "New parent at %d: %S::%S"
-                                                 limit
-                                                 (org-element-property 
:org-element--cache-sync-key parent)
-                                                 (org-element--format-element 
parent))
+                 (when future-change
+                   ;; Check all the newly added parents to not
+                   ;; intersect with future change.
+                   (let ((up parent))
+                     (while (and up
+                                 (or (not cached-before)
+                                     (> (org-element-property :begin up)
+                                        (org-element-property :begin 
cached-before))))
+                       (when (> (org-element-property :end up) future-change)
+                         ;; Offset future cache request.
+                         (org-element--cache-shift-positions
+                          up (- offset)
+                          (if (and (org-element-property :robust-begin up)
+                                   (org-element-property :robust-end up))
+                              '(:contents-end :end :robust-end)
+                            '(:contents-end :end))))
+                       (setq up (org-element-property :parent up)))))
+                 (org-element--cache-log-message "New parent at %S: %S::%S"
+                                      limit
+                                      (org-element-property 
:org-element--cache-sync-key parent)
+                                      (org-element--format-element parent))
                  (setf (org-element--request-parent request) parent)
                 (setf (org-element--request-phase request) 2))))))
     ;; Phase 2.
@@ -6138,7 +6173,7 @@ completing the request."
       ;; No re-parenting nor shifting planned: request is over.
       (when (and (not parent) (zerop offset))
         (org-element--cache-log-message "Empty offset. Request completed.")
-        (throw 'quit t))
+        (throw 'org-element--cache-quit t))
       (while node
        (let* ((data (avl-tree--node-data node))
               (key (org-element--cache-key data)))
@@ -6164,7 +6199,7 @@ completing the request."
                                (> (org-element-property :begin 
(org-element--request-parent next-request))
                                   (org-element-property :begin parent)))
                     (setf (org-element--request-parent next-request) parent)))
-                (throw 'quit t))
+                (throw 'org-element--cache-quit t))
              ;; Handle interruption request.  Update current request.
              (when (or exit-flag (org-element--cache-interrupt-p time-limit))
                 (org-element--cache-log-message "Interrupt: %s" (if exit-flag 
"threshold" "time limit"))
@@ -6185,7 +6220,7 @@ completing the request."
                (while (and parent
                            (<= (org-element-property :end parent) begin))
                  (setq parent (org-element-property :parent parent)))
-               (cond ((and (not parent) (zerop offset)) (throw 'quit nil))
+               (cond ((and (not parent) (zerop offset)) (throw 
'org-element--cache-quit nil))
                       ;; Consider scenario when DATA lays within
                       ;; sensitive lines of PARENT that was found
                       ;; during phase 2.  For example:
@@ -6245,7 +6280,7 @@ completing the request."
                          ;; else.
                          (org-element--cache-warn "Added org-data parent to 
non-headline element: %S\nIf this warning appears regularly, please report it 
to Org mode mailing list (M-x org-submit-bug-report)." data)
                          (org-element-cache-reset)
-                         (throw 'quit t))
+                         (throw 'org-element--cache-quit t))
                       (org-element-put-property data :parent parent)
                       (let ((s (org-element-property :structure parent)))
                         (when (and s (org-element-property :structure data))
@@ -6264,9 +6299,9 @@ completing the request."
                           (pop stack)))))))
       ;; We reached end of tree: synchronization complete.
       t))
-  (org-element--cache-log-message "org-element-cache: Finished process. The 
cache size is %d. The remaining sync requests: %S"
-                                  org-element--cache-size
-                                  (let ((print-level 2)) (prin1-to-string 
org-element--cache-sync-requests))))
+  (org-element--cache-log-message "org-element-cache: Finished process. The 
cache size is %S. The remaining sync requests: %S"
+                       org-element--cache-size
+                       (let ((print-level 2)) (prin1-to-string 
org-element--cache-sync-requests))))
 
 (defsubst org-element--open-end-p (element)
   "Check if ELEMENT in current buffer contains extra blank lines after
@@ -6854,7 +6889,7 @@ change, as an integer."
         ;; yet to the otherwise correct part of the cache (i.e, before
         ;; the first request).
         (org-element--cache-log-message "Adding new phase 0 request")
-        (when next (org-element--cache-sync (current-buffer) end beg))
+        (when next (org-element--cache-sync (current-buffer) end beg offset))
         (let ((first (org-element--cache-for-removal beg end offset)))
          (if first
              (push (let ((first-beg (org-element-property :begin first))
@@ -6912,13 +6947,13 @@ change, as an integer."
            ;; Simply shift additional elements, if any, by OFFSET.
            (if org-element--cache-sync-requests
                 (progn
-                  (org-element--cache-log-message "Nothing to remove. Updating 
offset of the next request by ๐™%d: %S"
+                  (org-element--cache-log-message "Nothing to remove. Updating 
offset of the next request by ๐™%S: %S"
                                        offset
                                        (let ((print-level 3))
                                          (car 
org-element--cache-sync-requests)))
                  (cl-incf (org-element--request-offset (car 
org-element--cache-sync-requests))
                           offset))
-              (org-element--cache-log-message "Nothing to remove. No elements 
in cache after %d. Terminating."
+              (org-element--cache-log-message "Nothing to remove. No elements 
in cache after %S. Terminating."
                                    end))))))
     (setq org-element--cache-change-warning nil)))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]