Commit | Line | Data |
---|---|---|
20908596 CD |
1 | ;;; org-remember.el --- Fast note taking in Org-mode |
2 | ||
3 | ;; Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. | |
4 | ||
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
6 | ;; Keywords: outlines, hypermedia, calendar, wp | |
7 | ;; Homepage: http://orgmode.org | |
8 | ;; Version: 6.02b | |
9 | ;; | |
10 | ;; This file is part of GNU Emacs. | |
11 | ;; | |
b1fc2b50 | 12 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
20908596 | 13 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
14 | ;; the Free Software Foundation, either version 3 of the License, or |
15 | ;; (at your option) any later version. | |
20908596 CD |
16 | |
17 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | ;; GNU General Public License for more details. | |
21 | ||
22 | ;; You should have received a copy of the GNU General Public License | |
b1fc2b50 | 23 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
20908596 CD |
24 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
25 | ;; | |
26 | ;;; Commentary: | |
27 | ||
28 | ;; This file contains the system to take fast notes with Org-mode. | |
29 | ;; This system is used together with John Wiegleys `remember.el'. | |
30 | ||
31 | ;;; Code: | |
32 | ||
33 | (eval-when-compile | |
34 | (require 'cl)) | |
35 | (require 'org) | |
36 | ||
37 | (declare-function remember "remember" (&optional initial)) | |
38 | (declare-function remember-buffer-desc "remember" ()) | |
39 | (declare-function remember-finalize "remember" ()) | |
40 | (defvar remember-save-after-remembering) | |
41 | (defvar remember-data-file) | |
42 | (defvar remember-register) | |
43 | (defvar remember-buffer) | |
44 | (defvar remember-handler-functions) | |
45 | (defvar remember-annotation-functions) | |
46 | ||
47 | (defgroup org-remember nil | |
48 | "Options concerning interaction with remember.el." | |
49 | :tag "Org Remember" | |
50 | :group 'org) | |
51 | ||
52 | (defcustom org-remember-store-without-prompt t | |
53 | "Non-nil means, `C-c C-c' stores remember note without further promts. | |
54 | In this case, you need `C-u C-c C-c' to get the prompts for | |
55 | note file and headline. | |
56 | When this variable is nil, `C-c C-c' give you the prompts, and | |
57 | `C-u C-c C-c' trigger the fasttrack." | |
58 | :group 'org-remember | |
59 | :type 'boolean) | |
60 | ||
61 | (defcustom org-remember-interactive-interface 'refile | |
62 | "The interface to be used for interactive filing of remember notes. | |
63 | This is only used when the interactive mode for selecting a filing | |
64 | location is used (see the variable `org-remember-store-without-prompt'). | |
65 | Allowed vaues are: | |
66 | outline The interface shows an outline of the relevant file | |
67 | and the correct heading is found by moving through | |
68 | the outline or by searching with incremental search. | |
69 | outline-path-completion Headlines in the current buffer are offered via | |
70 | completion. | |
71 | refile Use the refile interface, and offer headlines, | |
72 | possibly from different buffers." | |
73 | :group 'org-remember | |
74 | :type '(choice | |
75 | (const :tag "Refile" refile) | |
76 | (const :tag "Outline" outline) | |
77 | (const :tag "Outline-path-completion" outline-path-completion))) | |
78 | ||
79 | (defcustom org-remember-default-headline "" | |
80 | "The headline that should be the default location in the notes file. | |
81 | When filing remember notes, the cursor will start at that position. | |
82 | You can set this on a per-template basis with the variable | |
83 | `org-remember-templates'." | |
84 | :group 'org-remember | |
85 | :type 'string) | |
86 | ||
87 | (defcustom org-remember-templates nil | |
88 | "Templates for the creation of remember buffers. | |
89 | When nil, just let remember make the buffer. | |
90 | When not nil, this is a list of 5-element lists. In each entry, the first | |
91 | element is the name of the template, which should be a single short word. | |
92 | The second element is a character, a unique key to select this template. | |
93 | The third element is the template. | |
94 | ||
95 | The fourth element is optional and can specify a destination file for | |
96 | remember items created with this template. The default file is given | |
97 | by `org-default-notes-file'. If the file name is not an absolute path, | |
98 | it will be interpreted relative to `org-directory'. | |
99 | ||
100 | An optional fifth element can specify the headline in that file that should | |
101 | be offered first when the user is asked to file the entry. The default | |
102 | headline is given in the variable `org-remember-default-headline'. | |
103 | ||
104 | An optional sixth element specifies the contexts in which the user can | |
105 | select the template. This element can be either a list of major modes | |
106 | or a function. `org-remember' will first check whether the function | |
107 | returns `t' or if we are in any of the listed major modes, and select | |
108 | the template accordingly. | |
109 | ||
110 | The template specifies the structure of the remember buffer. It should have | |
111 | a first line starting with a star, to act as the org-mode headline. | |
112 | Furthermore, the following %-escapes will be replaced with content: | |
113 | ||
114 | %^{prompt} Prompt the user for a string and replace this sequence with it. | |
115 | A default value and a completion table ca be specified like this: | |
116 | %^{prompt|default|completion2|completion3|...} | |
117 | %t time stamp, date only | |
118 | %T time stamp with date and time | |
119 | %u, %U like the above, but inactive time stamps | |
120 | %^t like %t, but prompt for date. Similarly %^T, %^u, %^U | |
121 | You may define a prompt like %^{Please specify birthday}t | |
122 | %n user name (taken from `user-full-name') | |
123 | %a annotation, normally the link created with org-store-link | |
124 | %i initial content, the region active. If %i is indented, | |
125 | the entire inserted text will be indented as well. | |
126 | %c content of the clipboard, or current kill ring head | |
127 | %^g prompt for tags, with completion on tags in target file | |
128 | %^G prompt for tags, with completion all tags in all agenda files | |
129 | %:keyword specific information for certain link types, see below | |
130 | %[pathname] insert the contents of the file given by `pathname' | |
131 | %(sexp) evaluate elisp `(sexp)' and replace with the result | |
132 | %! Store this note immediately after filling the template | |
133 | ||
134 | %? After completing the template, position cursor here. | |
135 | ||
136 | Apart from these general escapes, you can access information specific to the | |
137 | link type that is created. For example, calling `remember' in emails or gnus | |
138 | will record the author and the subject of the message, which you can access | |
139 | with %:author and %:subject, respectively. Here is a complete list of what | |
140 | is recorded for each link type. | |
141 | ||
142 | Link type | Available information | |
143 | -------------------+------------------------------------------------------ | |
144 | bbdb | %:type %:name %:company | |
145 | vm, wl, mh, rmail | %:type %:subject %:message-id | |
146 | | %:from %:fromname %:fromaddress | |
147 | | %:to %:toname %:toaddress | |
148 | | %:fromto (either \"to NAME\" or \"from NAME\") | |
149 | gnus | %:group, for messages also all email fields | |
150 | w3, w3m | %:type %:url | |
151 | info | %:type %:file %:node | |
152 | calendar | %:type %:date" | |
153 | :group 'org-remember | |
154 | :get (lambda (var) ; Make sure all entries have at least 5 elements | |
155 | (mapcar (lambda (x) | |
156 | (if (not (stringp (car x))) (setq x (cons "" x))) | |
157 | (cond ((= (length x) 4) (append x '(""))) | |
158 | ((= (length x) 3) (append x '("" ""))) | |
159 | (t x))) | |
160 | (default-value var))) | |
161 | :type '(repeat | |
162 | :tag "enabled" | |
163 | (list :value ("" ?a "\n" nil nil nil) | |
164 | (string :tag "Name") | |
165 | (character :tag "Selection Key") | |
166 | (string :tag "Template") | |
167 | (choice | |
168 | (file :tag "Destination file") | |
169 | (const :tag "Prompt for file" nil)) | |
170 | (choice | |
171 | (string :tag "Destination headline") | |
172 | (const :tag "Selection interface for heading")) | |
173 | (choice | |
174 | (const :tag "Use by default" nil) | |
175 | (const :tag "Use in all contexts" t) | |
176 | (repeat :tag "Use only if in major mode" | |
177 | (symbol :tag "Major mode")) | |
178 | (function :tag "Perform a check against function"))))) | |
179 | ||
180 | (defvar annotation) ; from remember.el, dynamically scoped in `remember-mode' | |
181 | (defvar initial) ; from remember.el, dynamically scoped in `remember-mode' | |
182 | ||
183 | ;;;###autoload | |
184 | (defun org-remember-insinuate () | |
185 | "Setup remember.el for use wiht Org-mode." | |
186 | (require 'remember) | |
187 | (setq remember-annotation-functions '(org-remember-annotation)) | |
188 | (setq remember-handler-functions '(org-remember-handler)) | |
189 | (add-hook 'remember-mode-hook 'org-remember-apply-template)) | |
190 | ||
191 | ;;;###autoload | |
192 | (defun org-remember-annotation () | |
193 | "Return a link to the current location as an annotation for remember.el. | |
194 | If you are using Org-mode files as target for data storage with | |
195 | remember.el, then the annotations should include a link compatible with the | |
196 | conventions in Org-mode. This function returns such a link." | |
197 | (org-store-link nil)) | |
198 | ||
199 | (defconst org-remember-help | |
200 | "Select a destination location for the note. | |
201 | UP/DOWN=headline TAB=cycle visibility [Q]uit RET/<left>/<right>=Store | |
202 | RET on headline -> Store as sublevel entry to current headline | |
203 | RET at beg-of-buf -> Append to file as level 2 headline | |
204 | <left>/<right> -> before/after current headline, same headings level") | |
205 | ||
206 | (defvar org-remember-previous-location nil) | |
207 | (defvar org-force-remember-template-char) ;; dynamically scoped | |
208 | ||
209 | ;; Save the major mode of the buffer we called remember from | |
210 | (defvar org-select-template-temp-major-mode nil) | |
211 | ||
212 | ;; Temporary store the buffer where remember was called from | |
213 | (defvar org-select-template-original-buffer nil) | |
214 | ||
215 | (defun org-select-remember-template (&optional use-char) | |
216 | (when org-remember-templates | |
217 | (let* ((pre-selected-templates | |
218 | (mapcar | |
219 | (lambda (tpl) | |
220 | (let ((ctxt (nth 5 tpl)) | |
221 | (mode org-select-template-temp-major-mode) | |
222 | (buf org-select-template-original-buffer)) | |
223 | (and (or (not ctxt) (eq ctxt t) | |
224 | (and (listp ctxt) (memq mode ctxt)) | |
225 | (and (functionp ctxt) | |
226 | (with-current-buffer buf | |
227 | ;; Protect the user-defined function from error | |
228 | (condition-case nil (funcall ctxt) (error nil))))) | |
229 | tpl))) | |
230 | org-remember-templates)) | |
231 | ;; If no template at this point, add the default templates: | |
232 | (pre-selected-templates1 | |
233 | (if (not (delq nil pre-selected-templates)) | |
234 | (mapcar (lambda(x) (if (not (nth 5 x)) x)) | |
235 | org-remember-templates) | |
236 | pre-selected-templates)) | |
237 | ;; Then unconditionnally add template for any contexts | |
238 | (pre-selected-templates2 | |
239 | (append (mapcar (lambda(x) (if (eq (nth 5 x) t) x)) | |
240 | org-remember-templates) | |
241 | (delq nil pre-selected-templates1))) | |
242 | (templates (mapcar (lambda (x) | |
243 | (if (stringp (car x)) | |
244 | (append (list (nth 1 x) (car x)) (cddr x)) | |
245 | (append (list (car x) "") (cdr x)))) | |
246 | (delq nil pre-selected-templates2))) | |
247 | (char (or use-char | |
248 | (cond | |
249 | ((= (length templates) 1) | |
250 | (caar templates)) | |
251 | ((and (boundp 'org-force-remember-template-char) | |
252 | org-force-remember-template-char) | |
253 | (if (stringp org-force-remember-template-char) | |
254 | (string-to-char org-force-remember-template-char) | |
255 | org-force-remember-template-char)) | |
256 | (t | |
257 | (message "Select template: %s" | |
258 | (mapconcat | |
259 | (lambda (x) | |
260 | (cond | |
261 | ((not (string-match "\\S-" (nth 1 x))) | |
262 | (format "[%c]" (car x))) | |
263 | ((equal (downcase (car x)) | |
264 | (downcase (aref (nth 1 x) 0))) | |
265 | (format "[%c]%s" (car x) | |
266 | (substring (nth 1 x) 1))) | |
267 | (t (format "[%c]%s" (car x) (nth 1 x))))) | |
268 | templates " ")) | |
269 | (let ((inhibit-quit t) (char0 (read-char-exclusive))) | |
270 | (when (equal char0 ?\C-g) | |
271 | (jump-to-register remember-register) | |
272 | (kill-buffer remember-buffer)) | |
273 | char0)))))) | |
274 | (cddr (assoc char templates))))) | |
275 | ||
276 | (defun org-get-x-clipboard (value) | |
277 | "Get the value of the x clibboard, in a way that also works with XEmacs." | |
278 | (if (eq window-system 'x) | |
279 | (let ((x (if org-xemacs-p | |
280 | (org-no-warnings (get-selection-no-error value)) | |
281 | (and (fboundp 'x-selection-value) | |
282 | (x-selection-value value))))) | |
283 | (and (> (length x) 0) (set-text-properties 0 (length x) nil x) x)))) | |
284 | ||
285 | ;;;###autoload | |
286 | (defun org-remember-apply-template (&optional use-char skip-interactive) | |
287 | "Initialize *remember* buffer with template, invoke `org-mode'. | |
288 | This function should be placed into `remember-mode-hook' and in fact requires | |
289 | to be run from that hook to function properly." | |
290 | (if org-remember-templates | |
291 | (let* ((entry (org-select-remember-template use-char)) | |
292 | (tpl (car entry)) | |
293 | (plist-p (if org-store-link-plist t nil)) | |
294 | (file (if (and (nth 1 entry) (stringp (nth 1 entry)) | |
295 | (string-match "\\S-" (nth 1 entry))) | |
296 | (nth 1 entry) | |
297 | org-default-notes-file)) | |
298 | (headline (nth 2 entry)) | |
299 | (v-c (and (> (length kill-ring) 0) (current-kill 0))) | |
300 | (v-x (or (org-get-x-clipboard 'PRIMARY) | |
301 | (org-get-x-clipboard 'CLIPBOARD) | |
302 | (org-get-x-clipboard 'SECONDARY))) | |
303 | (v-t (format-time-string (car org-time-stamp-formats) (org-current-time))) | |
304 | (v-T (format-time-string (cdr org-time-stamp-formats) (org-current-time))) | |
305 | (v-u (concat "[" (substring v-t 1 -1) "]")) | |
306 | (v-U (concat "[" (substring v-T 1 -1) "]")) | |
307 | ;; `initial' and `annotation' are bound in `remember' | |
308 | (v-i (if (boundp 'initial) initial)) | |
309 | (v-a (if (and (boundp 'annotation) annotation) | |
310 | (if (equal annotation "[[]]") "" annotation) | |
311 | "")) | |
312 | (clipboards (remove nil (list v-i | |
313 | (org-get-x-clipboard 'PRIMARY) | |
314 | (org-get-x-clipboard 'CLIPBOARD) | |
315 | (org-get-x-clipboard 'SECONDARY) | |
316 | v-c))) | |
317 | (v-A (if (and v-a | |
318 | (string-match "\\[\\(\\[.*?\\]\\)\\(\\[.*?\\]\\)?\\]" v-a)) | |
319 | (replace-match "[\\1[%^{Link description}]]" nil nil v-a) | |
320 | v-a)) | |
321 | (v-n user-full-name) | |
322 | (org-startup-folded nil) | |
323 | org-time-was-given org-end-time-was-given x | |
324 | prompt completions char time pos default histvar) | |
325 | (when (and file (not (file-name-absolute-p file))) | |
326 | (setq file (expand-file-name file org-directory))) | |
327 | (setq org-store-link-plist | |
328 | (append (list :annotation v-a :initial v-i) | |
329 | org-store-link-plist)) | |
330 | (unless tpl (setq tpl "") (message "No template") (ding) (sit-for 1)) | |
331 | (erase-buffer) | |
332 | (insert (substitute-command-keys | |
333 | (format | |
334 | "## Filing location: Select interactively, default, or last used: | |
335 | ## %s to select file and header location interactively. | |
336 | ## %s \"%s\" -> \"* %s\" | |
337 | ## C-u C-u C-c C-c \"%s\" -> \"* %s\" | |
338 | ## To switch templates, use `\\[org-remember]'. To abort use `C-c C-k'.\n\n" | |
339 | (if org-remember-store-without-prompt " C-u C-c C-c" " C-c C-c") | |
340 | (if org-remember-store-without-prompt " C-c C-c" " C-u C-c C-c") | |
341 | (abbreviate-file-name (or file org-default-notes-file)) | |
342 | (or headline "") | |
343 | (or (car org-remember-previous-location) "???") | |
344 | (or (cdr org-remember-previous-location) "???")))) | |
345 | (insert tpl) (goto-char (point-min)) | |
346 | ;; Simple %-escapes | |
347 | (while (re-search-forward "%\\([tTuUaiAcx]\\)" nil t) | |
348 | (when (and initial (equal (match-string 0) "%i")) | |
349 | (save-match-data | |
350 | (let* ((lead (buffer-substring | |
351 | (point-at-bol) (match-beginning 0)))) | |
352 | (setq v-i (mapconcat 'identity | |
353 | (org-split-string initial "\n") | |
354 | (concat "\n" lead)))))) | |
355 | (replace-match | |
356 | (or (eval (intern (concat "v-" (match-string 1)))) "") | |
357 | t t)) | |
358 | ||
359 | ;; %[] Insert contents of a file. | |
360 | (goto-char (point-min)) | |
361 | (while (re-search-forward "%\\[\\(.+\\)\\]" nil t) | |
362 | (let ((start (match-beginning 0)) | |
363 | (end (match-end 0)) | |
364 | (filename (expand-file-name (match-string 1)))) | |
365 | (goto-char start) | |
366 | (delete-region start end) | |
367 | (condition-case error | |
368 | (insert-file-contents filename) | |
369 | (error (insert (format "%%![Couldn't insert %s: %s]" | |
370 | filename error)))))) | |
371 | ;; %() embedded elisp | |
372 | (goto-char (point-min)) | |
373 | (while (re-search-forward "%\\((.+)\\)" nil t) | |
374 | (goto-char (match-beginning 0)) | |
375 | (let ((template-start (point))) | |
376 | (forward-char 1) | |
377 | (let ((result | |
378 | (condition-case error | |
379 | (eval (read (current-buffer))) | |
380 | (error (format "%%![Error: %s]" error))))) | |
381 | (delete-region template-start (point)) | |
382 | (insert result)))) | |
383 | ||
384 | ;; From the property list | |
385 | (when plist-p | |
386 | (goto-char (point-min)) | |
387 | (while (re-search-forward "%\\(:[-a-zA-Z]+\\)" nil t) | |
388 | (and (setq x (or (plist-get org-store-link-plist | |
389 | (intern (match-string 1))) "")) | |
390 | (replace-match x t t)))) | |
391 | ||
392 | ;; Turn on org-mode in the remember buffer, set local variables | |
393 | (org-mode) | |
394 | (org-set-local 'org-finish-function 'org-remember-finalize) | |
395 | (if (and file (string-match "\\S-" file) (not (file-directory-p file))) | |
396 | (org-set-local 'org-default-notes-file file)) | |
397 | (if (and headline (stringp headline) (string-match "\\S-" headline)) | |
398 | (org-set-local 'org-remember-default-headline headline)) | |
399 | ;; Interactive template entries | |
400 | (goto-char (point-min)) | |
401 | (while (re-search-forward "%^\\({\\([^}]*\\)}\\)?\\([gGuUtTCL]\\)?" nil t) | |
402 | (setq char (if (match-end 3) (match-string 3)) | |
403 | prompt (if (match-end 2) (match-string 2))) | |
404 | (goto-char (match-beginning 0)) | |
405 | (replace-match "") | |
406 | (setq completions nil default nil) | |
407 | (when prompt | |
408 | (setq completions (org-split-string prompt "|") | |
409 | prompt (pop completions) | |
410 | default (car completions) | |
411 | histvar (intern (concat | |
412 | "org-remember-template-prompt-history::" | |
413 | (or prompt ""))) | |
414 | completions (mapcar 'list completions))) | |
415 | (cond | |
416 | ((member char '("G" "g")) | |
417 | (let* ((org-last-tags-completion-table | |
418 | (org-global-tags-completion-table | |
419 | (if (equal char "G") (org-agenda-files) (and file (list file))))) | |
420 | (org-add-colon-after-tag-completion t) | |
421 | (ins (completing-read | |
422 | (if prompt (concat prompt ": ") "Tags: ") | |
423 | 'org-tags-completion-function nil nil nil | |
424 | 'org-tags-history))) | |
425 | (setq ins (mapconcat 'identity | |
426 | (org-split-string ins (org-re "[^[:alnum:]_@]+")) | |
427 | ":")) | |
428 | (when (string-match "\\S-" ins) | |
429 | (or (equal (char-before) ?:) (insert ":")) | |
430 | (insert ins) | |
431 | (or (equal (char-after) ?:) (insert ":"))))) | |
432 | ((equal char "C") | |
433 | (cond ((= (length clipboards) 1) (insert (car clipboards))) | |
434 | ((> (length clipboards) 1) | |
435 | (insert (read-string "Clipboard/kill value: " | |
436 | (car clipboards) '(clipboards . 1) | |
437 | (car clipboards)))))) | |
438 | ((equal char "L") | |
439 | (cond ((= (length clipboards) 1) | |
440 | (org-insert-link 0 (car clipboards))) | |
441 | ((> (length clipboards) 1) | |
442 | (org-insert-link 0 (read-string "Clipboard/kill value: " | |
443 | (car clipboards) | |
444 | '(clipboards . 1) | |
445 | (car clipboards)))))) | |
446 | (char | |
447 | (setq org-time-was-given (equal (upcase char) char)) | |
448 | (setq time (org-read-date (equal (upcase char) "U") t nil | |
449 | prompt)) | |
450 | (org-insert-time-stamp time org-time-was-given | |
451 | (member char '("u" "U")) | |
452 | nil nil (list org-end-time-was-given))) | |
453 | (t | |
454 | (insert (org-completing-read | |
455 | (concat (if prompt prompt "Enter string") | |
456 | (if default (concat " [" default "]")) | |
457 | ": ") | |
458 | completions nil nil nil histvar default))))) | |
459 | (goto-char (point-min)) | |
460 | (if (re-search-forward "%\\?" nil t) | |
461 | (replace-match "") | |
462 | (and (re-search-forward "^[^#\n]" nil t) (backward-char 1)))) | |
463 | (org-mode) | |
464 | (org-set-local 'org-finish-function 'org-remember-finalize)) | |
465 | (when (save-excursion | |
466 | (goto-char (point-min)) | |
467 | (re-search-forward "%!" nil t)) | |
468 | (replace-match "") | |
469 | (add-hook 'post-command-hook 'org-remember-finish-immediately 'append))) | |
470 | ||
471 | (defun org-remember-finish-immediately () | |
472 | "File remember note immediately. | |
473 | This should be run in `post-command-hook' and will remove itself | |
474 | from that hook." | |
475 | (remove-hook 'post-command-hook 'org-remember-finish-immediately) | |
476 | (when org-finish-function | |
477 | (funcall org-finish-function))) | |
478 | ||
479 | (defvar org-clock-marker) ; Defined below | |
480 | (defun org-remember-finalize () | |
481 | "Finalize the remember process." | |
482 | (unless (fboundp 'remember-finalize) | |
483 | (defalias 'remember-finalize 'remember-buffer)) | |
484 | (when (and org-clock-marker | |
485 | (equal (marker-buffer org-clock-marker) (current-buffer))) | |
486 | ;; FIXME: test this, this is w/o notetaking! | |
487 | (let (org-log-note-clock-out) (org-clock-out))) | |
488 | (when buffer-file-name | |
489 | (save-buffer) | |
490 | (setq buffer-file-name nil)) | |
491 | (remember-finalize)) | |
492 | ||
493 | ;;;###autoload | |
494 | (defun org-remember (&optional goto org-force-remember-template-char) | |
495 | "Call `remember'. If this is already a remember buffer, re-apply template. | |
496 | If there is an active region, make sure remember uses it as initial content | |
497 | of the remember buffer. | |
498 | ||
499 | When called interactively with a `C-u' prefix argument GOTO, don't remember | |
500 | anything, just go to the file/headline where the selected template usually | |
501 | stores its notes. With a double prefix arg `C-u C-u', go to the last | |
502 | note stored by remember. | |
503 | ||
504 | Lisp programs can set ORG-FORCE-REMEMBER-TEMPLATE-CHAR to a character | |
505 | associated with a template in `org-remember-templates'." | |
506 | (interactive "P") | |
507 | (cond | |
508 | ((equal goto '(4)) (org-go-to-remember-target)) | |
509 | ((equal goto '(16)) (org-remember-goto-last-stored)) | |
510 | (t | |
511 | ;; set temporary variables that will be needed in | |
512 | ;; `org-select-remember-template' | |
513 | (setq org-select-template-temp-major-mode major-mode) | |
514 | (setq org-select-template-original-buffer (current-buffer)) | |
515 | (if (eq org-finish-function 'org-remember-finalize) | |
516 | (progn | |
517 | (when (< (length org-remember-templates) 2) | |
518 | (error "No other template available")) | |
519 | (erase-buffer) | |
520 | (let ((annotation (plist-get org-store-link-plist :annotation)) | |
521 | (initial (plist-get org-store-link-plist :initial))) | |
522 | (org-remember-apply-template)) | |
523 | (message "Press C-c C-c to remember data")) | |
524 | (if (org-region-active-p) | |
525 | (org-do-remember (buffer-substring (point) (mark))) | |
526 | (org-do-remember)))))) | |
527 | ||
528 | (defun org-remember-goto-last-stored () | |
529 | "Go to the location where the last remember note was stored." | |
530 | (interactive) | |
531 | (bookmark-jump "org-remember-last-stored") | |
532 | (message "This is the last note stored by remember")) | |
533 | ||
534 | (defun org-go-to-remember-target (&optional template-key) | |
535 | "Go to the target location of a remember template. | |
536 | The user is queried for the template." | |
537 | (interactive) | |
538 | (let* (org-select-template-temp-major-mode | |
539 | (entry (org-select-remember-template template-key)) | |
540 | (file (nth 1 entry)) | |
541 | (heading (nth 2 entry)) | |
542 | visiting) | |
543 | (unless (and file (stringp file) (string-match "\\S-" file)) | |
544 | (setq file org-default-notes-file)) | |
545 | (when (and file (not (file-name-absolute-p file))) | |
546 | (setq file (expand-file-name file org-directory))) | |
547 | (unless (and heading (stringp heading) (string-match "\\S-" heading)) | |
548 | (setq heading org-remember-default-headline)) | |
549 | (setq visiting (org-find-base-buffer-visiting file)) | |
550 | (if (not visiting) (find-file-noselect file)) | |
551 | (switch-to-buffer (or visiting (get-file-buffer file))) | |
552 | (widen) | |
553 | (goto-char (point-min)) | |
554 | (if (re-search-forward | |
555 | (concat "^\\*+[ \t]+" (regexp-quote heading) | |
556 | (org-re "\\([ \t]+:[[:alnum:]@_:]*\\)?[ \t]*$")) | |
557 | nil t) | |
558 | (goto-char (match-beginning 0)) | |
559 | (error "Target headline not found: %s" heading)))) | |
560 | ||
561 | ;;;###autoload | |
562 | (defun org-remember-handler () | |
563 | "Store stuff from remember.el into an org file. | |
564 | First prompts for an org file. If the user just presses return, the value | |
565 | of `org-default-notes-file' is used. | |
566 | Then the command offers the headings tree of the selected file in order to | |
567 | file the text at a specific location. | |
568 | You can either immediately press RET to get the note appended to the | |
569 | file, or you can use vertical cursor motion and visibility cycling (TAB) to | |
570 | find a better place. Then press RET or <left> or <right> in insert the note. | |
571 | ||
572 | Key Cursor position Note gets inserted | |
573 | ----------------------------------------------------------------------------- | |
574 | RET buffer-start as level 1 heading at end of file | |
575 | RET on headline as sublevel of the heading at cursor | |
576 | RET no heading at cursor position, level taken from context. | |
577 | Or use prefix arg to specify level manually. | |
578 | <left> on headline as same level, before current heading | |
579 | <right> on headline as same level, after current heading | |
580 | ||
581 | So the fastest way to store the note is to press RET RET to append it to | |
582 | the default file. This way your current train of thought is not | |
583 | interrupted, in accordance with the principles of remember.el. | |
584 | You can also get the fast execution without prompting by using | |
585 | C-u C-c C-c to exit the remember buffer. See also the variable | |
586 | `org-remember-store-without-prompt'. | |
587 | ||
588 | Before being stored away, the function ensures that the text has a | |
589 | headline, i.e. a first line that starts with a \"*\". If not, a headline | |
590 | is constructed from the current date and some additional data. | |
591 | ||
592 | If the variable `org-adapt-indentation' is non-nil, the entire text is | |
593 | also indented so that it starts in the same column as the headline | |
594 | \(i.e. after the stars). | |
595 | ||
596 | See also the variable `org-reverse-note-order'." | |
597 | (goto-char (point-min)) | |
598 | (while (looking-at "^[ \t]*\n\\|^##.*\n") | |
599 | (replace-match "")) | |
600 | (goto-char (point-max)) | |
601 | (beginning-of-line 1) | |
602 | (while (looking-at "[ \t]*$\\|##.*") | |
603 | (delete-region (1- (point)) (point-max)) | |
604 | (beginning-of-line 1)) | |
605 | (catch 'quit | |
606 | (if org-note-abort (throw 'quit nil)) | |
607 | (let* ((txt (buffer-substring (point-min) (point-max))) | |
608 | (fastp (org-xor (equal current-prefix-arg '(4)) | |
609 | org-remember-store-without-prompt)) | |
610 | (file (cond | |
611 | (fastp org-default-notes-file) | |
612 | ((and (eq org-remember-interactive-interface 'refile) | |
613 | org-refile-targets) | |
614 | org-default-notes-file) | |
615 | ((not (and (equal current-prefix-arg '(16)) | |
616 | org-remember-previous-location)) | |
617 | (org-get-org-file)))) | |
618 | (heading org-remember-default-headline) | |
619 | (visiting (and file (org-find-base-buffer-visiting file))) | |
620 | (org-startup-folded nil) | |
621 | (org-startup-align-all-tables nil) | |
622 | (org-goto-start-pos 1) | |
623 | spos exitcmd level indent reversed) | |
624 | (if (and (equal current-prefix-arg '(16)) org-remember-previous-location) | |
625 | (setq file (car org-remember-previous-location) | |
626 | heading (cdr org-remember-previous-location) | |
627 | fastp t)) | |
628 | (setq current-prefix-arg nil) | |
629 | (if (string-match "[ \t\n]+\\'" txt) | |
630 | (setq txt (replace-match "" t t txt))) | |
631 | ;; Modify text so that it becomes a nice subtree which can be inserted | |
632 | ;; into an org tree. | |
633 | (let* ((lines (split-string txt "\n")) | |
634 | first) | |
635 | (setq first (car lines) lines (cdr lines)) | |
636 | (if (string-match "^\\*+ " first) | |
637 | ;; Is already a headline | |
638 | (setq indent nil) | |
639 | ;; We need to add a headline: Use time and first buffer line | |
640 | (setq lines (cons first lines) | |
641 | first (concat "* " (current-time-string) | |
642 | " (" (remember-buffer-desc) ")") | |
643 | indent " ")) | |
644 | (if (and org-adapt-indentation indent) | |
645 | (setq lines (mapcar | |
646 | (lambda (x) | |
647 | (if (string-match "\\S-" x) | |
648 | (concat indent x) x)) | |
649 | lines))) | |
650 | (setq txt (concat first "\n" | |
651 | (mapconcat 'identity lines "\n")))) | |
652 | (if (string-match "\n[ \t]*\n[ \t\n]*\\'" txt) | |
653 | (setq txt (replace-match "\n\n" t t txt)) | |
654 | (if (string-match "[ \t\n]*\\'" txt) | |
655 | (setq txt (replace-match "\n" t t txt)))) | |
656 | ;; Put the modified text back into the remember buffer, for refile. | |
657 | (erase-buffer) | |
658 | (insert txt) | |
659 | (goto-char (point-min)) | |
660 | (when (and (eq org-remember-interactive-interface 'refile) | |
661 | (not fastp)) | |
662 | (org-refile nil (or visiting (find-file-noselect file))) | |
663 | (throw 'quit t)) | |
664 | ;; Find the file | |
665 | (if (not visiting) (find-file-noselect file)) | |
666 | (with-current-buffer (or visiting (get-file-buffer file)) | |
667 | (unless (org-mode-p) | |
668 | (error "Target files for remember notes must be in Org-mode")) | |
669 | (save-excursion | |
670 | (save-restriction | |
671 | (widen) | |
672 | (and (goto-char (point-min)) | |
673 | (not (re-search-forward "^\\* " nil t)) | |
674 | (insert "\n* " (or heading "Notes") "\n")) | |
675 | (setq reversed (org-notes-order-reversed-p)) | |
676 | ||
677 | ;; Find the default location | |
678 | (when (and heading (stringp heading) (string-match "\\S-" heading)) | |
679 | (goto-char (point-min)) | |
680 | (if (re-search-forward | |
681 | (concat "^\\*+[ \t]+" (regexp-quote heading) | |
682 | (org-re "\\([ \t]+:[[:alnum:]@_:]*\\)?[ \t]*$")) | |
683 | nil t) | |
684 | (setq org-goto-start-pos (match-beginning 0)) | |
685 | (when fastp | |
686 | (goto-char (point-max)) | |
687 | (unless (bolp) (newline)) | |
688 | (insert "* " heading "\n") | |
689 | (setq org-goto-start-pos (point-at-bol 0))))) | |
690 | ||
691 | ;; Ask the User for a location, using the appropriate interface | |
692 | (cond | |
693 | (fastp (setq spos org-goto-start-pos | |
694 | exitcmd 'return)) | |
695 | ((eq org-remember-interactive-interface 'outline) | |
696 | (setq spos (org-get-location (current-buffer) | |
697 | org-remember-help) | |
698 | exitcmd (cdr spos) | |
699 | spos (car spos))) | |
700 | ((eq org-remember-interactive-interface 'outline-path-completion) | |
701 | (let ((org-refile-targets '((nil . (:maxlevel . 10)))) | |
702 | (org-refile-use-outline-path t)) | |
703 | (setq spos (org-refile-get-location "Heading: ") | |
704 | exitcmd 'return | |
705 | spos (nth 3 spos)))) | |
706 | (t (error "This should not happen"))) | |
707 | (if (not spos) (throw 'quit nil)) ; return nil to show we did | |
708 | ; not handle this note | |
709 | (goto-char spos) | |
710 | (cond ((org-on-heading-p t) | |
711 | (org-back-to-heading t) | |
712 | (setq level (funcall outline-level)) | |
713 | (cond | |
714 | ((eq exitcmd 'return) | |
715 | ;; sublevel of current | |
716 | (setq org-remember-previous-location | |
717 | (cons (abbreviate-file-name file) | |
718 | (org-get-heading 'notags))) | |
719 | (if reversed | |
720 | (outline-next-heading) | |
721 | (org-end-of-subtree t) | |
722 | (if (not (bolp)) | |
723 | (if (looking-at "[ \t]*\n") | |
724 | (beginning-of-line 2) | |
725 | (end-of-line 1) | |
726 | (insert "\n")))) | |
727 | (bookmark-set "org-remember-last-stored") | |
728 | (org-paste-subtree (org-get-valid-level level 1) txt)) | |
729 | ((eq exitcmd 'left) | |
730 | ;; before current | |
731 | (bookmark-set "org-remember-last-stored") | |
732 | (org-paste-subtree level txt)) | |
733 | ((eq exitcmd 'right) | |
734 | ;; after current | |
735 | (org-end-of-subtree t) | |
736 | (bookmark-set "org-remember-last-stored") | |
737 | (org-paste-subtree level txt)) | |
738 | (t (error "This should not happen")))) | |
739 | ||
740 | ((and (bobp) (not reversed)) | |
741 | ;; Put it at the end, one level below level 1 | |
742 | (save-restriction | |
743 | (widen) | |
744 | (goto-char (point-max)) | |
745 | (if (not (bolp)) (newline)) | |
746 | (bookmark-set "org-remember-last-stored") | |
747 | (org-paste-subtree (org-get-valid-level 1 1) txt))) | |
748 | ||
749 | ((and (bobp) reversed) | |
750 | ;; Put it at the start, as level 1 | |
751 | (save-restriction | |
752 | (widen) | |
753 | (goto-char (point-min)) | |
754 | (re-search-forward "^\\*+ " nil t) | |
755 | (beginning-of-line 1) | |
756 | (bookmark-set "org-remember-last-stored") | |
757 | (org-paste-subtree 1 txt))) | |
758 | (t | |
759 | ;; Put it right there, with automatic level determined by | |
760 | ;; org-paste-subtree or from prefix arg | |
761 | (bookmark-set "org-remember-last-stored") | |
762 | (org-paste-subtree | |
763 | (if (numberp current-prefix-arg) current-prefix-arg) | |
764 | txt))) | |
765 | (when remember-save-after-remembering | |
766 | (save-buffer) | |
767 | (if (not visiting) (kill-buffer (current-buffer))))))))) | |
768 | ||
769 | t) ;; return t to indicate that we took care of this note. | |
770 | ||
771 | ||
772 | (defun org-do-remember (&optional initial) | |
773 | "Call remember." | |
774 | (remember initial)) | |
775 | ||
776 | (provide 'org-remember) | |
777 | ||
778 | ;;; org-remember.el ends here | |
779 | ||
88ac7b50 | 780 | ;; arch-tag: 497f30d0-4bc3-4097-8622-2d27ac5f2698 |