Commit | Line | Data |
---|---|---|
47ffc456 CD |
1 | ;;; org-list.el --- Plain lists for Org-mode |
2 | ;; | |
1e4f816a CD |
3 | ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 |
4 | ;; Free Software Foundation, Inc. | |
47ffc456 CD |
5 | ;; |
6 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
33306645 | 7 | ;; Bastien Guerry <bzg AT altern DOT org> |
47ffc456 CD |
8 | ;; Keywords: outlines, hypermedia, calendar, wp |
9 | ;; Homepage: http://orgmode.org | |
d6685abc | 10 | ;; Version: 6.20c |
47ffc456 CD |
11 | ;; |
12 | ;; This file is part of GNU Emacs. | |
13 | ;; | |
14 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
15 | ;; it under the terms of the GNU General Public License as published by | |
16 | ;; the Free Software Foundation, either version 3 of the License, or | |
17 | ;; (at your option) any later version. | |
18 | ||
19 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
33306645 | 21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
47ffc456 CD |
22 | ;; GNU General Public License for more details. |
23 | ||
24 | ;; You should have received a copy of the GNU General Public License | |
25 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
26 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
27 | ;; | |
28 | ;;; Commentary: | |
29 | ||
30 | ;; This file contains the code dealing with plain lists in Org-mode. | |
31 | ||
32 | ;;; Code: | |
33 | ||
34 | (require 'org-macs) | |
35 | (require 'org-compat) | |
36 | ||
37 | (defvar org-blank-before-new-entry) | |
38 | (defvar org-M-RET-may-split-line) | |
39 | ||
40 | (declare-function org-invisible-p "org" ()) | |
41 | (declare-function org-on-heading-p "org" (&optional invisible-ok)) | |
9fc10007 GM |
42 | (declare-function outline-next-heading "outline" ()) |
43 | (declare-function outline-back-to-heading "outline" (&optional invisible-ok)) | |
47ffc456 CD |
44 | (declare-function org-back-to-heading "org" (&optional invisible-ok)) |
45 | (declare-function org-back-over-empty-lines "org" ()) | |
46 | (declare-function org-skip-whitespace "org" ()) | |
47 | (declare-function org-trim "org" (s)) | |
48 | (declare-function org-get-indentation "org" (&optional line)) | |
ff4be292 | 49 | (declare-function org-timer-item "org-timer" (&optional arg)) |
0bd48b37 | 50 | (declare-function org-combine-plists "org" (&rest plists)) |
47ffc456 CD |
51 | |
52 | (defgroup org-plain-lists nil | |
53 | "Options concerning plain lists in Org-mode." | |
54 | :tag "Org Plain lists" | |
55 | :group 'org-structure) | |
56 | ||
57 | (defcustom org-cycle-include-plain-lists nil | |
58 | "Non-nil means, include plain lists into visibility cycling. | |
59 | This means that during cycling, plain list items will *temporarily* be | |
60 | interpreted as outline headlines with a level given by 1000+i where i is the | |
61 | indentation of the bullet. In all other operations, plain list items are | |
33306645 | 62 | not seen as headlines. For example, you cannot assign a TODO keyword to |
47ffc456 CD |
63 | such an item." |
64 | :group 'org-plain-lists | |
65 | :type 'boolean) | |
66 | ||
67 | (defcustom org-plain-list-ordered-item-terminator t | |
68 | "The character that makes a line with leading number an ordered list item. | |
69 | Valid values are ?. and ?\). To get both terminators, use t. While | |
70 | ?. may look nicer, it creates the danger that a line with leading | |
71 | number may be incorrectly interpreted as an item. ?\) therefore is | |
72 | the safe choice." | |
73 | :group 'org-plain-lists | |
74 | :type '(choice (const :tag "dot like in \"2.\"" ?.) | |
75 | (const :tag "paren like in \"2)\"" ?\)) | |
76 | (const :tab "both" t))) | |
77 | ||
ce4fdcb9 CD |
78 | (defcustom org-list-two-spaces-after-bullet-regexp nil |
79 | "A regular expression matching bullets that should have 2 spaces after them. | |
80 | When nil, no bullet will have two spaces after them. | |
33306645 | 81 | When a string, it will be used as a regular expression. When the bullet |
ce4fdcb9 | 82 | type of a list is changed, the new bullet type will be matched against this |
33306645 | 83 | regexp. If it matches, there will be two spaces instead of one after |
ce4fdcb9 CD |
84 | the bullet in each item of he list." |
85 | :group 'org-plain-list | |
86 | :type '(choice | |
87 | (const :tag "never" nil) | |
88 | (regexp))) | |
89 | ||
47ffc456 CD |
90 | (defcustom org-empty-line-terminates-plain-lists nil |
91 | "Non-nil means, an empty line ends all plain list levels. | |
92 | When nil, empty lines are part of the preceeding item." | |
93 | :group 'org-plain-lists | |
94 | :type 'boolean) | |
95 | ||
96 | (defcustom org-auto-renumber-ordered-lists t | |
97 | "Non-nil means, automatically renumber ordered plain lists. | |
98 | Renumbering happens when the sequence have been changed with | |
99 | \\[org-shiftmetaup] or \\[org-shiftmetadown]. After other editing commands, | |
100 | use \\[org-ctrl-c-ctrl-c] to trigger renumbering." | |
101 | :group 'org-plain-lists | |
102 | :type 'boolean) | |
103 | ||
104 | (defcustom org-provide-checkbox-statistics t | |
105 | "Non-nil means, update checkbox statistics after insert and toggle. | |
106 | When this is set, checkbox statistics is updated each time you either insert | |
107 | a new checkbox with \\[org-insert-todo-heading] or toggle a checkbox | |
108 | with \\[org-ctrl-c-ctrl-c\\]." | |
109 | :group 'org-plain-lists | |
110 | :type 'boolean) | |
111 | ||
112 | (defcustom org-description-max-indent 20 | |
113 | "Maximum indentation for the second line of a description list. | |
114 | When the indentation would be larger than this, it will become | |
115 | 5 characters instead." | |
116 | :group 'org-plain-lists | |
117 | :type 'integer) | |
118 | ||
119 | (defvar org-list-beginning-re | |
120 | "^\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) +\\(.*\\)$") | |
121 | ||
122 | (defcustom org-list-radio-list-templates | |
123 | '((latex-mode "% BEGIN RECEIVE ORGLST %n | |
124 | % END RECEIVE ORGLST %n | |
125 | \\begin{comment} | |
126 | #+ORGLST: SEND %n org-list-to-latex | |
127 | | | | | |
128 | \\end{comment}\n") | |
129 | (texinfo-mode "@c BEGIN RECEIVE ORGLST %n | |
130 | @c END RECEIVE ORGLST %n | |
131 | @ignore | |
132 | #+ORGLST: SEND %n org-list-to-texinfo | |
133 | | | | | |
134 | @end ignore\n") | |
135 | (html-mode "<!-- BEGIN RECEIVE ORGLST %n --> | |
136 | <!-- END RECEIVE ORGLST %n --> | |
137 | <!-- | |
138 | #+ORGLST: SEND %n org-list-to-html | |
139 | | | | | |
140 | -->\n")) | |
141 | "Templates for radio lists in different major modes. | |
142 | All occurrences of %n in a template will be replaced with the name of the | |
143 | list, obtained by prompting the user." | |
144 | :group 'org-plain-lists | |
145 | :type '(repeat | |
146 | (list (symbol :tag "Major mode") | |
147 | (string :tag "Format")))) | |
148 | ||
149 | ;;;; Plain list items, including checkboxes | |
150 | ||
151 | ;;; Plain list items | |
152 | ||
153 | (defun org-at-item-p () | |
154 | "Is point in a line starting a hand-formatted item?" | |
155 | (let ((llt org-plain-list-ordered-item-terminator)) | |
156 | (save-excursion | |
157 | (goto-char (point-at-bol)) | |
158 | (looking-at | |
159 | (cond | |
160 | ((eq llt t) "\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)") | |
161 | ((= llt ?.) "\\([ \t]*\\([-+]\\|\\([0-9]+\\.\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)") | |
162 | ((= llt ?\)) "\\([ \t]*\\([-+]\\|\\([0-9]+))\\)\\|[ \t]+\\*\\)\\( \\|$\\)") | |
163 | (t (error "Invalid value of `org-plain-list-ordered-item-terminator'"))))))) | |
164 | ||
65c439fd CD |
165 | (defun org-at-item-bullet-p () |
166 | "Is point at the bullet of a plain list item?" | |
167 | (and (org-at-item-p) | |
168 | (not (member (char-after) '(?\ ?\t))) | |
169 | (< (point) (match-end 0)))) | |
170 | ||
47ffc456 CD |
171 | (defun org-in-item-p () |
172 | "It the cursor inside a plain list item. | |
173 | Does not have to be the first line." | |
174 | (save-excursion | |
175 | (condition-case nil | |
176 | (progn | |
177 | (org-beginning-of-item) | |
178 | (org-at-item-p) | |
179 | t) | |
180 | (error nil)))) | |
181 | ||
182 | (defun org-insert-item (&optional checkbox) | |
183 | "Insert a new item at the current level. | |
184 | Return t when things worked, nil when we are not in an item." | |
185 | (when (save-excursion | |
186 | (condition-case nil | |
187 | (progn | |
188 | (org-beginning-of-item) | |
189 | (org-at-item-p) | |
190 | (if (org-invisible-p) (error "Invisible item")) | |
191 | t) | |
192 | (error nil))) | |
193 | (let* ((bul (match-string 0)) | |
194 | (descp (save-excursion (goto-char (match-beginning 0)) | |
195 | (beginning-of-line 1) | |
196 | (save-match-data | |
ff4be292 CD |
197 | (and (looking-at "[ \t]*\\(.*?\\) ::") |
198 | (match-string 1))))) | |
0bd48b37 CD |
199 | (empty-line-p (save-excursion |
200 | (goto-char (match-beginning 0)) | |
201 | (and (not (bobp)) | |
202 | (or (beginning-of-line 0) t) | |
203 | (save-match-data | |
204 | (looking-at "[ \t]*$"))))) | |
ff4be292 CD |
205 | (timerp (and descp |
206 | (save-match-data | |
207 | (string-match "^[-+*][ \t]+[0-9]+:[0-9]+:[0-9]+$" | |
208 | descp)))) | |
47ffc456 CD |
209 | (eow (save-excursion (beginning-of-line 1) (looking-at "[ \t]*") |
210 | (match-end 0))) | |
0bd48b37 CD |
211 | (blank-a (cdr (assq 'plain-list-item org-blank-before-new-entry))) |
212 | (blank (if (eq blank-a 'auto) empty-line-p blank-a)) | |
47ffc456 CD |
213 | pos) |
214 | (if descp (setq checkbox nil)) | |
ff4be292 CD |
215 | (if timerp |
216 | (progn (org-timer-item) t) | |
217 | (cond | |
218 | ((and (org-at-item-p) (<= (point) eow)) | |
219 | ;; before the bullet | |
220 | (beginning-of-line 1) | |
221 | (open-line (if blank 2 1))) | |
222 | ((<= (point) eow) | |
223 | (beginning-of-line 1)) | |
224 | (t | |
225 | (unless (org-get-alist-option org-M-RET-may-split-line 'item) | |
226 | (end-of-line 1) | |
227 | (delete-horizontal-space)) | |
228 | (newline (if blank 2 1)))) | |
229 | (insert bul | |
230 | (if checkbox "[ ]" "") | |
231 | (if descp (concat (if checkbox " " "") | |
232 | (read-string "Term: ") " :: ") "")) | |
233 | (just-one-space) | |
234 | (setq pos (point)) | |
235 | (end-of-line 1) | |
236 | (unless (= (point) pos) (just-one-space) (backward-delete-char 1))) | |
237 | (org-maybe-renumber-ordered-list) | |
238 | (and checkbox (org-update-checkbox-count-maybe)) | |
239 | t))) | |
47ffc456 CD |
240 | |
241 | ;;; Checkboxes | |
242 | ||
243 | (defun org-at-item-checkbox-p () | |
244 | "Is point at a line starting a plain-list item with a checklet?" | |
245 | (and (org-at-item-p) | |
246 | (save-excursion | |
247 | (goto-char (match-end 0)) | |
248 | (skip-chars-forward " \t") | |
249 | (looking-at "\\[[- X]\\]")))) | |
250 | ||
d6685abc CD |
251 | (defun org-toggle-checkbox (&optional toggle-presence) |
252 | "Toggle the checkbox in the current line. | |
253 | With prefix arg TOGGLE-PRESENCE, add or remove checkboxes. | |
254 | When there is an active region, toggle status or presence of the checkbox | |
255 | in the first line, and make every item in the region have the same | |
256 | status or precence, respectively." | |
47ffc456 CD |
257 | (interactive "P") |
258 | (catch 'exit | |
d6685abc | 259 | (let (beg end status first-present first-status) |
47ffc456 CD |
260 | (cond |
261 | ((org-region-active-p) | |
262 | (setq beg (region-beginning) end (region-end))) | |
263 | ((org-on-heading-p) | |
264 | (setq beg (point) end (save-excursion (outline-next-heading) (point)))) | |
265 | ((org-at-item-checkbox-p) | |
266 | (let ((pos (point))) | |
d6685abc CD |
267 | (if toggle-presence |
268 | (progn | |
269 | (replace-match "") | |
270 | (goto-char (match-beginning 0)) | |
271 | (just-one-space)) | |
272 | (replace-match | |
273 | (cond ((member (match-string 0) '("[ ]" "[-]")) "[X]") | |
274 | (t "[ ]")) | |
275 | t t)) | |
47ffc456 CD |
276 | (goto-char pos)) |
277 | (throw 'exit t)) | |
d6685abc CD |
278 | ((org-at-item-p) |
279 | ;; add a checkbox | |
280 | (save-excursion | |
281 | (goto-char (match-end 0)) | |
282 | (insert "[ ] ")) | |
283 | (throw 'exit t)) | |
47ffc456 | 284 | (t (error "Not at a checkbox or heading, and no active region"))) |
d6685abc | 285 | (setq end (move-marker (make-marker) end)) |
47ffc456 CD |
286 | (save-excursion |
287 | (goto-char beg) | |
d6685abc CD |
288 | (setq first-present (org-at-item-checkbox-p) |
289 | first-status (and first-present (equal (match-string 0) "[X]"))) | |
47ffc456 | 290 | (while (< (point) end) |
d6685abc CD |
291 | (if toggle-presence |
292 | (cond | |
293 | ((and first-present (org-at-item-checkbox-p)) | |
294 | (save-excursion | |
295 | (replace-match "") | |
296 | (goto-char (match-beginning 0)) | |
297 | (just-one-space))) | |
298 | ((and (not first-present) (not (org-at-item-checkbox-p)) | |
299 | (org-at-item-p)) | |
300 | (save-excursion | |
301 | (goto-char (match-end 0)) | |
302 | (insert "[ ] ")))) | |
303 | (when (org-at-item-checkbox-p) | |
304 | (setq status (equal (match-string 0) "[X]")) | |
305 | (replace-match | |
306 | (if first-status "[ ]" "[X]") t t))) | |
47ffc456 CD |
307 | (beginning-of-line 2))))) |
308 | (org-update-checkbox-count-maybe)) | |
309 | ||
310 | (defun org-update-checkbox-count-maybe () | |
311 | "Update checkbox statistics unless turned off by user." | |
312 | (when org-provide-checkbox-statistics | |
313 | (org-update-checkbox-count))) | |
314 | ||
315 | (defun org-update-checkbox-count (&optional all) | |
316 | "Update the checkbox statistics in the current section. | |
317 | This will find all statistic cookies like [57%] and [6/12] and update them | |
318 | with the current numbers. With optional prefix argument ALL, do this for | |
319 | the whole buffer." | |
320 | (interactive "P") | |
321 | (save-excursion | |
322 | (let* ((buffer-invisibility-spec (org-inhibit-invisibility)) ; Emacs 21 | |
323 | (beg (condition-case nil | |
ce4fdcb9 | 324 | (progn (org-back-to-heading) (point)) |
47ffc456 CD |
325 | (error (point-min)))) |
326 | (end (move-marker (make-marker) | |
327 | (progn (outline-next-heading) (point)))) | |
328 | (re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)") | |
329 | (re-box "^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)") | |
330 | (re-find (concat re "\\|" re-box)) | |
331 | beg-cookie end-cookie is-percent c-on c-off lim | |
33306645 CD |
332 | eline curr-ind next-ind continue-from startsearch |
333 | (cstat 0) | |
334 | ) | |
47ffc456 CD |
335 | (when all |
336 | (goto-char (point-min)) | |
337 | (outline-next-heading) | |
338 | (setq beg (point) end (point-max))) | |
339 | (goto-char end) | |
340 | ;; find each statistic cookie | |
341 | (while (re-search-backward re-find beg t) | |
342 | (setq beg-cookie (match-beginning 1) | |
33306645 | 343 | end-cookie (match-end 1) |
47ffc456 CD |
344 | cstat (+ cstat (if end-cookie 1 0)) |
345 | startsearch (point-at-eol) | |
346 | continue-from (point-at-bol) | |
33306645 | 347 | is-percent (match-beginning 2) |
47ffc456 CD |
348 | lim (cond |
349 | ((org-on-heading-p) (outline-next-heading) (point)) | |
350 | ((org-at-item-p) (org-end-of-item) (point)) | |
351 | (t nil)) | |
33306645 CD |
352 | c-on 0 |
353 | c-off 0) | |
47ffc456 | 354 | (when lim |
33306645 CD |
355 | ;; find first checkbox for this cookie and gather |
356 | ;; statistics from all that are at this indentation level | |
357 | (goto-char startsearch) | |
358 | (if (re-search-forward re-box lim t) | |
359 | (progn | |
360 | (org-beginning-of-item) | |
361 | (setq curr-ind (org-get-indentation)) | |
362 | (setq next-ind curr-ind) | |
363 | (while (and (bolp) (org-at-item-p) (= curr-ind next-ind)) | |
364 | (save-excursion (end-of-line) (setq eline (point))) | |
365 | (if (re-search-forward re-box eline t) | |
47ffc456 CD |
366 | (if (member (match-string 2) '("[ ]" "[-]")) |
367 | (setq c-off (1+ c-off)) | |
33306645 CD |
368 | (setq c-on (1+ c-on)) |
369 | ) | |
370 | ) | |
371 | (org-end-of-item) | |
372 | (setq next-ind (org-get-indentation)) | |
373 | ))) | |
47ffc456 | 374 | (goto-char continue-from) |
33306645 | 375 | ;; update cookie |
47ffc456 CD |
376 | (when end-cookie |
377 | (delete-region beg-cookie end-cookie) | |
378 | (goto-char beg-cookie) | |
379 | (insert | |
380 | (if is-percent | |
381 | (format "[%d%%]" (/ (* 100 c-on) (max 1 (+ c-on c-off)))) | |
382 | (format "[%d/%d]" c-on (+ c-on c-off))))) | |
33306645 CD |
383 | ;; update items checkbox if it has one |
384 | (when (org-at-item-p) | |
385 | (org-beginning-of-item) | |
386 | (when (and (> (+ c-on c-off) 0) | |
47ffc456 | 387 | (re-search-forward re-box (point-at-eol) t)) |
33306645 CD |
388 | (setq beg-cookie (match-beginning 2) |
389 | end-cookie (match-end 2)) | |
390 | (delete-region beg-cookie end-cookie) | |
391 | (goto-char beg-cookie) | |
392 | (cond ((= c-off 0) (insert "[X]")) | |
393 | ((= c-on 0) (insert "[ ]")) | |
394 | (t (insert "[-]"))) | |
395 | ))) | |
47ffc456 CD |
396 | (goto-char continue-from)) |
397 | (when (interactive-p) | |
33306645 | 398 | (message "Checkbox statistics updated %s (%d places)" |
47ffc456 CD |
399 | (if all "in entire file" "in current outline entry") cstat))))) |
400 | ||
401 | (defun org-get-checkbox-statistics-face () | |
402 | "Select the face for checkbox statistics. | |
403 | The face will be `org-done' when all relevant boxes are checked. Otherwise | |
404 | it will be `org-todo'." | |
405 | (if (match-end 1) | |
406 | (if (equal (match-string 1) "100%") 'org-done 'org-todo) | |
407 | (if (and (> (match-end 2) (match-beginning 2)) | |
408 | (equal (match-string 2) (match-string 3))) | |
409 | 'org-done | |
410 | 'org-todo))) | |
411 | ||
412 | (defun org-beginning-of-item () | |
413 | "Go to the beginning of the current hand-formatted item. | |
414 | If the cursor is not in an item, throw an error." | |
415 | (interactive) | |
416 | (let ((pos (point)) | |
417 | (limit (save-excursion | |
418 | (condition-case nil | |
419 | (progn | |
420 | (org-back-to-heading) | |
421 | (beginning-of-line 2) (point)) | |
422 | (error (point-min))))) | |
423 | (ind-empty (if org-empty-line-terminates-plain-lists 0 10000)) | |
424 | ind ind1) | |
425 | (if (org-at-item-p) | |
426 | (beginning-of-line 1) | |
427 | (beginning-of-line 1) | |
428 | (skip-chars-forward " \t") | |
429 | (setq ind (current-column)) | |
430 | (if (catch 'exit | |
431 | (while t | |
432 | (beginning-of-line 0) | |
433 | (if (or (bobp) (< (point) limit)) (throw 'exit nil)) | |
434 | ||
435 | (if (looking-at "[ \t]*$") | |
436 | (setq ind1 ind-empty) | |
437 | (skip-chars-forward " \t") | |
438 | (setq ind1 (current-column))) | |
439 | (if (< ind1 ind) | |
440 | (progn (beginning-of-line 1) (throw 'exit (org-at-item-p)))))) | |
441 | nil | |
442 | (goto-char pos) | |
443 | (error "Not in an item"))))) | |
444 | ||
445 | (defun org-end-of-item () | |
446 | "Go to the end of the current hand-formatted item. | |
447 | If the cursor is not in an item, throw an error." | |
448 | (interactive) | |
449 | (let* ((pos (point)) | |
450 | ind1 | |
451 | (ind-empty (if org-empty-line-terminates-plain-lists 0 10000)) | |
452 | (limit (save-excursion (outline-next-heading) (point))) | |
453 | (ind (save-excursion | |
454 | (org-beginning-of-item) | |
455 | (skip-chars-forward " \t") | |
456 | (current-column))) | |
457 | (end (catch 'exit | |
458 | (while t | |
459 | (beginning-of-line 2) | |
460 | (if (eobp) (throw 'exit (point))) | |
461 | (if (>= (point) limit) (throw 'exit (point-at-bol))) | |
462 | (if (looking-at "[ \t]*$") | |
463 | (setq ind1 ind-empty) | |
464 | (skip-chars-forward " \t") | |
465 | (setq ind1 (current-column))) | |
466 | (if (<= ind1 ind) | |
467 | (throw 'exit (point-at-bol))))))) | |
468 | (if end | |
469 | (goto-char end) | |
470 | (goto-char pos) | |
471 | (error "Not in an item")))) | |
472 | ||
473 | (defun org-next-item () | |
474 | "Move to the beginning of the next item in the current plain list. | |
475 | Error if not at a plain list, or if this is the last item in the list." | |
476 | (interactive) | |
477 | (let (ind ind1 (pos (point))) | |
478 | (org-beginning-of-item) | |
479 | (setq ind (org-get-indentation)) | |
480 | (org-end-of-item) | |
481 | (setq ind1 (org-get-indentation)) | |
482 | (unless (and (org-at-item-p) (= ind ind1)) | |
483 | (goto-char pos) | |
484 | (error "On last item")))) | |
485 | ||
486 | (defun org-previous-item () | |
487 | "Move to the beginning of the previous item in the current plain list. | |
488 | Error if not at a plain list, or if this is the first item in the list." | |
489 | (interactive) | |
490 | (let (beg ind ind1 (pos (point))) | |
491 | (org-beginning-of-item) | |
492 | (setq beg (point)) | |
493 | (setq ind (org-get-indentation)) | |
494 | (goto-char beg) | |
495 | (catch 'exit | |
496 | (while t | |
497 | (beginning-of-line 0) | |
498 | (if (looking-at "[ \t]*$") | |
499 | nil | |
500 | (if (<= (setq ind1 (org-get-indentation)) ind) | |
501 | (throw 'exit t))))) | |
502 | (condition-case nil | |
503 | (if (or (not (org-at-item-p)) | |
504 | (< ind1 (1- ind))) | |
505 | (error "") | |
506 | (org-beginning-of-item)) | |
507 | (error (goto-char pos) | |
508 | (error "On first item"))))) | |
509 | ||
510 | (defun org-first-list-item-p () | |
511 | "Is this heading the item in a plain list?" | |
512 | (unless (org-at-item-p) | |
513 | (error "Not at a plain list item")) | |
514 | (org-beginning-of-item) | |
515 | (= (point) (save-excursion (org-beginning-of-item-list)))) | |
516 | ||
517 | (defun org-move-item-down () | |
518 | "Move the plain list item at point down, i.e. swap with following item. | |
519 | Subitems (items with larger indentation) are considered part of the item, | |
520 | so this really moves item trees." | |
521 | (interactive) | |
522 | (let ((col (current-column)) | |
523 | (pos (point)) | |
524 | beg beg0 end end0 ind ind1 txt ne-end ne-beg) | |
525 | (org-beginning-of-item) | |
526 | (setq beg0 (point)) | |
527 | (save-excursion | |
528 | (setq ne-beg (org-back-over-empty-lines)) | |
529 | (setq beg (point))) | |
530 | (goto-char beg0) | |
531 | (setq ind (org-get-indentation)) | |
532 | (org-end-of-item) | |
533 | (setq end0 (point)) | |
534 | (setq ind1 (org-get-indentation)) | |
535 | (setq ne-end (org-back-over-empty-lines)) | |
536 | (setq end (point)) | |
537 | (goto-char beg0) | |
538 | (when (and (org-first-list-item-p) (< ne-end ne-beg)) | |
539 | ;; include less whitespace | |
540 | (save-excursion | |
541 | (goto-char beg) | |
542 | (forward-line (- ne-beg ne-end)) | |
543 | (setq beg (point)))) | |
544 | (goto-char end0) | |
545 | (if (and (org-at-item-p) (= ind ind1)) | |
546 | (progn | |
547 | (org-end-of-item) | |
548 | (org-back-over-empty-lines) | |
549 | (setq txt (buffer-substring beg end)) | |
550 | (save-excursion | |
551 | (delete-region beg end)) | |
552 | (setq pos (point)) | |
553 | (insert txt) | |
554 | (goto-char pos) (org-skip-whitespace) | |
555 | (org-maybe-renumber-ordered-list) | |
556 | (move-to-column col)) | |
557 | (goto-char pos) | |
558 | (move-to-column col) | |
559 | (error "Cannot move this item further down")))) | |
560 | ||
561 | (defun org-move-item-up (arg) | |
562 | "Move the plain list item at point up, i.e. swap with previous item. | |
563 | Subitems (items with larger indentation) are considered part of the item, | |
564 | so this really moves item trees." | |
565 | (interactive "p") | |
566 | (let ((col (current-column)) (pos (point)) | |
567 | beg beg0 end ind ind1 txt | |
568 | ne-beg ne-ins ins-end) | |
569 | (org-beginning-of-item) | |
570 | (setq beg0 (point)) | |
571 | (setq ind (org-get-indentation)) | |
572 | (save-excursion | |
573 | (setq ne-beg (org-back-over-empty-lines)) | |
574 | (setq beg (point))) | |
575 | (goto-char beg0) | |
576 | (org-end-of-item) | |
577 | (org-back-over-empty-lines) | |
578 | (setq end (point)) | |
579 | (goto-char beg0) | |
580 | (catch 'exit | |
581 | (while t | |
582 | (beginning-of-line 0) | |
583 | (if (looking-at "[ \t]*$") | |
584 | (if org-empty-line-terminates-plain-lists | |
585 | (progn | |
586 | (goto-char pos) | |
587 | (error "Cannot move this item further up")) | |
588 | nil) | |
589 | (if (<= (setq ind1 (org-get-indentation)) ind) | |
590 | (throw 'exit t))))) | |
591 | (condition-case nil | |
592 | (org-beginning-of-item) | |
593 | (error (goto-char beg0) | |
594 | (move-to-column col) | |
595 | (error "Cannot move this item further up"))) | |
596 | (setq ind1 (org-get-indentation)) | |
597 | (if (and (org-at-item-p) (= ind ind1)) | |
598 | (progn | |
599 | (setq ne-ins (org-back-over-empty-lines)) | |
600 | (setq txt (buffer-substring beg end)) | |
601 | (save-excursion | |
602 | (delete-region beg end)) | |
603 | (setq pos (point)) | |
604 | (insert txt) | |
605 | (setq ins-end (point)) | |
606 | (goto-char pos) (org-skip-whitespace) | |
607 | ||
608 | (when (and (org-first-list-item-p) (> ne-ins ne-beg)) | |
609 | ;; Move whitespace back to beginning | |
610 | (save-excursion | |
611 | (goto-char ins-end) | |
612 | (let ((kill-whole-line t)) | |
613 | (kill-line (- ne-ins ne-beg)) (point))) | |
614 | (insert (make-string (- ne-ins ne-beg) ?\n))) | |
615 | ||
616 | (org-maybe-renumber-ordered-list) | |
617 | (move-to-column col)) | |
618 | (goto-char pos) | |
619 | (move-to-column col) | |
620 | (error "Cannot move this item further up")))) | |
621 | ||
622 | (defun org-maybe-renumber-ordered-list () | |
623 | "Renumber the ordered list at point if setup allows it. | |
624 | This tests the user option `org-auto-renumber-ordered-lists' before | |
625 | doing the renumbering." | |
626 | (interactive) | |
627 | (when (and org-auto-renumber-ordered-lists | |
628 | (org-at-item-p)) | |
629 | (if (match-beginning 3) | |
630 | (org-renumber-ordered-list 1) | |
631 | (org-fix-bullet-type)))) | |
632 | ||
633 | (defun org-maybe-renumber-ordered-list-safe () | |
634 | (condition-case nil | |
635 | (save-excursion | |
636 | (org-maybe-renumber-ordered-list)) | |
637 | (error nil))) | |
638 | ||
639 | (defun org-cycle-list-bullet (&optional which) | |
640 | "Cycle through the different itemize/enumerate bullets. | |
641 | This cycle the entire list level through the sequence: | |
642 | ||
33306645 | 643 | `-' -> `+' -> `*' -> `1.' -> `1)' |
47ffc456 CD |
644 | |
645 | If WHICH is a string, use that as the new bullet. If WHICH is an integer, | |
33306645 | 646 | 0 means `-', 1 means `+' etc." |
47ffc456 CD |
647 | (interactive "P") |
648 | (org-preserve-lc | |
649 | (org-beginning-of-item-list) | |
650 | (org-at-item-p) | |
651 | (beginning-of-line 1) | |
652 | (let ((current (match-string 0)) | |
653 | (prevp (eq which 'previous)) | |
ce4fdcb9 | 654 | new old) |
47ffc456 CD |
655 | (setq new (cond |
656 | ((and (numberp which) | |
657 | (nth (1- which) '("-" "+" "*" "1." "1)")))) | |
658 | ((string-match "-" current) (if prevp "1)" "+")) | |
659 | ((string-match "\\+" current) | |
660 | (if prevp "-" (if (looking-at "\\S-") "1." "*"))) | |
661 | ((string-match "\\*" current) (if prevp "+" "1.")) | |
ce4fdcb9 CD |
662 | ((string-match "\\." current) |
663 | (if prevp (if (looking-at "\\S-") "+" "*") "1)")) | |
47ffc456 CD |
664 | ((string-match ")" current) (if prevp "1." "-")) |
665 | (t (error "This should not happen")))) | |
ce4fdcb9 CD |
666 | (and (looking-at "\\([ \t]*\\)\\(\\S-+\\)") |
667 | (setq old (match-string 2)) | |
668 | (replace-match (concat "\\1" new))) | |
669 | (org-shift-item-indentation (- (length new) (length old))) | |
47ffc456 CD |
670 | (org-fix-bullet-type) |
671 | (org-maybe-renumber-ordered-list)))) | |
672 | ||
673 | (defun org-get-string-indentation (s) | |
674 | "What indentation has S due to SPACE and TAB at the beginning of the string?" | |
675 | (let ((n -1) (i 0) (w tab-width) c) | |
676 | (catch 'exit | |
677 | (while (< (setq n (1+ n)) (length s)) | |
678 | (setq c (aref s n)) | |
679 | (cond ((= c ?\ ) (setq i (1+ i))) | |
680 | ((= c ?\t) (setq i (* (/ (+ w i) w) w))) | |
681 | (t (throw 'exit t))))) | |
682 | i)) | |
683 | ||
684 | (defun org-renumber-ordered-list (arg) | |
685 | "Renumber an ordered plain list. | |
686 | Cursor needs to be in the first line of an item, the line that starts | |
687 | with something like \"1.\" or \"2)\"." | |
688 | (interactive "p") | |
689 | (unless (and (org-at-item-p) | |
690 | (match-beginning 3)) | |
691 | (error "This is not an ordered list")) | |
692 | (let ((line (org-current-line)) | |
693 | (col (current-column)) | |
694 | (ind (org-get-string-indentation | |
695 | (buffer-substring (point-at-bol) (match-beginning 3)))) | |
696 | ;; (term (substring (match-string 3) -1)) | |
697 | ind1 (n (1- arg)) | |
ce4fdcb9 | 698 | fmt bobp old new) |
47ffc456 CD |
699 | ;; find where this list begins |
700 | (org-beginning-of-item-list) | |
701 | (setq bobp (bobp)) | |
702 | (looking-at "[ \t]*[0-9]+\\([.)]\\)") | |
703 | (setq fmt (concat "%d" (match-string 1))) | |
704 | (beginning-of-line 0) | |
705 | ;; walk forward and replace these numbers | |
706 | (catch 'exit | |
707 | (while t | |
708 | (catch 'next | |
709 | (if bobp (setq bobp nil) (beginning-of-line 2)) | |
710 | (if (eobp) (throw 'exit nil)) | |
711 | (if (looking-at "[ \t]*$") (throw 'next nil)) | |
712 | (skip-chars-forward " \t") (setq ind1 (current-column)) | |
713 | (if (> ind1 ind) (throw 'next t)) | |
714 | (if (< ind1 ind) (throw 'exit t)) | |
715 | (if (not (org-at-item-p)) (throw 'exit nil)) | |
ce4fdcb9 | 716 | (setq old (match-string 2)) |
47ffc456 CD |
717 | (delete-region (match-beginning 2) (match-end 2)) |
718 | (goto-char (match-beginning 2)) | |
ce4fdcb9 CD |
719 | (insert (setq new (format fmt (setq n (1+ n))))) |
720 | (org-shift-item-indentation (- (length new) (length old)))))) | |
47ffc456 CD |
721 | (goto-line line) |
722 | (org-move-to-column col))) | |
723 | ||
724 | (defun org-fix-bullet-type () | |
ce4fdcb9 CD |
725 | "Make sure all items in this list have the same bullet as the first item. |
726 | Also, fix the indentation." | |
47ffc456 CD |
727 | (interactive) |
728 | (unless (org-at-item-p) (error "This is not a list")) | |
729 | (let ((line (org-current-line)) | |
730 | (col (current-column)) | |
731 | (ind (current-indentation)) | |
ce4fdcb9 | 732 | ind1 bullet oldbullet) |
47ffc456 CD |
733 | ;; find where this list begins |
734 | (org-beginning-of-item-list) | |
735 | (beginning-of-line 1) | |
736 | ;; find out what the bullet type is | |
737 | (looking-at "[ \t]*\\(\\S-+\\)") | |
ce4fdcb9 CD |
738 | (setq bullet (concat (match-string 1) " ")) |
739 | (if (and org-list-two-spaces-after-bullet-regexp | |
740 | (string-match org-list-two-spaces-after-bullet-regexp bullet)) | |
741 | (setq bullet (concat bullet " "))) | |
47ffc456 CD |
742 | ;; walk forward and replace these numbers |
743 | (beginning-of-line 0) | |
744 | (catch 'exit | |
745 | (while t | |
746 | (catch 'next | |
747 | (beginning-of-line 2) | |
748 | (if (eobp) (throw 'exit nil)) | |
749 | (if (looking-at "[ \t]*$") (throw 'next nil)) | |
750 | (skip-chars-forward " \t") (setq ind1 (current-column)) | |
751 | (if (> ind1 ind) (throw 'next t)) | |
752 | (if (< ind1 ind) (throw 'exit t)) | |
753 | (if (not (org-at-item-p)) (throw 'exit nil)) | |
754 | (skip-chars-forward " \t") | |
ce4fdcb9 CD |
755 | (looking-at "\\S-+ *") |
756 | (setq oldbullet (match-string 0)) | |
757 | (replace-match bullet) | |
758 | (org-shift-item-indentation (- (length bullet) (length oldbullet)))))) | |
47ffc456 CD |
759 | (goto-line line) |
760 | (org-move-to-column col) | |
761 | (if (string-match "[0-9]" bullet) | |
762 | (org-renumber-ordered-list 1)))) | |
763 | ||
ce4fdcb9 CD |
764 | (defun org-shift-item-indentation (delta) |
765 | "Shift the indentation in current item by DELTA." | |
766 | (save-excursion | |
767 | (let ((beg (point-at-bol)) | |
768 | (end (progn (org-end-of-item) (point))) | |
769 | i) | |
770 | (goto-char end) | |
771 | (beginning-of-line 0) | |
772 | (while (> (point) beg) | |
773 | (when (looking-at "[ \t]*\\S-") | |
774 | ;; this is not an empty line | |
775 | (setq i (org-get-indentation)) | |
776 | (if (and (> i 0) (> (setq i (+ i delta)) 0)) | |
777 | (indent-line-to i))) | |
778 | (beginning-of-line 0))))) | |
779 | ||
47ffc456 CD |
780 | (defun org-beginning-of-item-list () |
781 | "Go to the beginning of the current item list. | |
782 | I.e. to the first item in this list." | |
783 | (interactive) | |
784 | (org-beginning-of-item) | |
785 | (let ((pos (point-at-bol)) | |
33306645 | 786 | (ind (org-get-indentation)) |
47ffc456 CD |
787 | ind1) |
788 | ;; find where this list begins | |
789 | (catch 'exit | |
790 | (while t | |
791 | (catch 'next | |
792 | (beginning-of-line 0) | |
793 | (if (looking-at "[ \t]*$") | |
794 | (throw (if (bobp) 'exit 'next) t)) | |
795 | (skip-chars-forward " \t") (setq ind1 (current-column)) | |
796 | (if (or (< ind1 ind) | |
797 | (and (= ind1 ind) | |
798 | (not (org-at-item-p))) | |
799 | (and (= (point-at-bol) (point-min)) | |
800 | (setq pos (point-min)))) | |
801 | (throw 'exit t) | |
802 | (when (org-at-item-p) (setq pos (point-at-bol))))))) | |
803 | (goto-char pos))) | |
804 | ||
805 | ||
806 | (defun org-end-of-item-list () | |
807 | "Go to the end of the current item list. | |
808 | I.e. to the text after the last item." | |
809 | (interactive) | |
810 | (org-beginning-of-item) | |
811 | (let ((pos (point-at-bol)) | |
33306645 | 812 | (ind (org-get-indentation)) |
47ffc456 CD |
813 | ind1) |
814 | ;; find where this list begins | |
815 | (catch 'exit | |
816 | (while t | |
817 | (catch 'next | |
818 | (beginning-of-line 2) | |
819 | (if (looking-at "[ \t]*$") | |
820 | (throw (if (eobp) 'exit 'next) t)) | |
821 | (skip-chars-forward " \t") (setq ind1 (current-column)) | |
822 | (if (or (< ind1 ind) | |
823 | (and (= ind1 ind) | |
824 | (not (org-at-item-p))) | |
825 | (eobp)) | |
826 | (progn | |
827 | (setq pos (point-at-bol)) | |
828 | (throw 'exit t)))))) | |
829 | (goto-char pos))) | |
830 | ||
831 | ||
832 | (defvar org-last-indent-begin-marker (make-marker)) | |
833 | (defvar org-last-indent-end-marker (make-marker)) | |
834 | ||
835 | (defun org-outdent-item (arg) | |
836 | "Outdent a local list item." | |
837 | (interactive "p") | |
838 | (org-indent-item (- arg))) | |
839 | ||
840 | (defun org-indent-item (arg) | |
841 | "Indent a local list item." | |
842 | (interactive "p") | |
843 | (unless (org-at-item-p) | |
844 | (error "Not on an item")) | |
845 | (save-excursion | |
846 | (let (beg end ind ind1 tmp delta ind-down ind-up) | |
847 | (if (memq last-command '(org-shiftmetaright org-shiftmetaleft)) | |
848 | (setq beg org-last-indent-begin-marker | |
849 | end org-last-indent-end-marker) | |
850 | (org-beginning-of-item) | |
851 | (setq beg (move-marker org-last-indent-begin-marker (point))) | |
852 | (org-end-of-item) | |
853 | (setq end (move-marker org-last-indent-end-marker (point)))) | |
854 | (goto-char beg) | |
855 | (setq tmp (org-item-indent-positions) | |
856 | ind (car tmp) | |
857 | ind-down (nth 2 tmp) | |
858 | ind-up (nth 1 tmp) | |
859 | delta (if (> arg 0) | |
860 | (if ind-down (- ind-down ind) 2) | |
861 | (if ind-up (- ind-up ind) -2))) | |
862 | (if (< (+ delta ind) 0) (error "Cannot outdent beyond margin")) | |
863 | (while (< (point) end) | |
864 | (beginning-of-line 1) | |
865 | (skip-chars-forward " \t") (setq ind1 (current-column)) | |
866 | (delete-region (point-at-bol) (point)) | |
867 | (or (eolp) (org-indent-to-column (+ ind1 delta))) | |
868 | (beginning-of-line 2)))) | |
869 | (org-fix-bullet-type) | |
870 | (org-maybe-renumber-ordered-list-safe) | |
871 | (save-excursion | |
872 | (beginning-of-line 0) | |
873 | (condition-case nil (org-beginning-of-item) (error nil)) | |
874 | (org-maybe-renumber-ordered-list-safe))) | |
875 | ||
876 | (defun org-item-indent-positions () | |
877 | "Return indentation for plain list items. | |
33306645 CD |
878 | This returns a list with three values: The current indentation, the |
879 | parent indentation and the indentation a child should have. | |
47ffc456 CD |
880 | Assumes cursor in item line." |
881 | (let* ((bolpos (point-at-bol)) | |
882 | (ind (org-get-indentation)) | |
883 | ind-down ind-up pos) | |
884 | (save-excursion | |
885 | (org-beginning-of-item-list) | |
886 | (skip-chars-backward "\n\r \t") | |
887 | (when (org-in-item-p) | |
888 | (org-beginning-of-item) | |
889 | (setq ind-up (org-get-indentation)))) | |
890 | (setq pos (point)) | |
891 | (save-excursion | |
892 | (cond | |
893 | ((and (condition-case nil (progn (org-previous-item) t) | |
894 | (error nil)) | |
895 | (or (forward-char 1) t) | |
896 | (re-search-forward "^\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)" bolpos t)) | |
897 | (setq ind-down (org-get-indentation))) | |
898 | ((and (goto-char pos) | |
899 | (org-at-item-p)) | |
900 | (goto-char (match-end 0)) | |
901 | (skip-chars-forward " \t") | |
902 | (setq ind-down (current-column))))) | |
903 | (list ind ind-up ind-down))) | |
904 | ||
905 | ||
906 | ;;; Send and receive lists | |
907 | ||
908 | (defun org-list-parse-list (&optional delete) | |
909 | "Parse the list at point and maybe DELETE it. | |
910 | Return a list containing first level items as strings and | |
911 | sublevels as a list of strings." | |
912 | (let* ((item-beginning (org-list-item-beginning)) | |
33306645 CD |
913 | (start (car item-beginning)) |
914 | (end (org-list-end (cdr item-beginning))) | |
915 | output itemsep ltype) | |
47ffc456 CD |
916 | (while (re-search-forward org-list-beginning-re end t) |
917 | (goto-char (match-beginning 3)) | |
918 | (save-match-data | |
33306645 CD |
919 | (cond ((string-match "[0-9]" (match-string 2)) |
920 | (setq itemsep "[0-9]+\\(?:\\.\\|)\\)" | |
921 | ltype 'ordered)) | |
922 | ((string-match "^.*::" (match-string 0)) | |
923 | (setq itemsep "[-+]" ltype 'descriptive)) | |
924 | (t (setq itemsep "[-+]" ltype 'unordered)))) | |
47ffc456 CD |
925 | (let* ((indent1 (match-string 1)) |
926 | (nextitem (save-excursion | |
927 | (save-match-data | |
928 | (or (and (re-search-forward | |
929 | (concat "^" indent1 itemsep " *?") end t) | |
930 | (match-beginning 0)) end)))) | |
931 | (item (buffer-substring | |
932 | (point) | |
933 | (or (and (re-search-forward | |
934 | org-list-beginning-re end t) | |
935 | (goto-char (match-beginning 0))) | |
936 | (goto-char end)))) | |
937 | (nextindent (match-string 1)) | |
938 | (item (org-trim item)) | |
0bd48b37 CD |
939 | (item (if (string-match "^\\[\\([xX ]\\)\\]" item) |
940 | (replace-match (if (equal (match-string 1 item) " ") | |
941 | "[CBOFF]" | |
942 | "[CBON]") | |
943 | t nil item) | |
944 | item))) | |
47ffc456 CD |
945 | (push item output) |
946 | (when (> (length nextindent) | |
947 | (length indent1)) | |
948 | (narrow-to-region (point) nextitem) | |
949 | (push (org-list-parse-list) output) | |
950 | (widen)))) | |
951 | (when delete (delete-region start end)) | |
952 | (setq output (nreverse output)) | |
953 | (push ltype output))) | |
954 | ||
955 | (defun org-list-item-beginning () | |
956 | "Find the beginning of the list item. | |
957 | Return a cons which car is the beginning position of the item and | |
958 | cdr is the indentation string." | |
959 | (save-excursion | |
960 | (if (not (or (looking-at org-list-beginning-re) | |
961 | (re-search-backward | |
962 | org-list-beginning-re nil t))) | |
963 | (progn (goto-char (point-min)) (point)) | |
964 | (cons (match-beginning 0) (match-string 1))))) | |
965 | ||
966 | (defun org-list-end (indent) | |
967 | "Return the position of the end of the list. | |
968 | INDENT is the indentation of the list." | |
969 | (save-excursion | |
970 | (catch 'exit | |
971 | (while (or (looking-at org-list-beginning-re) | |
972 | (looking-at (concat "^" indent "[ \t]+\\|^$"))) | |
973 | (if (eq (point) (point-max)) | |
974 | (throw 'exit (point-max))) | |
975 | (forward-line 1))) (point))) | |
976 | ||
977 | (defun org-list-insert-radio-list () | |
978 | "Insert a radio list template appropriate for this major mode." | |
979 | (interactive) | |
980 | (let* ((e (assq major-mode org-list-radio-list-templates)) | |
981 | (txt (nth 1 e)) | |
982 | name pos) | |
983 | (unless e (error "No radio list setup defined for %s" major-mode)) | |
984 | (setq name (read-string "List name: ")) | |
985 | (while (string-match "%n" txt) | |
986 | (setq txt (replace-match name t t txt))) | |
987 | (or (bolp) (insert "\n")) | |
988 | (setq pos (point)) | |
989 | (insert txt) | |
990 | (goto-char pos))) | |
991 | ||
992 | (defun org-list-send-list (&optional maybe) | |
993 | "Send a tranformed version of this list to the receiver position. | |
994 | With argument MAYBE, fail quietly if no transformation is defined for | |
995 | this list." | |
996 | (interactive) | |
997 | (catch 'exit | |
998 | (unless (org-at-item-p) (error "Not at a list")) | |
999 | (save-excursion | |
1000 | (goto-char (car (org-list-item-beginning))) | |
1001 | (beginning-of-line 0) | |
1002 | (unless (looking-at "#\\+ORGLST: *SEND +\\([a-zA-Z0-9_]+\\) +\\([^ \t\r\n]+\\)\\( +.*\\)?") | |
1003 | (if maybe | |
1004 | (throw 'exit nil) | |
1005 | (error "Don't know how to transform this list")))) | |
1006 | (let* ((name (match-string 1)) | |
33306645 | 1007 | (item-beginning (org-list-item-beginning)) |
47ffc456 CD |
1008 | (transform (intern (match-string 2))) |
1009 | (txt (buffer-substring-no-properties | |
33306645 | 1010 | (car item-beginning) |
47ffc456 CD |
1011 | (org-list-end (cdr item-beginning)))) |
1012 | (list (org-list-parse-list)) | |
33306645 | 1013 | beg) |
47ffc456 CD |
1014 | (unless (fboundp transform) |
1015 | (error "No such transformation function %s" transform)) | |
1016 | (setq txt (funcall transform list)) | |
1017 | ;; Find the insertion place | |
1018 | (save-excursion | |
1019 | (goto-char (point-min)) | |
1020 | (unless (re-search-forward | |
1021 | (concat "BEGIN RECEIVE ORGLST +" name "\\([ \t]\\|$\\)") nil t) | |
1022 | (error "Don't know where to insert translated list")) | |
1023 | (goto-char (match-beginning 0)) | |
1024 | (beginning-of-line 2) | |
1025 | (setq beg (point)) | |
1026 | (unless (re-search-forward (concat "END RECEIVE ORGLST +" name) nil t) | |
1027 | (error "Cannot find end of insertion region")) | |
1028 | (beginning-of-line 1) | |
1029 | (delete-region beg (point)) | |
1030 | (goto-char beg) | |
1031 | (insert txt "\n")) | |
1032 | (message "List converted and installed at receiver location")))) | |
1033 | ||
1034 | (defun org-list-to-generic (list params) | |
1035 | "Convert a LIST parsed through `org-list-parse-list' to other formats. | |
1036 | ||
1037 | Valid parameters PARAMS are | |
1038 | ||
33306645 CD |
1039 | :ustart String to start an unordered list |
1040 | :uend String to end an unordered list | |
47ffc456 | 1041 | |
33306645 CD |
1042 | :ostart String to start an ordered list |
1043 | :oend String to end an ordered list | |
47ffc456 | 1044 | |
33306645 CD |
1045 | :dstart String to start a descriptive list |
1046 | :dend String to end a descriptive list | |
47ffc456 | 1047 | :dtstart String to start a descriptive term |
33306645 | 1048 | :dtend String to end a descriptive term |
47ffc456 | 1049 | :ddstart String to start a description |
33306645 | 1050 | :ddend String to end a description |
47ffc456 | 1051 | |
33306645 CD |
1052 | :splice When set to t, return only list body lines, don't wrap |
1053 | them into :[u/o]start and :[u/o]end. Default is nil. | |
47ffc456 | 1054 | |
33306645 CD |
1055 | :istart String to start a list item |
1056 | :iend String to end a list item | |
1057 | :isep String to separate items | |
0bd48b37 CD |
1058 | :lsep String to separate sublists |
1059 | ||
1060 | :cboff String to insert for an unchecked checkbox | |
1061 | :cbon String to insert for a checked checkbox" | |
47ffc456 CD |
1062 | (interactive) |
1063 | (let* ((p params) sublist | |
1064 | (splicep (plist-get p :splice)) | |
1065 | (ostart (plist-get p :ostart)) | |
33306645 | 1066 | (oend (plist-get p :oend)) |
47ffc456 | 1067 | (ustart (plist-get p :ustart)) |
33306645 | 1068 | (uend (plist-get p :uend)) |
47ffc456 | 1069 | (dstart (plist-get p :dstart)) |
33306645 | 1070 | (dend (plist-get p :dend)) |
47ffc456 | 1071 | (dtstart (plist-get p :dtstart)) |
33306645 | 1072 | (dtend (plist-get p :dtend)) |
47ffc456 | 1073 | (ddstart (plist-get p :ddstart)) |
33306645 | 1074 | (ddend (plist-get p :ddend)) |
47ffc456 | 1075 | (istart (plist-get p :istart)) |
33306645 CD |
1076 | (iend (plist-get p :iend)) |
1077 | (isep (plist-get p :isep)) | |
0bd48b37 CD |
1078 | (lsep (plist-get p :lsep)) |
1079 | (cbon (plist-get p :cbon)) | |
1080 | (cboff (plist-get p :cboff))) | |
47ffc456 CD |
1081 | (let ((wrapper |
1082 | (cond ((eq (car list) 'ordered) | |
1083 | (concat ostart "\n%s" oend "\n")) | |
1084 | ((eq (car list) 'unordered) | |
1085 | (concat ustart "\n%s" uend "\n")) | |
1086 | ((eq (car list) 'descriptive) | |
1087 | (concat dstart "\n%s" dend "\n")))) | |
1088 | rtn term defstart defend) | |
1089 | (while (setq sublist (pop list)) | |
1090 | (cond ((symbolp sublist) nil) | |
1091 | ((stringp sublist) | |
33306645 CD |
1092 | (when (string-match "^\\(.*\\) ::" sublist) |
1093 | (setq term (org-trim (format (concat dtstart "%s" dtend) | |
1094 | (match-string 1 sublist)))) | |
1095 | (setq sublist (substring sublist (1+ (length term))))) | |
0bd48b37 CD |
1096 | (if (string-match "\\[CBON\\]" sublist) |
1097 | (setq sublist (replace-match cbon t t sublist))) | |
1098 | (if (string-match "\\[CBOFF\\]" sublist) | |
1099 | (setq sublist (replace-match cboff t t sublist))) | |
33306645 CD |
1100 | (setq rtn (concat rtn istart term ddstart |
1101 | sublist ddend iend isep))) | |
1102 | (t (setq rtn (concat rtn ;; previous list | |
1103 | lsep ;; list separator | |
1104 | (org-list-to-generic sublist p) | |
1105 | lsep ;; list separator | |
1106 | ))))) | |
47ffc456 CD |
1107 | (format wrapper rtn)))) |
1108 | ||
0bd48b37 CD |
1109 | (defun org-list-to-latex (list &optional params) |
1110 | "Convert LIST into a LaTeX list. | |
1111 | LIST is as returnd by `org-list-parse-list'. PARAMS is a property list | |
1112 | with overruling parameters for `org-list-to-generic'." | |
47ffc456 | 1113 | (org-list-to-generic |
0bd48b37 CD |
1114 | list |
1115 | (org-combine-plists | |
1116 | '(:splicep nil :ostart "\\begin{enumerate}" :oend "\\end{enumerate}" | |
1117 | :ustart "\\begin{itemize}" :uend "\\end{itemize}" | |
1118 | :dstart "\\begin{description}" :dend "\\end{description}" | |
1119 | :dtstart "[" :dtend "]" | |
1120 | :ddstart "" :ddend "" | |
1121 | :istart "\\item " :iend "" | |
1122 | :isep "\n" :lsep "\n" | |
1123 | :cbon "\\texttt{[X]}" :cboff "\\texttt{[ ]}") | |
1124 | params))) | |
1125 | ||
1126 | (defun org-list-to-html (list &optional params) | |
1127 | "Convert LIST into a HTML list. | |
1128 | LIST is as returnd by `org-list-parse-list'. PARAMS is a property list | |
1129 | with overruling parameters for `org-list-to-generic'." | |
47ffc456 | 1130 | (org-list-to-generic |
0bd48b37 CD |
1131 | list |
1132 | (org-combine-plists | |
1133 | '(:splicep nil :ostart "<ol>" :oend "</ol>" | |
1134 | :ustart "<ul>" :uend "</ul>" | |
1135 | :dstart "<dl>" :dend "</dl>" | |
1136 | :dtstart "<dt>" :dtend "</dt>" | |
1137 | :ddstart "<dd>" :ddend "</dd>" | |
1138 | :istart "<li>" :iend "</li>" | |
1139 | :isep "\n" :lsep "\n" | |
1140 | :cbon "<code>[X]</code>" :cboff "<code>[ ]</code>") | |
1141 | params))) | |
1142 | ||
1143 | (defun org-list-to-texinfo (list &optional params) | |
1144 | "Convert LIST into a Texinfo list. | |
1145 | LIST is as returnd by `org-list-parse-list'. PARAMS is a property list | |
1146 | with overruling parameters for `org-list-to-generic'." | |
47ffc456 | 1147 | (org-list-to-generic |
0bd48b37 CD |
1148 | list |
1149 | (org-combine-plists | |
1150 | '(:splicep nil :ostart "@itemize @minus" :oend "@end itemize" | |
1151 | :ustart "@enumerate" :uend "@end enumerate" | |
1152 | :dstart "@table" :dend "@end table" | |
1153 | :dtstart "@item " :dtend "\n" | |
1154 | :ddstart "" :ddend "" | |
1155 | :istart "@item\n" :iend "" | |
1156 | :isep "\n" :lsep "\n" | |
1157 | :cbon "@code{[X]}" :cboff "@code{[ ]}") | |
1158 | params))) | |
47ffc456 CD |
1159 | |
1160 | (provide 'org-list) | |
1161 | ||
3048977d | 1162 | ;; arch-tag: 73cf50c1-200f-4d1d-8a53-4e842a5b11c8 |
47ffc456 | 1163 | ;;; org-list.el ends here |