Agenda: Fix problems with follow mode.
[org-mode.git] / contrib / lisp / org-indent.el
blobb50b11335a9f8e29b8852c416337d96d2b848bcb
1 ;;; org-indent.el --- Dynamic indentation for Org-mode
2 ;; Copyright (C) 2008 Free Software Foundation, Inc.
3 ;;
4 ;; Author: Carsten Dominik <carsten at orgmode dot org>
5 ;; Keywords: outlines, hypermedia, calendar, wp
6 ;; Homepage: http://orgmode.org
7 ;; Version: 0.07
8 ;;
9 ;; This file is not part of GNU Emacs.
11 ;; This file is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 3, or (at your option)
14 ;; any later version.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27 ;;; Commentary:
29 ;; This is an experimental implementation of dynamic virtual indentation.
30 ;; It works by adding overlays to a buffer to make sure lines are
31 ;; indented according to outline structure. While this works, there are
32 ;; problems with the implementation: It uses overlays, which use markers,
33 ;; and for large files, this is using too much resources. It might be
34 ;; possible to com up with an implementation using text properties,
35 ;; I believe this is less resource intensive. However, it does not work
36 ;; to put the text property onto the newline, because that interferes with
37 ;; outline visibility. Maybe this is a bug in outline?
39 ;;; Indentation
41 (defcustom org-startup-indented nil
42 "Non-nil means, turn on `org-indent-mode' on startup.
43 This can also be configured on a per-file basis by adding one of
44 the following lines anywhere in the buffer:
46 #+STARTUP: localindent
47 #+STARTUP: indent
48 #+STARTUP: noindent"
49 :group 'org-structure
50 :type '(choice
51 (const :tag "Not" nil)
52 (const :tag "Locally" local)
53 (const :tag "Globally (slow on startup in large files)" t)))
55 (defconst org-indent-max 80
56 "Maximum indentation in characters")
57 (defconst org-indent-strings nil
58 "Vector with all indentation strings.
59 It is a const because it will be set only once in `org-indent-initialize'.")
60 (defvar org-indent-inhibit-after-change nil
61 "Inhibit the action of the indentation after-change-hook.
62 This variable should be scoped dynamically.")
63 (defcustom org-indent-boundary-char ?\ ; comment to protect space char
64 "The end of the virtual indentation strings, a single-character string.
65 The default is just a space, but if you wish, you can use \"|\" or so."
66 :group 'org-structure
67 :set (lambda (var val)
68 (set var val)
69 (and org-indent-strings (org-indent-initialize)))
70 :type 'character)
72 (defun org-indent-initialize ()
73 "Initialize the indentation strings and set the idle times."
74 (unless org-indent-strings
75 ; (run-with-idle-timer 10 t 'org-indent-indent-buffer)
76 (run-with-idle-timer 0.5 t 'org-indent-refresh-section)
78 (setq org-indent-strings (make-vector (1+ org-indent-max) nil))
79 ;; Initialize the indentation strings
80 (aset org-indent-strings 0 "")
81 (loop for i from 1 to org-indent-max do
82 (aset org-indent-strings i
83 (org-add-props
84 (concat (make-string (1- i) ?\ )
85 (char-to-string org-indent-boundary-char))
86 nil 'face 'org-indent))))
88 (define-minor-mode org-indent-mode
89 "Toggle the minor more `org-indent-mode'."
90 nil " Ind" nil
91 (if (org-bound-and-true-p org-inhibit-startup)
92 (setq org-indent-mode nil)
93 (if org-indent-mode
94 (progn
95 (or org-indent-strings (org-indent-initialize))
96 (org-set-local 'org-adapt-indentation nil)
97 (org-add-hook 'after-change-functions
98 'org-indent-after-change-function nil t)
99 (org-restart-font-lock)
100 (org-indent-indent-buffer))
101 (save-excursion
102 (save-restriction
103 (org-indent-remove-overlays (point-min) (point-max))
104 (remove-hook 'after-change-functions
105 'org-indent-after-change-function 'local)
106 (kill-local-variable 'org-adapt-indentation))))))
108 (defface org-indent
109 (org-compatible-face nil
110 '((((class color) (min-colors 16) (background dark)
111 (:underline nil :strike-through nil :foreground "grey10")))
112 (((class color) (min-colors 16) (background light))
113 (:underline nil :strike-through nil :foreground "grey90"))
114 (t (:underline nil :strike-through nil))))
115 "Face for outline indentation.
116 The default is to make it look like whitespace. But you may find it
117 useful to make it, for example, look like the fringes."
118 :group 'org-faces)
120 (defun org-indent-indent-buffer ()
121 "Add indentation overlays for the whole buffer."
122 (interactive)
123 (when org-indent-mode
124 (save-excursion
125 (save-restriction
126 (org-indent-remove-overlays (point-min) (point-max))
127 (org-indent-add-overlays (point-min) (point-max))))))
129 (defun org-indent-remove-overlays (beg end)
130 "Remove indentations between BEG and END."
131 (mapc (lambda (o)
132 (and (org-overlay-get o 'org-indent)
133 (org-delete-overlay o)))
134 (org-overlays-in beg end)))
136 (defun org-indent-add-overlays (beg end &optional n)
137 "Add indentation overlays between BEG and END.
138 Assumes that BEG is at the beginning of a line."
139 (when org-indent-mode
140 (let (o)
141 (save-excursion
142 (goto-char beg)
143 (while (and (<= (point) end) (not (eobp)))
144 (cond
145 ((not (bolp)))
146 ((looking-at outline-regexp)
147 (setq n (- (match-end 0) (match-beginning 0)))
148 (org-indent-remove-overlays (max (point-min) (1- (point))) (point)))
150 (org-indent-indent-line n)))
151 (beginning-of-line 2))))))
153 (defun org-indent-indent-line (n)
154 "Add an indentation overlay with width N to the current line.
155 Point is assumed to be at the beginning of the line for this."
156 (let (ov)
157 (setq ov (org-make-overlay (1- (point)) (point)))
158 (org-overlay-put ov 'after-string (aref org-indent-strings n))
159 (org-overlay-put ov 'evaporate t)
160 (org-overlay-put ov 'org-indent n)
161 (org-unmodified
162 (put-text-property (max (point-min) (1- (point)))
163 (point-at-eol) 'org-indent-level n))))
165 (defun org-indent-after-change-function (beg end ndel)
166 (if (or (not org-indent-mode) (= beg end)
167 org-indent-inhibit-after-change)
168 () ; not in the mood to do anything here....
169 (let ((inhibit-quit t) n)
170 (save-match-data
171 (save-excursion
172 (save-restriction
173 (widen)
174 (goto-char beg)
175 (when (search-forward "\n" end t)
176 ;; a newline was inserted
177 (setq n (or (get-text-property beg 'org-indent-level)
178 (get-text-property
179 (or (save-excursion (previous-single-property-change
180 beg 'org-indent-level))
181 (point-min))
182 'org-indent-level)
184 (org-indent-local-refresh beg end n))))))))
186 (defun org-indent-local-refresh (beg end n)
187 "Refresh indentation locally from BEG to END, starting with indentation N."
188 (goto-char end)
189 (setq end (min (point-max) (1+ (point-at-eol))))
190 (goto-char beg)
191 (beginning-of-line 0)
192 (org-indent-remove-overlays (max (point-min) (1- (point))) end)
193 (org-indent-add-overlays (point) end n))
195 (defun org-indent-refresh-section ()
196 "Refresh indentation overlays in the current outline subtree."
197 (when org-indent-mode
198 (save-excursion
199 (let ((org-indent-inhibit-after-change t)
200 beg end)
201 (condition-case nil
202 (progn
203 (outline-back-to-heading t)
204 (setq beg (point)))
205 (error (progn
206 (goto-char (point-min))
207 (setq beg (point)))))
208 (outline-next-heading)
209 (setq end (point))
210 (org-indent-remove-overlays beg end)
211 (org-indent-add-overlays beg end)))))
213 (defun org-indent-refresh-subtree ()
214 "Refresh indentation overlays in the current outline subtree."
215 (when org-indent-mode
216 (save-excursion
217 (let ((org-indent-inhibit-after-change t)
218 beg end)
219 (setq beg (point))
220 (setq end (save-excursion (org-end-of-subtree t t)))
221 (org-indent-remove-overlays beg end)
222 (org-indent-add-overlays beg end)))))
224 (provide 'org-indent)
226 ;;; org-indent.el ends here