commit | author | age
|
76bbd0
|
1 |
;;; org-capture.el --- Fast note taking in Org -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2010-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Carsten Dominik <carsten at orgmode dot org> |
|
6 |
;; Keywords: outlines, hypermedia, calendar, wp |
|
7 |
;; Homepage: https://orgmode.org |
|
8 |
;; |
|
9 |
;; This file is part of GNU Emacs. |
|
10 |
;; |
|
11 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
12 |
;; it under the terms of the GNU General Public License as published by |
|
13 |
;; the Free Software Foundation, either version 3 of the License, or |
|
14 |
;; (at your option) any later version. |
|
15 |
|
|
16 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
17 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 |
;; GNU General Public License for more details. |
|
20 |
|
|
21 |
;; You should have received a copy of the GNU General Public License |
|
22 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
23 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
24 |
;; |
|
25 |
;;; Commentary: |
|
26 |
|
|
27 |
;; This file contains an alternative implementation of the functionality |
|
28 |
;; that used to be provided by org-remember.el. The implementation is more |
|
29 |
;; streamlined, can produce more target types (e.g. plain list items or |
|
30 |
;; table lines). Also, it does not use a temporary buffer for editing |
|
31 |
;; the captured entry - instead it uses an indirect buffer that visits |
|
32 |
;; the new entry already in the target buffer (this was an idea by Samuel |
|
33 |
;; Wales). John Wiegley's excellent `remember.el' is not needed anymore |
|
34 |
;; for this implementation, even though we borrow heavily from its ideas. |
|
35 |
|
|
36 |
;; This implementation heavily draws on ideas by James TD Smith and |
|
37 |
;; Samuel Wales, and, of cause, uses John Wiegley's remember.el as inspiration. |
|
38 |
|
|
39 |
;;; TODO |
|
40 |
|
|
41 |
;; - find a clever way to not always insert an annotation maybe a |
|
42 |
;; predicate function that can check for conditions for %a to be |
|
43 |
;; used. This could be one of the properties. |
|
44 |
|
|
45 |
;; - Should there be plist members that arrange for properties to be |
|
46 |
;; asked for, like James proposed in his RFC? |
|
47 |
|
|
48 |
;;; Code: |
|
49 |
|
|
50 |
(require 'cl-lib) |
|
51 |
(require 'org) |
|
52 |
|
|
53 |
(declare-function org-at-encrypted-entry-p "org-crypt" ()) |
|
54 |
(declare-function org-clock-update-mode-line "org-clock" (&optional refresh)) |
|
55 |
(declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction)) |
|
56 |
(declare-function org-decrypt-entry "org-crypt" ()) |
|
57 |
(declare-function org-encrypt-entry "org-crypt" ()) |
|
58 |
(declare-function org-table-analyze "org-table" ()) |
|
59 |
(declare-function org-table-current-dline "org-table" ()) |
|
60 |
(declare-function org-table-goto-line "org-table" (N)) |
|
61 |
|
|
62 |
(defvar org-end-time-was-given) |
|
63 |
(defvar org-remember-default-headline) |
|
64 |
(defvar org-remember-templates) |
|
65 |
(defvar org-table-hlines) |
|
66 |
(defvar org-table-current-begin-pos) |
|
67 |
(defvar dired-buffers) |
|
68 |
|
|
69 |
(defvar org-capture-clock-was-started nil |
|
70 |
"Internal flag, noting if the clock was started.") |
|
71 |
|
|
72 |
(defvar org-capture-last-stored-marker (make-marker) |
|
73 |
"Marker pointing to the entry most recently stored with `org-capture'.") |
|
74 |
|
|
75 |
;; The following variable is scoped dynamically by org-protocol |
|
76 |
;; to indicate that the link properties have already been stored |
|
77 |
(defvar org-capture-link-is-already-stored nil) |
|
78 |
|
|
79 |
(defvar org-capture-is-refiling nil |
|
80 |
"Non-nil when capture process is refiling an entry.") |
|
81 |
|
|
82 |
(defvar org-capture--prompt-history-table (make-hash-table :test #'equal) |
|
83 |
"Hash table for all history lists per prompt.") |
|
84 |
|
|
85 |
(defvar org-capture--prompt-history nil |
|
86 |
"History list for prompt placeholders.") |
|
87 |
|
|
88 |
(defgroup org-capture nil |
|
89 |
"Options concerning capturing new entries." |
|
90 |
:tag "Org Capture" |
|
91 |
:group 'org) |
|
92 |
|
|
93 |
(defun org-capture-upgrade-templates (templates) |
|
94 |
"Update the template list to the new format. |
|
95 |
TEMPLATES is a template list, as in `org-capture-templates'. The |
|
96 |
new format unifies all the date/week tree targets into one that |
|
97 |
also allows for an optional outline path to specify a target." |
|
98 |
(let ((modified-templates |
|
99 |
(mapcar |
|
100 |
(lambda (entry) |
|
101 |
(pcase entry |
|
102 |
;; Match templates with an obsolete "tree" target type. Replace |
|
103 |
;; it with common `file+olp-datetree'. Add new properties |
|
104 |
;; (i.e., `:time-prompt' and `:tree-type') if needed. |
|
105 |
(`(,key ,desc ,type (file+datetree . ,path) ,tpl . ,props) |
|
106 |
`(,key ,desc ,type (file+olp+datetree ,@path) ,tpl ,@props)) |
|
107 |
(`(,key ,desc ,type (file+datetree+prompt . ,path) ,tpl . ,props) |
|
108 |
`(,key ,desc ,type (file+olp+datetree ,@path) ,tpl |
|
109 |
:time-prompt t ,@props)) |
|
110 |
(`(,key ,desc ,type (file+weektree . ,path) ,tpl . ,props) |
|
111 |
`(,key ,desc ,type (file+olp+datetree ,@path) ,tpl |
|
112 |
:tree-type week ,@props)) |
|
113 |
(`(,key ,desc ,type (file+weektree+prompt . ,path) ,tpl . ,props) |
|
114 |
`(,key ,desc ,type (file+olp+datetree ,@path) ,tpl |
|
115 |
:tree-type week :time-prompt t ,@props)) |
|
116 |
;; Other templates are left unchanged. |
|
117 |
(_ entry))) |
|
118 |
templates))) |
|
119 |
(unless (equal modified-templates templates) |
|
120 |
(message "Deprecated date/weektree capture templates changed to `file+olp+datetree'.")) |
|
121 |
modified-templates)) |
|
122 |
|
|
123 |
(defcustom org-capture-templates nil |
|
124 |
"Templates for the creation of new entries. |
|
125 |
|
|
126 |
Each entry is a list with the following items: |
|
127 |
|
|
128 |
keys The keys that will select the template, as a string, characters |
|
129 |
only, for example \"a\" for a template to be selected with a |
|
130 |
single key, or \"bt\" for selection with two keys. When using |
|
131 |
several keys, keys using the same prefix key must be together |
|
132 |
in the list and preceded by a 2-element entry explaining the |
|
133 |
prefix key, for example |
|
134 |
|
|
135 |
(\"b\" \"Templates for marking stuff to buy\") |
|
136 |
|
|
137 |
The \"C\" key is used by default for quick access to the |
|
138 |
customization of the template variable. But if you want to use |
|
139 |
that key for a template, you can. |
|
140 |
|
|
141 |
description A short string describing the template, will be shown during |
|
142 |
selection. |
|
143 |
|
|
144 |
type The type of entry. Valid types are: |
|
145 |
entry an Org node, with a headline. Will be filed |
|
146 |
as the child of the target entry or as a |
|
147 |
top-level entry. |
|
148 |
item a plain list item, will be placed in the |
|
149 |
first plain list at the target |
|
150 |
location. |
|
151 |
checkitem a checkbox item. This differs from the |
|
152 |
plain list item only is so far as it uses a |
|
153 |
different default template. |
|
154 |
table-line a new line in the first table at target location. |
|
155 |
plain text to be inserted as it is. |
|
156 |
|
|
157 |
target Specification of where the captured item should be placed. |
|
158 |
In Org files, targets usually define a node. Entries will |
|
159 |
become children of this node, other types will be added to the |
|
160 |
table or list in the body of this node. |
|
161 |
|
|
162 |
Most target specifications contain a file name. If that file |
|
163 |
name is the empty string, it defaults to `org-default-notes-file'. |
|
164 |
A file can also be given as a variable or as a function called |
|
165 |
with no argument. When an absolute path is not specified for a |
|
166 |
target, it is taken as relative to `org-directory'. |
|
167 |
|
|
168 |
Valid values are: |
|
169 |
|
|
170 |
(file \"path/to/file\") |
|
171 |
Text will be placed at the beginning or end of that file |
|
172 |
|
|
173 |
(id \"id of existing Org entry\") |
|
174 |
File as child of this entry, or in the body of the entry |
|
175 |
|
|
176 |
(file+headline \"path/to/file\" \"node headline\") |
|
177 |
Fast configuration if the target heading is unique in the file |
|
178 |
|
|
179 |
(file+olp \"path/to/file\" \"Level 1 heading\" \"Level 2\" ...) |
|
180 |
For non-unique headings, the full outline path is safer |
|
181 |
|
|
182 |
(file+regexp \"path/to/file\" \"regexp to find location\") |
|
183 |
File to the entry matching regexp |
|
184 |
|
|
185 |
(file+olp+datetree \"path/to/file\" \"Level 1 heading\" ...) |
|
186 |
Will create a heading in a date tree for today's date. |
|
187 |
If no heading is given, the tree will be on top level. |
|
188 |
To prompt for date instead of using TODAY, use the |
|
189 |
:time-prompt property. To create a week-tree, use the |
|
190 |
:tree-type property. |
|
191 |
|
|
192 |
(file+function \"path/to/file\" function-finding-location) |
|
193 |
A function to find the right location in the file |
|
194 |
|
|
195 |
(clock) |
|
196 |
File to the entry that is currently being clocked |
|
197 |
|
|
198 |
(function function-finding-location) |
|
199 |
Most general way: write your own function which both visits |
|
200 |
the file and moves point to the right location |
|
201 |
|
|
202 |
template The template for creating the capture item. If you leave this |
|
203 |
empty, an appropriate default template will be used. See below |
|
204 |
for more details. Instead of a string, this may also be one of |
|
205 |
|
|
206 |
(file \"/path/to/template-file\") |
|
207 |
(function function-returning-the-template) |
|
208 |
|
|
209 |
in order to get a template from a file, or dynamically |
|
210 |
from a function. |
|
211 |
|
|
212 |
The rest of the entry is a property list of additional options. Recognized |
|
213 |
properties are: |
|
214 |
|
|
215 |
:prepend Normally newly captured information will be appended at |
|
216 |
the target location (last child, last table line, |
|
217 |
last list item...). Setting this property will |
|
218 |
change that. |
|
219 |
|
|
220 |
:immediate-finish When set, do not offer to edit the information, just |
|
221 |
file it away immediately. This makes sense if the |
|
222 |
template only needs information that can be added |
|
223 |
automatically. |
|
224 |
|
|
225 |
:jump-to-captured When set, jump to the captured entry when finished. |
|
226 |
|
|
227 |
:empty-lines Set this to the number of lines the should be inserted |
|
228 |
before and after the new item. Default 0, only common |
|
229 |
other value is 1. |
|
230 |
|
|
231 |
:empty-lines-before Set this to the number of lines the should be inserted |
|
232 |
before the new item. Overrides :empty-lines for the |
|
233 |
number lines inserted before. |
|
234 |
|
|
235 |
:empty-lines-after Set this to the number of lines the should be inserted |
|
236 |
after the new item. Overrides :empty-lines for the |
|
237 |
number of lines inserted after. |
|
238 |
|
|
239 |
:clock-in Start the clock in this item. |
|
240 |
|
|
241 |
:clock-keep Keep the clock running when filing the captured entry. |
|
242 |
|
|
243 |
:clock-resume Start the interrupted clock when finishing the capture. |
|
244 |
Note that :clock-keep has precedence over :clock-resume. |
|
245 |
When setting both to t, the current clock will run and |
|
246 |
the previous one will not be resumed. |
|
247 |
|
|
248 |
:time-prompt Prompt for a date/time to be used for date/week trees |
|
249 |
and when filling the template. |
|
250 |
|
|
251 |
:tree-type When `week', make a week tree instead of the month tree. |
|
252 |
|
|
253 |
:unnarrowed Do not narrow the target buffer, simply show the |
|
254 |
full buffer. Default is to narrow it so that you |
|
255 |
only see the new stuff. |
|
256 |
|
|
257 |
:table-line-pos Specification of the location in the table where the |
|
258 |
new line should be inserted. It should be a string like |
|
259 |
\"II-3\", meaning that the new line should become the |
|
260 |
third line before the second horizontal separator line. |
|
261 |
|
|
262 |
:kill-buffer If the target file was not yet visited by a buffer when |
|
263 |
capture was invoked, kill the buffer again after capture |
|
264 |
is finalized. |
|
265 |
|
|
266 |
The template defines the text to be inserted. Often this is an |
|
267 |
Org mode entry (so the first line should start with a star) that |
|
268 |
will be filed as a child of the target headline. It can also be |
|
269 |
freely formatted text. Furthermore, the following %-escapes will |
|
270 |
be replaced with content and expanded: |
|
271 |
|
|
272 |
%[pathname] Insert the contents of the file given by |
|
273 |
`pathname'. These placeholders are expanded at the very |
|
274 |
beginning of the process so they can be used to extend the |
|
275 |
current template. |
|
276 |
%(sexp) Evaluate elisp `(sexp)' and replace it with the results. |
|
277 |
Only placeholders pre-existing within the template, or |
|
278 |
introduced with %[pathname] are expanded this way. Since this |
|
279 |
happens after expanding non-interactive %-escapes, those can |
|
280 |
be used to fill the expression. |
|
281 |
%<...> The result of format-time-string on the ... format specification. |
|
282 |
%t Time stamp, date only. The time stamp is the current time, |
|
283 |
except when called from agendas with `\\[org-agenda-capture]' or |
|
284 |
with `org-capture-use-agenda-date' set. |
|
285 |
%T Time stamp as above, with date and time. |
|
286 |
%u, %U Like the above, but inactive time stamps. |
|
287 |
%i Initial content, copied from the active region. If %i is |
|
288 |
indented, the entire inserted text will be indented as well. |
|
289 |
%a Annotation, normally the link created with `org-store-link'. |
|
290 |
%A Like %a, but prompt for the description part. |
|
291 |
%l Like %a, but only insert the literal link. |
|
292 |
%c Current kill ring head. |
|
293 |
%x Content of the X clipboard. |
|
294 |
%k Title of currently clocked task. |
|
295 |
%K Link to currently clocked task. |
|
296 |
%n User name (taken from the variable `user-full-name'). |
|
297 |
%f File visited by current buffer when org-capture was called. |
|
298 |
%F Full path of the file or directory visited by current buffer. |
|
299 |
%:keyword Specific information for certain link types, see below. |
|
300 |
%^g Prompt for tags, with completion on tags in target file. |
|
301 |
%^G Prompt for tags, with completion on all tags in all agenda files. |
|
302 |
%^t Like %t, but prompt for date. Similarly %^T, %^u, %^U. |
|
303 |
You may define a prompt like: %^{Please specify birthday}t. |
|
304 |
The default date is that of %t, see above. |
|
305 |
%^C Interactive selection of which kill or clip to use. |
|
306 |
%^L Like %^C, but insert as link. |
|
307 |
%^{prop}p Prompt the user for a value for property `prop'. |
|
308 |
%^{prompt} Prompt the user for a string and replace this sequence with it. |
|
309 |
A default value and a completion table ca be specified like this: |
|
310 |
%^{prompt|default|completion2|completion3|...}. |
|
311 |
%? After completing the template, position cursor here. |
|
312 |
%\\1 ... %\\N Insert the text entered at the nth %^{prompt}, where N |
|
313 |
is a number, starting from 1. |
|
314 |
|
|
315 |
Apart from these general escapes, you can access information specific to |
|
316 |
the link type that is created. For example, calling `org-capture' in emails |
|
317 |
or in Gnus will record the author and the subject of the message, which you |
|
318 |
can access with \"%:from\" and \"%:subject\", respectively. Here is a |
|
319 |
complete list of what is recorded for each link type. |
|
320 |
|
|
321 |
Link type | Available information |
|
322 |
------------------------+------------------------------------------------------ |
|
323 |
bbdb | %:type %:name %:company |
|
324 |
vm, wl, mh, mew, rmail, | %:type %:subject %:message-id |
|
325 |
gnus | %:from %:fromname %:fromaddress |
|
326 |
| %:to %:toname %:toaddress |
|
327 |
| %:fromto (either \"to NAME\" or \"from NAME\") |
|
328 |
| %:date %:date-timestamp (as active timestamp) |
|
329 |
| %:date-timestamp-inactive (as inactive timestamp) |
|
330 |
gnus | %:group, for messages also all email fields |
|
331 |
eww, w3, w3m | %:type %:url |
|
332 |
info | %:type %:file %:node |
|
333 |
calendar | %:type %:date |
|
334 |
|
|
335 |
When you need to insert a literal percent sign in the template, |
|
336 |
you can escape ambiguous cases with a backward slash, e.g., \\%i." |
|
337 |
:group 'org-capture |
|
338 |
:version "24.1" |
|
339 |
:set (lambda (s v) (set s (org-capture-upgrade-templates v))) |
|
340 |
:type |
|
341 |
(let ((file-variants '(choice :tag "Filename " |
|
342 |
(file :tag "Literal") |
|
343 |
(function :tag "Function") |
|
344 |
(variable :tag "Variable") |
|
345 |
(sexp :tag "Form")))) |
|
346 |
`(repeat |
|
347 |
(choice :value ("" "" entry (file "~/org/notes.org") "") |
|
348 |
(list :tag "Multikey description" |
|
349 |
(string :tag "Keys ") |
|
350 |
(string :tag "Description")) |
|
351 |
(list :tag "Template entry" |
|
352 |
(string :tag "Keys ") |
|
353 |
(string :tag "Description ") |
|
354 |
(choice :tag "Capture Type " :value entry |
|
355 |
(const :tag "Org entry" entry) |
|
356 |
(const :tag "Plain list item" item) |
|
357 |
(const :tag "Checkbox item" checkitem) |
|
358 |
(const :tag "Plain text" plain) |
|
359 |
(const :tag "Table line" table-line)) |
|
360 |
(choice :tag "Target location" |
|
361 |
(list :tag "File" |
|
362 |
(const :format "" file) |
|
363 |
,file-variants) |
|
364 |
(list :tag "ID" |
|
365 |
(const :format "" id) |
|
366 |
(string :tag " ID")) |
|
367 |
(list :tag "File & Headline" |
|
368 |
(const :format "" file+headline) |
|
369 |
,file-variants |
|
370 |
(string :tag " Headline")) |
|
371 |
(list :tag "File & Outline path" |
|
372 |
(const :format "" file+olp) |
|
373 |
,file-variants |
|
374 |
(repeat :tag "Outline path" :inline t |
|
375 |
(string :tag "Headline"))) |
|
376 |
(list :tag "File & Regexp" |
|
377 |
(const :format "" file+regexp) |
|
378 |
,file-variants |
|
379 |
(regexp :tag " Regexp")) |
|
380 |
(list :tag "File [ & Outline path ] & Date tree" |
|
381 |
(const :format "" file+olp+datetree) |
|
382 |
,file-variants |
|
383 |
(option (repeat :tag "Outline path" :inline t |
|
384 |
(string :tag "Headline")))) |
|
385 |
(list :tag "File & function" |
|
386 |
(const :format "" file+function) |
|
387 |
,file-variants |
|
388 |
(sexp :tag " Function")) |
|
389 |
(list :tag "Current clocking task" |
|
390 |
(const :format "" clock)) |
|
391 |
(list :tag "Function" |
|
392 |
(const :format "" function) |
|
393 |
(sexp :tag " Function"))) |
|
394 |
(choice :tag "Template " |
|
395 |
(string) |
|
396 |
(list :tag "File" |
|
397 |
(const :format "" file) |
|
398 |
(file :tag "Template file")) |
|
399 |
(list :tag "Function" |
|
400 |
(const :format "" function) |
|
401 |
(function :tag "Template function"))) |
|
402 |
(plist :inline t |
|
403 |
;; Give the most common options as checkboxes |
|
404 |
:options (((const :format "%v " :prepend) (const t)) |
|
405 |
((const :format "%v " :immediate-finish) (const t)) |
|
406 |
((const :format "%v " :jump-to-captured) (const t)) |
|
407 |
((const :format "%v " :empty-lines) (const 1)) |
|
408 |
((const :format "%v " :empty-lines-before) (const 1)) |
|
409 |
((const :format "%v " :empty-lines-after) (const 1)) |
|
410 |
((const :format "%v " :clock-in) (const t)) |
|
411 |
((const :format "%v " :clock-keep) (const t)) |
|
412 |
((const :format "%v " :clock-resume) (const t)) |
|
413 |
((const :format "%v " :time-prompt) (const t)) |
|
414 |
((const :format "%v " :tree-type) (const week)) |
|
415 |
((const :format "%v " :unnarrowed) (const t)) |
|
416 |
((const :format "%v " :table-line-pos) (string)) |
|
417 |
((const :format "%v " :kill-buffer) (const t))))))))) |
|
418 |
|
|
419 |
(defcustom org-capture-before-finalize-hook nil |
|
420 |
"Hook that is run right before a capture process is finalized. |
|
421 |
The capture buffer is still current when this hook runs and it is |
|
422 |
widened to the entire buffer." |
|
423 |
:group 'org-capture |
|
424 |
:version "24.1" |
|
425 |
:type 'hook) |
|
426 |
|
|
427 |
(defcustom org-capture-after-finalize-hook nil |
|
428 |
"Hook that is run right after a capture process is finalized. |
|
429 |
Suitable for window cleanup." |
|
430 |
:group 'org-capture |
|
431 |
:version "24.1" |
|
432 |
:type 'hook) |
|
433 |
|
|
434 |
(defcustom org-capture-prepare-finalize-hook nil |
|
435 |
"Hook that is run before the finalization starts. |
|
436 |
The capture buffer is current and still narrowed." |
|
437 |
:group 'org-capture |
|
438 |
:version "24.1" |
|
439 |
:type 'hook) |
|
440 |
|
|
441 |
(defcustom org-capture-bookmark t |
|
442 |
"When non-nil, add a bookmark pointing at the last stored |
|
443 |
position when capturing." |
|
444 |
:group 'org-capture |
|
445 |
:version "24.3" |
|
446 |
:type 'boolean) |
|
447 |
|
|
448 |
;;; The property list for keeping information about the capture process |
|
449 |
|
|
450 |
(defvar org-capture-plist nil |
|
451 |
"Plist for the current capture process, global, to avoid having to pass it.") |
|
452 |
|
|
453 |
(defvar org-capture-current-plist nil |
|
454 |
"Local variable holding the plist in a capture buffer. |
|
455 |
This is used to store the plist for use when finishing a capture process |
|
456 |
because another such process might have changed the global variable by then. |
|
457 |
|
|
458 |
Each time a new capture buffer has been set up, the global `org-capture-plist' |
|
459 |
is copied to this variable, which is local in the indirect buffer.") |
|
460 |
|
|
461 |
(defvar org-capture-clock-keep nil |
|
462 |
"Local variable to store the value of the :clock-keep parameter. |
|
463 |
This is needed in case org-capture-finalize is called interactively.") |
|
464 |
|
|
465 |
(defun org-capture-put (&rest stuff) |
|
466 |
"Add properties to the capture property list `org-capture-plist'." |
|
467 |
(while stuff |
|
468 |
(setq org-capture-plist (plist-put org-capture-plist |
|
469 |
(pop stuff) (pop stuff))))) |
|
470 |
(defun org-capture-get (prop &optional local) |
|
471 |
"Get properties from the capture property list `org-capture-plist'. |
|
472 |
When LOCAL is set, use the local variable `org-capture-current-plist', |
|
473 |
this is necessary after initialization of the capture process, |
|
474 |
to avoid conflicts with other active capture processes." |
|
475 |
(plist-get (if local org-capture-current-plist org-capture-plist) prop)) |
|
476 |
|
|
477 |
(defun org-capture-member (prop &optional local) |
|
478 |
"Is PROP a property in `org-capture-plist'. |
|
479 |
When LOCAL is set, use the local variable `org-capture-current-plist', |
|
480 |
this is necessary after initialization of the capture process, |
|
481 |
to avoid conflicts with other active capture processes." |
|
482 |
(plist-get (if local org-capture-current-plist org-capture-plist) prop)) |
|
483 |
|
|
484 |
;;; The minor mode |
|
485 |
|
|
486 |
(defvar org-capture-mode-map (make-sparse-keymap) |
|
487 |
"Keymap for `org-capture-mode', a minor mode. |
|
488 |
Use this map to set additional keybindings for when Org mode is used |
|
489 |
for a capture buffer.") |
|
490 |
|
|
491 |
(defvar org-capture-mode-hook nil |
|
492 |
"Hook for the minor `org-capture-mode'.") |
|
493 |
|
|
494 |
(define-minor-mode org-capture-mode |
|
495 |
"Minor mode for special key bindings in a capture buffer. |
|
496 |
|
|
497 |
Turning on this mode runs the normal hook `org-capture-mode-hook'." |
|
498 |
nil " Rem" org-capture-mode-map |
|
499 |
(setq-local |
|
500 |
header-line-format |
|
501 |
(substitute-command-keys |
|
502 |
"\\<org-capture-mode-map>Capture buffer. Finish \ |
|
503 |
`\\[org-capture-finalize]', refile `\\[org-capture-refile]', \ |
|
504 |
abort `\\[org-capture-kill]'."))) |
|
505 |
(define-key org-capture-mode-map "\C-c\C-c" 'org-capture-finalize) |
|
506 |
(define-key org-capture-mode-map "\C-c\C-k" 'org-capture-kill) |
|
507 |
(define-key org-capture-mode-map "\C-c\C-w" 'org-capture-refile) |
|
508 |
|
|
509 |
;;; The main commands |
|
510 |
|
|
511 |
(defvar org-capture-initial nil) |
|
512 |
(defvar org-capture-entry nil) |
|
513 |
|
|
514 |
;;;###autoload |
|
515 |
(defun org-capture-string (string &optional keys) |
|
516 |
"Capture STRING with the template selected by KEYS." |
|
517 |
(interactive "sInitial text: \n") |
|
518 |
(let ((org-capture-initial string) |
|
519 |
(org-capture-entry (org-capture-select-template keys))) |
|
520 |
(org-capture))) |
|
521 |
|
|
522 |
(defcustom org-capture-templates-contexts nil |
|
523 |
"Alist of capture templates and valid contexts. |
|
524 |
|
|
525 |
For example, if you have a capture template \"c\" and you want |
|
526 |
this template to be accessible only from `message-mode' buffers, |
|
527 |
use this: |
|
528 |
|
|
529 |
\\='((\"c\" ((in-mode . \"message-mode\")))) |
|
530 |
|
|
531 |
Here are the available contexts definitions: |
|
532 |
|
|
533 |
in-file: command displayed only in matching files |
|
534 |
in-mode: command displayed only in matching modes |
|
535 |
not-in-file: command not displayed in matching files |
|
536 |
not-in-mode: command not displayed in matching modes |
|
537 |
in-buffer: command displayed only in matching buffers |
|
538 |
not-in-buffer: command not displayed in matching buffers |
|
539 |
[function]: a custom function taking no argument |
|
540 |
|
|
541 |
If you define several checks, the agenda command will be |
|
542 |
accessible if there is at least one valid check. |
|
543 |
|
|
544 |
You can also bind a key to another capture template depending on |
|
545 |
contextual rules. |
|
546 |
|
|
547 |
\\='((\"c\" \"d\" ((in-mode . \"message-mode\")))) |
|
548 |
|
|
549 |
Here it means: in `message-mode buffers', use \"c\" as the |
|
550 |
key for the capture template otherwise associated with \"d\". |
|
551 |
\(The template originally associated with \"d\" is not displayed |
|
552 |
to avoid duplicates.)" |
|
553 |
:version "24.3" |
|
554 |
:group 'org-capture |
|
555 |
:type '(repeat (list :tag "Rule" |
|
556 |
(string :tag " Capture key") |
|
557 |
(string :tag "Replace by template") |
|
558 |
(repeat :tag "Available when" |
|
559 |
(choice |
|
560 |
(cons :tag "Condition" |
|
561 |
(choice |
|
562 |
(const :tag "In file" in-file) |
|
563 |
(const :tag "Not in file" not-in-file) |
|
564 |
(const :tag "In buffer" in-buffer) |
|
565 |
(const :tag "Not in buffer" not-in-buffer) |
|
566 |
(const :tag "In mode" in-mode) |
|
567 |
(const :tag "Not in mode" not-in-mode)) |
|
568 |
(regexp)) |
|
569 |
(function :tag "Custom function")))))) |
|
570 |
|
|
571 |
(defcustom org-capture-use-agenda-date nil |
|
572 |
"Non-nil means use the date at point when capturing from agendas. |
|
573 |
When nil, you can still capture using the date at point with |
|
574 |
`\\[org-agenda-capture]'." |
|
575 |
:group 'org-capture |
|
576 |
:version "24.3" |
|
577 |
:type 'boolean) |
|
578 |
|
|
579 |
;;;###autoload |
|
580 |
(defun org-capture (&optional goto keys) |
|
581 |
"Capture something. |
|
582 |
\\<org-capture-mode-map> |
|
583 |
This will let you select a template from `org-capture-templates', and |
|
584 |
then file the newly captured information. The text is immediately |
|
585 |
inserted at the target location, and an indirect buffer is shown where |
|
586 |
you can edit it. Pressing `\\[org-capture-finalize]' brings you back to the \ |
|
587 |
previous |
|
588 |
state of Emacs, so that you can continue your work. |
|
589 |
|
|
590 |
When called interactively with a `\\[universal-argument]' prefix argument \ |
|
591 |
GOTO, don't |
|
592 |
capture anything, just go to the file/headline where the selected |
|
593 |
template stores its notes. |
|
594 |
|
|
595 |
With a `\\[universal-argument] \\[universal-argument]' prefix argument, go to \ |
|
596 |
the last note stored. |
|
597 |
|
|
598 |
When called with a `C-0' (zero) prefix, insert a template at point. |
|
599 |
|
|
600 |
When called with a `C-1' (one) prefix, force prompting for a date when |
|
601 |
a datetree entry is made. |
|
602 |
|
|
603 |
ELisp programs can set KEYS to a string associated with a template |
|
604 |
in `org-capture-templates'. In this case, interactive selection |
|
605 |
will be bypassed. |
|
606 |
|
|
607 |
If `org-capture-use-agenda-date' is non-nil, capturing from the |
|
608 |
agenda will use the date at point as the default date. Then, a |
|
609 |
`C-1' prefix will tell the capture process to use the HH:MM time |
|
610 |
of the day at point (if any) or the current HH:MM time." |
|
611 |
(interactive "P") |
|
612 |
(when (and org-capture-use-agenda-date |
|
613 |
(eq major-mode 'org-agenda-mode)) |
|
614 |
(setq org-overriding-default-time |
|
615 |
(org-get-cursor-date (equal goto 1)))) |
|
616 |
(cond |
|
617 |
((equal goto '(4)) (org-capture-goto-target)) |
|
618 |
((equal goto '(16)) (org-capture-goto-last-stored)) |
|
619 |
(t |
|
620 |
(let* ((orig-buf (current-buffer)) |
|
621 |
(annotation (if (and (boundp 'org-capture-link-is-already-stored) |
|
622 |
org-capture-link-is-already-stored) |
|
623 |
(plist-get org-store-link-plist :annotation) |
|
624 |
(ignore-errors (org-store-link nil)))) |
|
625 |
(entry (or org-capture-entry (org-capture-select-template keys))) |
|
626 |
initial) |
|
627 |
(setq initial (or org-capture-initial |
|
628 |
(and (org-region-active-p) |
|
629 |
(buffer-substring (point) (mark))))) |
|
630 |
(when (stringp initial) |
|
631 |
(remove-text-properties 0 (length initial) '(read-only t) initial)) |
|
632 |
(when (stringp annotation) |
|
633 |
(remove-text-properties 0 (length annotation) |
|
634 |
'(read-only t) annotation)) |
|
635 |
(cond |
|
636 |
((equal entry "C") |
|
637 |
(customize-variable 'org-capture-templates)) |
|
638 |
((equal entry "q") |
|
639 |
(user-error "Abort")) |
|
640 |
(t |
|
641 |
(org-capture-set-plist entry) |
|
642 |
(org-capture-get-template) |
|
643 |
(org-capture-put :original-buffer orig-buf |
|
644 |
:original-file (or (buffer-file-name orig-buf) |
|
645 |
(and (featurep 'dired) |
|
646 |
(car (rassq orig-buf |
|
647 |
dired-buffers)))) |
|
648 |
:original-file-nondirectory |
|
649 |
(and (buffer-file-name orig-buf) |
|
650 |
(file-name-nondirectory |
|
651 |
(buffer-file-name orig-buf))) |
|
652 |
:annotation annotation |
|
653 |
:initial initial |
|
654 |
:return-to-wconf (current-window-configuration) |
|
655 |
:default-time |
|
656 |
(or org-overriding-default-time |
|
657 |
(org-current-time))) |
|
658 |
(org-capture-set-target-location) |
|
659 |
(condition-case error |
|
660 |
(org-capture-put :template (org-capture-fill-template)) |
|
661 |
((error quit) |
|
662 |
(if (get-buffer "*Capture*") (kill-buffer "*Capture*")) |
|
663 |
(error "Capture abort: %s" error))) |
|
664 |
|
|
665 |
(setq org-capture-clock-keep (org-capture-get :clock-keep)) |
|
666 |
(if (equal goto 0) |
|
667 |
;;insert at point |
|
668 |
(org-capture-insert-template-here) |
|
669 |
(condition-case error |
|
670 |
(org-capture-place-template |
|
671 |
(eq (car (org-capture-get :target)) 'function)) |
|
672 |
((error quit) |
|
673 |
(if (and (buffer-base-buffer (current-buffer)) |
|
674 |
(string-prefix-p "CAPTURE-" (buffer-name))) |
|
675 |
(kill-buffer (current-buffer))) |
|
676 |
(set-window-configuration (org-capture-get :return-to-wconf)) |
|
677 |
(error "Capture template `%s': %s" |
|
678 |
(org-capture-get :key) |
|
679 |
(nth 1 error)))) |
|
680 |
(if (and (derived-mode-p 'org-mode) |
|
681 |
(org-capture-get :clock-in)) |
|
682 |
(condition-case nil |
|
683 |
(progn |
|
684 |
(if (org-clock-is-active) |
|
685 |
(org-capture-put :interrupted-clock |
|
686 |
(copy-marker org-clock-marker))) |
|
687 |
(org-clock-in) |
|
688 |
(setq-local org-capture-clock-was-started t)) |
|
689 |
(error |
|
690 |
"Could not start the clock in this capture buffer"))) |
|
691 |
(if (org-capture-get :immediate-finish) |
|
692 |
(org-capture-finalize))))))))) |
|
693 |
|
|
694 |
(defun org-capture-get-template () |
|
695 |
"Get the template from a file or a function if necessary." |
|
696 |
(let ((txt (org-capture-get :template)) file) |
|
697 |
(cond |
|
698 |
((and (listp txt) (eq (car txt) 'file)) |
|
699 |
(if (file-exists-p |
|
700 |
(setq file (expand-file-name (nth 1 txt) org-directory))) |
|
701 |
(setq txt (org-file-contents file)) |
|
702 |
(setq txt (format "* Template file %s not found" (nth 1 txt))))) |
|
703 |
((and (listp txt) (eq (car txt) 'function)) |
|
704 |
(if (fboundp (nth 1 txt)) |
|
705 |
(setq txt (funcall (nth 1 txt))) |
|
706 |
(setq txt (format "* Template function %s not found" (nth 1 txt))))) |
|
707 |
((not txt) (setq txt "")) |
|
708 |
((stringp txt)) |
|
709 |
(t (setq txt "* Invalid capture template"))) |
|
710 |
(org-capture-put :template txt))) |
|
711 |
|
|
712 |
(defun org-capture-finalize (&optional stay-with-capture) |
|
713 |
"Finalize the capture process. |
|
714 |
With prefix argument STAY-WITH-CAPTURE, jump to the location of the |
|
715 |
captured item after finalizing." |
|
716 |
(interactive "P") |
|
717 |
(when (org-capture-get :jump-to-captured) |
|
718 |
(setq stay-with-capture t)) |
|
719 |
(unless (and org-capture-mode |
|
720 |
(buffer-base-buffer (current-buffer))) |
|
721 |
(error "This does not seem to be a capture buffer for Org mode")) |
|
722 |
|
|
723 |
(run-hooks 'org-capture-prepare-finalize-hook) |
|
724 |
|
|
725 |
;; Did we start the clock in this capture buffer? |
|
726 |
(when (and org-capture-clock-was-started |
|
727 |
org-clock-marker |
|
728 |
(eq (marker-buffer org-clock-marker) (buffer-base-buffer)) |
|
729 |
(>= org-clock-marker (point-min)) |
|
730 |
(< org-clock-marker (point-max))) |
|
731 |
;; Looks like the clock we started is still running. |
|
732 |
(if org-capture-clock-keep |
|
733 |
;; User may have completed clocked heading from the template. |
|
734 |
;; Refresh clock mode line. |
|
735 |
(org-clock-update-mode-line t) |
|
736 |
;; Clock out. Possibly resume interrupted clock. |
|
737 |
(let (org-log-note-clock-out) (org-clock-out)) |
|
738 |
(when (and (org-capture-get :clock-resume 'local) |
|
739 |
(markerp (org-capture-get :interrupted-clock 'local)) |
|
740 |
(buffer-live-p (marker-buffer |
|
741 |
(org-capture-get :interrupted-clock 'local)))) |
|
742 |
(let ((clock-in-task (org-capture-get :interrupted-clock 'local))) |
|
743 |
(org-with-point-at clock-in-task (org-clock-in))) |
|
744 |
(message "Interrupted clock has been resumed")))) |
|
745 |
|
|
746 |
(let ((abort-note nil)) |
|
747 |
;; Store the size of the capture buffer |
|
748 |
(org-capture-put :captured-entry-size (- (point-max) (point-min))) |
|
749 |
(widen) |
|
750 |
;; Store the insertion point in the target buffer |
|
751 |
(org-capture-put :insertion-point (point)) |
|
752 |
|
|
753 |
(if org-note-abort |
|
754 |
(let ((beg (org-capture-get :begin-marker 'local)) |
|
755 |
(end (org-capture-get :end-marker 'local))) |
|
756 |
(if (not (and beg end)) (setq abort-note 'dirty) |
|
757 |
(setq abort-note t) |
|
758 |
(org-with-wide-buffer (kill-region beg end)))) |
|
759 |
|
|
760 |
;; Postprocessing: Update Statistics cookies, do the sorting |
|
761 |
(when (derived-mode-p 'org-mode) |
|
762 |
(save-excursion |
|
763 |
(when (ignore-errors (org-back-to-heading)) |
|
764 |
(org-update-parent-todo-statistics) |
|
765 |
(org-update-checkbox-count))) |
|
766 |
;; FIXME Here we should do the sorting |
|
767 |
;; If we have added a table line, maybe recompute? |
|
768 |
(when (and (eq (org-capture-get :type 'local) 'table-line) |
|
769 |
(org-at-table-p)) |
|
770 |
(if (org-table-get-stored-formulas) |
|
771 |
(org-table-recalculate 'all) ;; FIXME: Should we iterate??? |
|
772 |
(org-table-align)))) |
|
773 |
;; Store this place as the last one where we stored something |
|
774 |
;; Do the marking in the base buffer, so that it makes sense after |
|
775 |
;; the indirect buffer has been killed. |
|
776 |
(org-capture-store-last-position) |
|
777 |
|
|
778 |
;; Run the hook |
|
779 |
(run-hooks 'org-capture-before-finalize-hook)) |
|
780 |
|
|
781 |
(when (org-capture-get :decrypted) |
|
782 |
(save-excursion |
|
783 |
(goto-char (org-capture-get :decrypted)) |
|
784 |
(org-encrypt-entry))) |
|
785 |
|
|
786 |
;; Kill the indirect buffer |
|
787 |
(save-buffer) |
|
788 |
(let ((return-wconf (org-capture-get :return-to-wconf 'local)) |
|
789 |
(new-buffer (org-capture-get :new-buffer 'local)) |
|
790 |
(kill-buffer (org-capture-get :kill-buffer 'local)) |
|
791 |
(base-buffer (buffer-base-buffer (current-buffer)))) |
|
792 |
|
|
793 |
;; Kill the indirect buffer |
|
794 |
(kill-buffer (current-buffer)) |
|
795 |
|
|
796 |
;; Narrow back the target buffer to its previous state |
|
797 |
(with-current-buffer (org-capture-get :buffer) |
|
798 |
(let ((reg (org-capture-get :initial-target-region)) |
|
799 |
(pos (org-capture-get :initial-target-position)) |
|
800 |
(ipt (org-capture-get :insertion-point)) |
|
801 |
(size (org-capture-get :captured-entry-size))) |
|
802 |
(if (not reg) |
|
803 |
(widen) |
|
804 |
(cond ((< ipt (car reg)) |
|
805 |
;; insertion point is before the narrowed region |
|
806 |
(narrow-to-region (+ size (car reg)) (+ size (cdr reg)))) |
|
807 |
((> ipt (cdr reg)) |
|
808 |
;; insertion point is after the narrowed region |
|
809 |
(narrow-to-region (car reg) (cdr reg))) |
|
810 |
(t |
|
811 |
;; insertion point is within the narrowed region |
|
812 |
(narrow-to-region (car reg) (+ size (cdr reg))))) |
|
813 |
;; now place back the point at its original position |
|
814 |
(if (< ipt (car reg)) |
|
815 |
(goto-char (+ size pos)) |
|
816 |
(goto-char (if (< ipt pos) (+ size pos) pos)))))) |
|
817 |
|
|
818 |
;; Kill the target buffer if that is desired |
|
819 |
(when (and base-buffer new-buffer kill-buffer) |
|
820 |
(with-current-buffer base-buffer (save-buffer)) |
|
821 |
(kill-buffer base-buffer)) |
|
822 |
|
|
823 |
;; Restore the window configuration before capture |
|
824 |
(set-window-configuration return-wconf)) |
|
825 |
|
|
826 |
(run-hooks 'org-capture-after-finalize-hook) |
|
827 |
;; Special cases |
|
828 |
(cond |
|
829 |
(abort-note |
|
830 |
(cl-case abort-note |
|
831 |
(clean |
|
832 |
(message "Capture process aborted and target buffer cleaned up")) |
|
833 |
(dirty |
|
834 |
(error "Capture process aborted, but target buffer could not be \ |
|
835 |
cleaned up correctly")))) |
|
836 |
(stay-with-capture |
|
837 |
(org-capture-goto-last-stored))) |
|
838 |
;; Return if we did store something |
|
839 |
(not abort-note))) |
|
840 |
|
|
841 |
(defun org-capture-refile () |
|
842 |
"Finalize the current capture and then refile the entry. |
|
843 |
Refiling is done from the base buffer, because the indirect buffer is then |
|
844 |
already gone. Any prefix argument will be passed to the refile command." |
|
845 |
(interactive) |
|
846 |
(unless (eq (org-capture-get :type 'local) 'entry) |
|
847 |
(user-error "Refiling from a capture buffer makes only sense \ |
|
848 |
for `entry'-type templates")) |
|
849 |
(let* ((base (or (buffer-base-buffer) (current-buffer))) |
|
850 |
(pos (make-marker)) |
|
851 |
(org-capture-is-refiling t) |
|
852 |
(kill-buffer (org-capture-get :kill-buffer 'local)) |
|
853 |
(jump-to-captured (org-capture-get :jump-to-captured 'local))) |
|
854 |
;; Since `org-capture-finalize' may alter buffer contents (e.g., |
|
855 |
;; empty lines) around entry, use a marker to refer to the |
|
856 |
;; headline to be refiled. Place the marker in the base buffer, |
|
857 |
;; as the current indirect one is going to be killed. |
|
858 |
(set-marker pos (save-excursion (org-back-to-heading t) (point)) base) |
|
859 |
;; `org-capture-finalize' calls `org-capture-goto-last-stored' too |
|
860 |
;; early. We want to wait for the refiling to be over, so we |
|
861 |
;; control when the latter function is called. |
|
862 |
(org-capture-put :kill-buffer nil :jump-to-captured nil) |
|
863 |
(unwind-protect |
|
864 |
(progn |
|
865 |
(org-capture-finalize) |
|
866 |
(save-window-excursion |
|
867 |
(with-current-buffer base |
|
868 |
(org-with-wide-buffer |
|
869 |
(goto-char pos) |
|
870 |
(call-interactively 'org-refile)))) |
|
871 |
(when kill-buffer (kill-buffer base)) |
|
872 |
(when jump-to-captured (org-capture-goto-last-stored))) |
|
873 |
(set-marker pos nil)))) |
|
874 |
|
|
875 |
(defun org-capture-kill () |
|
876 |
"Abort the current capture process." |
|
877 |
(interactive) |
|
878 |
;; FIXME: This does not do the right thing, we need to remove the |
|
879 |
;; new stuff by hand it is easy: undo, then kill the buffer |
|
880 |
(let ((org-note-abort t) |
|
881 |
(org-capture-before-finalize-hook nil)) |
|
882 |
(org-capture-finalize))) |
|
883 |
|
|
884 |
(defun org-capture-goto-last-stored () |
|
885 |
"Go to the location where the last capture note was stored." |
|
886 |
(interactive) |
|
887 |
(org-goto-marker-or-bmk org-capture-last-stored-marker |
|
888 |
(plist-get org-bookmark-names-plist |
|
889 |
:last-capture)) |
|
890 |
(message "This is the last note stored by a capture process")) |
|
891 |
|
|
892 |
;;; Supporting functions for handling the process |
|
893 |
|
|
894 |
(defun org-capture-put-target-region-and-position () |
|
895 |
"Store the initial region with `org-capture-put'." |
|
896 |
(org-capture-put |
|
897 |
:initial-target-region |
|
898 |
;; Check if the buffer is currently narrowed |
|
899 |
(when (org-buffer-narrowed-p) |
|
900 |
(cons (point-min) (point-max)))) |
|
901 |
;; store the current point |
|
902 |
(org-capture-put :initial-target-position (point))) |
|
903 |
|
|
904 |
(defvar org-time-was-given) ; dynamically scoped parameter |
|
905 |
(defun org-capture-set-target-location (&optional target) |
|
906 |
"Find TARGET buffer and position. |
|
907 |
Store them in the capture property list." |
|
908 |
(let ((target-entry-p t)) |
|
909 |
(save-excursion |
|
910 |
(pcase (or target (org-capture-get :target)) |
|
911 |
(`(file ,path) |
|
912 |
(set-buffer (org-capture-target-buffer path)) |
|
913 |
(org-capture-put-target-region-and-position) |
|
914 |
(widen) |
|
915 |
(setq target-entry-p nil)) |
|
916 |
(`(id ,id) |
|
917 |
(pcase (org-id-find id) |
|
918 |
(`(,path . ,position) |
|
919 |
(set-buffer (org-capture-target-buffer path)) |
|
920 |
(widen) |
|
921 |
(org-capture-put-target-region-and-position) |
|
922 |
(goto-char position)) |
|
923 |
(_ (error "Cannot find target ID \"%s\"" id)))) |
|
924 |
(`(file+headline ,path ,headline) |
|
925 |
(set-buffer (org-capture-target-buffer path)) |
|
926 |
;; Org expects the target file to be in Org mode, otherwise |
|
927 |
;; it throws an error. However, the default notes files |
|
928 |
;; should work out of the box. In this case, we switch it to |
|
929 |
;; Org mode. |
|
930 |
(unless (derived-mode-p 'org-mode) |
|
931 |
(org-display-warning |
|
932 |
(format "Capture requirement: switching buffer %S to Org mode" |
|
933 |
(current-buffer))) |
|
934 |
(org-mode)) |
|
935 |
(org-capture-put-target-region-and-position) |
|
936 |
(widen) |
|
937 |
(goto-char (point-min)) |
|
938 |
(if (re-search-forward (format org-complex-heading-regexp-format |
|
939 |
(regexp-quote headline)) |
|
940 |
nil t) |
|
941 |
(beginning-of-line) |
|
942 |
(goto-char (point-max)) |
|
943 |
(unless (bolp) (insert "\n")) |
|
944 |
(insert "* " headline "\n") |
|
945 |
(beginning-of-line 0))) |
|
946 |
(`(file+olp ,path . ,outline-path) |
|
947 |
(let ((m (org-find-olp (cons (org-capture-expand-file path) |
|
948 |
outline-path)))) |
|
949 |
(set-buffer (marker-buffer m)) |
|
950 |
(org-capture-put-target-region-and-position) |
|
951 |
(widen) |
|
952 |
(goto-char m) |
|
953 |
(set-marker m nil))) |
|
954 |
(`(file+regexp ,path ,regexp) |
|
955 |
(set-buffer (org-capture-target-buffer path)) |
|
956 |
(org-capture-put-target-region-and-position) |
|
957 |
(widen) |
|
958 |
(goto-char (point-min)) |
|
959 |
(if (not (re-search-forward regexp nil t)) |
|
960 |
(error "No match for target regexp in file %s" path) |
|
961 |
(goto-char (if (org-capture-get :prepend) |
|
962 |
(match-beginning 0) |
|
963 |
(match-end 0))) |
|
964 |
(org-capture-put :exact-position (point)) |
|
965 |
(setq target-entry-p |
|
966 |
(and (derived-mode-p 'org-mode) (org-at-heading-p))))) |
|
967 |
(`(file+olp+datetree ,path . ,outline-path) |
|
968 |
(let ((m (if outline-path |
|
969 |
(org-find-olp (cons (org-capture-expand-file path) |
|
970 |
outline-path)) |
|
971 |
(set-buffer (org-capture-target-buffer path)) |
|
972 |
(point-marker)))) |
|
973 |
(set-buffer (marker-buffer m)) |
|
974 |
(org-capture-put-target-region-and-position) |
|
975 |
(widen) |
|
976 |
(goto-char m) |
|
977 |
(set-marker m nil) |
|
978 |
(require 'org-datetree) |
|
979 |
(org-capture-put-target-region-and-position) |
|
980 |
(widen) |
|
981 |
;; Make a date/week tree entry, with the current date (or |
|
982 |
;; yesterday, if we are extending dates for a couple of hours) |
|
983 |
(funcall |
|
984 |
(if (eq (org-capture-get :tree-type) 'week) |
|
985 |
#'org-datetree-find-iso-week-create |
|
986 |
#'org-datetree-find-date-create) |
|
987 |
(calendar-gregorian-from-absolute |
|
988 |
(cond |
|
989 |
(org-overriding-default-time |
|
990 |
;; Use the overriding default time. |
|
991 |
(time-to-days org-overriding-default-time)) |
|
992 |
((or (org-capture-get :time-prompt) |
|
993 |
(equal current-prefix-arg 1)) |
|
994 |
;; Prompt for date. |
|
995 |
(let ((prompt-time (org-read-date |
|
996 |
nil t nil "Date for tree entry:" |
|
997 |
(current-time)))) |
|
998 |
(org-capture-put |
|
999 |
:default-time |
|
1000 |
(cond ((and (or (not (boundp 'org-time-was-given)) |
|
1001 |
(not org-time-was-given)) |
|
1002 |
(not (= (time-to-days prompt-time) (org-today)))) |
|
1003 |
;; Use 00:00 when no time is given for another |
|
1004 |
;; date than today? |
|
1005 |
(apply #'encode-time |
|
1006 |
(append `(0 0 ,org-extend-today-until) |
|
1007 |
(cl-cdddr (decode-time prompt-time))))) |
|
1008 |
((string-match "\\([^ ]+\\)--?[^ ]+[ ]+\\(.*\\)" |
|
1009 |
org-read-date-final-answer) |
|
1010 |
;; Replace any time range by its start. |
|
1011 |
(apply #'encode-time |
|
1012 |
(org-read-date-analyze |
|
1013 |
(replace-match "\\1 \\2" nil nil |
|
1014 |
org-read-date-final-answer) |
|
1015 |
prompt-time (decode-time prompt-time)))) |
|
1016 |
(t prompt-time))) |
|
1017 |
(time-to-days prompt-time))) |
|
1018 |
(t |
|
1019 |
;; Current date, possibly corrected for late night |
|
1020 |
;; workers. |
|
1021 |
(org-today)))) |
|
1022 |
;; the following is the keep-restriction argument for |
|
1023 |
;; org-datetree-find-date-create |
|
1024 |
(if outline-path 'subtree-at-point)))) |
|
1025 |
(`(file+function ,path ,function) |
|
1026 |
(set-buffer (org-capture-target-buffer path)) |
|
1027 |
(org-capture-put-target-region-and-position) |
|
1028 |
(widen) |
|
1029 |
(funcall function) |
|
1030 |
(org-capture-put :exact-position (point)) |
|
1031 |
(setq target-entry-p |
|
1032 |
(and (derived-mode-p 'org-mode) (org-at-heading-p)))) |
|
1033 |
(`(function ,fun) |
|
1034 |
(funcall fun) |
|
1035 |
(org-capture-put :exact-position (point)) |
|
1036 |
(setq target-entry-p |
|
1037 |
(and (derived-mode-p 'org-mode) (org-at-heading-p)))) |
|
1038 |
(`(clock) |
|
1039 |
(if (and (markerp org-clock-hd-marker) |
|
1040 |
(marker-buffer org-clock-hd-marker)) |
|
1041 |
(progn (set-buffer (marker-buffer org-clock-hd-marker)) |
|
1042 |
(org-capture-put-target-region-and-position) |
|
1043 |
(widen) |
|
1044 |
(goto-char org-clock-hd-marker)) |
|
1045 |
(error "No running clock that could be used as capture target"))) |
|
1046 |
(target (error "Invalid capture target specification: %S" target))) |
|
1047 |
|
|
1048 |
(org-capture-put :buffer (current-buffer) |
|
1049 |
:pos (point) |
|
1050 |
:target-entry-p target-entry-p |
|
1051 |
:decrypted |
|
1052 |
(and (featurep 'org-crypt) |
|
1053 |
(org-at-encrypted-entry-p) |
|
1054 |
(save-excursion |
|
1055 |
(org-decrypt-entry) |
|
1056 |
(and (org-back-to-heading t) (point)))))))) |
|
1057 |
|
|
1058 |
(defun org-capture-expand-file (file) |
|
1059 |
"Expand functions, symbols and file names for FILE. |
|
1060 |
When FILE is a function, call it. When it is a form, evaluate |
|
1061 |
it. When it is a variable, return its value. When it is |
|
1062 |
a string, treat it as a file name, possibly expanding it |
|
1063 |
according to `org-directory', and return it. If it is the empty |
|
1064 |
string, however, return `org-default-notes-file'. In any other |
|
1065 |
case, raise an error." |
|
1066 |
(let ((location (cond ((equal file "") org-default-notes-file) |
|
1067 |
((stringp file) (expand-file-name file org-directory)) |
|
1068 |
((functionp file) (funcall file)) |
|
1069 |
((and (symbolp file) (boundp file)) (symbol-value file)) |
|
1070 |
(t nil)))) |
|
1071 |
(or (org-string-nw-p location) |
|
1072 |
(error "Invalid file location: %S" location)))) |
|
1073 |
|
|
1074 |
(defun org-capture-target-buffer (file) |
|
1075 |
"Get a buffer for FILE. |
|
1076 |
FILE is a generalized file location, as handled by |
|
1077 |
`org-capture-expand-file'." |
|
1078 |
(let ((file (org-capture-expand-file file))) |
|
1079 |
(or (org-find-base-buffer-visiting file) |
|
1080 |
(progn (org-capture-put :new-buffer t) |
|
1081 |
(find-file-noselect file))))) |
|
1082 |
|
|
1083 |
(defun org-capture-place-template (&optional inhibit-wconf-store) |
|
1084 |
"Insert the template at the target location, and display the buffer. |
|
1085 |
When `inhibit-wconf-store', don't store the window configuration, as it |
|
1086 |
may have been stored before." |
|
1087 |
(unless inhibit-wconf-store |
|
1088 |
(org-capture-put :return-to-wconf (current-window-configuration))) |
|
1089 |
(delete-other-windows) |
|
1090 |
(org-switch-to-buffer-other-window |
|
1091 |
(org-capture-get-indirect-buffer (org-capture-get :buffer) "CAPTURE")) |
|
1092 |
(widen) |
|
1093 |
(outline-show-all) |
|
1094 |
(goto-char (org-capture-get :pos)) |
|
1095 |
(setq-local outline-level 'org-outline-level) |
|
1096 |
(pcase (org-capture-get :type) |
|
1097 |
((or `nil `entry) (org-capture-place-entry)) |
|
1098 |
(`table-line (org-capture-place-table-line)) |
|
1099 |
(`plain (org-capture-place-plain-text)) |
|
1100 |
(`item (org-capture-place-item)) |
|
1101 |
(`checkitem (org-capture-place-item))) |
|
1102 |
(org-capture-mode 1) |
|
1103 |
(setq-local org-capture-current-plist org-capture-plist)) |
|
1104 |
|
|
1105 |
(defun org-capture-place-entry () |
|
1106 |
"Place the template as a new Org entry." |
|
1107 |
(let ((reversed? (org-capture-get :prepend)) |
|
1108 |
(level 1)) |
|
1109 |
(when (org-capture-get :exact-position) |
|
1110 |
(goto-char (org-capture-get :exact-position))) |
|
1111 |
(cond |
|
1112 |
;; Insert as a child of the current entry. |
|
1113 |
((org-capture-get :target-entry-p) |
|
1114 |
(setq level (org-get-valid-level |
|
1115 |
(if (org-at-heading-p) (org-outline-level) 1) |
|
1116 |
1)) |
|
1117 |
(if reversed? (outline-next-heading) (org-end-of-subtree t t))) |
|
1118 |
;; Insert as a top-level entry at the beginning of the file. |
|
1119 |
(reversed? |
|
1120 |
(goto-char (point-min)) |
|
1121 |
(unless (org-at-heading-p) (outline-next-heading))) |
|
1122 |
;; Otherwise, insert as a top-level entry at the end of the file. |
|
1123 |
(t (goto-char (point-max)))) |
|
1124 |
(unless (bolp) (insert "\n")) |
|
1125 |
(org-capture-empty-lines-before) |
|
1126 |
(let ((beg (point)) |
|
1127 |
(template (org-capture-get :template))) |
|
1128 |
(org-capture-verify-tree template) |
|
1129 |
(org-paste-subtree level template 'for-yank) |
|
1130 |
(org-capture-empty-lines-after) |
|
1131 |
(org-capture-position-for-last-stored beg) |
|
1132 |
(unless (org-at-heading-p) (outline-next-heading)) |
|
1133 |
(let ((end (point))) |
|
1134 |
(org-capture-mark-kill-region beg end) |
|
1135 |
(org-capture-narrow beg end) |
|
1136 |
(when (or (re-search-backward "%\\?" beg t) |
|
1137 |
(re-search-forward "%\\?" end t)) |
|
1138 |
(replace-match "")))))) |
|
1139 |
|
|
1140 |
(defun org-capture-place-item () |
|
1141 |
"Place the template as a new plain list item." |
|
1142 |
(let* ((txt (org-capture-get :template)) |
|
1143 |
(target-entry-p (org-capture-get :target-entry-p)) |
|
1144 |
(ind 0) |
|
1145 |
beg end) |
|
1146 |
(if (org-capture-get :exact-position) |
|
1147 |
(goto-char (org-capture-get :exact-position)) |
|
1148 |
(cond |
|
1149 |
((not target-entry-p) |
|
1150 |
;; Insert as top-level entry, either at beginning or at end of file |
|
1151 |
(setq beg (point-min) end (point-max))) |
|
1152 |
(t |
|
1153 |
(setq beg (1+ (point-at-eol)) |
|
1154 |
end (save-excursion (outline-next-heading) (point))))) |
|
1155 |
(setq ind nil) |
|
1156 |
(if (org-capture-get :prepend) |
|
1157 |
(progn |
|
1158 |
(goto-char beg) |
|
1159 |
(when (org-list-search-forward (org-item-beginning-re) end t) |
|
1160 |
(goto-char (match-beginning 0)) |
|
1161 |
(setq ind (org-get-indentation)))) |
|
1162 |
(goto-char end) |
|
1163 |
(when (org-list-search-backward (org-item-beginning-re) beg t) |
|
1164 |
(setq ind (org-get-indentation)) |
|
1165 |
(org-end-of-item))) |
|
1166 |
(unless ind (goto-char end))) |
|
1167 |
;; Remove common indentation |
|
1168 |
(setq txt (org-remove-indentation txt)) |
|
1169 |
;; Make sure this is indeed an item |
|
1170 |
(unless (string-match (concat "\\`" (org-item-re)) txt) |
|
1171 |
(setq txt (concat "- " |
|
1172 |
(mapconcat 'identity (split-string txt "\n") |
|
1173 |
"\n ")))) |
|
1174 |
;; Prepare surrounding empty lines. |
|
1175 |
(unless (bolp) (insert "\n")) |
|
1176 |
(org-capture-empty-lines-before) |
|
1177 |
(setq beg (point)) |
|
1178 |
(unless (eolp) (save-excursion (insert "\n"))) |
|
1179 |
(unless ind |
|
1180 |
(org-indent-line) |
|
1181 |
(setq ind (org-get-indentation)) |
|
1182 |
(delete-region beg (point))) |
|
1183 |
;; Set the correct indentation, depending on context |
|
1184 |
(setq ind (make-string ind ?\ )) |
|
1185 |
(setq txt (concat ind |
|
1186 |
(mapconcat 'identity (split-string txt "\n") |
|
1187 |
(concat "\n" ind)) |
|
1188 |
"\n")) |
|
1189 |
;; Insert item. |
|
1190 |
(insert txt) |
|
1191 |
(org-capture-empty-lines-after) |
|
1192 |
(org-capture-position-for-last-stored beg) |
|
1193 |
(setq end (point)) |
|
1194 |
(org-capture-mark-kill-region beg end) |
|
1195 |
(org-capture-narrow beg end) |
|
1196 |
(if (or (re-search-backward "%\\?" beg t) |
|
1197 |
(re-search-forward "%\\?" end t)) |
|
1198 |
(replace-match "")))) |
|
1199 |
|
|
1200 |
(defun org-capture-place-table-line () |
|
1201 |
"Place the template as a table line." |
|
1202 |
(require 'org-table) |
|
1203 |
(let* ((txt (org-capture-get :template)) |
|
1204 |
(target-entry-p (org-capture-get :target-entry-p)) |
|
1205 |
(table-line-pos (org-capture-get :table-line-pos)) |
|
1206 |
beg end) |
|
1207 |
(cond |
|
1208 |
((org-capture-get :exact-position) |
|
1209 |
(goto-char (org-capture-get :exact-position))) |
|
1210 |
((not target-entry-p) |
|
1211 |
;; Table is not necessarily under a heading |
|
1212 |
(setq beg (point-min) end (point-max))) |
|
1213 |
(t |
|
1214 |
;; WE are at a heading, limit search to the body |
|
1215 |
(setq beg (1+ (point-at-eol)) |
|
1216 |
end (save-excursion (outline-next-heading) (point))))) |
|
1217 |
(if (re-search-forward org-table-dataline-regexp end t) |
|
1218 |
(let ((b (org-table-begin)) (e (org-table-end)) (case-fold-search t)) |
|
1219 |
(goto-char e) |
|
1220 |
(if (looking-at "[ \t]*#\\+tblfm:") |
|
1221 |
(forward-line 1)) |
|
1222 |
(narrow-to-region b (point))) |
|
1223 |
(goto-char end) |
|
1224 |
(insert "\n| |\n|----|\n| |\n") |
|
1225 |
(narrow-to-region (1+ end) (point))) |
|
1226 |
;; We are narrowed to the table, or to an empty line if there was no table |
|
1227 |
|
|
1228 |
;; Check if the template is good |
|
1229 |
(if (not (string-match org-table-dataline-regexp txt)) |
|
1230 |
(setq txt "| %?Bad template |\n")) |
|
1231 |
(if (functionp table-line-pos) |
|
1232 |
(setq table-line-pos (funcall table-line-pos)) |
|
1233 |
(setq table-line-pos (eval table-line-pos))) |
|
1234 |
(cond |
|
1235 |
((and table-line-pos |
|
1236 |
(string-match "\\(I+\\)\\([-+][0-9]\\)" table-line-pos)) |
|
1237 |
(goto-char (point-min)) |
|
1238 |
;; we have a complex line specification |
|
1239 |
(let ((ll (ignore-errors |
|
1240 |
(save-match-data (org-table-analyze)) |
|
1241 |
(aref org-table-hlines |
|
1242 |
(- (match-end 1) (match-beginning 1))))) |
|
1243 |
(delta (string-to-number (match-string 2 table-line-pos)))) |
|
1244 |
;; The user wants a special position in the table |
|
1245 |
(unless ll |
|
1246 |
(error "Invalid table line specification \"%s\"" table-line-pos)) |
|
1247 |
(goto-char org-table-current-begin-pos) |
|
1248 |
(forward-line (+ ll delta (if (< delta 0) 0 -1))) |
|
1249 |
(org-table-insert-row 'below) |
|
1250 |
(beginning-of-line 1) |
|
1251 |
(delete-region (point) (1+ (point-at-eol))) |
|
1252 |
(setq beg (point)) |
|
1253 |
(insert txt) |
|
1254 |
(setq end (point)))) |
|
1255 |
((org-capture-get :prepend) |
|
1256 |
(goto-char (point-min)) |
|
1257 |
(re-search-forward org-table-hline-regexp nil t) |
|
1258 |
(beginning-of-line 1) |
|
1259 |
(re-search-forward org-table-dataline-regexp nil t) |
|
1260 |
(beginning-of-line 1) |
|
1261 |
(setq beg (point)) |
|
1262 |
(org-table-insert-row) |
|
1263 |
(beginning-of-line 1) |
|
1264 |
(delete-region (point) (1+ (point-at-eol))) |
|
1265 |
(insert txt) |
|
1266 |
(setq end (point))) |
|
1267 |
(t |
|
1268 |
(goto-char (point-max)) |
|
1269 |
(re-search-backward org-table-dataline-regexp nil t) |
|
1270 |
(beginning-of-line 1) |
|
1271 |
(org-table-insert-row 'below) |
|
1272 |
(beginning-of-line 1) |
|
1273 |
(delete-region (point) (1+ (point-at-eol))) |
|
1274 |
(setq beg (point)) |
|
1275 |
(insert txt) |
|
1276 |
(setq end (point)))) |
|
1277 |
(goto-char beg) |
|
1278 |
(org-capture-position-for-last-stored 'table-line) |
|
1279 |
(if (or (re-search-backward "%\\?" beg t) |
|
1280 |
(re-search-forward "%\\?" end t)) |
|
1281 |
(replace-match "")) |
|
1282 |
(org-table-align))) |
|
1283 |
|
|
1284 |
(defun org-capture-place-plain-text () |
|
1285 |
"Place the template plainly. |
|
1286 |
If the target locator points at an Org node, place the template into |
|
1287 |
the text of the entry, before the first child. If not, place the |
|
1288 |
template at the beginning or end of the file. |
|
1289 |
Of course, if exact position has been required, just put it there." |
|
1290 |
(let* ((txt (org-capture-get :template)) |
|
1291 |
beg end) |
|
1292 |
(cond |
|
1293 |
((org-capture-get :exact-position) |
|
1294 |
(goto-char (org-capture-get :exact-position))) |
|
1295 |
((and (org-capture-get :target-entry-p) |
|
1296 |
(bolp) |
|
1297 |
(looking-at org-outline-regexp)) |
|
1298 |
;; we should place the text into this entry |
|
1299 |
(if (org-capture-get :prepend) |
|
1300 |
;; Skip meta data and drawers |
|
1301 |
(org-end-of-meta-data t) |
|
1302 |
;; go to ent of the entry text, before the next headline |
|
1303 |
(outline-next-heading))) |
|
1304 |
(t |
|
1305 |
;; beginning or end of file |
|
1306 |
(goto-char (if (org-capture-get :prepend) (point-min) (point-max))))) |
|
1307 |
(or (bolp) (newline)) |
|
1308 |
(org-capture-empty-lines-before) |
|
1309 |
(setq beg (point)) |
|
1310 |
(insert txt) |
|
1311 |
(org-capture-empty-lines-after) |
|
1312 |
(org-capture-position-for-last-stored beg) |
|
1313 |
(setq end (point)) |
|
1314 |
(org-capture-mark-kill-region beg (1- end)) |
|
1315 |
(org-capture-narrow beg (1- end)) |
|
1316 |
(if (or (re-search-backward "%\\?" beg t) |
|
1317 |
(re-search-forward "%\\?" end t)) |
|
1318 |
(replace-match "")))) |
|
1319 |
|
|
1320 |
(defun org-capture-mark-kill-region (beg end) |
|
1321 |
"Mark the region that will have to be killed when aborting capture." |
|
1322 |
(let ((m1 (copy-marker beg)) |
|
1323 |
(m2 (copy-marker end t))) |
|
1324 |
(org-capture-put :begin-marker m1) |
|
1325 |
(org-capture-put :end-marker m2))) |
|
1326 |
|
|
1327 |
(defun org-capture-position-for-last-stored (where) |
|
1328 |
"Memorize the position that should later become the position of last capture." |
|
1329 |
(cond |
|
1330 |
((integerp where) |
|
1331 |
(org-capture-put :position-for-last-stored |
|
1332 |
(move-marker (make-marker) where |
|
1333 |
(or (buffer-base-buffer (current-buffer)) |
|
1334 |
(current-buffer))))) |
|
1335 |
((eq where 'table-line) |
|
1336 |
(org-capture-put :position-for-last-stored |
|
1337 |
(list 'table-line |
|
1338 |
(org-table-current-dline)))) |
|
1339 |
(t (error "This should not happen")))) |
|
1340 |
|
|
1341 |
(defun org-capture-store-last-position () |
|
1342 |
"Store the last-captured position." |
|
1343 |
(let* ((where (org-capture-get :position-for-last-stored 'local)) |
|
1344 |
(pos (cond |
|
1345 |
((markerp where) |
|
1346 |
(prog1 (marker-position where) |
|
1347 |
(move-marker where nil))) |
|
1348 |
((and (listp where) (eq (car where) 'table-line)) |
|
1349 |
(if (org-at-table-p) |
|
1350 |
(save-excursion |
|
1351 |
(org-table-goto-line (nth 1 where)) |
|
1352 |
(point-at-bol)) |
|
1353 |
(point)))))) |
|
1354 |
(with-current-buffer (buffer-base-buffer (current-buffer)) |
|
1355 |
(org-with-point-at pos |
|
1356 |
(when org-capture-bookmark |
|
1357 |
(let ((bookmark (plist-get org-bookmark-names-plist :last-capture))) |
|
1358 |
(when bookmark (with-demoted-errors (bookmark-set bookmark))))) |
|
1359 |
(move-marker org-capture-last-stored-marker (point)))))) |
|
1360 |
|
|
1361 |
(defun org-capture-narrow (beg end) |
|
1362 |
"Narrow, unless configuration says not to narrow." |
|
1363 |
(unless (org-capture-get :unnarrowed) |
|
1364 |
(narrow-to-region beg end) |
|
1365 |
(goto-char beg))) |
|
1366 |
|
|
1367 |
(defun org-capture-empty-lines-before (&optional n) |
|
1368 |
"Set the correct number of empty lines before the insertion point. |
|
1369 |
Point will be after the empty lines, so insertion can directly be done." |
|
1370 |
(setq n (or n (org-capture-get :empty-lines-before) |
|
1371 |
(org-capture-get :empty-lines) 0)) |
|
1372 |
(let ((pos (point))) |
|
1373 |
(org-back-over-empty-lines) |
|
1374 |
(delete-region (point) pos) |
|
1375 |
(if (> n 0) (newline n)))) |
|
1376 |
|
|
1377 |
(defun org-capture-empty-lines-after (&optional n) |
|
1378 |
"Set the correct number of empty lines after the inserted string. |
|
1379 |
Point will remain at the first line after the inserted text." |
|
1380 |
(setq n (or n (org-capture-get :empty-lines-after) |
|
1381 |
(org-capture-get :empty-lines) 0)) |
|
1382 |
(org-back-over-empty-lines) |
|
1383 |
(while (looking-at "[ \t]*\n") (replace-match "")) |
|
1384 |
(let ((pos (point))) |
|
1385 |
(if (> n 0) (newline n)) |
|
1386 |
(goto-char pos))) |
|
1387 |
|
|
1388 |
(defvar org-clock-marker) ; Defined in org.el |
|
1389 |
|
|
1390 |
(defun org-capture-insert-template-here () |
|
1391 |
"Insert the capture template at point." |
|
1392 |
(let* ((template (org-capture-get :template)) |
|
1393 |
(type (org-capture-get :type)) |
|
1394 |
beg end pp) |
|
1395 |
(unless (bolp) (insert "\n")) |
|
1396 |
(setq beg (point)) |
|
1397 |
(cond |
|
1398 |
((and (eq type 'entry) (derived-mode-p 'org-mode)) |
|
1399 |
(org-capture-verify-tree (org-capture-get :template)) |
|
1400 |
(org-paste-subtree nil template t)) |
|
1401 |
((and (memq type '(item checkitem)) |
|
1402 |
(derived-mode-p 'org-mode) |
|
1403 |
(save-excursion (skip-chars-backward " \t\n") |
|
1404 |
(setq pp (point)) |
|
1405 |
(org-in-item-p))) |
|
1406 |
(goto-char pp) |
|
1407 |
(org-insert-item) |
|
1408 |
(skip-chars-backward " ") |
|
1409 |
(skip-chars-backward "-+*0123456789).") |
|
1410 |
(delete-region (point) (point-at-eol)) |
|
1411 |
(setq beg (point)) |
|
1412 |
(org-remove-indentation template) |
|
1413 |
(insert template) |
|
1414 |
(org-capture-empty-lines-after) |
|
1415 |
(goto-char beg) |
|
1416 |
(org-list-repair) |
|
1417 |
(org-end-of-item)) |
|
1418 |
(t |
|
1419 |
(insert template) |
|
1420 |
(org-capture-empty-lines-after) |
|
1421 |
(skip-chars-forward " \t\n") |
|
1422 |
(unless (eobp) (beginning-of-line)))) |
|
1423 |
(setq end (point)) |
|
1424 |
(goto-char beg) |
|
1425 |
(when (re-search-forward "%\\?" end t) |
|
1426 |
(replace-match "")))) |
|
1427 |
|
|
1428 |
(defun org-capture-set-plist (entry) |
|
1429 |
"Initialize the property list from the template definition." |
|
1430 |
(setq org-capture-plist (copy-sequence (nthcdr 5 entry))) |
|
1431 |
(org-capture-put :key (car entry) :description (nth 1 entry) |
|
1432 |
:target (nth 3 entry)) |
|
1433 |
(let ((txt (nth 4 entry)) (type (or (nth 2 entry) 'entry))) |
|
1434 |
(when (or (not txt) (and (stringp txt) (not (string-match "\\S-" txt)))) |
|
1435 |
;; The template may be empty or omitted for special types. |
|
1436 |
;; Here we insert the default templates for such cases. |
|
1437 |
(cond |
|
1438 |
((eq type 'item) (setq txt "- %?")) |
|
1439 |
((eq type 'checkitem) (setq txt "- [ ] %?")) |
|
1440 |
((eq type 'table-line) (setq txt "| %? |")) |
|
1441 |
((member type '(nil entry)) (setq txt "* %?\n %a")))) |
|
1442 |
(org-capture-put :template txt :type type))) |
|
1443 |
|
|
1444 |
(defun org-capture-goto-target (&optional template-key) |
|
1445 |
"Go to the target location of a capture template. |
|
1446 |
The user is queried for the template." |
|
1447 |
(interactive) |
|
1448 |
(let ((entry (org-capture-select-template template-key))) |
|
1449 |
(unless entry (error "No capture template selected")) |
|
1450 |
(org-capture-set-plist entry) |
|
1451 |
(org-capture-set-target-location) |
|
1452 |
(pop-to-buffer-same-window (org-capture-get :buffer)) |
|
1453 |
(goto-char (org-capture-get :pos)))) |
|
1454 |
|
|
1455 |
(defun org-capture-get-indirect-buffer (&optional buffer prefix) |
|
1456 |
"Make an indirect buffer for a capture process. |
|
1457 |
Use PREFIX as a prefix for the name of the indirect buffer." |
|
1458 |
(setq buffer (or buffer (current-buffer))) |
|
1459 |
(let ((n 1) (base (buffer-name buffer)) bname) |
|
1460 |
(setq bname (concat prefix "-" base)) |
|
1461 |
(while (buffer-live-p (get-buffer bname)) |
|
1462 |
(setq bname (concat prefix "-" (number-to-string (cl-incf n)) "-" base))) |
|
1463 |
(condition-case nil |
|
1464 |
(make-indirect-buffer buffer bname 'clone) |
|
1465 |
(error |
|
1466 |
(let ((buf (make-indirect-buffer buffer bname))) |
|
1467 |
(with-current-buffer buf (org-mode)) |
|
1468 |
buf))))) |
|
1469 |
|
|
1470 |
(defun org-capture-verify-tree (tree) |
|
1471 |
"Throw error if TREE is not a valid tree." |
|
1472 |
(unless (org-kill-is-subtree-p tree) |
|
1473 |
(error "Template is not a valid Org entry or tree"))) |
|
1474 |
|
|
1475 |
(defun org-mks (table title &optional prompt specials) |
|
1476 |
"Select a member of an alist with multiple keys. |
|
1477 |
|
|
1478 |
TABLE is the alist which should contain entries where the car is a string. |
|
1479 |
There should be two types of entries. |
|
1480 |
|
|
1481 |
1. prefix descriptions like (\"a\" \"Description\") |
|
1482 |
This indicates that `a' is a prefix key for multi-letter selection, and |
|
1483 |
that there are entries following with keys like \"ab\", \"ax\"... |
|
1484 |
|
|
1485 |
2. Select-able members must have more than two elements, with the first |
|
1486 |
being the string of keys that lead to selecting it, and the second a |
|
1487 |
short description string of the item. |
|
1488 |
|
|
1489 |
The command will then make a temporary buffer listing all entries |
|
1490 |
that can be selected with a single key, and all the single key |
|
1491 |
prefixes. When you press the key for a single-letter entry, it is selected. |
|
1492 |
When you press a prefix key, the commands (and maybe further prefixes) |
|
1493 |
under this key will be shown and offered for selection. |
|
1494 |
|
|
1495 |
TITLE will be placed over the selection in the temporary buffer, |
|
1496 |
PROMPT will be used when prompting for a key. SPECIAL is an |
|
1497 |
alist with (\"key\" \"description\") entries. When one of these |
|
1498 |
is selected, only the bare key is returned." |
|
1499 |
(save-window-excursion |
|
1500 |
(let ((inhibit-quit t) |
|
1501 |
(buffer (org-switch-to-buffer-other-window "*Org Select*")) |
|
1502 |
(prompt (or prompt "Select: ")) |
|
1503 |
current) |
|
1504 |
(unwind-protect |
|
1505 |
(catch 'exit |
|
1506 |
(while t |
|
1507 |
(erase-buffer) |
|
1508 |
(insert title "\n\n") |
|
1509 |
(let ((des-keys nil) |
|
1510 |
(allowed-keys '("\C-g")) |
|
1511 |
(cursor-type nil)) |
|
1512 |
;; Populate allowed keys and descriptions keys |
|
1513 |
;; available with CURRENT selector. |
|
1514 |
(let ((re (format "\\`%s\\(.\\)\\'" |
|
1515 |
(if current (regexp-quote current) ""))) |
|
1516 |
(prefix (if current (concat current " ") ""))) |
|
1517 |
(dolist (entry table) |
|
1518 |
(pcase entry |
|
1519 |
;; Description. |
|
1520 |
(`(,(and key (pred (string-match re))) ,desc) |
|
1521 |
(let ((k (match-string 1 key))) |
|
1522 |
(push k des-keys) |
|
1523 |
(push k allowed-keys) |
|
1524 |
(insert prefix "[" k "]" "..." " " desc "..." "\n"))) |
|
1525 |
;; Usable entry. |
|
1526 |
(`(,(and key (pred (string-match re))) ,desc . ,_) |
|
1527 |
(let ((k (match-string 1 key))) |
|
1528 |
(insert prefix "[" k "]" " " desc "\n") |
|
1529 |
(push k allowed-keys))) |
|
1530 |
(_ nil)))) |
|
1531 |
;; Insert special entries, if any. |
|
1532 |
(when specials |
|
1533 |
(insert "----------------------------------------------------\ |
|
1534 |
---------------------------\n") |
|
1535 |
(pcase-dolist (`(,key ,description) specials) |
|
1536 |
(insert (format "[%s] %s\n" key description)) |
|
1537 |
(push key allowed-keys))) |
|
1538 |
;; Display UI and let user select an entry or |
|
1539 |
;; a sub-level prefix. |
|
1540 |
(goto-char (point-min)) |
|
1541 |
(unless (pos-visible-in-window-p (point-max)) |
|
1542 |
(org-fit-window-to-buffer)) |
|
1543 |
(message prompt) |
|
1544 |
(let ((pressed (char-to-string (read-char-exclusive)))) |
|
1545 |
(while (not (member pressed allowed-keys)) |
|
1546 |
(message "Invalid key `%s'" pressed) (sit-for 1) |
|
1547 |
(message prompt) |
|
1548 |
(setq pressed (char-to-string (read-char-exclusive)))) |
|
1549 |
(setq current (concat current pressed)) |
|
1550 |
(cond |
|
1551 |
((equal pressed "\C-g") (user-error "Abort")) |
|
1552 |
;; Selection is a prefix: open a new menu. |
|
1553 |
((member pressed des-keys)) |
|
1554 |
;; Selection matches an association: return it. |
|
1555 |
((let ((entry (assoc current table))) |
|
1556 |
(and entry (throw 'exit entry)))) |
|
1557 |
;; Selection matches a special entry: return the |
|
1558 |
;; selection prefix. |
|
1559 |
((assoc current specials) (throw 'exit current)) |
|
1560 |
(t (error "No entry available"))))))) |
|
1561 |
(when buffer (kill-buffer buffer)))))) |
|
1562 |
|
|
1563 |
;;; The template code |
|
1564 |
(defun org-capture-select-template (&optional keys) |
|
1565 |
"Select a capture template. |
|
1566 |
Lisp programs can force the template by setting KEYS to a string." |
|
1567 |
(let ((org-capture-templates |
|
1568 |
(or (org-contextualize-keys |
|
1569 |
(org-capture-upgrade-templates org-capture-templates) |
|
1570 |
org-capture-templates-contexts) |
|
1571 |
'(("t" "Task" entry (file+headline "" "Tasks") |
|
1572 |
"* TODO %?\n %u\n %a"))))) |
|
1573 |
(if keys |
|
1574 |
(or (assoc keys org-capture-templates) |
|
1575 |
(error "No capture template referred to by \"%s\" keys" keys)) |
|
1576 |
(org-mks org-capture-templates |
|
1577 |
"Select a capture template\n=========================" |
|
1578 |
"Template key: " |
|
1579 |
'(("C" "Customize org-capture-templates") |
|
1580 |
("q" "Abort")))))) |
|
1581 |
|
|
1582 |
(defvar org-capture--clipboards nil |
|
1583 |
"List various clipboards values.") |
|
1584 |
|
|
1585 |
(defun org-capture-fill-template (&optional template initial annotation) |
|
1586 |
"Fill a template and return the filled template as a string. |
|
1587 |
The template may still contain \"%?\" for cursor positioning." |
|
1588 |
(let* ((template (or template (org-capture-get :template))) |
|
1589 |
(buffer (org-capture-get :buffer)) |
|
1590 |
(file (buffer-file-name (or (buffer-base-buffer buffer) buffer))) |
|
1591 |
(time (let* ((c (or (org-capture-get :default-time) (current-time))) |
|
1592 |
(d (decode-time c))) |
|
1593 |
(if (< (nth 2 d) org-extend-today-until) |
|
1594 |
(encode-time 0 59 23 (1- (nth 3 d)) (nth 4 d) (nth 5 d)) |
|
1595 |
c))) |
|
1596 |
(v-t (format-time-string (org-time-stamp-format nil) time)) |
|
1597 |
(v-T (format-time-string (org-time-stamp-format t) time)) |
|
1598 |
(v-u (format-time-string (org-time-stamp-format nil t) time)) |
|
1599 |
(v-U (format-time-string (org-time-stamp-format t t) time)) |
|
1600 |
(v-c (and kill-ring (current-kill 0))) |
|
1601 |
(v-x (or (org-get-x-clipboard 'PRIMARY) |
|
1602 |
(org-get-x-clipboard 'CLIPBOARD) |
|
1603 |
(org-get-x-clipboard 'SECONDARY) |
|
1604 |
"")) ;ensure it is a string |
|
1605 |
;; `initial' and `annotation' might have been passed. But if |
|
1606 |
;; the property list has them, we prefer those values. |
|
1607 |
(v-i (or (plist-get org-store-link-plist :initial) |
|
1608 |
(and (stringp initial) (org-no-properties initial)) |
|
1609 |
(org-capture-get :initial) |
|
1610 |
"")) |
|
1611 |
(v-a |
|
1612 |
(let ((a (or (plist-get org-store-link-plist :annotation) |
|
1613 |
annotation |
|
1614 |
(org-capture-get :annotation) |
|
1615 |
""))) |
|
1616 |
;; Is the link empty? Then we do not want it... |
|
1617 |
(if (equal a "[[]]") "" a))) |
|
1618 |
(l-re "\\[\\[\\(.*?\\)\\]\\(\\[.*?\\]\\)?\\]") |
|
1619 |
(v-A (if (and v-a (string-match l-re v-a)) |
|
1620 |
(replace-match "[[\\1][%^{Link description}]]" nil nil v-a) |
|
1621 |
v-a)) |
|
1622 |
(v-l (if (and v-a (string-match l-re v-a)) |
|
1623 |
(replace-match "\\1" nil nil v-a) |
|
1624 |
v-a)) |
|
1625 |
(v-n user-full-name) |
|
1626 |
(v-k (if (marker-buffer org-clock-marker) |
|
1627 |
(org-no-properties org-clock-heading) |
|
1628 |
"")) |
|
1629 |
(v-K (if (marker-buffer org-clock-marker) |
|
1630 |
(org-make-link-string |
|
1631 |
(format "%s::*%s" |
|
1632 |
(buffer-file-name (marker-buffer org-clock-marker)) |
|
1633 |
v-k) |
|
1634 |
v-k) |
|
1635 |
"")) |
|
1636 |
(v-f (or (org-capture-get :original-file-nondirectory) "")) |
|
1637 |
(v-F (or (org-capture-get :original-file) "")) |
|
1638 |
(org-capture--clipboards |
|
1639 |
(delq nil |
|
1640 |
(list v-i |
|
1641 |
(org-get-x-clipboard 'PRIMARY) |
|
1642 |
(org-get-x-clipboard 'CLIPBOARD) |
|
1643 |
(org-get-x-clipboard 'SECONDARY) |
|
1644 |
v-c)))) |
|
1645 |
|
|
1646 |
(setq org-store-link-plist (plist-put org-store-link-plist :annotation v-a)) |
|
1647 |
(setq org-store-link-plist (plist-put org-store-link-plist :initial v-i)) |
|
1648 |
|
|
1649 |
(unless template |
|
1650 |
(setq template "") |
|
1651 |
(message "no template") (ding) |
|
1652 |
(sit-for 1)) |
|
1653 |
(save-window-excursion |
|
1654 |
(org-switch-to-buffer-other-window (get-buffer-create "*Capture*")) |
|
1655 |
(erase-buffer) |
|
1656 |
(setq buffer-file-name nil) |
|
1657 |
(setq mark-active nil) |
|
1658 |
(insert template) |
|
1659 |
(goto-char (point-min)) |
|
1660 |
|
|
1661 |
;; %[] insert contents of a file. |
|
1662 |
(save-excursion |
|
1663 |
(while (re-search-forward "%\\[\\(.+\\)\\]" nil t) |
|
1664 |
(let ((filename (expand-file-name (match-string 1))) |
|
1665 |
(beg (copy-marker (match-beginning 0))) |
|
1666 |
(end (copy-marker (match-end 0)))) |
|
1667 |
(unless (org-capture-escaped-%) |
|
1668 |
(delete-region beg end) |
|
1669 |
(set-marker beg nil) |
|
1670 |
(set-marker end nil) |
|
1671 |
(condition-case error |
|
1672 |
(insert-file-contents filename) |
|
1673 |
(error |
|
1674 |
(insert (format "%%![couldn not insert %s: %s]" |
|
1675 |
filename |
|
1676 |
error)))))))) |
|
1677 |
|
|
1678 |
;; Mark %() embedded elisp for later evaluation. |
|
1679 |
(org-capture-expand-embedded-elisp 'mark) |
|
1680 |
|
|
1681 |
;; Expand non-interactive templates. |
|
1682 |
(let ((regexp "%\\(:[-a-za-z]+\\|<\\([^>\n]+\\)>\\|[aAcfFikKlntTuUx]\\)")) |
|
1683 |
(save-excursion |
|
1684 |
(while (re-search-forward regexp nil t) |
|
1685 |
;; `org-capture-escaped-%' may modify buffer and cripple |
|
1686 |
;; match-data. Use markers instead. Ditto for other |
|
1687 |
;; templates. |
|
1688 |
(let ((pos (copy-marker (match-beginning 0))) |
|
1689 |
(end (copy-marker (match-end 0))) |
|
1690 |
(value (match-string 1)) |
|
1691 |
(time-string (match-string 2))) |
|
1692 |
(unless (org-capture-escaped-%) |
|
1693 |
(delete-region pos end) |
|
1694 |
(set-marker pos nil) |
|
1695 |
(set-marker end nil) |
|
1696 |
(let* ((inside-sexp? (org-capture-inside-embedded-elisp-p)) |
|
1697 |
(replacement |
|
1698 |
(pcase (string-to-char value) |
|
1699 |
(?< (format-time-string time-string time)) |
|
1700 |
(?: |
|
1701 |
(or (plist-get org-store-link-plist (intern value)) |
|
1702 |
"")) |
|
1703 |
(?i |
|
1704 |
(if inside-sexp? v-i |
|
1705 |
;; Outside embedded Lisp, repeat leading |
|
1706 |
;; characters before initial place holder |
|
1707 |
;; every line. |
|
1708 |
(let ((lead (buffer-substring-no-properties |
|
1709 |
(line-beginning-position) (point)))) |
|
1710 |
(replace-regexp-in-string "\n\\(.\\)" |
|
1711 |
(concat lead "\\1") |
|
1712 |
v-i nil nil 1)))) |
|
1713 |
(?a v-a) |
|
1714 |
(?A v-A) |
|
1715 |
(?c v-c) |
|
1716 |
(?f v-f) |
|
1717 |
(?F v-F) |
|
1718 |
(?k v-k) |
|
1719 |
(?K v-K) |
|
1720 |
(?l v-l) |
|
1721 |
(?n v-n) |
|
1722 |
(?t v-t) |
|
1723 |
(?T v-T) |
|
1724 |
(?u v-u) |
|
1725 |
(?U v-U) |
|
1726 |
(?x v-x)))) |
|
1727 |
(insert |
|
1728 |
(if inside-sexp? |
|
1729 |
;; Escape sensitive characters. |
|
1730 |
(replace-regexp-in-string "[\\\"]" "\\\\\\&" replacement) |
|
1731 |
replacement)))))))) |
|
1732 |
|
|
1733 |
;; Expand %() embedded Elisp. Limit to Sexp originally marked. |
|
1734 |
(org-capture-expand-embedded-elisp) |
|
1735 |
|
|
1736 |
;; Expand interactive templates. This is the last step so that |
|
1737 |
;; template is mostly expanded when prompting happens. Turn on |
|
1738 |
;; Org mode and set local variables. This is to support |
|
1739 |
;; completion in interactive prompts. |
|
1740 |
(let ((org-inhibit-startup t)) (org-mode)) |
|
1741 |
(org-clone-local-variables buffer "\\`org-") |
|
1742 |
(let (strings) ; Stores interactive answers. |
|
1743 |
(save-excursion |
|
1744 |
(let ((regexp "%\\^\\(?:{\\([^}]*\\)}\\)?\\([CgGLptTuU]\\)?")) |
|
1745 |
(while (re-search-forward regexp nil t) |
|
1746 |
(let* ((items (and (match-end 1) |
|
1747 |
(save-match-data |
|
1748 |
(split-string (match-string-no-properties 1) |
|
1749 |
"|")))) |
|
1750 |
(key (match-string 2)) |
|
1751 |
(beg (copy-marker (match-beginning 0))) |
|
1752 |
(end (copy-marker (match-end 0))) |
|
1753 |
(prompt (nth 0 items)) |
|
1754 |
(default (nth 1 items)) |
|
1755 |
(completions (nthcdr 2 items))) |
|
1756 |
(unless (org-capture-escaped-%) |
|
1757 |
(delete-region beg end) |
|
1758 |
(set-marker beg nil) |
|
1759 |
(set-marker end nil) |
|
1760 |
(pcase key |
|
1761 |
((or "G" "g") |
|
1762 |
(let* ((org-last-tags-completion-table |
|
1763 |
(org-global-tags-completion-table |
|
1764 |
(cond ((equal key "G") (org-agenda-files)) |
|
1765 |
(file (list file)) |
|
1766 |
(t nil)))) |
|
1767 |
(org-add-colon-after-tag-completion t) |
|
1768 |
(ins (mapconcat |
|
1769 |
#'identity |
|
1770 |
(org-split-string |
|
1771 |
(completing-read |
|
1772 |
(if prompt (concat prompt ": ") "Tags: ") |
|
1773 |
'org-tags-completion-function nil nil nil |
|
1774 |
'org-tags-history) |
|
1775 |
"[^[:alnum:]_@#%]+") |
|
1776 |
":"))) |
|
1777 |
(when (org-string-nw-p ins) |
|
1778 |
(unless (eq (char-before) ?:) (insert ":")) |
|
1779 |
(insert ins) |
|
1780 |
(unless (eq (char-after) ?:) (insert ":")) |
|
1781 |
(and (org-at-heading-p) |
|
1782 |
(let ((org-ignore-region t)) |
|
1783 |
(org-set-tags nil 'align)))))) |
|
1784 |
((or "C" "L") |
|
1785 |
(let ((insert-fun (if (equal key "C") #'insert |
|
1786 |
(lambda (s) (org-insert-link 0 s))))) |
|
1787 |
(pcase org-capture--clipboards |
|
1788 |
(`nil nil) |
|
1789 |
(`(,value) (funcall insert-fun value)) |
|
1790 |
(`(,first-value . ,_) |
|
1791 |
(funcall insert-fun |
|
1792 |
(read-string "Clipboard/kill value: " |
|
1793 |
first-value |
|
1794 |
'org-capture--clipboards |
|
1795 |
first-value))) |
|
1796 |
(_ (error "Invalid `org-capture--clipboards' value: %S" |
|
1797 |
org-capture--clipboards))))) |
|
1798 |
("p" (org-set-property prompt nil)) |
|
1799 |
((or "t" "T" "u" "U") |
|
1800 |
;; These are the date/time related ones. |
|
1801 |
(let* ((upcase? (equal (upcase key) key)) |
|
1802 |
(org-end-time-was-given nil) |
|
1803 |
(time (org-read-date upcase? t nil prompt))) |
|
1804 |
(org-insert-time-stamp |
|
1805 |
time (or org-time-was-given upcase?) |
|
1806 |
(member key '("u" "U")) |
|
1807 |
nil nil (list org-end-time-was-given)))) |
|
1808 |
(`nil |
|
1809 |
;; Load history list for current prompt. |
|
1810 |
(setq org-capture--prompt-history |
|
1811 |
(gethash prompt org-capture--prompt-history-table)) |
|
1812 |
(push (org-completing-read |
|
1813 |
(concat (or prompt "Enter string") |
|
1814 |
(and default (format " [%s]" default)) |
|
1815 |
": ") |
|
1816 |
completions |
|
1817 |
nil nil nil 'org-capture--prompt-history default) |
|
1818 |
strings) |
|
1819 |
(insert (car strings)) |
|
1820 |
;; Save updated history list for current prompt. |
|
1821 |
(puthash prompt org-capture--prompt-history |
|
1822 |
org-capture--prompt-history-table)) |
|
1823 |
(_ |
|
1824 |
(error "Unknown template placeholder: \"%%^%s\"" |
|
1825 |
key)))))))) |
|
1826 |
|
|
1827 |
;; Replace %n escapes with nth %^{...} string. |
|
1828 |
(setq strings (nreverse strings)) |
|
1829 |
(save-excursion |
|
1830 |
(while (re-search-forward "%\\\\\\([1-9][0-9]*\\)" nil t) |
|
1831 |
(unless (org-capture-escaped-%) |
|
1832 |
(replace-match |
|
1833 |
(nth (1- (string-to-number (match-string 1))) strings) |
|
1834 |
nil t))))) |
|
1835 |
|
|
1836 |
;; Make sure there are no empty lines before the text, and that |
|
1837 |
;; it ends with a newline character. |
|
1838 |
(skip-chars-forward " \t\n") |
|
1839 |
(delete-region (point-min) (line-beginning-position)) |
|
1840 |
(goto-char (point-max)) |
|
1841 |
(skip-chars-backward " \t\n") |
|
1842 |
(delete-region (point) (point-max)) |
|
1843 |
(insert "\n") |
|
1844 |
|
|
1845 |
;; Return the expanded template and kill the capture buffer. |
|
1846 |
(untabify (point-min) (point-max)) |
|
1847 |
(set-buffer-modified-p nil) |
|
1848 |
(prog1 (buffer-substring-no-properties (point-min) (point-max)) |
|
1849 |
(kill-buffer (current-buffer)))))) |
|
1850 |
|
|
1851 |
(defun org-capture-escaped-% () |
|
1852 |
"Non-nil if % was escaped. |
|
1853 |
If yes, unescape it now. Assume match-data contains the |
|
1854 |
placeholder to check." |
|
1855 |
(save-excursion |
|
1856 |
(goto-char (match-beginning 0)) |
|
1857 |
(let ((n (abs (skip-chars-backward "\\\\")))) |
|
1858 |
(delete-char (/ (1+ n) 2)) |
|
1859 |
(= (% n 2) 1)))) |
|
1860 |
|
|
1861 |
(defun org-capture-expand-embedded-elisp (&optional mark) |
|
1862 |
"Evaluate embedded elisp %(sexp) and replace with the result. |
|
1863 |
When optional MARK argument is non-nil, mark Sexp with a text |
|
1864 |
property (`org-embedded-elisp') for later evaluation. Only |
|
1865 |
marked Sexp are evaluated when this argument is nil." |
|
1866 |
(save-excursion |
|
1867 |
(goto-char (point-min)) |
|
1868 |
(while (re-search-forward "%(" nil t) |
|
1869 |
(cond |
|
1870 |
((get-text-property (match-beginning 0) 'org-embedded-elisp) |
|
1871 |
(goto-char (match-beginning 0)) |
|
1872 |
(let ((template-start (point))) |
|
1873 |
(forward-char 1) |
|
1874 |
(let* ((sexp (read (current-buffer))) |
|
1875 |
(result (org-eval |
|
1876 |
(org-capture--expand-keyword-in-embedded-elisp |
|
1877 |
sexp)))) |
|
1878 |
(delete-region template-start (point)) |
|
1879 |
(cond |
|
1880 |
((not result) nil) |
|
1881 |
((stringp result) (insert result)) |
|
1882 |
(t (error |
|
1883 |
"Capture template sexp `%s' must evaluate to string or nil" |
|
1884 |
sexp)))))) |
|
1885 |
((not mark) nil) |
|
1886 |
;; Only mark valid and non-escaped sexp. |
|
1887 |
((org-capture-escaped-%) nil) |
|
1888 |
(t |
|
1889 |
(let ((end (with-syntax-table emacs-lisp-mode-syntax-table |
|
1890 |
(ignore-errors (scan-sexps (1- (point)) 1))))) |
|
1891 |
(when end |
|
1892 |
(put-text-property (- (point) 2) end 'org-embedded-elisp t)))))))) |
|
1893 |
|
|
1894 |
(defun org-capture--expand-keyword-in-embedded-elisp (attr) |
|
1895 |
"Recursively replace capture link keywords in ATTR sexp. |
|
1896 |
Such keywords are prefixed with \"%:\". See |
|
1897 |
`org-capture-template' for more information." |
|
1898 |
(cond ((consp attr) |
|
1899 |
(mapcar 'org-capture--expand-keyword-in-embedded-elisp attr)) |
|
1900 |
((symbolp attr) |
|
1901 |
(let* ((attr-symbol (symbol-name attr)) |
|
1902 |
(key (and (string-match "%\\(:.*\\)" attr-symbol) |
|
1903 |
(intern (match-string 1 attr-symbol))))) |
|
1904 |
(or (plist-get org-store-link-plist key) |
|
1905 |
attr))) |
|
1906 |
(t attr))) |
|
1907 |
|
|
1908 |
(defun org-capture-inside-embedded-elisp-p () |
|
1909 |
"Non-nil if point is inside of embedded elisp %(sexp). |
|
1910 |
Assume sexps have been marked with |
|
1911 |
`org-capture-expand-embedded-elisp' beforehand." |
|
1912 |
(get-text-property (point) 'org-embedded-elisp)) |
|
1913 |
|
|
1914 |
;;;###autoload |
|
1915 |
(defun org-capture-import-remember-templates () |
|
1916 |
"Set `org-capture-templates' to be similar to `org-remember-templates'." |
|
1917 |
(interactive) |
|
1918 |
(when (and (yes-or-no-p |
|
1919 |
"Import old remember templates into org-capture-templates? ") |
|
1920 |
(yes-or-no-p |
|
1921 |
"Note that this will remove any templates currently defined in `org-capture-templates'. Do you still want to go ahead? ")) |
|
1922 |
(require 'org-remember) |
|
1923 |
(setq org-capture-templates |
|
1924 |
(mapcar |
|
1925 |
(lambda (entry) |
|
1926 |
(let ((desc (car entry)) |
|
1927 |
(key (char-to-string (nth 1 entry))) |
|
1928 |
(template (nth 2 entry)) |
|
1929 |
(file (or (nth 3 entry) org-default-notes-file)) |
|
1930 |
(position (or (nth 4 entry) org-remember-default-headline)) |
|
1931 |
(type 'entry) |
|
1932 |
(prepend org-reverse-note-order) |
|
1933 |
immediate target jump-to-captured) |
|
1934 |
(cond |
|
1935 |
((member position '(top bottom)) |
|
1936 |
(setq target (list 'file file) |
|
1937 |
prepend (eq position 'top))) |
|
1938 |
((eq position 'date-tree) |
|
1939 |
(setq target (list 'file+datetree file) |
|
1940 |
prepend nil)) |
|
1941 |
(t (setq target (list 'file+headline file position)))) |
|
1942 |
|
|
1943 |
(when (string-match "%!" template) |
|
1944 |
(setq template (replace-match "" t t template) |
|
1945 |
immediate t)) |
|
1946 |
|
|
1947 |
(when (string-match "%&" template) |
|
1948 |
(setq jump-to-captured t)) |
|
1949 |
|
|
1950 |
(append (list key desc type target template) |
|
1951 |
(if prepend '(:prepend t)) |
|
1952 |
(if immediate '(:immediate-finish t)) |
|
1953 |
(if jump-to-captured '(:jump-to-captured t))))) |
|
1954 |
|
|
1955 |
org-remember-templates)))) |
|
1956 |
|
|
1957 |
|
|
1958 |
(provide 'org-capture) |
|
1959 |
|
|
1960 |
;;; org-capture.el ends here |