emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* overlay showing computed value of macro?
@ 2018-11-22 18:19 Matt Price
  2018-11-22 21:06 ` Matt Price
  0 siblings, 1 reply; 7+ messages in thread
From: Matt Price @ 2018-11-22 18:19 UTC (permalink / raw)
  To: Org Mode

[-- Attachment #1: Type: text/plain, Size: 1115 bytes --]

I use macros in my course syllabi to automate creation of timestamps It
looksl ike this:

# see bottom of this email for the defn of ~get-ts~
#+MACRO: ts (eval (get-ts+7))


** Week {{{n}}} ({{{ts}}}): Time Scales in Rivers, Geology, and Human
Relationships
* Week {{{n}}} ({{{ts}}}): +READING WEEK: NO CLASS
** Week {{{n}}} ({{{ts}}}): Communities, States, and Water Governance

This makes it much easier for me to re-arrange headings while I'm planning
schedules.  But it's a little hard to tell, for instance, if I have the
right number of class meetings, or if the University holidays are in the
right place, etc.

It would be nice for me to overlay the macro invocations with the values
that they will evaluate to on export. I guess I would have to figure out
when to trigger recalculation of the values (maybe not too often, say only
when the macro is first created, when it's edited or deleted/deformed, and
perhaps on structure editing.

I do not understand emacs overlays very well so this feels a little
daunting to me.  Does anyone out there have ideas about how to do it
effectively?

Thanks as always!

[-- Attachment #2: Type: text/html, Size: 1478 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-22 18:19 overlay showing computed value of macro? Matt Price
@ 2018-11-22 21:06 ` Matt Price
  2018-11-22 22:37   ` Matt Price
  0 siblings, 1 reply; 7+ messages in thread
From: Matt Price @ 2018-11-22 21:06 UTC (permalink / raw)
  To: Org Mode

[-- Attachment #1: Type: text/plain, Size: 1141 bytes --]

On Thu, Nov 22, 2018 at 1:19 PM Matt Price <moptop99@gmail.com> wrote:

> It would be nice for me to overlay the macro invocations with the values
> that they will evaluate to on export. I guess I would have to figure out
> when to trigger recalculation of the values (maybe not too often, say only
> when the macro is first created, when it's edited or deleted/deformed, and
> perhaps on structure editing.
>
> I do not understand emacs overlays very well so this feels a little
> daunting to me.  Does anyone out there have ideas about how to do it
> effectively?
>
>
This is what I've come up with; it feels somewhat close but not quite there
yet. I can create, remove, and the overlays, which is great.  What I miss
is the awesomeness of the way that overlays work with org links. I love how
the link target and the enclosing [[  ][  ]] are invisible until I delete
one of the "[" elements.  However, with my code, the whole macro expression
{{{macroname}}} ius invisible until I delete the whole thing, at which
point the overlay helpfully disappears.  Is htere a way to get something
like the link behaviour?


> Thanks as always!
>
>

[-- Attachment #2: Type: text/html, Size: 1837 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-22 21:06 ` Matt Price
@ 2018-11-22 22:37   ` Matt Price
  2018-11-23  9:08     ` Matt Price
  0 siblings, 1 reply; 7+ messages in thread
From: Matt Price @ 2018-11-22 22:37 UTC (permalink / raw)
  To: Org Mode

[-- Attachment #1: Type: text/plain, Size: 4504 bytes --]

On Thu, Nov 22, 2018 at 4:06 PM Matt Price <moptop99@gmail.com> wrote:

>
>
> On Thu, Nov 22, 2018 at 1:19 PM Matt Price <moptop99@gmail.com> wrote:
>
>> It would be nice for me to overlay the macro invocations with the values
>> that they will evaluate to on export. I guess I would have to figure out
>> when to trigger recalculation of the values (maybe not too often, say only
>> when the macro is first created, when it's edited or deleted/deformed, and
>> perhaps on structure editing.
>>
>> I do not understand emacs overlays very well so this feels a little
>> daunting to me.  Does anyone out there have ideas about how to do it
>> effectively?
>>
>>
> This is what I've come up with; it feels somewhat close but not quite
> there yet. I can create, remove, and the overlays, which is great.  What I
> miss is the awesomeness of the way that overlays work with org links. I
> love how the link target and the enclosing [[  ][  ]] are invisible until I
> delete one of the "[" elements.  However, with my code, the whole macro
> expression {{{macroname}}} ius invisible until I delete the whole thing, at
> which point the overlay helpfully disappears.  Is htere a way to get
> something like the link behaviour?
>

Oops! Here is the actual code (a bit long actually!  Largely stolen from
org source code).


#+begin_src emacs-lisp

(defun mwp-show-macros ()
  (interactive)
  (remove-overlays (point-min) (point-max) 'macro-ov-p t)
  (save-excursion
    (goto-char (point-min))
    ;; keeping this properties/keywordsstuff b/c I don't quite understand it
    (let* ((keywords nil)
           (properties-regexp (format "\\`EXPORT_%s\\+?\\'"
                      (regexp-opt keywords)))
       record)
      (while (re-search-forward "{{{[-A-Za-z0-9_]" nil t)
        (unless (save-match-data (org-in-commented-heading-p))
          (let* ((datum (save-match-data (org-element-context)))
             (type (org-element-type datum))
             (macro
              (cond
           ((eq type 'macro) datum)
           ;; In parsed keywords and associated node
           ;; properties, force macro recognition.
           ((or (and (eq type 'keyword)
                 (member (org-element-property :key datum) keywords))
                (and (eq type 'node-property)
                 (string-match-p properties-regexp
                         (org-element-property :key datum))))
            (save-excursion
              (goto-char (match-beginning 0))
              (org-element-macro-parser))))))
        (when macro
          (let* ((key (org-element-property :key macro))
             (value (org-macro-expand macro org-macro-templates))
             (begin (org-element-property :begin macro))
                     (end (save-excursion
                            (goto-char (org-element-property :end macro))
                    (skip-chars-backward " \t")
                    (point)))
             (signature (list begin
                      macro
                      (org-element-property :args macro))))
            ;; Avoid circular dependencies by checking if the same
            ;; macro with the same arguments is expanded at the
            ;; same position twice.
            (cond ((member signature record)
               (error "Circular macro expansion: %s" key))
              (value
               (push signature record)
               (let ((ov (make-overlay begin end)))
                         (overlay-put ov 'invisible t)
                         (overlay-put ov 'evaporate t)
                         (overlay-put ov 'macro-ov-p t)
                 (overlay-put ov 'before-string value )))
              ;; Special "results" macro: if it is not defined,
              ;; simply leave it as-is.  It will be expanded in
              ;; a second phase.
              ((equal key "results"))
              (t
               ;; (error "Undefined Org macro: %s; aborting"
               ;;        (org-element-property :key macro))
                       )))))))))
  (org-macro--counter-initialize)
  (setq-local mwp-macro-overlays t)
  )


(defun mwp-hide-macros ()
  (interactive)
(remove-overlays (point-min) (point-max) 'macro-ov-p t)
(setq-local mwp-macro-overlays nil)
;; (cl-loop for o in (overlays-in (point-min) (point-max))
;;          if (overlay-get o 'macro-ov-p)
;;          ())
)

(defun mwp-toggle-macros ()
  (interactive)
  (if mwp-macro-overlays
      (mwp-hide-macros)
    (mwp-show-macros)) )

(defvar-local mwp-macro-overlays nil)
#+end_src

[-- Attachment #2: Type: text/html, Size: 6350 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-22 22:37   ` Matt Price
@ 2018-11-23  9:08     ` Matt Price
  2018-11-23  9:32       ` Eric S Fraga
  0 siblings, 1 reply; 7+ messages in thread
From: Matt Price @ 2018-11-23  9:08 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 5848 bytes --]

OH man, I know this is a lot of replies to my own thread.  Sorry for the
noise. I just wanted to point out something I somehow failed to see as I
was pretending to cook Thanksgiving dinner while working on this project
(!!) -- my code does just fine producing overlays, which might be useful to
some people, but it does not really help with my own need, which is to
calculate timestamp values based on the position of the macro call in the
buffer.  That's because I'm using a function that searches backwards for
previous timestames, and the overlays are of course *not part of the buffer
string*, so they don't get picked up by regexp-search-backward.

To fix this I replaced my original function with a group of functions that
searches the buffer string *and* the overlays, looking for a timestamp
regep.  For the overlays, I endedu p writing a recursive function cause I
oculdn't think howelse to do it.

This is all much more verbose than my original function and I think pretty
inelegant but I would be interested to hear what others htink.

Here's the original function, which doesn't look for overlays:

(defun get-ts+7 (&optional weekly-times)
  "Return a string of the form <%Y-%m-d %a> where the date
elements are 7 days later than the (n - WEEKLY-TIMES timestamp)
in the buffer. That is, if WEEKLY-TIMES is nil or 1, return a
date one week later than the *PREVIOUS* timestamp. If
WEEKLY-TIMES is 2, return a time one week later than the
*SECOND-TO-LAST* timestamp above the previous location.

If there are not enough timestamps, right now it returns the last
found timestamp. "
  (interactive)
  (setq weekly-times (or weekly-times 1))
  (defun ts-search ()
    (save-match-data
      (goto-char (re-search-backward
                  (org-re-timestamp 'all) nil t))
      (let ((thismatch  (match-string 0)))
        (message "match: %s" thismatch)
        thismatch)))
  (let* (r
         (base-date (save-excursion
                     (cl-dotimes (time weekly-times r)
                       (condition-case nil
                           (progn (setq  r (or (ts-search) r))
                                  (message "r is %s" r))
                         (error
                          (message "Drat, there were %s timestamps, using
the last one I found."
                                   (if (= 0 time) "no" time))
                          (return r))))))

        (result nil))
    (message "base-time is %s" base-date)
    (if base-date
        (format-time-string "<%Y-%m-%d %a>"
                            (time-add
                             (date-to-time base-date) (days-to-time (1+
7))))
      "NO PREVIOUS TIMESTAMP") ))
-------------------------

and here are the several functions I felt I needed to solve the problem of
searching in 2 dimmensions:


(defun ts-search-both ()
  (save-match-data
    (let* ((bufts
            (save-match-data
              (set-match-data nil)
              (save-excursion
                (re-search-backward
                 (org-re-timestamp 'all) nil t)
                (if (match-beginning 0)
                    `(,(match-beginning 0) ,(match-string 0))
                  nil))))
           (ovts (save-match-data (save-excursion  (ts-search-ov)))))
      (cond
       ((and bufts ovts)
        (if (> (car bufts) (car ovts))
            (goto-char (car bufts))
            (cadr bufts)
          (cadr ovts)))
       (bufts
        (goto-char (car bufts))
        (cadr bufts))
       (ovts
        (goto-char (car ovts))
        (cadr ovts))
       (t
        (goto-char (point-min)) nil)
       )
      )))

(defun ts-search-ov ()
  (let* ((ovend   (previous-single-char-property-change (point)
'macro-ov-p))
         (ovs (overlays-at ovend))
         (m  (cl-loop for o in ovs
                      if (string-match (org-re-timestamp 'all) (overlay-get
o 'before-string))
                      ;;(goto-char (overlay-start o))
                      return (match-string 0 (overlay-get o
'before-string)))))
    (cond
     (m `(,(point) ,m))
     ((>= (point-min) ovend )
      (goto-char ovend)
      nil)
     (t
      (goto-char ovend)
      (ts-search-ov))
      )))

(defun get-ts+7-inc-ov (&optional weekly-times)
  "Return a string of the form <%Y-%m-d %a> where the date
elements are 7 days later than the (n - WEEKLY-TIMES timestamp)
in the buffer. That is, if WEEKLY-TIMES is nil or 1, return a
date one week later than the *PREVIOUS* timestamp. If
WEEKLY-TIMES is 2, return a time one week later than the
*SECOND-TO-LAST* timestamp above the previous location.

If there are not enough timestamps, right now it returns the last
found timestamp. "
  (interactive)
  (setq weekly-times (or weekly-times 1))

  (let* (r
         (base-date (save-excursion
                     (cl-dotimes (time weekly-times r)
                       (condition-case nil
                           (let ((s (ts-search-both)))
                             (if s
                                 (setq r s)
                               (message "Drat, there were %s timestamps,
using the last one I found."
                                        (if (= 0 time) "no" time))
                               (return r)))

                         ;; (error
                         ;;  (message "Drat, there were %s timestamps,
using the last one I found."
                         ;;           (if (= 0 time) "no" time))
                         ;;  (return r))
                         ))))

        (result nil))
    (message "base-time is %s" base-date)
    (if base-date
        (format-time-string "<%Y-%m-%d %a>"
                            (time-add
                             (date-to-time base-date) (days-to-time (1+
7))))
      "NO PREVIOUS TIMESTAMP") ))


-------------------------------

I'd be interested in feedback if others have some interest i nthe problem.

[-- Attachment #2: Type: text/html, Size: 8004 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-23  9:08     ` Matt Price
@ 2018-11-23  9:32       ` Eric S Fraga
  2018-11-23 12:49         ` Matt Price
  0 siblings, 1 reply; 7+ messages in thread
From: Eric S Fraga @ 2018-11-23  9:32 UTC (permalink / raw)
  To: Matt Price; +Cc: emacs-orgmode

Matt,

you might like to have a look at how the n() macro was implemented as it
has some similar goals.  Check out org-macro-initialize-templates and
org-macro--counter-increment.

-- 
Eric S Fraga via Emacs 27.0.50, Org release_9.1.14-1035-gfeb442

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-23  9:32       ` Eric S Fraga
@ 2018-11-23 12:49         ` Matt Price
  2018-11-23 14:15           ` Eric S Fraga
  0 siblings, 1 reply; 7+ messages in thread
From: Matt Price @ 2018-11-23 12:49 UTC (permalink / raw)
  To: Org Mode

[-- Attachment #1: Type: text/plain, Size: 454 bytes --]

On Fri, Nov 23, 2018 at 4:32 AM Eric S Fraga <esflists@gmail.com> wrote:

> Matt,
>
> you might like to have a look at how the n() macro was implemented as it
> has some similar goals.  Check out org-macro-initialize-templates and
> org-macro--counter-increment.
>

ah.  no stupid searching backwards through the buffer.  just track
everyting in a variable.

that... is smarter.


> --
> Eric S Fraga via Emacs 27.0.50, Org release_9.1.14-1035-gfeb442
>

[-- Attachment #2: Type: text/html, Size: 916 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: overlay showing computed value of macro?
  2018-11-23 12:49         ` Matt Price
@ 2018-11-23 14:15           ` Eric S Fraga
  0 siblings, 0 replies; 7+ messages in thread
From: Eric S Fraga @ 2018-11-23 14:15 UTC (permalink / raw)
  To: Matt Price; +Cc: Org Mode

On Friday, 23 Nov 2018 at 07:49, Matt Price wrote:
> ah.  no stupid searching backwards through the buffer.  just track
> everyting in a variable.
>
> that... is smarter.

I hadn't looked to see how it was done but I do know that I can usually
learn something from what Nicolas has done!

-- 
Eric S Fraga via Emacs 27.0.50, Org release_9.1.14-1035-gfeb442

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-11-23 14:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-22 18:19 overlay showing computed value of macro? Matt Price
2018-11-22 21:06 ` Matt Price
2018-11-22 22:37   ` Matt Price
2018-11-23  9:08     ` Matt Price
2018-11-23  9:32       ` Eric S Fraga
2018-11-23 12:49         ` Matt Price
2018-11-23 14:15           ` Eric S Fraga

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).