From 818c449e2c61b7be490750914a06c62823901663 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sun, 27 Feb 2011 15:52:36 +0100 Subject: [PATCH] org-indent: remove refresh timer * lisp/org-indent.el (org-indent-fix-section-after-idle-time): remove variable. (org-indent-initialize): remove timer. (org-indent-add-properties): refactor code. (org-indent-refresh-subtree, org-indent-refresh-section, org-indent-refresh-buffer,org-indent-set-initial-properties): remove functions. (org-indent-deleted-headline): new variable. (org-indent-notify-deleted-headline,org-indent-refresh-maybe): new functions. (org-indent-mode): insert new functions into a hook. --- lisp/org-indent.el | 276 +++++++++++++++++++++++++---------------------------- 1 file changed, 132 insertions(+), 144 deletions(-) diff --git a/lisp/org-indent.el b/lisp/org-indent.el index 5b091be5e..0debd8a65 100644 --- a/lisp/org-indent.el +++ b/lisp/org-indent.el @@ -39,7 +39,7 @@ (declare-function org-inlinetask-get-task-level "org-inlinetask" ()) (declare-function org-inlinetask-in-task-p "org-inlinetask" ()) -(declare-function org-inlinetask-outline-regexp "org-inlinetask" ()) +(declare-function org-list-item-body-column "org-list" (item)) (defgroup org-indent nil "Options concerning dynamic virtual outline indentation." @@ -59,6 +59,12 @@ It will be set in `org-indent-initialize'.") It will be set in `org-indent-initialize'.") (defvar org-hide-leading-stars-before-indent-mode nil "Used locally.") +(defvar org-indent-outline-re (concat "^" outline-regexp) + "Regexp matching and headline or inline task.") +(defvar org-indent-deleted-headline-flag nil + "Non nil if the last deletion acted on an headline. +It is modified by `org-indent-notify-deleted-headline'.") + (defcustom org-indent-boundary-char ?\ ; comment to protect space char "The end of the virtual indentation strings, a single-character string. @@ -89,28 +95,8 @@ turn on `org-hide-leading-stars'." :group 'org-indent :type 'integer) -(defcustom org-indent-fix-section-after-idle-time 0.2 - "Seconds of idle time before fixing virtual indentation of section. -The hooking-in of virtual indentation is not yet perfect. Occasionally, -a change does not trigger to proper change of indentation. For this we -have a timer action that fixes indentation in the current section after -a short amount idle time. If we ever get the integration to work perfectly, -this variable can be set to nil to get rid of the timer." - :group 'org-indent - :type '(choice - (const "Do not install idle timer" nil) - (number :tag "Idle time"))) - (defun org-indent-initialize () - "Initialize the indentation strings and set the idle timer." - ;; We use an idle timer to "repair" the current section, because the - ;; redisplay seems to have some problems. - (unless org-indent-strings - (when org-indent-fix-section-after-idle-time - (run-with-idle-timer - org-indent-fix-section-after-idle-time - t 'org-indent-refresh-view))) - ;; Initialize the indentation and star vectors + "Initialize the indentation strings." (setq org-indent-strings (make-vector (1+ org-indent-max) nil)) (setq org-indent-stars (make-vector (1+ org-indent-max) nil)) (aset org-indent-strings 0 nil) @@ -130,9 +116,9 @@ this variable can be set to nil to get rid of the timer." (define-minor-mode org-indent-mode "When active, indent text according to outline structure. + Internally this works by adding `line-prefix' and `wrap-prefix' -properties to all lines. These properties are updated locally in idle -time." +properties, after each buffer modifiation, on the modified zone." nil " Ind" nil (cond ((org-bound-and-true-p org-inhibit-startup) @@ -160,10 +146,9 @@ time." (make-local-variable 'buffer-substring-filters) (add-to-list 'buffer-substring-filters 'org-indent-remove-properties-from-string) - (org-add-hook 'org-after-demote-entry-hook - 'org-indent-refresh-subtree nil 'local) - (org-add-hook 'org-after-promote-entry-hook - 'org-indent-refresh-subtree nil 'local) + (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))) (t ;; mode was turned off (or we refused to turn it on) @@ -177,10 +162,9 @@ time." (setq buffer-substring-filters (delq 'org-indent-remove-properties-from-string buffer-substring-filters)) - (remove-hook 'org-after-promote-entry-hook - 'org-indent-refresh-subtree 'local) - (remove-hook 'org-after-demote-entry-hook - 'org-indent-refresh-subtree 'local) + (remove-hook 'after-change-functions 'org-indent-refresh-maybe 'local) + (remove-hook 'before-change-functions + 'org-indent-notify-deleted-headline 'local) (and font-lock-mode (org-restart-font-lock)) (redraw-display)))))) @@ -195,18 +179,18 @@ useful to make it ever so slightly different." (defun org-indent-indent-buffer () "Add indentation properties for the whole buffer." (interactive) - (when org-indent-mode - (save-excursion - (save-restriction - (widen) - (org-indent-remove-properties (point-min) (point-max)) - (org-indent-add-properties (point-min) (point-max)))))) + (if (not (org-mode-p)) + (error "Buffer major mode must be Org") + (message "Initializing 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)))) + (message "Indentation of buffer initialized."))) -(defun org-indent-remove-properties (beg end) +(defsubst org-indent-remove-properties (beg end) "Remove indentations between BEG and END." - (let ((inhibit-modification-hooks t)) - (with-silent-modifications - (remove-text-properties beg end '(line-prefix nil wrap-prefix nil))))) + (remove-text-properties beg end '(line-prefix nil wrap-prefix nil))) (defun org-indent-remove-properties-from-string (string) "Remove indentation properties from STRING." @@ -216,118 +200,122 @@ useful to make it ever so slightly different." (defun org-indent-add-properties (beg end) "Add indentation properties between BEG and END." - (save-excursion + (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 - ;; current `line-prefix' when line is inside an inline task or - ;; not. - (let* ((inhibit-modification-hooks t) - (case-fold-search t) + ;; 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)) - (inline-end-re (and (featurep 'org-inlinetask) - (concat (org-inlinetask-outline-regexp) - "end[ \t]*$"))) - (pf (org-with-limited-levels - (save-excursion - (and (ignore-errors (org-back-to-heading t)) - (looking-at org-outline-regexp) - (aref org-indent-strings - (- (match-end 0) (match-beginning 0))))))) - (pf-inline (and inline-end-re + (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) - (aref org-indent-strings - (1+ (org-inlinetask-get-task-level)))))) - ;; 2. For each line, `line-prefix' is based on the value of the - ;; previous `line-prefix' (stored in PF and INLINE-PF). - ;; `wrap-prefix' computation is done with the current - ;; `line-prefix' value. - (with-silent-modifications - (while (< (point) end) - (cond - ;; Empty line: do nothing. - ((eolp) (forward-line 1)) - ;; List item: `line-prefix' doesn't change, but - ;; `wrap-prefix' is set where body starts. - ((org-at-item-p) - (let* ((line (or pf-inline pf)) - (wrap (aref org-indent-strings - (+ (org-list-item-body-column (point)) - (length line))))) - (add-text-properties (point) (point-at-eol) - `(line-prefix ,line wrap-prefix ,wrap)) - (forward-line 1))) - ;; Normal line: `line-prefix' doesn't change, but - ;; `wrap-prefix' also takes into account indentation. - ((not (looking-at org-outline-regexp)) - (let* ((line (or pf-inline pf)) - (wrap (aref org-indent-strings - (+ (length line) (org-get-indentation))))) - (add-text-properties (point) (point-at-eol) - `(line-prefix ,line wrap-prefix ,wrap)) - (forward-line 1))) - ;; Headline: `line-prefix' is nil, `wrap-prefix' is set - ;; where headline starts and its value becomes a reference - ;; for following lines. - ((looking-at limited-re) - (let ((wrap (aref org-indent-strings - (- (match-end 0) (match-beginning 0))))) - (add-text-properties (point) (point-at-eol) - `(line-prefix nil wrap-prefix ,wrap)) - (setq pf wrap) - (forward-line 1))) - ;; End of inline task: both `line-prefix' and `wrap-prefix' - ;; are nil. PF-INLINE is also nil, as following lines are - ;; out of the inline task. - ((looking-at inline-end-re) - (add-text-properties (point) (point-at-eol) - '(line-prefix nil wrap-prefix nil)) - (setq pf-inline nil) - (forward-line 1)) - ;; Beginnig of inline task: determine if the tasks contains - ;; text (and set PF-INLINE accordingly) or is only one line - ;; long by looking the status of the following line. In any - ;; case, `line-prefix' is nil and `wrap-prefix' is set - ;; where headline starts. - (t - (let ((wrap (progn - (looking-at org-outline-regexp) - (aref org-indent-strings - (- (match-end 0) (match-beginning 0)))))) - (add-text-properties (point) (point-at-eol) - `(line-prefix nil wrap-prefix ,wrap)) - (forward-line 1) - (setq pf-inline (and (not (eobp)) - (org-inlinetask-in-task-p) - wrap)))))))))) + (+ (* 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 (aref (if h org-indent-stars org-indent-strings) l)) + (wrap (aref org-indent-strings w))) + (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-refresh-view (&rest ignore) - "Refresh indentation properties in the visible portion of buffer. -IGNORE all arguments that might be passed to the function." - (interactive) - (when org-indent-mode - (save-excursion - (let ((beg (window-start)) - (end (window-end nil t))) - (org-indent-add-properties beg end))))) +(defun org-indent-notify-deleted-headline (beg end) + "Set `org-indent-deleted-headline-flag' depending on the current command. -(defun org-indent-refresh-subtree () - "Refresh indentation properties in the current outline subtree. -Point is assumed to be at an headline." - (interactive) - (when org-indent-mode - (save-excursion - (let ((beg (point-at-bol)) - (end (save-excursion (org-end-of-subtree t t)))) - (org-indent-add-properties beg end))))) +BEG and END are the positions of the beginning and end of the +range of deleted text. -(defun org-indent-refresh-buffer () - "Refresh indentation properties in the whole buffer." - (interactive) +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) + (re-search-forward org-indent-outline-re end t))))) + (save-match-data + (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. +BEG and END are the positions of the beginning and end of the +range of inserted text. DUMMY is an unused argument. + +This function is meant to be called by `after-change-functions'." (when org-indent-mode - (org-indent-mode -1) - (org-indent-mode 1))) + (save-match-data + (cond + ;; An headline was deleted. + (org-indent-deleted-headline-flag + (setq org-indent-deleted-headline-flag nil) + (let ((end (save-excursion (outline-next-heading) (point)))) + (org-indent-remove-properties beg end) + (org-indent-add-properties beg end))) + ;; An headline was inserted. + ((and (/= beg end) + (save-excursion + (goto-char beg) + (re-search-forward org-outline-regexp-bol end t))) + (let ((end (save-excursion + (goto-char end) (outline-next-heading) (point)))) + (org-indent-remove-properties beg end) + (org-indent-add-properties beg end))) + ;; At an headline, modifying stars. + ((save-excursion (goto-char beg) + (and (org-at-heading-p) (< beg (match-end 0)))) + (let ((beg (point-at-bol)) + (end (save-excursion (outline-next-heading) (point)))) + (org-indent-remove-properties beg end) + (org-indent-add-properties beg end))) + ;; Else, refresh properties of modified area. + (t (org-indent-add-properties beg end)))))) (provide 'org-indent) -- 2.11.4.GIT