Browse Source

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.
Nicolas Goaziou 14 years ago
parent
commit
818c449e2c
1 changed files with 132 additions and 144 deletions
  1. 132 144
      lisp/org-indent.el

+ 132 - 144
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)