Mercurial > emacs
comparison lisp/textmodes/org.el @ 90488:4094c5298ae1
Merge from emacs--devo--0
Patches applied:
* emacs--devo--0 (patch 314-319)
- Update from CVS
- Merge from gnus--rel--5.10
* gnus--rel--5.10 (patch 107)
- Update from CVS
Revision: emacs@sv.gnu.org/emacs--unicode--0--patch-78
author | Miles Bader <miles@gnu.org> |
---|---|
date | Tue, 20 Jun 2006 07:35:06 +0000 |
parents | 138027c8c982 9853142939b3 |
children | 138ce2701550 |
comparison
equal
deleted
inserted
replaced
90487:ef80dfaa8269 | 90488:4094c5298ae1 |
---|---|
3 ;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. | 3 ;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. |
4 ;; | 4 ;; |
5 ;; Author: Carsten Dominik <dominik at science dot uva dot nl> | 5 ;; Author: Carsten Dominik <dominik at science dot uva dot nl> |
6 ;; Keywords: outlines, hypermedia, calendar, wp | 6 ;; Keywords: outlines, hypermedia, calendar, wp |
7 ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ | 7 ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ |
8 ;; Version: 4.36b | 8 ;; Version: 4.38 |
9 ;; | 9 ;; |
10 ;; This file is part of GNU Emacs. | 10 ;; This file is part of GNU Emacs. |
11 ;; | 11 ;; |
12 ;; GNU Emacs is free software; you can redistribute it and/or modify | 12 ;; GNU Emacs is free software; you can redistribute it and/or modify |
13 ;; it under the terms of the GNU General Public License as published by | 13 ;; it under the terms of the GNU General Public License as published by |
88 ;; excellent reference card made by Philip Rooke. This card can be found | 88 ;; excellent reference card made by Philip Rooke. This card can be found |
89 ;; in the etc/ directory of Emacs 22. | 89 ;; in the etc/ directory of Emacs 22. |
90 ;; | 90 ;; |
91 ;; Recent changes | 91 ;; Recent changes |
92 ;; -------------- | 92 ;; -------------- |
93 ;; Version 4.38 | |
94 ;; - noutline.el is now required (important for XEmacs users only). | |
95 ;; - Dynamic blocks. | |
96 ;; - Archiving of all level 1 trees without open TODO items. | |
97 ;; - Clock reports can be inserted into the file in a special section. | |
98 ;; - FAQ removed from the manual, now only on the web. | |
99 ;; - Bug fixes. | |
100 ;; | |
93 ;; Version 4.37 | 101 ;; Version 4.37 |
94 ;; - Clock-feature for measuring time spent on specific items. | 102 ;; - Clock-feature for measuring time spent on specific items. |
95 ;; - Improved emphasizing allows configuration and stacking. | 103 ;; - Improved emphasizing allows configuration and stacking. |
96 ;; | 104 ;; |
97 ;; Version 4.36 | 105 ;; Version 4.36 |
168 ;;; Code: | 176 ;;; Code: |
169 | 177 |
170 (eval-when-compile | 178 (eval-when-compile |
171 (require 'cl) | 179 (require 'cl) |
172 (require 'calendar)) | 180 (require 'calendar)) |
173 (require 'outline) | 181 ;; For XEmacs, noutline is not yet provided by outline.el, so arrange for |
182 ;; the file noutline.el being loaded. | |
183 (if (featurep 'xemacs) (condition-case nil (require 'noutline))) | |
184 ;; We require noutline, which might be provided in outline.el | |
185 (require 'outline) (require 'noutline) | |
186 ;; Other stuff we need. | |
174 (require 'time-date) | 187 (require 'time-date) |
175 (require 'easymenu) | 188 (require 'easymenu) |
176 | 189 |
177 ;;; Customization variables | 190 ;;; Customization variables |
178 | 191 |
179 (defvar org-version "4.36b" | 192 (defvar org-version "4.38" |
180 "The version number of the file org.el.") | 193 "The version number of the file org.el.") |
181 (defun org-version () | 194 (defun org-version () |
182 (interactive) | 195 (interactive) |
183 (message "Org-mode version %s" org-version)) | 196 (message "Org-mode version %s" org-version)) |
184 | 197 |
2200 stacked Non-nil means, allow stacked styles. This works only in HTML | 2213 stacked Non-nil means, allow stacked styles. This works only in HTML |
2201 export. When this is set, all marker characters (as given in | 2214 export. When this is set, all marker characters (as given in |
2202 `org-emphasis-alist') will be allowed as pre/post, aiding | 2215 `org-emphasis-alist') will be allowed as pre/post, aiding |
2203 inside-out matching. | 2216 inside-out matching. |
2204 Use customize to modify this, or restart emacs after changing it." | 2217 Use customize to modify this, or restart emacs after changing it." |
2205 :group 'org-fixme | 2218 :group 'org-font-lock |
2206 :set 'org-set-emph-re | 2219 :set 'org-set-emph-re |
2207 :type '(list | 2220 :type '(list |
2208 (sexp :tag "Allowed chars in pre ") | 2221 (sexp :tag "Allowed chars in pre ") |
2209 (sexp :tag "Allowed chars in post ") | 2222 (sexp :tag "Allowed chars in post ") |
2210 (sexp :tag "Forbidden chars in border ") | 2223 (sexp :tag "Forbidden chars in border ") |
2214 | 2227 |
2215 (defcustom org-emphasis-alist | 2228 (defcustom org-emphasis-alist |
2216 '(("*" bold "<b>" "</b>") | 2229 '(("*" bold "<b>" "</b>") |
2217 ("/" italic "<i>" "</i>") | 2230 ("/" italic "<i>" "</i>") |
2218 ("_" underline "<u>" "</u>") | 2231 ("_" underline "<u>" "</u>") |
2219 ("=" shadow "<code>" "</code>")) | 2232 ("=" shadow "<code>" "</code>") |
2233 ("+" (:strike-through t) "<del>" "</del>") | |
2234 ) | |
2220 "Special syntax for emphasised text. | 2235 "Special syntax for emphasised text. |
2221 Text starting and ending with a special character will be emphasized, for | 2236 Text starting and ending with a special character will be emphasized, for |
2222 example *bold*, _underlined_ and /italic/. This variable sets the marker | 2237 example *bold*, _underlined_ and /italic/. This variable sets the marker |
2223 characters, the face to bbe used by font-lock for highlighting in Org-mode | 2238 characters, the face to bbe used by font-lock for highlighting in Org-mode |
2224 emacs buffers, and the HTML tags to be used for this. | 2239 emacs buffers, and the HTML tags to be used for this. |
2225 Use customize to modify this, or restart emacs after changing it." | 2240 Use customize to modify this, or restart emacs after changing it." |
2226 :group 'org-fixme | 2241 :group 'org-font-lock |
2227 :set 'org-set-emph-re | 2242 :set 'org-set-emph-re |
2228 :type '(repeat | 2243 :type '(repeat |
2229 (list | 2244 (list |
2230 (string :tag "Marker character") | 2245 (string :tag "Marker character") |
2231 (face :tag "Font-lock-face") | 2246 (choice |
2247 (face :tag "Font-lock-face") | |
2248 (plist :tag "Face property list")) | |
2232 (string :tag "HTML start tag") | 2249 (string :tag "HTML start tag") |
2233 (string :tag "HTML end tag")))) | 2250 (string :tag "HTML end tag")))) |
2234 | 2251 |
2235 (defgroup org-faces nil | 2252 (defgroup org-faces nil |
2236 "Faces in Org-mode." | 2253 "Faces in Org-mode." |
2706 (defvar wl-summary-buffer-elmo-folder) ; from wanderlust | 2723 (defvar wl-summary-buffer-elmo-folder) ; from wanderlust |
2707 (defvar wl-summary-buffer-folder-name) ; from wanderlust | 2724 (defvar wl-summary-buffer-folder-name) ; from wanderlust |
2708 (defvar gnus-group-name) ; from gnus | 2725 (defvar gnus-group-name) ; from gnus |
2709 (defvar gnus-article-current) ; from gnus | 2726 (defvar gnus-article-current) ; from gnus |
2710 (defvar w3m-current-url) ; from w3m | 2727 (defvar w3m-current-url) ; from w3m |
2728 (defvar w3m-current-title) ; from w3m | |
2711 (defvar mh-progs) ; from MH-E | 2729 (defvar mh-progs) ; from MH-E |
2712 (defvar mh-current-folder) ; from MH-E | 2730 (defvar mh-current-folder) ; from MH-E |
2713 (defvar mh-show-folder-buffer) ; from MH-E | 2731 (defvar mh-show-folder-buffer) ; from MH-E |
2714 (defvar mh-index-folder) ; from MH-E | 2732 (defvar mh-index-folder) ; from MH-E |
2715 (defvar mh-searcher) ; from MH-E | 2733 (defvar mh-searcher) ; from MH-E |
2821 (interactive-p) | 2839 (interactive-p) |
2822 (= (point-min) (point-max))) | 2840 (= (point-min) (point-max))) |
2823 (insert " -*- mode: org -*-\n\n")) | 2841 (insert " -*- mode: org -*-\n\n")) |
2824 | 2842 |
2825 (unless org-inhibit-startup | 2843 (unless org-inhibit-startup |
2826 (if org-startup-align-all-tables | 2844 (when org-startup-align-all-tables |
2827 (org-table-map-tables 'org-table-align)) | 2845 (let ((bmp (buffer-modified-p))) |
2846 (org-table-map-tables 'org-table-align) | |
2847 (set-buffer-modified-p bmp))) | |
2828 (if org-startup-with-deadline-check | 2848 (if org-startup-with-deadline-check |
2829 (call-interactively 'org-check-deadlines) | 2849 (call-interactively 'org-check-deadlines) |
2830 (cond | 2850 (cond |
2831 ((eq org-startup-folded t) | 2851 ((eq org-startup-folded t) |
2832 (org-cycle '(4))) | 2852 (org-cycle '(4))) |
3720 (diff (abs (- level (length up-head))))) | 3740 (diff (abs (- level (length up-head))))) |
3721 (if (= level 1) (error "Cannot promote to level 0. UNDO to recover")) | 3741 (if (= level 1) (error "Cannot promote to level 0. UNDO to recover")) |
3722 (replace-match up-head nil t) | 3742 (replace-match up-head nil t) |
3723 ;; Fixup tag positioning | 3743 ;; Fixup tag positioning |
3724 (and org-auto-align-tags (org-set-tags nil t)) | 3744 (and org-auto-align-tags (org-set-tags nil t)) |
3725 (if org-adapt-indentation | 3745 (if org-adapt-indentation (org-fixup-indentation (- diff))))) |
3726 (org-fixup-indentation (if (> diff 1) "^ " "^ ") "" | |
3727 (if (> diff 1) "^ ? ?\\S-" "^ ?\\S-"))))) | |
3728 | 3746 |
3729 (defun org-demote () | 3747 (defun org-demote () |
3730 "Demote the current heading lower down the tree. | 3748 "Demote the current heading lower down the tree. |
3731 If the region is active in `transient-mark-mode', demote all headings | 3749 If the region is active in `transient-mark-mode', demote all headings |
3732 in the region." | 3750 in the region." |
3735 (down-head (make-string (org-get-legal-level level 1) ?*)) | 3753 (down-head (make-string (org-get-legal-level level 1) ?*)) |
3736 (diff (abs (- level (length down-head))))) | 3754 (diff (abs (- level (length down-head))))) |
3737 (replace-match down-head nil t) | 3755 (replace-match down-head nil t) |
3738 ;; Fixup tag positioning | 3756 ;; Fixup tag positioning |
3739 (and org-auto-align-tags (org-set-tags nil t)) | 3757 (and org-auto-align-tags (org-set-tags nil t)) |
3740 (if org-adapt-indentation | 3758 (if org-adapt-indentation (org-fixup-indentation diff)))) |
3741 (org-fixup-indentation "^ " (if (> diff 1) " " " ") "^\\S-")))) | |
3742 | 3759 |
3743 (defun org-map-tree (fun) | 3760 (defun org-map-tree (fun) |
3744 "Call FUN for every heading underneath the current one." | 3761 "Call FUN for every heading underneath the current one." |
3745 (org-back-to-heading) | 3762 (org-back-to-heading) |
3746 (let ((level (funcall outline-level))) | 3763 (let ((level (funcall outline-level))) |
3765 (outline-next-heading) | 3782 (outline-next-heading) |
3766 (< (point) end)) | 3783 (< (point) end)) |
3767 (not (eobp))) | 3784 (not (eobp))) |
3768 (funcall fun))))) | 3785 (funcall fun))))) |
3769 | 3786 |
3770 ;; FIXME: this does not work well with Tabulators. This has to be re-written entirely. | 3787 (defun org-fixup-indentation (diff) |
3771 (defun org-fixup-indentation (from to prohibit) | 3788 "Change the indentation in the current entry by DIFF |
3772 "Change the indentation in the current entry by re-replacing FROM with TO. | 3789 However, if any line in the current entry has no indentation, or if it |
3773 However, if the regexp PROHIBIT matches at all, don't do anything. | 3790 would end up with no indentation after the change, nothing at all is done." |
3774 This is being used to change indentation along with the length of the | |
3775 heading marker. But if there are any lines which are not indented, nothing | |
3776 is changed at all." | |
3777 (save-excursion | 3791 (save-excursion |
3778 (let ((end (save-excursion (outline-next-heading) | 3792 (let ((end (save-excursion (outline-next-heading) |
3779 (point-marker)))) | 3793 (point-marker))) |
3794 (prohibit (if (> diff 0) | |
3795 "^\\S-" | |
3796 (concat "^ \\{0," (int-to-string (- diff)) "\\}\\S-"))) | |
3797 col) | |
3780 (unless (save-excursion (re-search-forward prohibit end t)) | 3798 (unless (save-excursion (re-search-forward prohibit end t)) |
3781 (while (re-search-forward from end t) | 3799 (while (re-search-forward "^[ \t]+" end t) |
3782 (replace-match to) | 3800 (goto-char (match-end 0)) |
3783 (beginning-of-line 2))) | 3801 (setq col (current-column)) |
3802 (if (< diff 0) (replace-match "")) | |
3803 (indent-to (+ diff col)))) | |
3784 (move-marker end nil)))) | 3804 (move-marker end nil)))) |
3785 | 3805 |
3786 ;;; Vertical tree motion, cutting and pasting of subtrees | 3806 ;;; Vertical tree motion, cutting and pasting of subtrees |
3787 | 3807 |
3788 (defun org-move-subtree-up (&optional arg) | 3808 (defun org-move-subtree-up (&optional arg) |
3982 (while (setq start (string-match re kill (1+ start))) | 4002 (while (setq start (string-match re kill (1+ start))) |
3983 (if (< (- (match-end 0) (match-beginning 0)) start-level) | 4003 (if (< (- (match-end 0) (match-beginning 0)) start-level) |
3984 (throw 'exit nil))) | 4004 (throw 'exit nil))) |
3985 t)))) | 4005 t)))) |
3986 | 4006 |
4007 (defun org-narrow-to-subtree () | |
4008 "Narrow buffer to the current subtree." | |
4009 (interactive) | |
4010 (save-excursion | |
4011 (narrow-to-region | |
4012 (progn (org-back-to-heading) (point)) | |
4013 (progn (org-end-of-subtree t) (point))))) | |
4014 | |
3987 ;;; Plain list items | 4015 ;;; Plain list items |
3988 | 4016 |
3989 (defun org-at-item-p () | 4017 (defun org-at-item-p () |
3990 "Is point in a line starting a hand-formatted item?" | 4018 "Is point in a line starting a hand-formatted item?" |
3991 (let ((llt org-plain-list-ordered-item-terminator)) | 4019 (let ((llt org-plain-list-ordered-item-terminator)) |
4290 (indent-to-column (+ ind1 arg)) | 4318 (indent-to-column (+ ind1 arg)) |
4291 (beginning-of-line 2))))) | 4319 (beginning-of-line 2))))) |
4292 | 4320 |
4293 ;;; Archiving | 4321 ;;; Archiving |
4294 | 4322 |
4295 (defun org-archive-subtree () | 4323 (defun org-archive-subtree (&optional find-done) |
4296 "Move the current subtree to the archive. | 4324 "Move the current subtree to the archive. |
4297 The archive can be a certain top-level heading in the current file, or in | 4325 The archive can be a certain top-level heading in the current file, or in |
4298 a different file. The tree will be moved to that location, the subtree | 4326 a different file. The tree will be moved to that location, the subtree |
4299 heading be marked DONE, and the current time will be added." | 4327 heading be marked DONE, and the current time will be added. |
4300 (interactive) | 4328 |
4301 ;; Save all relevant TODO keyword-relatex variables | 4329 When called with prefix argument FIND-DONE, find whole trees without any |
4302 (let ((tr-org-todo-line-regexp org-todo-line-regexp) ; keep despite compiler | 4330 open TODO items and archive them (after getting confirmation from the user). |
4303 (tr-org-todo-keywords org-todo-keywords) | 4331 If the cursor is not at a headline when this comand is called, try all level |
4304 (tr-org-todo-interpretation org-todo-interpretation) | 4332 1 trees. If the cursor is on a headline, only try the direct children of |
4305 (tr-org-done-string org-done-string) | 4333 this heading. " |
4306 (tr-org-todo-regexp org-todo-regexp) | 4334 (interactive "P") |
4307 (tr-org-todo-line-regexp org-todo-line-regexp) | 4335 (if find-done |
4308 (this-buffer (current-buffer)) | 4336 (org-archive-all-done) |
4309 file heading buffer level newfile-p) | 4337 ;; Save all relevant TODO keyword-relatex variables |
4310 (if (string-match "\\(.*\\)::\\(.*\\)" org-archive-location) | 4338 |
4339 (let ((tr-org-todo-line-regexp org-todo-line-regexp) ; keep despite compiler | |
4340 (tr-org-todo-keywords org-todo-keywords) | |
4341 (tr-org-todo-interpretation org-todo-interpretation) | |
4342 (tr-org-done-string org-done-string) | |
4343 (tr-org-todo-regexp org-todo-regexp) | |
4344 (tr-org-todo-line-regexp org-todo-line-regexp) | |
4345 (this-buffer (current-buffer)) | |
4346 file heading buffer level newfile-p) | |
4347 (if (string-match "\\(.*\\)::\\(.*\\)" org-archive-location) | |
4348 (progn | |
4349 (setq file (format (match-string 1 org-archive-location) | |
4350 (file-name-nondirectory buffer-file-name)) | |
4351 heading (match-string 2 org-archive-location))) | |
4352 (error "Invalid `org-archive-location'")) | |
4353 (if (> (length file) 0) | |
4354 (setq newfile-p (not (file-exists-p file)) | |
4355 buffer (find-file-noselect file)) | |
4356 (setq buffer (current-buffer))) | |
4357 (unless buffer | |
4358 (error "Cannot access file \"%s\"" file)) | |
4359 (if (and (> (length heading) 0) | |
4360 (string-match "^\\*+" heading)) | |
4361 (setq level (match-end 0)) | |
4362 (setq heading nil level 0)) | |
4363 (save-excursion | |
4364 ;; We first only copy, in case something goes wrong | |
4365 ;; we need to protect this-command, to avoid kill-region sets it, | |
4366 ;; which would lead to duplication of subtrees | |
4367 (let (this-command) (org-copy-subtree)) | |
4368 (set-buffer buffer) | |
4369 ;; Enforce org-mode for the archive buffer | |
4370 (if (not (eq major-mode 'org-mode)) | |
4371 ;; Force the mode for future visits. | |
4372 (let ((org-insert-mode-line-in-empty-file t)) | |
4373 (call-interactively 'org-mode))) | |
4374 (when newfile-p | |
4375 (goto-char (point-max)) | |
4376 (insert (format "\nArchived entries from file %s\n\n" | |
4377 (buffer-file-name this-buffer)))) | |
4378 ;; Force the TODO keywords of the original buffer | |
4379 (let ((org-todo-line-regexp tr-org-todo-line-regexp) | |
4380 (org-todo-keywords tr-org-todo-keywords) | |
4381 (org-todo-interpretation tr-org-todo-interpretation) | |
4382 (org-done-string tr-org-done-string) | |
4383 (org-todo-regexp tr-org-todo-regexp) | |
4384 (org-todo-line-regexp tr-org-todo-line-regexp)) | |
4385 (goto-char (point-min)) | |
4386 (if heading | |
4387 (progn | |
4388 (if (re-search-forward | |
4389 (concat "\\(^\\|\r\\)" | |
4390 (regexp-quote heading) "[ \t]*\\($\\|\r\\)") | |
4391 nil t) | |
4392 (goto-char (match-end 0)) | |
4393 ;; Heading not found, just insert it at the end | |
4394 (goto-char (point-max)) | |
4395 (or (bolp) (insert "\n")) | |
4396 (insert "\n" heading "\n") | |
4397 (end-of-line 0)) | |
4398 ;; Make the subtree visible | |
4399 (show-subtree) | |
4400 (org-end-of-subtree t) | |
4401 (skip-chars-backward " \t\r\n]") | |
4402 (and (looking-at "[ \t\r\n]*") | |
4403 (replace-match "\n\n"))) | |
4404 ;; No specific heading, just go to end of file. | |
4405 (goto-char (point-max)) (insert "\n")) | |
4406 ;; Paste | |
4407 (org-paste-subtree (1+ level)) | |
4408 ;; Mark the entry as done, i.e. set to last work in org-todo-keywords | |
4409 (if org-archive-mark-done | |
4410 (org-todo (length org-todo-keywords))) | |
4411 ;; Move cursor to right after the TODO keyword | |
4412 (when org-archive-stamp-time | |
4413 (beginning-of-line 1) | |
4414 (looking-at org-todo-line-regexp) | |
4415 (goto-char (or (match-end 2) (match-beginning 3))) | |
4416 (insert "(" (format-time-string (cdr org-time-stamp-formats) | |
4417 (org-current-time)) | |
4418 ")")) | |
4419 ;; Save the buffer, if it is not the same buffer. | |
4420 (if (not (eq this-buffer buffer)) (save-buffer)))) | |
4421 ;; Here we are back in the original buffer. Everything seems to have | |
4422 ;; worked. So now cut the tree and finish up. | |
4423 (let (this-command) (org-cut-subtree)) | |
4424 (if (and (not (eobp)) (looking-at "[ \t]*$")) (kill-line)) | |
4425 (message "Subtree archived %s" | |
4426 (if (eq this-buffer buffer) | |
4427 (concat "under heading: " heading) | |
4428 (concat "in file: " (abbreviate-file-name file))))))) | |
4429 | |
4430 (defun org-archive-all-done () | |
4431 "Archive sublevels of the current tree without open TODO items. | |
4432 If the cursor is not on a headline, try all level 1 trees. If | |
4433 it is on a headline, try all direct children." | |
4434 (let ((re (concat "^\\*+ +" org-not-done-regexp)) re1 | |
4435 (begm (make-marker)) | |
4436 (endm (make-marker)) | |
4437 beg end (cntarch 0)) | |
4438 (if (org-on-heading-p) | |
4311 (progn | 4439 (progn |
4312 (setq file (format (match-string 1 org-archive-location) | 4440 (setq re1 (concat "^" (regexp-quote |
4313 (file-name-nondirectory buffer-file-name)) | 4441 (make-string |
4314 heading (match-string 2 org-archive-location))) | 4442 (1+ (- (match-end 0) (match-beginning 0))) |
4315 (error "Invalid `org-archive-location'")) | 4443 ?*)) |
4316 (if (> (length file) 0) | 4444 " ")) |
4317 (setq newfile-p (not (file-exists-p file)) | 4445 (move-marker begm (point)) |
4318 buffer (find-file-noselect file)) | 4446 (move-marker endm (org-end-of-subtree))) |
4319 (setq buffer (current-buffer))) | 4447 (setq re1 "^* ") |
4320 (unless buffer | 4448 (move-marker begm (point-min)) |
4321 (error "Cannot access file \"%s\"" file)) | 4449 (move-marker endm (point-max))) |
4322 (if (and (> (length heading) 0) | |
4323 (string-match "^\\*+" heading)) | |
4324 (setq level (match-end 0)) | |
4325 (setq heading nil level 0)) | |
4326 (save-excursion | 4450 (save-excursion |
4327 ;; We first only copy, in case something goes wrong | 4451 (goto-char begm) |
4328 ;; we need to protect this-command, to avoid kill-region sets it, | 4452 (while (re-search-forward re1 endm t) |
4329 ;; which would lead to duplication of subtrees | 4453 beg (match-beginning 0) |
4330 (let (this-command) (org-copy-subtree)) | 4454 end (save-excursion (org-end-of-subtree t) (point))) |
4331 (set-buffer buffer) | 4455 (goto-char beg) |
4332 ;; Enforce org-mode for the archive buffer | 4456 (if (re-search-forward re end t) |
4333 (if (not (eq major-mode 'org-mode)) | 4457 (goto-char end) |
4334 ;; Force the mode for future visits. | 4458 (goto-char beg) |
4335 (let ((org-insert-mode-line-in-empty-file t)) | 4459 (if (y-or-n-p "Archive this subtree (no open TODO items)? ") |
4336 (call-interactively 'org-mode))) | 4460 (progn |
4337 (when newfile-p | 4461 (org-archive-subtree) |
4338 (goto-char (point-max)) | 4462 (setq cntarch (1+ cntarch))) |
4339 (insert (format "\nArchived entries from file %s\n\n" | 4463 (goto-char end)))) |
4340 (buffer-file-name this-buffer)))) | 4464 (message "%d trees archived" cntarch))) |
4341 ;; Force the TODO keywords of the original buffer | 4465 |
4342 (let ((org-todo-line-regexp tr-org-todo-line-regexp) | 4466 ;;; Dynamic blocks |
4343 (org-todo-keywords tr-org-todo-keywords) | 4467 |
4344 (org-todo-interpretation tr-org-todo-interpretation) | 4468 (defun org-find-dblock (name) |
4345 (org-done-string tr-org-done-string) | 4469 "Find the first dynamic block with name NAME in the buffer. |
4346 (org-todo-regexp tr-org-todo-regexp) | 4470 If not found, stay at current position and return nil." |
4347 (org-todo-line-regexp tr-org-todo-line-regexp)) | 4471 (let (pos) |
4348 (goto-char (point-min)) | 4472 (save-excursion |
4349 (if heading | 4473 (goto-char (point-min)) |
4350 (progn | 4474 (setq pos (and (re-search-forward (concat "^#\\+BEGIN:[ \t]+" name "\\>") |
4351 (if (re-search-forward | 4475 nil t) |
4352 (concat "\\(^\\|\r\\)" | 4476 (match-beginning 0)))) |
4353 (regexp-quote heading) "[ \t]*\\($\\|\r\\)") | 4477 (if pos (goto-char pos)) |
4354 nil t) | 4478 pos)) |
4355 (goto-char (match-end 0)) | 4479 |
4356 ;; Heading not found, just insert it at the end | 4480 (defconst org-dblock-start-re |
4357 (goto-char (point-max)) | 4481 "^#\\+BEGIN:[ \t]+\\(\\S-+\\)[ \t]+\\(.*\\)" |
4358 (or (bolp) (insert "\n")) | 4482 "Matches the startline of a dynamic block, with parameters.") |
4359 (insert "\n" heading "\n") | 4483 |
4360 (end-of-line 0)) | 4484 (defconst org-dblock-end-re "^#\\+END\\([: \t\r\n]\\|$\\)" |
4361 ;; Make the subtree visible | 4485 "Matches the end of a dyhamic block.") |
4362 (show-subtree) | 4486 |
4363 (org-end-of-subtree t) | 4487 (defun org-create-dblock (plist) |
4364 (skip-chars-backward " \t\r\n]") | 4488 "Create a dynamic block section, with parameters taken from PLIST. |
4365 (and (looking-at "[ \t\r\n]*") | 4489 PLIST must containe a :name entry which is used as name of the block." |
4366 (replace-match "\n\n"))) | 4490 (unless (bolp) (newline)) |
4367 ;; No specific heading, just go to end of file. | 4491 (let ((name (plist-get plist :name))) |
4368 (goto-char (point-max)) (insert "\n")) | 4492 (insert "#+BEGIN: " name) |
4369 ;; Paste | 4493 (while plist |
4370 (org-paste-subtree (1+ level)) | 4494 (if (eq (car plist) :name) |
4371 ;; Mark the entry as done, i.e. set to last work in org-todo-keywords | 4495 (setq plist (cddr plist)) |
4372 (if org-archive-mark-done | 4496 (insert " " (prin1-to-string (pop plist))))) |
4373 (org-todo (length org-todo-keywords))) | 4497 (insert "\n\n#+END:\n") |
4374 ;; Move cursor to right after the TODO keyword | 4498 (beginning-of-line -2))) |
4375 (when org-archive-stamp-time | 4499 |
4376 (beginning-of-line 1) | 4500 (defun org-prepare-dblock () |
4377 (looking-at org-todo-line-regexp) | 4501 "Prepare dynamic block for refresh. |
4378 (goto-char (or (match-end 2) (match-beginning 3))) | 4502 This empties the block, puts the cursor at the insert position and returns |
4379 (insert "(" (format-time-string (cdr org-time-stamp-formats) | 4503 the property list including an extra property :name with the block name." |
4380 (org-current-time)) | 4504 (unless (looking-at org-dblock-start-re) |
4381 ")")) | 4505 (error "Not at a dynamic block")) |
4382 ;; Save the buffer, if it is not the same buffer. | 4506 (let* ((beg (match-beginning 0)) |
4383 (if (not (eq this-buffer buffer)) (save-buffer)))) | 4507 (begdel (1+ (match-end 0))) |
4384 ;; Here we are back in the original buffer. Everything seems to have | 4508 (name (match-string 1)) |
4385 ;; worked. So now cut the tree and finish up. | 4509 (params (append (list :name name) |
4386 (let (this-command) (org-cut-subtree)) | 4510 (read (concat "(" (match-string 2) ")"))))) |
4387 (if (and (not (eobp)) (looking-at "[ \t]*$")) (kill-line)) | 4511 (unless (re-search-forward org-dblock-end-re nil t) |
4388 (message "Subtree archived %s" | 4512 (error "Dynamic block not terminated")) |
4389 (if (eq this-buffer buffer) | 4513 (delete-region begdel (match-beginning 0)) |
4390 (concat "under heading: " heading) | 4514 (goto-char begdel) |
4391 (concat "in file: " (abbreviate-file-name file)))))) | 4515 (open-line 1) |
4516 params)) | |
4517 | |
4518 (defun org-map-dblocks (&optional command) | |
4519 "Apply COMMAND to all dynamic blocks in the current buffer. | |
4520 If COMMAND is not given, use `org-update-dblock'." | |
4521 (let ((cmd (or command 'org-update-dblock)) | |
4522 pos) | |
4523 (save-excursion | |
4524 (goto-char (point-min)) | |
4525 (while (re-search-forward org-dblock-start-re nil t) | |
4526 (goto-char (setq pos (match-beginning 0))) | |
4527 (condition-case nil | |
4528 (funcall cmd) | |
4529 (error (message "Error during update of dynamic block"))) | |
4530 (goto-char pos) | |
4531 (unless (re-search-forward org-dblock-end-re nil t) | |
4532 (error "Dynamic block not terminated")))))) | |
4533 | |
4534 (defun org-dblock-update (&optional arg) | |
4535 "User command for updating dynamic blocks. | |
4536 Update the dynamic block at point. With prefix ARG, update all dynamic | |
4537 blocks in the buffer." | |
4538 (interactive "P") | |
4539 (if arg | |
4540 (org-update-all-dblocks) | |
4541 (or (looking-at org-dblock-start-re) | |
4542 (org-beginning-of-dblock)) | |
4543 (org-update-dblock))) | |
4544 | |
4545 (defun org-update-dblock () | |
4546 "Update the dynamic block at point | |
4547 This means to empty the block, parse for parameters and then call | |
4548 the correct writing function." | |
4549 (let* ((pos (point)) | |
4550 (params (org-prepare-dblock)) | |
4551 (name (plist-get params :name)) | |
4552 (cmd (intern (concat "org-dblock-write:" name)))) | |
4553 (funcall cmd params) | |
4554 (goto-char pos))) | |
4555 | |
4556 (defun org-beginning-of-dblock () | |
4557 "Find the beginning of the dynamic block at point. | |
4558 Error if there is no scuh block at point." | |
4559 (let ((pos (point)) | |
4560 beg end) | |
4561 (end-of-line 1) | |
4562 (if (and (re-search-backward org-dblock-start-re nil t) | |
4563 (setq beg (match-beginning 0)) | |
4564 (re-search-forward org-dblock-end-re nil t) | |
4565 (> (match-end 0) pos)) | |
4566 (goto-char beg) | |
4567 (goto-char pos) | |
4568 (error "Not in a dynamic block")))) | |
4569 | |
4570 (defun org-update-all-dblocks () | |
4571 "Update all dynamic blocks in the buffer. | |
4572 This function can be used in a hook." | |
4573 (when (eq major-mode 'org-mode) | |
4574 (org-map-dblocks 'org-update-dblock))) | |
4575 | |
4392 | 4576 |
4393 ;;; Completion | 4577 ;;; Completion |
4394 | 4578 |
4395 (defun org-complete (&optional arg) | 4579 (defun org-complete (&optional arg) |
4396 "Perform completion on word at point. | 4580 "Perform completion on word at point. |
4781 "Highlight from BEG to END and mark the highlight is an occur headline." | 4965 "Highlight from BEG to END and mark the highlight is an occur headline." |
4782 (let ((ov (org-make-overlay beg end))) | 4966 (let ((ov (org-make-overlay beg end))) |
4783 (org-overlay-put ov 'face 'secondary-selection) | 4967 (org-overlay-put ov 'face 'secondary-selection) |
4784 (push ov org-occur-highlights))) | 4968 (push ov org-occur-highlights))) |
4785 | 4969 |
4970 (defvar org-inhibit-highlight-removal nil) | |
4786 (defun org-remove-occur-highlights (&optional beg end noremove) | 4971 (defun org-remove-occur-highlights (&optional beg end noremove) |
4787 "Remove the occur highlights from the buffer. | 4972 "Remove the occur highlights from the buffer. |
4788 BEG and END are ignored. If NOREMOVE is nil, remove this function | 4973 BEG and END are ignored. If NOREMOVE is nil, remove this function |
4789 from the `before-change-functions' in the current buffer." | 4974 from the `before-change-functions' in the current buffer." |
4790 (interactive) | 4975 (interactive) |
4791 (mapc 'org-delete-overlay org-occur-highlights) | 4976 (unless org-inhibit-highlight-removal |
4792 (setq org-occur-highlights nil) | 4977 (mapc 'org-delete-overlay org-occur-highlights) |
4793 (unless noremove | 4978 (setq org-occur-highlights nil) |
4794 (remove-hook 'before-change-functions | 4979 (unless noremove |
4795 'org-remove-occur-highlights 'local))) | 4980 (remove-hook 'before-change-functions |
4981 'org-remove-occur-highlights 'local)))) | |
4796 | 4982 |
4797 ;;; Priorities | 4983 ;;; Priorities |
4798 | 4984 |
4799 (defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\)\\] ?\\)" | 4985 (defvar org-priority-regexp ".*?\\(\\[#\\([A-Z]\\)\\] ?\\)" |
4800 "Regular expression matching the priority indicator.") | 4986 "Regular expression matching the priority indicator.") |
5447 | 5633 |
5448 (defun org-clock-sum () | 5634 (defun org-clock-sum () |
5449 "Sum the times for each subtree. | 5635 "Sum the times for each subtree. |
5450 Puts the resulting times in minutes as a text property on each headline." | 5636 Puts the resulting times in minutes as a text property on each headline." |
5451 (interactive) | 5637 (interactive) |
5452 (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t)) | 5638 (let* ((bmp (buffer-modified-p)) |
5453 (let* ((re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" | 5639 (re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" |
5454 org-clock-string | 5640 org-clock-string |
5455 ".*=>[ \t]*\\([0-9]+\\):\\([0-9]+\\)[ \t]*$")) | 5641 ".*=>[ \t]*\\([0-9]+\\):\\([0-9]+\\)[ \t]*$")) |
5456 (lmax 30) | 5642 (lmax 30) |
5457 (ltimes (make-vector lmax 0)) | 5643 (ltimes (make-vector lmax 0)) |
5458 (t1 0) | 5644 (t1 0) |
5459 (level 0) | 5645 (level 0) |
5460 (lastlevel 0) time) | 5646 (lastlevel 0) time) |
5647 (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t)) | |
5461 (save-excursion | 5648 (save-excursion |
5462 (goto-char (point-max)) | 5649 (goto-char (point-max)) |
5463 (while (re-search-backward re nil t) | 5650 (while (re-search-backward re nil t) |
5464 (if (match-end 2) | 5651 (if (match-end 2) |
5465 ;; A time | 5652 ;; A time |
5473 (setq t1 0 time (aref ltimes level)) | 5660 (setq t1 0 time (aref ltimes level)) |
5474 (loop for l from level to (1- lmax) do | 5661 (loop for l from level to (1- lmax) do |
5475 (aset ltimes l 0)) | 5662 (aset ltimes l 0)) |
5476 (goto-char (match-beginning 0)) | 5663 (goto-char (match-beginning 0)) |
5477 (put-text-property (point) (point-at-eol) :org-clock-minutes time)))) | 5664 (put-text-property (point) (point-at-eol) :org-clock-minutes time)))) |
5478 (setq org-clock-file-total-minutes (aref ltimes 0))))) | 5665 (setq org-clock-file-total-minutes (aref ltimes 0))) |
5666 (set-buffer-modified-p bmp))) | |
5479 | 5667 |
5480 (defun org-clock-display (&optional total-only) | 5668 (defun org-clock-display (&optional total-only) |
5481 "Show subtree times in the entire buffer. | 5669 "Show subtree times in the entire buffer. |
5482 If TOTAL-ONLY is non-nil, only show the total time for the entire file | 5670 If TOTAL-ONLY is non-nil, only show the total time for the entire file |
5483 in the echo area." | 5671 in the echo area." |
5508 (let* ((c 60) (h (floor (/ time 60))) (m (- time (* 60 h))) | 5696 (let* ((c 60) (h (floor (/ time 60))) (m (- time (* 60 h))) |
5509 (l (if level (org-get-legal-level level 0) 0)) | 5697 (l (if level (org-get-legal-level level 0) 0)) |
5510 (off 0) | 5698 (off 0) |
5511 ov tx) | 5699 ov tx) |
5512 (move-to-column c) | 5700 (move-to-column c) |
5513 (if (eolp) (setq off 1)) | |
5514 (unless (eolp) (skip-chars-backward "^ \t")) | 5701 (unless (eolp) (skip-chars-backward "^ \t")) |
5515 (skip-chars-backward " \t") | 5702 (skip-chars-backward " \t") |
5516 (setq ov (org-make-overlay (- (point) off) (point-at-eol)) | 5703 (setq ov (org-make-overlay (1- (point)) (point-at-eol)) |
5517 tx (concat (make-string (+ off (max 0 (- c (current-column)))) ?.) | 5704 tx (concat (buffer-substring (1- (point)) (point)) |
5705 (make-string (+ off (max 0 (- c (current-column)))) ?.) | |
5518 (org-add-props (format "%s %2d:%02d%s" | 5706 (org-add-props (format "%s %2d:%02d%s" |
5519 (make-string l ?*) h m | 5707 (make-string l ?*) h m |
5520 (make-string (- 10 l) ?\ )) | 5708 (make-string (- 10 l) ?\ )) |
5521 '(face secondary-selection)) | 5709 '(face secondary-selection)) |
5522 "")) | 5710 "")) |
5526 (defun org-remove-clock-overlays (&optional beg end noremove) | 5714 (defun org-remove-clock-overlays (&optional beg end noremove) |
5527 "Remove the occur highlights from the buffer. | 5715 "Remove the occur highlights from the buffer. |
5528 BEG and END are ignored. If NOREMOVE is nil, remove this function | 5716 BEG and END are ignored. If NOREMOVE is nil, remove this function |
5529 from the `before-change-functions' in the current buffer." | 5717 from the `before-change-functions' in the current buffer." |
5530 (interactive) | 5718 (interactive) |
5531 (mapc 'org-delete-overlay org-clock-overlays) | 5719 (unless org-inhibit-highlight-removal |
5532 (setq org-clock-overlays nil) | 5720 (mapc 'org-delete-overlay org-clock-overlays) |
5533 (unless noremove | 5721 (setq org-clock-overlays nil) |
5534 (remove-hook 'before-change-functions | 5722 (unless noremove |
5535 'org-remove-clock-overlays 'local))) | 5723 (remove-hook 'before-change-functions |
5724 'org-remove-clock-overlays 'local)))) | |
5536 | 5725 |
5537 (defun org-clock-out-if-current () | 5726 (defun org-clock-out-if-current () |
5538 "Clock out if the current entry contains the running clock. | 5727 "Clock out if the current entry contains the running clock. |
5539 This is used to stop the clock after a TODO entry is marked DONE." | 5728 This is used to stop the clock after a TODO entry is marked DONE." |
5540 (when (and (equal state org-done-string) | 5729 (when (and (equal state org-done-string) |
5554 (y-or-n-p (format "Clock-out in buffer %s before killing it? " | 5743 (y-or-n-p (format "Clock-out in buffer %s before killing it? " |
5555 (buffer-name)))) | 5744 (buffer-name)))) |
5556 (org-clock-out) | 5745 (org-clock-out) |
5557 (when (y-or-n-p "Save changed buffer?") | 5746 (when (y-or-n-p "Save changed buffer?") |
5558 (save-buffer)))) | 5747 (save-buffer)))) |
5748 | |
5749 (defun org-clock-report () | |
5750 "Create a table containing a report about clocked time. | |
5751 If the buffer contains lines | |
5752 #+BEGIN: clocktable :maxlevel 3 :emphasize nil | |
5753 | |
5754 #+END: clocktable | |
5755 then the table will be inserted between these lines, replacing whatever | |
5756 is was there before. If these lines are not in the buffer, the table | |
5757 is inserted at point, surrounded by the special lines. | |
5758 The BEGIN line can contain parameters. Allowed are: | |
5759 :maxlevel The maximum level to be included in the table. Default is 3. | |
5760 :emphasize t/nil, if levell 1 and level 2 should be bold/italic in the table." | |
5761 (interactive) | |
5762 (org-remove-clock-overlays) | |
5763 (unless (org-find-dblock "clocktable") | |
5764 (org-create-dblock (list :name "clocktable" | |
5765 :maxlevel 2 :emphasize nil))) | |
5766 (org-update-dblock)) | |
5767 | |
5768 (defun org-dblock-write:clocktable (params) | |
5769 "Write the standard clocktable." | |
5770 (let ((hlchars '((1 . "*") (2 . ?/))) | |
5771 (emph nil) | |
5772 (pos (point)) ipos | |
5773 (ins (make-marker)) | |
5774 time h m p level hlc hdl maxlevel) | |
5775 (setq maxlevel (or (plist-get params :maxlevel) 3) | |
5776 emph (plist-get params :emphasize)) | |
5777 (move-marker ins (point)) | |
5778 (setq ipos (point)) | |
5779 (insert-before-markers "Clock summary at [" | |
5780 (substring | |
5781 (format-time-string (cdr org-time-stamp-formats)) | |
5782 1 -1) | |
5783 "]\n|L|Headline|Time|\n") | |
5784 (org-clock-sum) | |
5785 (setq h (/ org-clock-file-total-minutes 60) | |
5786 m (- org-clock-file-total-minutes (* 60 h))) | |
5787 (insert-before-markers "|-\n|0|" "*Total file time*| " | |
5788 (format "*%d:%02d*" h m) | |
5789 "|\n") | |
5790 (goto-char (point-min)) | |
5791 (while (setq p (next-single-property-change (point) :org-clock-minutes)) | |
5792 (goto-char p) | |
5793 (when (setq time (get-text-property p :org-clock-minutes)) | |
5794 (beginning-of-line 1) | |
5795 (when (and (looking-at "\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[0-9a-zA-Z_@:]+:\\)?[ \t]*$") | |
5796 (setq level (- (match-end 1) (match-beginning 1))) | |
5797 (<= level maxlevel)) | |
5798 (setq hlc (if emph (or (cdr (assoc level hlchars)) "") "") | |
5799 hdl (match-string 2) | |
5800 h (/ time 60) | |
5801 m (- time (* 60 h))) | |
5802 (save-excursion | |
5803 (goto-char ins) | |
5804 (if (= level 1) (insert-before-markers "|-\n")) | |
5805 (insert-before-markers | |
5806 "| " (int-to-string level) "|" hlc hdl hlc " |" | |
5807 (make-string (1- level) ?|) | |
5808 hlc | |
5809 (format "%d:%02d" h m) | |
5810 hlc | |
5811 " |\n"))))) | |
5812 (goto-char ins) | |
5813 (backward-delete-char 1) | |
5814 (goto-char ipos) | |
5815 (skip-chars-forward "^|") | |
5816 (org-table-align))) | |
5817 | |
5818 (defun org-collect-clock-time-entries () | |
5819 "Return an internal list with clocking information. | |
5820 This list has one entry for each CLOCK interval. | |
5821 FIXME: describe the elements." | |
5822 (interactive) | |
5823 (let ((re (concat "^[ \t]*" org-clock-string | |
5824 " *\\[\\(.*?\\)\\]--\\[\\(.*?\\)\\]")) | |
5825 rtn beg end next cont level title total closedp leafp | |
5826 clockpos titlepos h m donep) | |
5827 (save-excursion | |
5828 (org-clock-sum) | |
5829 (goto-char (point-min)) | |
5830 (while (re-search-forward re nil t) | |
5831 (setq clockpos (match-beginning 0) | |
5832 beg (match-string 1) end (match-string 2) | |
5833 cont (match-end 0)) | |
5834 (setq beg (apply 'encode-time (org-parse-time-string beg)) | |
5835 end (apply 'encode-time (org-parse-time-string end))) | |
5836 (org-back-to-heading t) | |
5837 (setq donep (org-entry-is-done-p)) | |
5838 (setq titlepos (point) | |
5839 total (or (get-text-property (1+ (point)) :org-clock-minutes) 0) | |
5840 h (/ total 60) m (- total (* 60 h)) | |
5841 total (cons h m)) | |
5842 (looking-at "\\(\\*+\\) +\\(.*\\)") | |
5843 (setq level (- (match-end 1) (match-beginning 1)) | |
5844 title (org-match-string-no-properties 2)) | |
5845 (save-excursion (outline-next-heading) (setq next (point))) | |
5846 (setq closedp (re-search-forward org-closed-time-regexp next t)) | |
5847 (goto-char next) | |
5848 (setq leafp (and (looking-at "^\\*+ ") | |
5849 (<= (- (match-end 0) (point)) level))) | |
5850 (push (list beg end clockpos closedp donep | |
5851 total title titlepos level leafp) | |
5852 rtn) | |
5853 (goto-char cont))) | |
5854 (nreverse rtn))) | |
5559 | 5855 |
5560 ;;; Agenda, and Diary Integration | 5856 ;;; Agenda, and Diary Integration |
5561 | 5857 |
5562 ;;; Define the mode | 5858 ;;; Define the mode |
5563 | 5859 |
9184 | 9480 |
9185 ((eq major-mode 'w3-mode) | 9481 ((eq major-mode 'w3-mode) |
9186 (setq cpltxt (url-view-url t) | 9482 (setq cpltxt (url-view-url t) |
9187 link (org-make-link cpltxt))) | 9483 link (org-make-link cpltxt))) |
9188 ((eq major-mode 'w3m-mode) | 9484 ((eq major-mode 'w3m-mode) |
9189 (setq cpltxt w3m-current-url | 9485 (setq cpltxt (or w3m-current-title w3m-current-url) |
9190 link (org-make-link cpltxt))) | 9486 link (org-make-link w3m-current-url))) |
9191 | 9487 |
9192 ((setq search (run-hook-with-args-until-success | 9488 ((setq search (run-hook-with-args-until-success |
9193 'org-create-file-search-functions)) | 9489 'org-create-file-search-functions)) |
9194 (setq link (concat "file:" (abbreviate-file-name buffer-file-name) | 9490 (setq link (concat "file:" (abbreviate-file-name buffer-file-name) |
9195 "::" search)) | 9491 "::" search)) |
9196 (setq cpltxt (or description link))) | 9492 (setq cpltxt (or description link))) |
9493 | |
9494 ((eq major-mode 'image-mode) | |
9495 (setq cpltxt (concat "file:" | |
9496 (abbreviate-file-name buffer-file-name)) | |
9497 link (org-make-link cpltxt))) | |
9197 | 9498 |
9198 ((eq major-mode 'org-mode) | 9499 ((eq major-mode 'org-mode) |
9199 ;; Just link to current headline | 9500 ;; Just link to current headline |
9200 (setq cpltxt (concat "file:" | 9501 (setq cpltxt (concat "file:" |
9201 (abbreviate-file-name buffer-file-name))) | 9502 (abbreviate-file-name buffer-file-name))) |
9412 the current directory if the file is in the current directory or a | 9713 the current directory if the file is in the current directory or a |
9413 subdirectory. Otherwise, the link will be the absolute path as | 9714 subdirectory. Otherwise, the link will be the absolute path as |
9414 completed in the minibuffer (i.e. normally ~/path/to/file). | 9715 completed in the minibuffer (i.e. normally ~/path/to/file). |
9415 | 9716 |
9416 With two \\[universal-argument] prefixes, enforce an absolute path even if the file | 9717 With two \\[universal-argument] prefixes, enforce an absolute path even if the file |
9417 is in the current directory or below." | 9718 is in the current directory or below. |
9719 With three \\[universal-argument] prefixes, negate the meaning of | |
9720 `org-keep-stored-link-after-insertion'." | |
9418 (interactive "P") | 9721 (interactive "P") |
9419 (let (link desc entry remove file (pos (point))) | 9722 (let (link desc entry remove file (pos (point))) |
9420 (cond | 9723 (cond |
9421 ((save-excursion | 9724 ((save-excursion |
9422 (skip-chars-forward "^]\n\r") | 9725 (skip-chars-forward "^]\n\r") |
9428 (setq remove (list (match-beginning 0) (match-end 0))) | 9731 (setq remove (list (match-beginning 0) (match-end 0))) |
9429 (setq desc (if (match-end 3) (org-match-string-no-properties 3))) | 9732 (setq desc (if (match-end 3) (org-match-string-no-properties 3))) |
9430 (setq link (read-string "Link: " | 9733 (setq link (read-string "Link: " |
9431 (org-link-unescape | 9734 (org-link-unescape |
9432 (org-match-string-no-properties 1))))) | 9735 (org-match-string-no-properties 1))))) |
9433 (complete-file | 9736 ((equal complete-file '(4)) |
9434 ;; Completing read for file names. | 9737 ;; Completing read for file names. |
9435 (setq file (read-file-name "File: ")) | 9738 (setq file (read-file-name "File: ")) |
9436 (let ((pwd (file-name-as-directory (expand-file-name "."))) | 9739 (let ((pwd (file-name-as-directory (expand-file-name "."))) |
9437 (pwd1 (file-name-as-directory (abbreviate-file-name | 9740 (pwd1 (file-name-as-directory (abbreviate-file-name |
9438 (expand-file-name "."))))) | 9741 (expand-file-name "."))))) |
9453 (setq link (org-completing-read | 9756 (setq link (org-completing-read |
9454 "Link: " org-stored-links nil nil nil | 9757 "Link: " org-stored-links nil nil nil |
9455 org-insert-link-history | 9758 org-insert-link-history |
9456 (or (car (car org-stored-links))))) | 9759 (or (car (car org-stored-links))))) |
9457 (setq entry (assoc link org-stored-links)) | 9760 (setq entry (assoc link org-stored-links)) |
9458 (if (not org-keep-stored-link-after-insertion) | 9761 (if (funcall (if (equal complete-file '(64)) 'not 'identity) |
9762 (not org-keep-stored-link-after-insertion)) | |
9459 (setq org-stored-links (delq (assoc link org-stored-links) | 9763 (setq org-stored-links (delq (assoc link org-stored-links) |
9460 org-stored-links))) | 9764 org-stored-links))) |
9461 (setq link (if entry (nth 1 entry) link) | 9765 (setq link (if entry (nth 1 entry) link) |
9462 desc (or desc (nth 2 entry))))) | 9766 desc (or desc (nth 2 entry))))) |
9463 | 9767 |
12197 \[F] publish current file | 12501 \[F] publish current file |
12198 \[P] publish current project | 12502 \[P] publish current project |
12199 \[X] publish... (project will be prompted for) | 12503 \[X] publish... (project will be prompted for) |
12200 \[A] publish all projects") | 12504 \[A] publish all projects") |
12201 (cmds | 12505 (cmds |
12202 '((?v . org-export-visible) | 12506 '((?t . org-insert-export-options-template) |
12507 (?v . org-export-visible) | |
12203 (?a . org-export-as-ascii) | 12508 (?a . org-export-as-ascii) |
12204 (?h . org-export-as-html) | 12509 (?h . org-export-as-html) |
12205 (?b . org-export-as-html-and-open) | 12510 (?b . org-export-as-html-and-open) |
12206 (?x . org-export-as-xoxo) | 12511 (?x . org-export-as-xoxo) |
12207 (?i . org-export-icalendar-this-file) | 12512 (?i . org-export-icalendar-this-file) |
12564 (replace-match | 12869 (replace-match |
12565 (concat | 12870 (concat |
12566 (match-string 1) "[[" (match-string 2) ":" (match-string 3) "]]") | 12871 (match-string 1) "[[" (match-string 2) ":" (match-string 3) "]]") |
12567 t t)) | 12872 t t)) |
12568 ;; Find multiline emphasis and put them into single line | 12873 ;; Find multiline emphasis and put them into single line |
12569 (when (assq :emph-multiline parameters) | 12874 (when (memq :emph-multiline parameters) |
12570 (goto-char (point-min)) | 12875 (goto-char (point-min)) |
12571 (while (re-search-forward org-emph-re nil t) | 12876 (while (re-search-forward org-emph-re nil t) |
12572 (subst-char-in-region (match-beginning 0) (match-end 0) ?\n ?\ t) | 12877 (subst-char-in-region (match-beginning 0) (match-end 0) ?\n ?\ t) |
12573 (goto-char (1- (match-end 0))))) | 12878 (goto-char (1- (match-end 0))))) |
12574 | 12879 |
12856 continue to use it. The prefix arg ARG is passed through to the exporting | 13161 continue to use it. The prefix arg ARG is passed through to the exporting |
12857 command." | 13162 command." |
12858 (interactive | 13163 (interactive |
12859 (list (progn | 13164 (list (progn |
12860 (message "Export visible: [a]SCII [h]tml [b]rowse HTML [x]OXO [ ]keep buffer") | 13165 (message "Export visible: [a]SCII [h]tml [b]rowse HTML [x]OXO [ ]keep buffer") |
12861 (char-to-string (read-char-exclusive))) | 13166 (read-char-exclusive)) |
12862 current-prefix-arg)) | 13167 current-prefix-arg)) |
12863 (if (not (member type '("a" "\C-a" "b" "\C-b" "h" "x" " "))) | 13168 (if (not (member type '(?a ?\C-a ?b ?\C-b ?h ?x ?\ ))) |
12864 (error "Invalid export key")) | 13169 (error "Invalid export key")) |
12865 ;; FIXME: do this more explicit? | 13170 (let* ((binding (cdr (assoc type |
12866 (let* ((binding (key-binding (concat "\C-c\C-x" type))) | 13171 '((?a . org-export-as-ascii) |
12867 (keepp (equal type " ")) | 13172 (?\C-a . org-export-as-ascii) |
13173 (?b . org-export-as-html-and-open) | |
13174 (?\C-b . org-export-as-html-and-open) | |
13175 (?h . org-export-as-html) | |
13176 (?x . org-export-as-xoxo))))) | |
13177 (keepp (equal type ?\ )) | |
12868 (file buffer-file-name) | 13178 (file buffer-file-name) |
12869 (buffer (get-buffer-create "*Org Export Visible*")) | 13179 (buffer (get-buffer-create "*Org Export Visible*")) |
12870 s e) | 13180 s e) |
12871 (with-current-buffer buffer (erase-buffer)) | 13181 (with-current-buffer buffer (erase-buffer)) |
12872 (save-excursion | 13182 (save-excursion |
13047 (let* ((opt-plist (org-combine-plists (org-default-export-plist) | 13357 (let* ((opt-plist (org-combine-plists (org-default-export-plist) |
13048 ext-plist | 13358 ext-plist |
13049 (org-infile-export-plist))) | 13359 (org-infile-export-plist))) |
13050 | 13360 |
13051 (style (plist-get opt-plist :style)) | 13361 (style (plist-get opt-plist :style)) |
13362 (link-validate (plist-get opt-plist :link-validation-function)) | |
13363 valid | |
13052 (odd org-odd-levels-only) | 13364 (odd org-odd-levels-only) |
13053 (region-p (org-region-active-p)) | 13365 (region-p (org-region-active-p)) |
13054 (region | 13366 (region |
13055 (buffer-substring | 13367 (buffer-substring |
13056 (if region-p (region-beginning) (point-min)) | 13368 (if region-p (region-beginning) (point-min)) |
13066 (filename (concat (file-name-as-directory | 13378 (filename (concat (file-name-as-directory |
13067 (org-export-directory :html opt-plist)) | 13379 (org-export-directory :html opt-plist)) |
13068 (file-name-sans-extension | 13380 (file-name-sans-extension |
13069 (file-name-nondirectory buffer-file-name)) | 13381 (file-name-nondirectory buffer-file-name)) |
13070 ".html")) | 13382 ".html")) |
13383 (current-dir (file-name-directory buffer-file-name)) | |
13071 (buffer (find-file-noselect filename)) | 13384 (buffer (find-file-noselect filename)) |
13072 (levels-open (make-vector org-level-max nil)) | 13385 (levels-open (make-vector org-level-max nil)) |
13073 (date (format-time-string "%Y/%m/%d" (current-time))) | 13386 (date (format-time-string "%Y/%m/%d" (current-time))) |
13074 (time (format-time-string "%X" (org-current-time))) | 13387 (time (format-time-string "%X" (org-current-time))) |
13075 (author (plist-get opt-plist :author)) | 13388 (author (plist-get opt-plist :author)) |
13312 thefile file-is-image-p search) | 13625 thefile file-is-image-p search) |
13313 (save-match-data | 13626 (save-match-data |
13314 (if (string-match "::\\(.*\\)" filename) | 13627 (if (string-match "::\\(.*\\)" filename) |
13315 (setq search (match-string 1 filename) | 13628 (setq search (match-string 1 filename) |
13316 filename (replace-match "" t nil filename))) | 13629 filename (replace-match "" t nil filename))) |
13630 (setq valid | |
13631 (if (functionp link-validate) | |
13632 (funcall link-validate filename current-dir) | |
13633 t)) | |
13317 (setq file-is-image-p | 13634 (setq file-is-image-p |
13318 (string-match (org-image-file-name-regexp) filename)) | 13635 (string-match (org-image-file-name-regexp) filename)) |
13319 (setq thefile (if abs-p (expand-file-name filename) filename)) | 13636 (setq thefile (if abs-p (expand-file-name filename) filename)) |
13320 (when (and org-export-html-link-org-files-as-html | 13637 (when (and org-export-html-link-org-files-as-html |
13321 (string-match "\\.org$" thefile)) | 13638 (string-match "\\.org$" thefile)) |
13337 (setq rpl (if (and file-is-image-p | 13654 (setq rpl (if (and file-is-image-p |
13338 (or (eq t org-export-html-inline-images) | 13655 (or (eq t org-export-html-inline-images) |
13339 (and org-export-html-inline-images | 13656 (and org-export-html-inline-images |
13340 (not descp)))) | 13657 (not descp)))) |
13341 (concat "<img src=\"" thefile "\"/>") | 13658 (concat "<img src=\"" thefile "\"/>") |
13342 (concat "<a href=\"" thefile "\">" desc "</a>"))))) | 13659 (concat "<a href=\"" thefile "\">" desc "</a>"))) |
13660 (if (not valid) (setq rpl desc)))) | |
13343 ((member type '("bbdb" "vm" "wl" "mhe" "rmail" "gnus" "shell" "info" "elisp")) | 13661 ((member type '("bbdb" "vm" "wl" "mhe" "rmail" "gnus" "shell" "info" "elisp")) |
13344 (setq rpl (concat "<i><" type ":" | 13662 (setq rpl (concat "<i><" type ":" |
13345 (save-match-data (org-link-unescape path)) | 13663 (save-match-data (org-link-unescape path)) |
13346 "></i>")))) | 13664 "></i>")))) |
13347 (setq line (replace-match rpl t t line) | 13665 (setq line (replace-match rpl t t line) |
13648 (set-buffer " org-tmp2 ") | 13966 (set-buffer " org-tmp2 ") |
13649 (buffer-substring (point-min) (point-max)))) | 13967 (buffer-substring (point-min) (point-max)))) |
13650 | 13968 |
13651 (defun org-html-handle-time-stamps (s) | 13969 (defun org-html-handle-time-stamps (s) |
13652 "Format time stamps in string S, or remove them." | 13970 "Format time stamps in string S, or remove them." |
13653 (let (r b) | 13971 (catch 'exit |
13654 (while (string-match org-maybe-keyword-time-regexp s) | 13972 (let (r b) |
13655 (or b (setq b (substring s 0 (match-beginning 0)))) | 13973 (while (string-match org-maybe-keyword-time-regexp s) |
13656 (if (not org-export-with-timestamps) | 13974 ;; FIXME: is it good to never export CLOCK, or do we need control? |
13657 (setq r (concat r (substring s 0 (match-beginning 0))) | 13975 (if (and (match-end 1) (equal (match-string 1 s) org-clock-string)) |
13658 s (substring s (match-end 0))) | 13976 (throw 'exit "")) |
13659 (setq r (concat | 13977 (or b (setq b (substring s 0 (match-beginning 0)))) |
13660 r (substring s 0 (match-beginning 0)) | 13978 (if (not org-export-with-timestamps) |
13661 (if (match-end 1) | 13979 (setq r (concat r (substring s 0 (match-beginning 0))) |
13662 (format "@<span class=\"timestamp-kwd\">%s @</span>" | 13980 s (substring s (match-end 0))) |
13663 (match-string 1 s))) | 13981 (setq r (concat |
13664 (format " @<span class=\"timestamp\">%s@</span>" | 13982 r (substring s 0 (match-beginning 0)) |
13665 (substring (match-string 3 s) 1 -1))) | 13983 (if (match-end 1) |
13666 s (substring s (match-end 0))))) | 13984 (format "@<span class=\"timestamp-kwd\">%s @</span>" |
13667 ;; Line break of line started and ended with time stamp stuff | 13985 (match-string 1 s))) |
13668 (if (not r) | 13986 (format " @<span class=\"timestamp\">%s@</span>" |
13669 s | 13987 (substring (match-string 3 s) 1 -1))) |
13670 (setq r (concat r s)) | 13988 s (substring s (match-end 0))))) |
13671 (unless (string-match "\\S-" (concat b s)) | 13989 ;; Line break if line started and ended with time stamp stuff |
13672 (setq r (concat r "@<br/>"))) | 13990 (if (not r) |
13673 r))) | 13991 s |
13992 (setq r (concat r s)) | |
13993 (unless (string-match "\\S-" (concat b s)) | |
13994 (setq r (concat r "@<br/>"))) | |
13995 r)))) | |
13674 | 13996 |
13675 (defun org-html-protect (s) | 13997 (defun org-html-protect (s) |
13676 ;; convert & to &, < to < and > to > | 13998 ;; convert & to &, < to < and > to > |
13677 (let ((start 0)) | 13999 (let ((start 0)) |
13678 (while (string-match "&" s start) | 14000 (while (string-match "&" s start) |
14210 (define-key org-mode-map [?\C-c ?\C-x (right)] 'org-shiftright) | 14532 (define-key org-mode-map [?\C-c ?\C-x (right)] 'org-shiftright) |
14211 | 14533 |
14212 ;; All the other keys | 14534 ;; All the other keys |
14213 | 14535 |
14214 (define-key org-mode-map "\C-c\C-a" 'show-all) ; in case allout messed up. | 14536 (define-key org-mode-map "\C-c\C-a" 'show-all) ; in case allout messed up. |
14537 (define-key org-mode-map "\C-xns" 'org-narrow-to-subtree) | |
14215 (define-key org-mode-map "\C-c$" 'org-archive-subtree) | 14538 (define-key org-mode-map "\C-c$" 'org-archive-subtree) |
14216 (define-key org-mode-map "\C-c\C-j" 'org-goto) | 14539 (define-key org-mode-map "\C-c\C-j" 'org-goto) |
14217 (define-key org-mode-map "\C-c\C-t" 'org-todo) | 14540 (define-key org-mode-map "\C-c\C-t" 'org-todo) |
14218 (define-key org-mode-map "\C-c\C-s" 'org-schedule) | 14541 (define-key org-mode-map "\C-c\C-s" 'org-schedule) |
14219 (define-key org-mode-map "\C-c\C-d" 'org-deadline) | 14542 (define-key org-mode-map "\C-c\C-d" 'org-deadline) |
14253 (define-key org-mode-map "\C-c*" 'org-table-recalculate) | 14576 (define-key org-mode-map "\C-c*" 'org-table-recalculate) |
14254 (define-key org-mode-map [(control ?#)] 'org-table-rotate-recalc-marks) | 14577 (define-key org-mode-map [(control ?#)] 'org-table-rotate-recalc-marks) |
14255 (define-key org-mode-map "\C-c~" 'org-table-create-with-table.el) | 14578 (define-key org-mode-map "\C-c~" 'org-table-create-with-table.el) |
14256 (define-key org-mode-map "\C-c\C-q" 'org-table-wrap-region) | 14579 (define-key org-mode-map "\C-c\C-q" 'org-table-wrap-region) |
14257 (define-key org-mode-map "\C-c\C-e" 'org-export) | 14580 (define-key org-mode-map "\C-c\C-e" 'org-export) |
14258 ;(define-key org-mode-map "\C-c\C-xa" 'org-export-as-ascii) | |
14259 ;(define-key org-mode-map "\C-c\C-x\C-a" 'org-export-as-ascii) | |
14260 ;(define-key org-mode-map "\C-c\C-xv" 'org-export-visible) | |
14261 ;(define-key org-mode-map "\C-c\C-x\C-v" 'org-export-visible) | |
14262 ;; OPML support is only an option for the future | |
14263 ;(define-key org-mode-map "\C-c\C-xo" 'org-export-as-opml) | |
14264 ;(define-key org-mode-map "\C-c\C-x\C-o" 'org-export-as-opml) | |
14265 ;(define-key org-mode-map "\C-c\C-xi" 'org-export-icalendar-this-file) | |
14266 ;(define-key org-mode-map "\C-c\C-x\C-i" 'org-export-icalendar-all-agenda-files) | |
14267 ;(define-key org-mode-map "\C-c\C-xc" 'org-export-icalendar-combine-agenda-files) | |
14268 ;(define-key org-mode-map "\C-c\C-x\C-c" 'org-export-icalendar-combine-agenda-files) | |
14269 ;(define-key org-mode-map "\C-c\C-xt" 'org-insert-export-options-template) | |
14270 (define-key org-mode-map "\C-c:" 'org-toggle-fixed-width-section) | 14581 (define-key org-mode-map "\C-c:" 'org-toggle-fixed-width-section) |
14271 ;(define-key org-mode-map "\C-c\C-xh" 'org-export-as-html) | |
14272 ;(define-key org-mode-map "\C-c\C-xx" 'org-export-as-xoxo) | |
14273 ;(define-key org-mode-map "\C-c\C-x\C-x" 'org-export-as-xoxo) | |
14274 ;(define-key org-mode-map "\C-c\C-xb" 'org-export-as-html-and-open) | |
14275 ;(define-key org-mode-map "\C-c\C-x\C-b" 'org-export-as-html-and-open) | |
14276 | 14582 |
14277 (define-key org-mode-map "\C-c\C-x\C-k" 'org-cut-special) | 14583 (define-key org-mode-map "\C-c\C-x\C-k" 'org-cut-special) |
14278 (define-key org-mode-map "\C-c\C-x\C-w" 'org-cut-special) | 14584 (define-key org-mode-map "\C-c\C-x\C-w" 'org-cut-special) |
14279 (define-key org-mode-map "\C-c\C-x\M-w" 'org-copy-special) | 14585 (define-key org-mode-map "\C-c\C-x\M-w" 'org-copy-special) |
14280 (define-key org-mode-map "\C-c\C-x\C-y" 'org-paste-special) | 14586 (define-key org-mode-map "\C-c\C-x\C-y" 'org-paste-special) |
14281 | 14587 |
14282 (define-key org-mode-map "\C-c\C-x\C-i" 'org-clock-in) | 14588 (define-key org-mode-map "\C-c\C-x\C-i" 'org-clock-in) |
14283 (define-key org-mode-map "\C-c\C-x\C-o" 'org-clock-out) | 14589 (define-key org-mode-map "\C-c\C-x\C-o" 'org-clock-out) |
14284 (define-key org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel) | 14590 (define-key org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel) |
14285 (define-key org-mode-map "\C-c\C-x\C-d" 'org-clock-display) | 14591 (define-key org-mode-map "\C-c\C-x\C-d" 'org-clock-display) |
14286 | 14592 (define-key org-mode-map "\C-c\C-x\C-r" 'org-clock-report) |
14287 ;(define-key org-mode-map "\C-c\C-ef" 'org-publish-current-file) | 14593 |
14288 ;(define-key org-mode-map "\C-c\C-ep" 'org-publish-current-project) | 14594 (define-key org-mode-map "\C-c\C-x\C-u" 'org-dblock-update) |
14289 ;(define-key org-mode-map "\C-c\C-ec" 'org-publish) | |
14290 ;(define-key org-mode-map "\C-c\C-ea" 'org-publish-all) | |
14291 ;(define-key org-mode-map "\C-c\C-e\C-f" 'org-publish-current-file) | |
14292 ;(define-key org-mode-map "\C-c\C-e\C-p" 'org-publish-current-project) | |
14293 ;(define-key org-mode-map "\C-c\C-e\C-c" 'org-publish) | |
14294 ;(define-key org-mode-map "\C-c\C-e\C-a" 'org-publish-all) | |
14295 | 14595 |
14296 (when (featurep 'xemacs) | 14596 (when (featurep 'xemacs) |
14297 (define-key org-mode-map 'button3 'popup-mode-menu)) | 14597 (define-key org-mode-map 'button3 'popup-mode-menu)) |
14298 | 14598 |
14299 (defsubst org-table-p () (org-at-table-p)) | 14599 (defsubst org-table-p () (org-at-table-p)) |
14783 ("Logging work" | 15083 ("Logging work" |
14784 ["Clock in" org-clock-in t] | 15084 ["Clock in" org-clock-in t] |
14785 ["Clock out" org-clock-out t] | 15085 ["Clock out" org-clock-out t] |
14786 ["Clock cancel" org-clock-cancel t] | 15086 ["Clock cancel" org-clock-cancel t] |
14787 ["Display times" org-clock-display t] | 15087 ["Display times" org-clock-display t] |
15088 ["Create clock table" org-clock-report t] | |
14788 "--" | 15089 "--" |
14789 ["Record DONE time" | 15090 ["Record DONE time" |
14790 (progn (setq org-log-done (not org-log-done)) | 15091 (progn (setq org-log-done (not org-log-done)) |
14791 (message "Switching to %s will %s record a timestamp" | 15092 (message "Switching to %s will %s record a timestamp" |
14792 org-done-string | 15093 org-done-string |
15282 (progn | 15583 (progn |
15283 ;; Go to end of line before heading | 15584 ;; Go to end of line before heading |
15284 (forward-char -1) | 15585 (forward-char -1) |
15285 (if (memq (preceding-char) '(?\n ?\^M)) | 15586 (if (memq (preceding-char) '(?\n ?\^M)) |
15286 ;; leave blank line before heading | 15587 ;; leave blank line before heading |
15287 (forward-char -1)))))) | 15588 (forward-char -1))))) |
15589 (point)) | |
15288 | 15590 |
15289 (defun org-show-subtree () | 15591 (defun org-show-subtree () |
15290 "Show everything after this heading at deeper levels." | 15592 "Show everything after this heading at deeper levels." |
15291 (outline-flag-region | 15593 (outline-flag-region |
15292 (point) | 15594 (point) |
15332 (or (org-invisible-p) | 15634 (or (org-invisible-p) |
15333 (save-excursion (goto-char (max (point-min) (1- (point)))) | 15635 (save-excursion (goto-char (max (point-min) (1- (point)))) |
15334 (org-invisible-p))) | 15636 (org-invisible-p))) |
15335 (org-show-hierarchy-above))) | 15637 (org-show-hierarchy-above))) |
15336 | 15638 |
15639 | |
15640 ;;; Experimental code | |
15641 | |
15642 | |
15337 ;;; Finish up | 15643 ;;; Finish up |
15338 | 15644 |
15339 (provide 'org) | 15645 (provide 'org) |
15340 | 15646 |
15341 (run-hooks 'org-load-hook) | 15647 (run-hooks 'org-load-hook) |
15342 | 15648 |
15343 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd | 15649 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd |