org-indent.el 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. (defconst org-indent-max 80
  37. "Maximum indentation in characters")
  38. (defconst org-indent-strings nil
  39. "Vector with all indentation strings.
  40. It is a const because it will be set only once in `org-indent-initialize'.")
  41. (defvar org-indent-inhibit-after-change nil
  42. "Inhibit the action of the indentation after-change-hook.
  43. This variable should be scoped dynamically.")
  44. (defcustom org-indent-boundary-char ?\ ; comment to protect space char
  45. "The end of the virtual indentation strings, a single-character string.
  46. The default is just a space, but if you wish, you can use \"|\" or so."
  47. :group 'org-structure
  48. :set (lambda (var val)
  49. (set var val)
  50. (and org-indent-strings (org-indent-initialize)))
  51. :type 'character)
  52. (defun org-indent-initialize ()
  53. "Initialize the indentation strings and set the idle times."
  54. (unless org-indent-strings
  55. ; (run-with-idle-timer 10 t 'org-indent-indent-buffer)
  56. (run-with-idle-timer 0.5 t 'org-indent-refresh-section)
  57. )
  58. (setq org-indent-strings (make-vector (1+ org-indent-max) nil))
  59. ;; Initialize the indentation strings
  60. (aset org-indent-strings 0 "")
  61. (loop for i from 1 to org-indent-max do
  62. (aset org-indent-strings i
  63. (org-add-props
  64. (concat (make-string (1- i) ?\ )
  65. (char-to-string org-indent-boundary-char))
  66. nil 'face 'org-indent))))
  67. (define-minor-mode org-indent-mode
  68. "Toggle the minor more `org-indent-mode'."
  69. nil " Ind" nil
  70. (if (org-bound-and-true-p org-inhibit-startup)
  71. (setq org-indent-mode nil)
  72. (if org-indent-mode
  73. (progn
  74. (or org-indent-strings (org-indent-initialize))
  75. (org-set-local 'org-adapt-indentation nil)
  76. (org-add-hook 'after-change-functions
  77. 'org-indent-after-change-function nil t)
  78. (org-restart-font-lock)
  79. (org-indent-indent-buffer))
  80. (save-excursion
  81. (save-restriction
  82. (org-indent-remove-overlays (point-min) (point-max))
  83. (remove-hook 'after-change-functions
  84. 'org-indent-after-change-function 'local)
  85. (kill-local-variable 'org-adapt-indentation))))))
  86. (defface org-indent
  87. (org-compatible-face nil
  88. '((((class color) (min-colors 16) (background dark)
  89. (:underline nil :strike-through nil :foreground "grey10")))
  90. (((class color) (min-colors 16) (background light))
  91. (:underline nil :strike-through nil :foreground "grey90"))
  92. (t (:underline nil :strike-through nil))))
  93. "Face for outline indentation.
  94. The default is to make it look like whitespace. But you may find it
  95. useful to make it, for example, look like the fringes."
  96. :group 'org-faces)
  97. (defun org-indent-indent-buffer ()
  98. "Add indentation overlays for the whole buffer."
  99. (interactive)
  100. (when org-indent-mode
  101. (save-excursion
  102. (save-restriction
  103. (org-indent-remove-overlays (point-min) (point-max))
  104. (org-indent-add-overlays (point-min) (point-max))))))
  105. (defun org-indent-remove-overlays (beg end)
  106. "Remove indentations between BEG and END."
  107. (mapc (lambda (o)
  108. (and (org-overlay-get o 'org-indent)
  109. (org-delete-overlay o)))
  110. (org-overlays-in beg end)))
  111. (defun org-indent-add-overlays (beg end &optional n)
  112. "Add indentation overlays between BEG and END.
  113. Assumes that BEG is at the beginning of a line."
  114. (when org-indent-mode
  115. (let (o)
  116. (save-excursion
  117. (goto-char beg)
  118. (while (and (<= (point) end) (not (eobp)))
  119. (cond
  120. ((not (bolp)))
  121. ((looking-at outline-regexp)
  122. (setq n (- (match-end 0) (match-beginning 0)))
  123. (org-indent-remove-overlays (max (point-min) (1- (point))) (point)))
  124. (n
  125. (org-indent-indent-line n)))
  126. (beginning-of-line 2))))))
  127. (defun org-indent-indent-line (n)
  128. "Add an indentation overlay with width N to the current line.
  129. Point is assumed to be at the beginning of the line for this."
  130. (let (ov)
  131. (setq ov (org-make-overlay (1- (point)) (point)))
  132. (org-overlay-put ov 'after-string (aref org-indent-strings n))
  133. (org-overlay-put ov 'evaporate t)
  134. (org-overlay-put ov 'org-indent n)
  135. (org-unmodified
  136. (put-text-property (max (point-min) (1- (point)))
  137. (point-at-eol) 'org-indent-level n))))
  138. (defun org-indent-after-change-function (beg end ndel)
  139. (if (or (not org-indent-mode) (= beg end)
  140. org-indent-inhibit-after-change)
  141. () ; not in the mood to do anything here....
  142. (let ((inhibit-quit t) n)
  143. (save-match-data
  144. (save-excursion
  145. (save-restriction
  146. (widen)
  147. (goto-char beg)
  148. (when (search-forward "\n" end t)
  149. ;; a newline was inserted
  150. (setq n (or (get-text-property beg 'org-indent-level)
  151. (get-text-property
  152. (or (save-excursion (previous-single-property-change
  153. beg 'org-indent-level))
  154. (point-min))
  155. 'org-indent-level)
  156. 0))
  157. (org-indent-local-refresh beg end n))))))))
  158. (defun org-indent-local-refresh (beg end n)
  159. "Refresh indentation locally from BEG to END, starting with indentation N."
  160. (goto-char end)
  161. (setq end (min (point-max) (1+ (point-at-eol))))
  162. (goto-char beg)
  163. (beginning-of-line 0)
  164. (org-indent-remove-overlays (max (point-min) (1- (point))) end)
  165. (org-indent-add-overlays (point) end n))
  166. (defun org-indent-refresh-section ()
  167. "Refresh indentation overlays in the current outline subtree."
  168. (when org-indent-mode
  169. (save-excursion
  170. (let ((org-indent-inhibit-after-change t)
  171. beg end)
  172. (condition-case nil
  173. (progn
  174. (outline-back-to-heading t)
  175. (setq beg (point)))
  176. (error (progn
  177. (goto-char (point-min))
  178. (setq beg (point)))))
  179. (outline-next-heading)
  180. (setq end (point))
  181. (org-indent-remove-overlays beg end)
  182. (org-indent-add-overlays beg end)))))
  183. (defun org-indent-refresh-subtree ()
  184. "Refresh indentation overlays in the current outline subtree."
  185. (when org-indent-mode
  186. (save-excursion
  187. (let ((org-indent-inhibit-after-change t)
  188. beg end)
  189. (setq beg (point))
  190. (setq end (save-excursion (org-end-of-subtree t t)))
  191. (org-indent-remove-overlays beg end)
  192. (org-indent-add-overlays beg end)))))
  193. (provide 'org-indent)
  194. ;;; org-indent.el ends here