1

The function (org-heading-components) and (org-element-property) produce integers for the number of stars and also for the priority. I'd like to store the entire headline as a variable and then use re-search-forward (or a similar function) to go back to that heading, but I foresee the problem that will occur when it cannot find an integer. I need to store the whole heading as a variable, because I often have todo entries with duplicate titles but the other components are diferent.

For example, the following todo:

** Active [#A] Ask the geniuses on stackoverflow how to do this.  :lawlist:

when evaluated with (org-heading-components) looks like this:

(2 2 "Active" 65 "Ask the geniuses on stackoverflow how to do this." ":lawlist:")

So, when storing that as a variable and later using re-search-forward there will be problems because 2 2 is not the same as **, and 65 is not the same as [#A].

(defun lawlist ()
(interactive)
  (let* (
        (beg (point))
        (complete-heading (org-heading-components) ))
  * * *
  (goto-char (point-min))
  (re-search-forward complete-heading nil t) ))
Jonathan Leech-Pepin
  • 7,684
  • 2
  • 29
  • 45
lawlist
  • 13,099
  • 3
  • 49
  • 158

3 Answers3

2

You should be able to convert the output as follows:

  • The first # is the current level (# of stars)
  • The second number is the reduced headline level, applicable if org-odd-levels-only is set, but this is not regarding output.
  • Todo keyword
  • Priority character (65 is ASCII code for A)
  • Headline text
  • Tags or nil

The following will return the headline string as shown in the buffer. It will not work with re-search-forward but will work with search-forward (It does not escape any characters).

(defun zin/search-test ()
  (interactive)
  (let ((head (org-element-interpret-data (org-element-at-point))))
    (message "%s" (format "%s" (car (split-string head "\n"))))))

This does not set it to any variable, you'll have to wrap it in an appropriate function that will set your desired variable. Then use (search-forward <var> nil t) to match it, without it erroring out if it cannot find it.

Jonathan Leech-Pepin
  • 7,684
  • 2
  • 29
  • 45
1

There's a brilliant part of org that might suit you: org-id-copy and org-id-goto. It works with precision across buffers and sessions: org-id-copy produces a string. You can feed that string to org-id-goto which will take you to that heading. Even if you've closed the original buffer. Even if you've restarted Emacs.

abo-abo
  • 20,038
  • 3
  • 50
  • 71
  • `org-agenda-switch-to` appears to be able to do something similar, but without the need for inserting an ID properties drawer into the todo. If possible, I'd like to avoid inserting an ID properties drawer solely for the purpose of finding the todo later on. I use org-toodledo synchronization for my calendar, and an extra properties drawer would not synchronize to the Toodledo server. Once synchronized, each todo have their own hash identification string, however, I would like search for a todo to work with new todo (without a hash id) before synchronization with the server has occurred. – lawlist Oct 27 '13 at 19:34
  • I modified `org-heading-components` so that it does not convert the two stars and priority into integers, and I tweaked it a bit more so that `re-search-forward` can search for two literal asterisks at flush-left. I've posted a sample answer. Thank you for having taken the time to look at this thread, and suggest possible solutions -- greatly appreciated !. – lawlist Oct 27 '13 at 21:05
0

EDIT (December 15, 2013):  Updated solution based upon the variable org-heading-regexp (defined within org.el) and a modification thereof to include (if it exists) a second line containing a deadline - i.e., lawlist-org-heading-regexp. The revision also includes a nifty function regexp-quote that was just taught to me by @Drew over on superuser: https://superuser.com/questions/688781/how-to-highlight-string-and-unhighlight-string-in-buffer-make-overlay?noredirect=1#comment874515_688781  (buffer-substring-no-properties beg end) is used to set the string as a variable.

EDIT (December 17, 2013):   Added isearch-highlight and isearch-dehighlight, and commented out highlight-regexp and unhighlight-regexp. When moving the point around with more complex functions, highlight-regexp does not reliably highlight the entire string -- this may be because the screen has not refreshed, or it may also be caused by other factors -- e.g., hl-line-mode, etc.) -- placing various sit-for 0 did not fix the issue with highlight-regexp -- isearch-highlight works better.

EDIT (January 6, 2014):  See also this related thread for a complete regexp to match any element of the entire todo from stars through to the end of the notes:  https://stackoverflow.com/a/20960301/2112489

(require 'org)

(defvar lawlist-org-heading-regexp
  "^\\(\\*+\\)\\(?: +\\(.*?\\)\\)?[ \t]*\\(\n.*DEADLINE.*$\\)"
    "Match headline, plus second line with a deadline.")

(defun example ()
(interactive)
  (switch-to-buffer (get-buffer-create "foo"))
  (org-mode)
  (insert "* Example\n\n")
  (insert "** Active [#A] This is an active todo. :lawlist:\n")
  (insert "   DEADLINE: <2013-12-15 Sun 08:00>  SCHEDULED: <2013-12-15 Sun>\n\n")
  (insert "** Next-Action [#B] This is an inactive todo. :lawlist:\n")
  (insert "   DEADLINE: <2013-12-16 Mon 08:00>  SCHEDULED: <2013-12-16 Mon>")
  (goto-char (point-min))
  (sit-for 2)
  (re-search-forward (regexp-quote "** Active [#A] "))
  (sit-for 2)
  (let ((init-pos (point)))
    (org-back-to-heading t)
    (let* (
          lawlist-item-whole
          lawlist-item-partial
          (beg (point)))
      (if (and
            (looking-at org-heading-regexp)
            (and (looking-at lawlist-org-heading-regexp) (match-string 3)))
        (re-search-forward lawlist-org-heading-regexp nil t)
        (re-search-forward org-heading-regexp nil t))
      (let ((end (point)))
        (setq lawlist-item-whole (buffer-substring-no-properties beg end))
        (setq lawlist-item-partial (buffer-substring-no-properties beg init-pos))
        (re-search-backward (regexp-quote lawlist-item-whole) nil t)
        ;; (highlight-regexp (regexp-quote lawlist-item-whole))
        (isearch-highlight beg end)
        (sit-for 2)
        ;; (unhighlight-regexp (regexp-quote lawlist-item-whole))
        (isearch-dehighlight)
        (re-search-forward (regexp-quote lawlist-item-partial) nil t) 
        (sit-for 2)
        (kill-buffer "foo")))))

EDIT (October 27, 2013):  Prior solution that is being preserved temporarily as a historical part of the evolution process towards a final answer. However, it is no longer a preferred method.

(defun lawlist-org-heading-components ()
  (org-back-to-heading t)
  (if (let (case-fold-search) (looking-at org-complex-heading-regexp))
    (concat
      (cond
        ((equal (org-match-string-no-properties 1) "**")
          "^[*][*]")
        ((equal (org-match-string-no-properties 1) "*")
          "^[*]"))
      (cond
        ((and (match-end 2) (aref (match-string 2) 1))
          (concat " " (org-match-string-no-properties 2))))
      (cond
        ((and (match-end 3) (aref (match-string 3) 2))
          (concat " \\" (org-match-string-no-properties 3))))
      (cond
        ((and (match-end 4) (aref (match-string 4) 3))
          (concat " " (org-match-string-no-properties 4))))
      (cond
        ((and (match-end 5) (aref (match-string 5) 4))
          (concat " " (org-match-string-no-properties 5)))))))
Community
  • 1
  • 1
lawlist
  • 13,099
  • 3
  • 49
  • 158