summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2014-06-18 00:11:44 +0200
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2014-06-19 22:32:23 +0200
commit8e49c823fd0fd9cfd0e541a5a88011795d2a855e (patch)
tree4edee38b6e274122d0dca1b25e0c8c399c50770d
parentb2f200f0a1e259547927ea46dc16b4ed693b9a3b (diff)
downloadorg-mode-8e49c823fd0fd9cfd0e541a5a88011795d2a855e.tar.gz
org-element: Optimize cache
* lisp/org-element.el (org-element--cache-for-removal): New function. (org-element--cache-submit-request): Do not synchronize cache when changes can be merged with next request. This shortcut is particularly useful when many changes happen in the same area, which is expensive to parse (e.g., a large list).
-rw-r--r--lisp/org-element.el153
1 files changed, 85 insertions, 68 deletions
diff --git a/lisp/org-element.el b/lisp/org-element.el
index d5ab563..280cf59 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -5491,79 +5491,96 @@ that range. See `after-change-functions' for more information."
;; Activate a timer to process the request during idle time.
(org-element--cache-set-timer (current-buffer)))))
+(defun org-element--cache-for-removal (beg end offset)
+ "Return first element to remove from cache.
+
+BEG and END are buffer positions delimiting buffer modifications.
+OFFSET is the size of the changes.
+
+Returned element is usually the first element in cache containing
+any position between BEG and END. As an exception, greater
+elements around the changes that are robust to contents
+modifications are preserved and updated according to the
+changes."
+ (let* ((elements (org-element--cache-find (1- beg) 'both))
+ (before (car elements))
+ (after (cdr elements)))
+ (if (not before) after
+ (let ((up before))
+ (while (setq up (org-element-property :parent up))
+ (if (and (memq (org-element-type up)
+ '(center-block
+ drawer dynamic-block inlinetask
+ property-drawer quote-block special-block))
+ (<= (org-element-property :contents-begin up) beg)
+ (> (org-element-property :contents-end up) end))
+ ;; UP is a robust greater element containing changes.
+ ;; We only need to extend its ending boundaries and
+ ;; those of all its parents.
+ (while up
+ (org-element--cache-shift-positions
+ up offset '(:contents-end :end))
+ (setq up (org-element-property :parent up)))
+ (setq before up)))
+ ;; We're at top level element containing ELEMENT: if it's
+ ;; altered by buffer modifications, it is first element in
+ ;; cache to be removed. Otherwise, that first element is the
+ ;; following one.
+ (if (< (org-element-property :end before) beg) after before)))))
+
(defun org-element--cache-submit-request (beg end offset)
"Submit a new cache synchronization request for current buffer.
BEG and END are buffer positions delimiting the minimal area
where cache data should be removed. OFFSET is the size of the
change, as an integer."
- ;; Make sure buffer positions in cache are correct until END. This
- ;; also ensures that pending cache requests have their phases
- ;; properly ordered. We need to provide OFFSET as optional
- ;; parameter since current modifications are not known yet to the
- ;; otherwise correct part of the cache (i.e, before the first
- ;; request).
- (org-element--cache-sync (current-buffer) end offset)
- (let ((first-element
- ;; Find the position of the first element in cache to remove.
- ;;
- ;; Partially modified elements will be removed during request
- ;; processing. As an exception, greater elements around the
- ;; changes that are robust to contents modifications are
- ;; preserved.
- ;;
- ;; We look just before BEG because an element ending at BEG
- ;; needs to be removed too.
- (let* ((elements (org-element--cache-find (1- beg) 'both))
- (before (car elements))
- (after (cdr elements)))
- (if (not before) after
- (let ((up before))
- (while (setq up (org-element-property :parent up))
- (if (and (memq (org-element-type up)
- '(center-block
- drawer dynamic-block inlinetask
- property-drawer quote-block special-block))
- (<= (org-element-property :contents-begin up) beg)
- (> (org-element-property :contents-end up) end))
- ;; UP is a greater element that is wrapped around
- ;; the changes. We only need to extend its
- ;; ending boundaries and those of all its
- ;; parents.
- (while up
- (org-element--cache-shift-positions
- up offset '(:contents-end :end))
- (setq up (org-element-property :parent up)))
- (setq before up)))
- ;; We're at top level element containing ELEMENT: if
- ;; it's altered by buffer modifications, it is first
- ;; element in cache to be removed. Otherwise, that
- ;; first element is the following one.
- (if (< (org-element-property :end before) beg) after before))))))
- (cond
- ;; Changes happened before the first known element. Shift the
- ;; rest of the cache.
- ((and first-element (> (org-element-property :begin first-element) end))
- (push (vector (org-element--cache-key first-element) nil nil offset nil 2)
- org-element--cache-sync-requests))
- ;; There is at least an element to remove. Find position past
- ;; every element containing END.
- (first-element
- (if (> (org-element-property :end first-element) end)
- (setq end (org-element-property :end first-element))
- (let ((element (org-element--cache-find end)))
- (setq end (org-element-property :end element))
- (let ((up element))
- (while (and (setq up (org-element-property :parent up))
- (>= (org-element-property :begin up) beg))
- (setq end (org-element-property :end up))))))
- (push (vector (org-element--cache-key first-element)
- (org-element-property :begin first-element)
- end offset nil 0)
- org-element--cache-sync-requests))
- ;; No element to remove. No need to re-parent either. Simply
- ;; shift additional elements, if any, by OFFSET.
- (org-element--cache-sync-requests
- (incf (aref (car org-element--cache-sync-requests) 2) offset)))))
+ (let ((next (car org-element--cache-sync-requests)))
+ (if (and next
+ (zerop (aref next 5))
+ (let ((offset (aref next 3)))
+ (and (>= (+ (aref next 2) offset) end)
+ (<= (+ (aref next 1) offset) end))))
+ ;; Current changes can be merged with first sync request: we
+ ;; can save a partial cache synchronization.
+ (progn
+ (incf (aref next 2) offset)
+ (incf (aref next 3) offset)
+ (when (> (aref next 1) beg)
+ (let ((first (org-element--cache-for-removal beg end offset)))
+ (when first
+ (aset next 0 (org-element--cache-key first))
+ (aset next 1 (org-element-property :begin first))))))
+ ;; Ensure cache is correct up to END. Also make sure that NEXT,
+ ;; if any, is no longer a 0-phase request, thus ensuring that
+ ;; phases are properly ordered. We need to provide OFFSET as
+ ;; optional parameter since current modifications are not known
+ ;; yet to the otherwise correct part of the cache (i.e, before
+ ;; the first request).
+ (org-element--cache-sync (current-buffer) end offset)
+ (let ((first (org-element--cache-for-removal beg end offset)))
+ (cond
+ ;; Changes happened before the first known element. Shift
+ ;; the rest of the cache.
+ ((and first (> (org-element-property :begin first) end))
+ (push (vector (org-element--cache-key first) nil nil offset nil 2)
+ org-element--cache-sync-requests))
+ ;; There is at least an element to remove. Find position
+ ;; past every element containing END.
+ (first
+ (if (> (org-element-property :end first) end)
+ (setq end (org-element-property :end first))
+ (let ((element (org-element--cache-find end)))
+ (setq end (org-element-property :end element))
+ (let ((up element))
+ (while (and (setq up (org-element-property :parent up))
+ (>= (org-element-property :begin up) beg))
+ (setq end (org-element-property :end up))))))
+ (push (vector (org-element--cache-key first)
+ (org-element-property :begin first)
+ end offset nil 0)
+ org-element--cache-sync-requests))
+ ;; No element to remove. No need to re-parent either.
+ ;; Simply shift additional elements, if any, by OFFSET.
+ (org-element--cache-sync-requests (incf (aref next 3) offset)))))))
;;;; Public Functions