From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Ryan C. Thompson" Subject: Org-remember-handler fix for empty remember buffer Date: Thu, 04 Jun 2009 13:45:11 -0400 Message-ID: <4A280827.2070705@virginia.edu> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MCNT6-0002Ca-NM for emacs-orgmode@gnu.org; Thu, 04 Jun 2009 20:39:36 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MCNT2-00029v-P7 for emacs-orgmode@gnu.org; Thu, 04 Jun 2009 20:39:36 -0400 Received: from [199.232.76.173] (port=48109 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MCMRy-0007fT-Uw for emacs-orgmode@gnu.org; Thu, 04 Jun 2009 19:34:23 -0400 Received: from mx20.gnu.org ([199.232.41.8]:33737) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1MCH0A-000723-2B for emacs-orgmode@gnu.org; Thu, 04 Jun 2009 13:45:18 -0400 Received: from fork2.mail.virginia.edu ([128.143.2.192]) by mx20.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MCH08-0000UK-B1 for emacs-orgmode@gnu.org; Thu, 04 Jun 2009 13:45:16 -0400 Received: from localhost (localhost [127.0.0.1]) by fork2.mail.virginia.edu (Postfix) with ESMTP id A780D1BFBC for ; Thu, 4 Jun 2009 13:45:14 -0400 (EDT) Received: from fork2.mail.virginia.edu ([127.0.0.1]) by localhost (fork2.mail.virginia.edu [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 13886-02 for ; Thu, 4 Jun 2009 13:45:14 -0400 (EDT) Received: from mail-qy0-f185.google.com (mail-qy0-f185.google.com [209.85.221.185]) by fork2.mail.virginia.edu (Postfix) with ESMTP id 6A51A1BFBB for ; Thu, 4 Jun 2009 13:45:14 -0400 (EDT) Received: by mail-qy0-f185.google.com with SMTP id 15so1456448qyk.8 for ; Thu, 04 Jun 2009 10:45:14 -0700 (PDT) List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org Hi, I ran into a problem with org's remember functionality, and found a fix for it. The problem is that if you attempt to either abort or remember an empty buffer (that is, a buffer containing only whitespace and comments), then org-mode hits an error and fails to do either, leaving the buffer annoyingly open. The bug is in the loop that deletes trailing comments and whitespace. If the buffer is empty, then this loop reaches the first line, and attempts to delete a region starting at position zero, which causes the error. Here is the modified function definition that checks for this condition. As a bonus, the function also auto-aborts instead of saving if the buffer is empty. Thank you, Ryan Thompson ;; Fix for empty remember buffer. (defun org-remember-handler () "Store stuff from remember.el into an org file. When the template has specified a file and a headline, the entry is filed there, or in the location defined by `org-default-notes-file' and `org-remember-default-headline'. If no defaults have been defined, or if the current prefix argument is 1 (so you must use `C-1 C-c C-c' to exit remember), an interactive process is used to select the target location. When the prefix is 0 (i.e. when remember is exited with `C-0 C-c C-c'), the entry is filed to the same location as the previous note. When the prefix is 2 (i.e. when remember is exited with `C-2 C-c C-c'), the entry is filed as a subentry of the entry where the clock is currently running. When `C-u' has been used as prefix argument, the note is stored and emacs moves point to the new location of the note, so that editing can be continued there (similar to inserting \"%&\" into the template). Before storing the note, the function ensures that the text has an org-mode-style headline, i.e. a first line that starts with a \"*\". If not, a headline is constructed from the current date and some additional data. If the variable `org-adapt-indentation' is non-nil, the entire text is also indented so that it starts in the same column as the headline \(i.e. after the stars). See also the variable `org-reverse-note-order'." (interactive) (when (and (equal current-prefix-arg 2) (not (marker-buffer org-clock-marker))) (error "No running clock")) (when (org-bound-and-true-p org-jump-to-target-location) (let* ((end (min (point-max) (1+ (point)))) (beg (point))) (if (= end beg) (setq beg (1- beg))) (put-text-property beg end 'org-position-cursor t))) (goto-char (point-min)) (while (looking-at "^[ \t]*\n\\|^##.*\n") (replace-match "")) (goto-char (point-max)) (beginning-of-line 1) (catch 'quit (while (looking-at "[ \t]*$\\|##.*") ;; Abort on empty buffer (if (= (point) (point-min)) (throw 'quit nil) (previous-line))) (delete-region (point) (point-max)) (backward-delete-char 1) (if org-note-abort (throw 'quit nil)) ;; Also abort on an empty (i.e. whitespace-only) buffer ;; (if (not (string-match "[^[:space:]]" (buffer-substring-no-properties (point-min) (point-max)))) (return t)) (let* ((visitp (org-bound-and-true-p org-jump-to-target-location)) (previousp (and (member current-prefix-arg '((16) 0)) org-remember-previous-location)) (clockp (equal current-prefix-arg 2)) (fastp (org-xor (equal current-prefix-arg 1) org-remember-store-without-prompt)) (file (cond (fastp org-default-notes-file) ((and (eq org-remember-interactive-interface 'refile) org-refile-targets) org-default-notes-file) ((not previousp) (org-get-org-file)))) (heading org-remember-default-headline) (visiting (and file (org-find-base-buffer-visiting file))) (org-startup-folded nil) (org-startup-align-all-tables nil) (org-goto-start-pos 1) spos exitcmd level reversed txt) (when (equal current-prefix-arg '(4)) (setq visitp t)) (when previousp (setq file (car org-remember-previous-location) visiting (and file (org-find-base-buffer-visiting file)) heading (cdr org-remember-previous-location) fastp t)) (when clockp (setq file (buffer-file-name (marker-buffer org-clock-marker)) visiting (and file (org-find-base-buffer-visiting file)) heading org-clock-heading-for-remember fastp t)) (setq current-prefix-arg nil) ;; Modify text so that it becomes a nice subtree which can be inserted ;; into an org tree. (goto-char (point-min)) (if (re-search-forward "[ \t\n]+\\'" nil t) ;; remove empty lines at end (replace-match "")) (goto-char (point-min)) (unless (looking-at org-outline-regexp) ;; add a headline (insert (concat "* " (current-time-string) " (" (remember-buffer-desc) ")\n")) (backward-char 1) (when org-adapt-indentation (while (re-search-forward "^" nil t) (insert " ")))) (goto-char (point-min)) (if (re-search-forward "\n[ \t]*\n[ \t\n]*\\'" nil t) (replace-match "\n\n") (if (re-search-forward "[ \t\n]*\\'") (replace-match "\n"))) (goto-char (point-min)) (setq txt (buffer-string)) (org-save-markers-in-region (point-min) (point-max)) (when (and (eq org-remember-interactive-interface 'refile) (not fastp)) (org-refile nil (or visiting (find-file-noselect file))) (and visitp (run-with-idle-timer 0.01 nil 'org-remember-visit-immediately)) (save-excursion (bookmark-jump "org-refile-last-stored") (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point))) (throw 'quit t)) ;; Find the file (with-current-buffer (or visiting (find-file-noselect file)) (unless (org-mode-p) (error "Target files for remember notes must be in Org-mode")) (save-excursion (save-restriction (widen) (and (goto-char (point-min)) (not (re-search-forward "^\\* " nil t)) (insert "\n* " (or (and (stringp heading) heading) "Notes") "\n")) (setq reversed (org-notes-order-reversed-p)) ;; Find the default location (when heading (cond ((eq heading 'top) (goto-char (point-min)) (or (looking-at org-outline-regexp) (re-search-forward org-outline-regexp nil t)) (setq org-goto-start-pos (or (match-beginning 0) (point-min)))) ((eq heading 'bottom) (goto-char (point-max)) (or (bolp) (newline)) (setq org-goto-start-pos (point))) ((and (stringp heading) (string-match "\\S-" heading)) (goto-char (point-min)) (if (re-search-forward (concat "^\\*+[ \t]+" (regexp-quote heading) (org-re "\\([ \t]+:[[:alnum:]@_:]*\\)?[ \t]*$")) nil t) (setq org-goto-start-pos (match-beginning 0)) (when fastp (goto-char (point-max)) (unless (bolp) (newline)) (insert "* " heading "\n") (setq org-goto-start-pos (point-at-bol 0))))) (t (goto-char (point-min)) (setq org-goto-start-pos (point) heading 'top)))) ;; Ask the User for a location, using the appropriate interface (cond ((and fastp (memq heading '(top bottom))) (setq spos org-goto-start-pos exitcmd (if (eq heading 'top) 'left nil))) (fastp (setq spos org-goto-start-pos exitcmd 'return)) ((eq org-remember-interactive-interface 'outline) (setq spos (org-get-location (current-buffer) org-remember-help) exitcmd (cdr spos) spos (car spos))) ((eq org-remember-interactive-interface 'outline-path-completion) (let ((org-refile-targets '((nil . (:maxlevel . 10)))) (org-refile-use-outline-path t)) (setq spos (org-refile-get-location "Heading: ") exitcmd 'return spos (nth 3 spos)))) (t (error "This should not happen"))) (if (not spos) (throw 'quit nil)) ; return nil to show we did ; not handle this note (and visitp (run-with-idle-timer 0.01 nil 'org-remember-visit-immediately)) (goto-char spos) (cond ((org-on-heading-p t) (org-back-to-heading t) (setq level (funcall outline-level)) (cond ((eq exitcmd 'return) ;; sublevel of current (setq org-remember-previous-location (cons (abbreviate-file-name file) (org-get-heading 'notags))) (if reversed (outline-next-heading) (org-end-of-subtree t) (if (not (bolp)) (if (looking-at "[ \t]*\n") (beginning-of-line 2) (end-of-line 1) (insert "\n")))) (org-paste-subtree (org-get-valid-level level 1) txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point))) ((eq exitcmd 'left) ;; before current (org-paste-subtree level txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point))) ((eq exitcmd 'right) ;; after current (org-end-of-subtree t) (org-paste-subtree level txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point))) (t (error "This should not happen")))) ((eq heading 'bottom) (org-paste-subtree 1 txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point))) ((and (bobp) (not reversed)) ;; Put it at the end, one level below level 1 (save-restriction (widen) (goto-char (point-max)) (if (not (bolp)) (newline)) (org-paste-subtree (org-get-valid-level 1 1) txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point)))) ((and (bobp) reversed) ;; Put it at the start, as level 1 (save-restriction (widen) (goto-char (point-min)) (re-search-forward "^\\*+ " nil t) (beginning-of-line 1) (org-paste-subtree 1 txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point)))) (t ;; Put it right there, with automatic level determined by ;; org-paste-subtree or from prefix arg (org-paste-subtree (if (numberp current-prefix-arg) current-prefix-arg) txt) (and org-auto-align-tags (org-set-tags nil t)) (bookmark-set "org-remember-last-stored") (move-marker org-remember-last-stored-marker (point)))) (when remember-save-after-remembering (save-buffer) (if (and (not visiting) (not (equal (marker-buffer org-clock-marker) (current-buffer)))) (kill-buffer (current-buffer))))))))) t) ;; return t to indicate that we took care of this note.