Commit | Line | Data |
---|---|---|
c8d0cf5c | 1 | ;;; org-inlinetask.el --- Tasks independent of outline hierarchy |
26bd9e87 | 2 | |
114f9c96 | 3 | ;; Copyright (C) 2009, 2010 Free Software Foundation, Inc. |
c8d0cf5c CD |
4 | ;; |
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
6 | ;; Keywords: outlines, hypermedia, calendar, wp | |
7 | ;; Homepage: http://orgmode.org | |
86fbb8ca | 8 | ;; Version: 7.01 |
26bd9e87 GM |
9 | |
10 | ;; This file is part of GNU Emacs. | |
11 | ||
12 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
54a0dee5 | 13 | |
c8d0cf5c | 14 | ;; it under the terms of the GNU General Public License as published by |
26bd9e87 GM |
15 | ;; the Free Software Foundation, either version 3 of the License, or |
16 | ;; (at your option) any later version. | |
c8d0cf5c CD |
17 | |
18 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | ;; GNU General Public License for more details. | |
22 | ||
23 | ;; You should have received a copy of the GNU General Public License | |
26bd9e87 GM |
24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
25 | ||
c8d0cf5c CD |
26 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
27 | ;; | |
28 | ;;; Commentary: | |
29 | ;; | |
30 | ;; This module implements inline tasks in Org-mode. Inline tasks are | |
31 | ;; tasks that have all the properties of normal outline nodes, including | |
32 | ;; the ability to store meta data like scheduling dates, TODO state, tags | |
33 | ;; and properties. However, these nodes are treated specially by the | |
34 | ;; visibility cycling and export commands. | |
35 | ;; | |
86fbb8ca | 36 | ;; Visibility cycling exempts these nodes from cycling. So whenever their |
c8d0cf5c | 37 | ;; parent is opened, so are these tasks. This will only work with |
8bfe682a | 38 | ;; `org-cycle', so if you are also using other commands to show/hide |
c8d0cf5c CD |
39 | ;; entries, you will occasionally find these tasks to behave like |
40 | ;; all other outline nodes, seemingly splitting the text of the parent | |
41 | ;; into children. | |
42 | ;; | |
43 | ;; Export commands do not treat these nodes as part of the sectioning | |
44 | ;; structure, but as a special inline text that is either removed, or | |
45 | ;; formatted in some special way. | |
46 | ;; | |
47 | ;; Special fontification of inline tasks, so that they can be immediately | |
48 | ;; recognized. From the stars of the headline, only the first and the | |
49 | ;; last two will be visible, the others will be hidden using the | |
50 | ;; `org-hide' face. | |
51 | ;; | |
52 | ;; An inline task is identified solely by a minimum outline level, given | |
53 | ;; by the variable `org-inlinetask-min-level', default 15. | |
54 | ;; | |
55 | ;; Inline tasks are normally assumed to contain at most a time planning | |
56 | ;; line (DEADLINE etc) after it, and then any number of drawers, for | |
57 | ;; example LOGBOOK of PROPERTIES. No empty lines are allowed. | |
58 | ;; If you need to have normal text as part of an inline task, you | |
59 | ;; can do so by adding an "END" headline with the same number of stars, | |
60 | ;; for example | |
61 | ;; | |
62 | ;; **************** TODO some small task | |
63 | ;; DEADLINE: <2009-03-30 Mon> | |
64 | ;; :PROPERTIES: | |
65 | ;; :SOMETHING: or other | |
66 | ;; :END: | |
67 | ;; And here is some extra text | |
68 | ;; **************** END | |
69 | ;; | |
70 | ;; Also, if you want to use refiling and archiving for inline tasks, | |
71 | ;; The END line must be present to make things work properly. | |
72 | ;; | |
8bfe682a | 73 | ;; This package installs one new command: |
c8d0cf5c CD |
74 | ;; |
75 | ;; C-c C-x t Insert a new inline task with END line | |
76 | ||
86fbb8ca | 77 | ;;; Code: |
c8d0cf5c CD |
78 | |
79 | (require 'org) | |
80 | ||
81 | (defgroup org-inlinetask nil | |
82 | "Options concerning inline tasks in Org mode." | |
83 | :tag "Org Inline Tasks" | |
84 | :group 'org-structure) | |
85 | ||
86 | (defcustom org-inlinetask-min-level 15 | |
87 | "Minimum level a headline must have before it is treated as an inline task. | |
88 | It is strongly recommended that you set `org-cycle-max-level' not at all, | |
89 | or to a number smaller than this one. In fact, when `org-cycle-max-level' is | |
90 | not set, it will be assumed to be one less than the value of smaller than | |
91 | the value of this variable." | |
92 | :group 'org-inlinetask | |
93 | :type 'boolean) | |
94 | ||
8bfe682a | 95 | (defcustom org-inlinetask-export t |
ed21c5c8 | 96 | "Non-nil means export inline tasks. |
8bfe682a CD |
97 | When nil, they will not be exported." |
98 | :group 'org-inlinetask | |
99 | :type 'boolean) | |
100 | ||
c8d0cf5c CD |
101 | (defvar org-odd-levels-only) |
102 | (defvar org-keyword-time-regexp) | |
103 | (defvar org-drawer-regexp) | |
104 | (defvar org-complex-heading-regexp) | |
105 | (defvar org-property-end-re) | |
106 | ||
86fbb8ca CD |
107 | (defcustom org-inlinetask-defaut-state nil |
108 | "Non-nil means make inline tasks have a TODO keyword initially. | |
109 | This should be the state `org-inlinetask-insert-task' should use by | |
110 | default, or nil of no state should be assigned." | |
111 | :group 'org-inlinetask | |
112 | :type '(choice | |
113 | (const :tag "No state" nil) | |
114 | (string :tag "Specific state"))) | |
115 | ||
116 | (defun org-inlinetask-insert-task (&optional no-state) | |
117 | "Insert an inline task. | |
118 | If prefix arg NO-STATE is set, ignore `org-inlinetask-defaut-state'." | |
119 | (interactive "P") | |
c8d0cf5c | 120 | (or (bolp) (newline)) |
86fbb8ca CD |
121 | (let ((indent org-inlinetask-min-level)) |
122 | (if org-odd-levels-only | |
123 | (setq indent (- (* 2 indent) 1))) | |
124 | (insert (make-string indent ?*) | |
125 | (if (or no-state (not org-inlinetask-defaut-state)) | |
126 | " \n" | |
127 | (concat " " org-inlinetask-defaut-state " \n")) | |
128 | (make-string indent ?*) " END\n")) | |
c8d0cf5c CD |
129 | (end-of-line -1)) |
130 | (define-key org-mode-map "\C-c\C-xt" 'org-inlinetask-insert-task) | |
131 | ||
8d642074 CD |
132 | (defvar htmlp) ; dynamically scoped into the next function |
133 | (defvar latexp) ; dynamically scoped into the next function | |
c8d0cf5c CD |
134 | (defun org-inlinetask-export-handler () |
135 | "Handle headlines with level larger or equal to `org-inlinetask-min-level'. | |
136 | Either remove headline and meta data, or do special formatting." | |
137 | (goto-char (point-min)) | |
138 | (let* ((nstars (if org-odd-levels-only | |
139 | (1- (* 2 (or org-inlinetask-min-level 200))) | |
140 | (or org-inlinetask-min-level 200))) | |
141 | (re1 (format "^\\(\\*\\{%d,\\}\\) .*\n" nstars)) | |
142 | (re2 (concat "^[ \t]*" org-keyword-time-regexp)) | |
8d642074 | 143 | headline beg end stars content indent) |
c8d0cf5c CD |
144 | (while (re-search-forward re1 nil t) |
145 | (setq headline (match-string 0) | |
146 | stars (match-string 1) | |
147 | content nil) | |
148 | (replace-match "") | |
149 | (while (looking-at re2) | |
150 | (delete-region (point) (1+ (point-at-eol)))) | |
151 | (while (looking-at org-drawer-regexp) | |
152 | (setq beg (point)) | |
153 | (if (re-search-forward org-property-end-re nil t) | |
154 | (delete-region beg (1+ (match-end 0))))) | |
155 | (setq beg (point)) | |
156 | (when (and (re-search-forward "^\\(\\*+\\) " nil t) | |
157 | (= (length (match-string 1)) (length stars)) | |
158 | (progn (goto-char (match-end 0)) | |
159 | (looking-at "END[ \t]*$"))) | |
160 | (setq content (buffer-substring beg (1- (point-at-bol)))) | |
161 | (delete-region beg (1+ (match-end 0)))) | |
162 | (goto-char beg) | |
8bfe682a CD |
163 | (when org-inlinetask-export |
164 | (when (string-match org-complex-heading-regexp headline) | |
165 | (setq headline (concat | |
166 | (if (match-end 2) | |
ed21c5c8 CD |
167 | (concat |
168 | (org-add-props | |
169 | (format | |
170 | "@<span class=\"%s %s\"> %s@</span>" | |
171 | (if (member (match-string 2 headline) | |
172 | org-done-keywords) | |
173 | "done" "todo") | |
174 | (match-string 2 headline) | |
175 | (match-string 2 headline)) | |
176 | nil 'org-protected t) | |
177 | " ") "") | |
8bfe682a CD |
178 | (match-string 4 headline))) |
179 | (when content | |
180 | (if (not (string-match "\\S-" content)) | |
181 | (setq content nil) | |
182 | (if (string-match "[ \t\n]+\\'" content) | |
183 | (setq content (substring content 0 (match-beginning 0)))) | |
184 | (setq content (org-remove-indentation content)) | |
185 | (if latexp (setq content (concat "\\quad \\\\\n" content))))) | |
186 | (insert (make-string (org-inlinetask-get-current-indentation) ?\ ) | |
187 | "- ") | |
188 | (setq indent (make-string (current-column) ?\ )) | |
189 | (insert headline " ::") | |
190 | (if content | |
191 | (insert (if htmlp " " (concat "\n" indent)) | |
192 | (mapconcat 'identity (org-split-string content "\n") | |
193 | (concat "\n" indent)) "\n") | |
194 | (insert "\n")) | |
195 | (insert indent) | |
196 | (backward-delete-char 2) | |
197 | (insert "THISISTHEINLINELISTTEMINATOR\n")))))) | |
198 | ||
199 | (defun org-inlinetask-get-current-indentation () | |
200 | "Get the indentation of the last non-while line above this one." | |
201 | (save-excursion | |
202 | (beginning-of-line 1) | |
203 | (skip-chars-backward " \t\n") | |
204 | (beginning-of-line 1) | |
205 | (or (org-at-item-p) | |
206 | (looking-at "[ \t]*")) | |
207 | (goto-char (match-end 0)) | |
208 | (current-column))) | |
c8d0cf5c CD |
209 | |
210 | (defun org-inlinetask-fontify (limit) | |
211 | "Fontify the inline tasks." | |
212 | (let* ((nstars (if org-odd-levels-only | |
213 | (1- (* 2 (or org-inlinetask-min-level 200))) | |
214 | (or org-inlinetask-min-level 200))) | |
215 | (re (concat "^\\(\\*\\)\\(\\*\\{" | |
216 | (format "%d" (- nstars 3)) | |
217 | ",\\}\\)\\(\\*\\* .*\\)"))) | |
218 | (while (re-search-forward re limit t) | |
219 | (add-text-properties (match-beginning 1) (match-end 1) | |
220 | '(face org-warning font-lock-fontified t)) | |
221 | (add-text-properties (match-beginning 2) (match-end 2) | |
222 | '(face org-hide font-lock-fontified t)) | |
223 | (add-text-properties (match-beginning 3) (match-end 3) | |
224 | '(face shadow font-lock-fontified t))))) | |
225 | ||
226 | (defun org-inlinetask-remove-END-maybe () | |
227 | "Remove an END line when present." | |
228 | (when (looking-at (format "\\([ \t]*\n\\)*\\*\\{%d,\\}[ \t]+END[ \t]*$" | |
229 | org-inlinetask-min-level)) | |
230 | (replace-match ""))) | |
231 | ||
8bfe682a CD |
232 | (defun org-inlinetask-remove-terminator () |
233 | (let (beg end) | |
234 | (save-excursion | |
235 | (goto-char (point-min)) | |
236 | (while (re-search-forward "THISISTHEINLINELISTTEMINATOR\n" nil t) | |
237 | (setq beg (match-beginning 0) end (match-end 0)) | |
238 | (save-excursion | |
239 | (beginning-of-line 1) | |
240 | (and (looking-at "<p\\(ara\\)?>THISISTHEINLINELISTTEMINATOR[ \t\n]*</p\\(ara\\)?>") | |
241 | (setq beg (point) end (match-end 0)))) | |
242 | (delete-region beg end))))) | |
243 | ||
c8d0cf5c CD |
244 | (eval-after-load "org-exp" |
245 | '(add-hook 'org-export-preprocess-after-tree-selection-hook | |
246 | 'org-inlinetask-export-handler)) | |
247 | (eval-after-load "org" | |
248 | '(add-hook 'org-font-lock-hook 'org-inlinetask-fontify)) | |
8bfe682a CD |
249 | (eval-after-load "org-html" |
250 | '(add-hook 'org-export-html-final-hook 'org-inlinetask-remove-terminator)) | |
251 | (eval-after-load "org-latex" | |
252 | '(add-hook 'org-export-latex-final-hook 'org-inlinetask-remove-terminator)) | |
253 | (eval-after-load "org-ascii" | |
254 | '(add-hook 'org-export-ascii-final-hook 'org-inlinetask-remove-terminator)) | |
255 | (eval-after-load "org-docbook" | |
256 | '(add-hook 'org-export-docbook-final-hook 'org-inlinetask-remove-terminator)) | |
c8d0cf5c CD |
257 | |
258 | (provide 'org-inlinetask) | |
259 | ||
260 | ;;; org-inlinetask.el ends here |