1 ;;; org-indent.el --- Dynamic indentation for Org-mode
2 ;; Copyright (C) 2008 Free Software Foundation, Inc.
4 ;; Author: Carsten Dominik <carsten at orgmode dot org>
5 ;; Keywords: outlines, hypermedia, calendar, wp
6 ;; Homepage: http://orgmode.org
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)
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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?
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
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."
67 :set
(lambda (var val
)
69 (and org-indent-strings
(org-indent-initialize)))
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
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'."
91 (if (org-bound-and-true-p org-inhibit-startup
)
92 (setq org-indent-mode nil
)
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))
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
))))))
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."
120 (defun org-indent-indent-buffer ()
121 "Add indentation overlays for the whole buffer."
123 (when org-indent-mode
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."
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
143 (while (and (<= (point) end
) (not (eobp)))
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."
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
)
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
)
175 (when (search-forward "\n" end t
)
176 ;; a newline was inserted
177 (setq n
(or (get-text-property beg
'org-indent-level
)
179 (or (save-excursion (previous-single-property-change
180 beg
'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."
189 (setq end
(min (point-max) (1+ (point-at-eol))))
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
199 (let ((org-indent-inhibit-after-change t
)
203 (outline-back-to-heading t
)
206 (goto-char (point-min))
207 (setq beg
(point)))))
208 (outline-next-heading)
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
217 (let ((org-indent-inhibit-after-change t
)
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