summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2011-07-21 16:20:26 +0200
committerNicolas Goaziou <n.goaziou@gmail.com>2011-08-18 10:41:31 +0200
commitacd6ed2140e2177b8a3b3ff23ed589c553b6d890 (patch)
tree527fdf32369a407b0e8e61093dedab8e14f3442a
parent9479991d4c453863c04d933865ee7ad7a9f47654 (diff)
downloadorg-mode-acd6ed2140e2177b8a3b3ff23ed589c553b6d890.tar.gz
org-indent: implement asynchronous initialization
* lisp/org-indent.el (org-indent-initial-marker, org-indent-initial-timer, org-indent-initial-lock): new variables. (org-indent-mode): at initialization, start an idle timer to indent the whole buffer. When the user is asking for control, interrupt the process, and resume at the same point when idle again. (org-indent-initialize-buffer): new function. (org-indent-add-properties): throw an interrupt when indentation of buffer is stopped during initialization.
-rw-r--r--lisp/org-indent.el208
1 files changed, 124 insertions, 84 deletions
diff --git a/lisp/org-indent.el b/lisp/org-indent.el
index ff86117..4b1b5ba 100644
--- a/lisp/org-indent.el
+++ b/lisp/org-indent.el
@@ -61,6 +61,12 @@ It will be set in `org-indent-initialize'.")
(defvar org-indent-stars nil
"Vector with all indentation star strings.
It will be set in `org-indent-initialize'.")
+(defvar org-indent-initial-marker nil
+ "Position of initialization process before interrupt.")
+(defvar org-indent-initial-timer nil
+ "Timer used for initialization.")
+(defvar org-indent-initial-lock nil
+ "Lock used of initialization.")
(defvar org-hide-leading-stars-before-indent-mode nil
"Used locally.")
(defvar org-indent-deleted-headline-flag nil
@@ -138,7 +144,8 @@ properties, after each buffer modifiation, on the modified zone."
;; mode was turned on.
(org-set-local 'indent-tabs-mode nil)
(or org-indent-strings (org-indent-initialize))
- (org-indent-indent-buffer)
+ (org-set-local 'org-indent-initial-marker (copy-marker 1))
+ (org-set-local 'org-indent-initial-lock nil)
(when org-indent-mode-turns-off-org-adapt-indentation
(org-set-local 'org-adapt-indentation nil))
(when org-indent-mode-turns-on-hiding-stars
@@ -151,12 +158,19 @@ properties, after each buffer modifiation, on the modified zone."
(org-add-hook 'after-change-functions 'org-indent-refresh-maybe nil 'local)
(org-add-hook 'before-change-functions
'org-indent-notify-deleted-headline nil 'local)
- (and font-lock-mode (org-restart-font-lock)))
+ (and font-lock-mode (org-restart-font-lock))
+ (with-silent-modifications
+ (org-indent-remove-properties (point-min) (point-max)))
+ (org-set-local 'org-indent-initial-timer
+ (run-with-idle-timer 0.2 t #'org-indent-initialize-buffer)))
(t
;; mode was turned off (or we refused to turn it on)
(save-excursion
(save-restriction
- (org-indent-remove-properties (point-min) (point-max))
+ (when org-indent-initialize-marker
+ (set-marker org-indent-initialize-marker nil))
+ (with-silent-modifications
+ (org-indent-remove-properties (point-min) (point-max)))
(kill-local-variable 'org-adapt-indentation)
(when (boundp 'org-hide-leading-stars-before-indent-mode)
(org-set-local 'org-hide-leading-stars
@@ -185,9 +199,8 @@ useful to make it ever so slightly different."
(error "Buffer major mode must be Org")
(message "Setting buffer indentation. It may take a few seconds...")
(org-with-wide-buffer
- (with-silent-modifications
- (org-indent-remove-properties (point-min) (point-max))
- (org-indent-add-properties (point-min) (point-max))))
+ (org-indent-remove-properties (point-min) (point-max))
+ (org-indent-add-properties (point-min) (point-max)))
(message "Indentation of buffer set.")))
(defsubst org-indent-remove-properties (beg end)
@@ -200,77 +213,102 @@ useful to make it ever so slightly different."
'(line-prefix nil wrap-prefix nil) string)
string)
-(defun org-indent-add-properties (beg end)
- "Add indentation properties between BEG and END."
- (org-with-wide-buffer
- (goto-char beg)
- (beginning-of-line)
- ;; 1. Initialize prefix at BEG. This is done by storing two
- ;; variables: INLINE-PF and PF, representing respectively
- ;; length of current `line-prefix' when line is inside an
- ;; inline task or not.
- (let* ((case-fold-search t)
- (limited-re (org-get-limited-outline-regexp))
- (added-ind-per-lvl (1- org-indent-indentation-per-level))
- (pf (let ((outline-regexp limited-re))
- (save-excursion
- (and (ignore-errors (org-back-to-heading t))
- (looking-at org-outline-regexp)
- (+ (* org-indent-indentation-per-level
- (- (match-end 0) (match-beginning 0) 2)) 2)))))
- (pf-inline (and (featurep 'org-inlinetask)
- (org-inlinetask-in-task-p)
- (+ (* org-indent-indentation-per-level
- (1- (org-inlinetask-get-task-level))) 2)))
- (set-prop-and-move
- (function
- ;; Set prefix properties `line-prefix' and `wrap-prefix'
- ;; in current line to, respectively, length L and W and
- ;; move forward. If H is non-nil, `line-prefix' will be
- ;; starred. Assume point is at bol.
- (lambda (l w h)
- (let ((line (if h (aref org-indent-stars
- (min l org-indent-max-levels))
- (aref org-indent-strings
- (min l org-indent-max))))
- (wrap (aref org-indent-strings (min w org-indent-max))))
- (add-text-properties (point) (point-at-eol)
- `(line-prefix ,line wrap-prefix ,wrap)))
- (forward-line 1)))))
- ;; 2. For each line, set `line-prefix' and `wrap-prefix'
- ;; properties depending on the type of line (headline, inline
- ;; task, item or other).
- (while (< (point) end)
- (cond
- ;; Empty line: do nothing.
- ((eolp) (forward-line 1))
- ;; Headline or inline task.
- ((looking-at "\\*+ ")
- (let* ((nstars (- (match-end 0) (match-beginning 0) 1))
- (line (* added-ind-per-lvl (1- nstars)))
- (wrap (+ line (1+ nstars))))
- (cond
- ;; Headline: new value for PF.
- ((looking-at limited-re)
- (funcall set-prop-and-move line wrap t)
- (setq pf wrap))
- ;; End of inline task: PF-INLINE is now nil.
- ((looking-at "\\*+ end[ \t]*$")
- (funcall set-prop-and-move line wrap t)
- (setq pf-inline nil))
- ;; Start of inline task. Determine if it contains text,
- ;; or is only one line long. Set PF-INLINE accordingly.
- (t (funcall set-prop-and-move line wrap t)
- (setq pf-inline (and (org-inlinetask-in-task-p) wrap))))))
- ;; List item: `wrap-prefix' is set where body starts.
- ((org-at-item-p)
- (let* ((line (or pf-inline pf 0))
- (wrap (+ (org-list-item-body-column (point)) line)))
- (funcall set-prop-and-move line wrap nil)))
- ;; Normal line: use PF-INLINE, PF or nil as prefixes.
- (t (let* ((line (or pf-inline pf 0))
- (wrap (+ line (org-get-indentation))))
- (funcall set-prop-and-move line wrap nil))))))))
+(defun org-indent-initialize-buffer ()
+ "Set virtual indentation for the whole buffer asynchronously."
+ (when (and org-indent-mode (not org-indent-initial-lock))
+ (org-with-wide-buffer
+ (setq org-indent-initial-lock t)
+ (let ((interruptp
+ ;; Always nil unless interrupted.
+ (catch 'interrupt
+ (and org-indent-initial-marker
+ (marker-position org-indent-initial-marker)
+ (org-indent-add-properties org-indent-initial-marker
+ (point-max) t)
+ nil))))
+ (move-marker org-indent-initial-marker interruptp)
+ ;; Job is complete: stop idle timer.
+ (unless interruptp (cancel-timer org-indent-initial-timer))))
+ (setq org-indent-initial-lock nil)))
+
+(defun org-indent-add-properties (beg end &optional async)
+ "Add indentation properties between BEG and END.
+
+If ASYNC is non-nil, allow to interrupt the process. This is
+done by throwing the `interrupt' tag along with the buffer
+position where the process stopped. Be sure to catch this tag if
+you want to use this feature."
+ (save-match-data
+ (org-with-wide-buffer
+ (goto-char beg)
+ (beginning-of-line)
+ ;; 1. Initialize prefix at BEG. This is done by storing two
+ ;; variables: INLINE-PF and PF, representing respectively
+ ;; length of current `line-prefix' when line is inside an
+ ;; inline task or not.
+ (let* ((case-fold-search t)
+ (limited-re (org-get-limited-outline-regexp))
+ (added-ind-per-lvl (1- org-indent-indentation-per-level))
+ (pf (save-excursion
+ (and (ignore-errors (let ((outline-regexp limited-re))
+ (org-back-to-heading t)))
+ (+ (* org-indent-indentation-per-level
+ (- (match-end 0) (match-beginning 0) 2)) 2))))
+ (pf-inline (and (featurep 'org-inlinetask)
+ (org-inlinetask-in-task-p)
+ (+ (* org-indent-indentation-per-level
+ (1- (org-inlinetask-get-task-level))) 2)))
+ (set-prop-and-move
+ (function
+ ;; Set prefix properties `line-prefix' and `wrap-prefix'
+ ;; in current line to, respectively, length L and W and
+ ;; move forward. If H is non-nil, `line-prefix' will be
+ ;; starred. Assume point is at bol.
+ (lambda (l w h)
+ (let ((line (if h (aref org-indent-stars
+ (min l org-indent-max-levels))
+ (aref org-indent-strings
+ (min l org-indent-max))))
+ (wrap (aref org-indent-strings (min w org-indent-max))))
+ (add-text-properties (point) (point-at-eol)
+ `(line-prefix ,line wrap-prefix ,wrap)))
+ (forward-line 1)))))
+ ;; 2. For each line, set `line-prefix' and `wrap-prefix'
+ ;; properties depending on the type of line (headline, inline
+ ;; task, item or other).
+ (while (< (point) end)
+ (cond
+ ;; When in async mode, check if interrupt is required.
+ ((and async (input-pending-p)) (throw 'interrupt (point)))
+ ;; Empty line: do nothing.
+ ((eolp) (forward-line 1))
+ ;; Headline or inline task.
+ ((looking-at org-outline-regexp)
+ (let* ((nstars (- (match-end 0) (match-beginning 0) 1))
+ (line (* added-ind-per-lvl (1- nstars)))
+ (wrap (+ line (1+ nstars))))
+ (cond
+ ;; Headline: new value for PF.
+ ((looking-at limited-re)
+ (funcall set-prop-and-move line wrap t)
+ (setq pf wrap))
+ ;; End of inline task: PF-INLINE is now nil.
+ ((looking-at "\\*+ end[ \t]*$")
+ (funcall set-prop-and-move line wrap t)
+ (setq pf-inline nil))
+ ;; Start of inline task. Determine if it contains text,
+ ;; or is only one line long. Set PF-INLINE accordingly.
+ (t (funcall set-prop-and-move line wrap t)
+ (setq pf-inline (and (org-inlinetask-in-task-p) wrap))))))
+ ;; List item: `wrap-prefix' is set where body starts.
+ ((org-at-item-p)
+ (let* ((line (or pf-inline pf 0))
+ (wrap (+ (org-list-item-body-column (point)) line)))
+ (funcall set-prop-and-move line wrap nil)))
+ ;; Normal line: use PF-INLINE, PF or nil as prefixes.
+ (t (let* ((line (or pf-inline pf 0))
+ (wrap (+ line (org-get-indentation))))
+ (funcall set-prop-and-move line wrap nil)))))))))
(defun org-indent-notify-deleted-headline (beg end)
"Set `org-indent-deleted-headline-flag' depending on the current command.
@@ -279,13 +317,15 @@ BEG and END are the positions of the beginning and end of the
range of deleted text.
This function is meant to be called by `before-change-functions'.
-Flag will be non-nil if command is going to delete an headline."
- (setq org-indent-deleted-headline-flag
- (and (/= beg end)
- (save-excursion
- (goto-char beg)
- (save-match-data
- (re-search-forward org-outline-regexp-bol end t))))))
+Flag will be non-nil if command is going to modify or delete an
+headline."
+ (when org-indent-mode
+ (setq org-indent-modified-headline-flag
+ (save-excursion
+ (goto-char beg)
+ (save-match-data
+ (or (and (org-at-heading-p) (< beg (match-end 0)))
+ (re-search-forward org-outline-regexp-bol end t)))))))
(defun org-indent-refresh-maybe (beg end dummy)
"Refresh indentation properties in an adequate portion of buffer.