org-indent.el 8.0 KB

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