tmedwards / sugarcube-2

SugarCube is a free (gratis and libre) story format for Twine/Twee.
https://www.motoslave.net/sugarcube/2/
BSD 2-Clause "Simplified" License
177 stars 41 forks source link

Rerun macro #157

Closed ChapelR closed 2 months ago

ChapelR commented 2 years ago

Is your feature request related to a problem? Instead of <<replace>> + <<include>> or <<replace>> + rewriting the original content, a macro that lets you "re-run"/re-wikify a target HTML element, similar to Harlowe's (rerun:) macro makes a lot of sense.

Not sure about the name <<rerun>>. I feel like <<update>> makes more sense, but Harlowe already named the feature, so I think it's best to stick to that.

Describe the solution you'd like.

@@#target;$cash@@ 

<<link "Spend money">>
  <<set $cash -= 10>>
  <<rerun "#target">>
<</link>> 

Describe alternatives you've considered. Just a convenience option. Replacing via DOM macros is simple enough for the same effect but always requires a bit of rewriting that could be easily eliminated for such a common construction.

Additional context. I plan to add this to my macro collection, but I actually think it would make sense as a standard library macro, so I figured I'd ask about it here first.

greyelf commented 2 years ago

I agree with the concept of a "rerun" macro being useful, however the example given lacks a clear means to indicate to the engine that the Custom Styled area will be the target of such a macro. And that is needed so that the engine knows to track the pre-wikified contents of that specific area post Passage processing, and not that of every "identified" area.

Harlowe's Hook system must have some way of knowing during Passage content processing that the hooked contents needs to be tracked post rendering, and it's possibly related to how a Hidden Hook delay the processing of its content until it is "revealed" using a (show:) macro.

HiEv commented 2 years ago

It might make it a bit clearer to use a macro to uniquely identify each potential target of a <<rerun>> macro (an error should be thrown if two targets use the same identifier), and then use that identifier in the <<rerun>> macro to find the target. So, by adding a <<target>> macro to be used alongside the <<rerun>> macro, the code would be something like:

<<target "cash">>$cash<</target>>

<<link "Spend money">>
    <<set $cash -= 10>>
    <<rerun "cash">>
<</link>> 

That would fit a bit better with how things tend to work in SugarCube.

As for Greyelf's problem, the <<rerun>> macro then would simply need to look for the matching <<target>> macro, which would store the un-wikified contents for re-processing. Thus the wikified version of the <<target>> macro call shown above might look something like:

<span id="macro-target-cash" data-id="cash" data-source="$cash">1000</span>

With that, a <<rerun "cash">> call would know to look for the #macro-target-cash element, and would then be able to re-wikify the value of the data-source attribute there into the contents of that <span> element. (After dealing with any escaping that needed to be corrected for, which is probably the most difficult part of this.)

This solves the both the problem of tracking the pre-wikified contents and identifying where to put it, plus makes it a bit cleaner looking for people familiar with SugarCube syntax.

tmedwards commented 2 years ago

I believe Cyrus beat you to the punch @ChapelR .

SEE: https://github.com/cyrusfirheir/cycy-wrote-custom-macros/blob/master/live-update

ChapelR commented 2 years ago

I do remember seeing it before now that you point it out...

Well, I guess just consider this a suggestion on whether to include something similar in the standard library.

tmedwards commented 2 years ago

How does the following grab you?

<<live [tags…]>>…<</live>> <<update [tags…]>>

Where optional, non-unique tags may be specified to limit update targets.

For example:

<<live>>
    Updates on <<update>>
<</live>>

<<live foo>>
    Updates on <<update>> or <<update foo>>
<</live>>

<<live foo bar>>
    Updates on <<update>>, <<update foo>>, or <<update bar>>
<</live>>
HiEv commented 2 years ago

Everything else looks good, except I'm not sure about the name <<live>> for the macro, since it sounds like it's doing something by itself when it isn't.

Perhaps having an optional "seconds" parameter for the <<live>> macro would make the macro name make sense, where you could put a parameter like "1s" to have it automatically update itself once per second. That probably should have a lower limit of "0.1s" or so. If you leave out that optional "seconds" parameter, then it would only update when triggered by the <<update>> macro as you described above.

I can definitely see uses for both of those forms.

Side Note: Thinking about your explanatory examples above, I realize that you'll probably want <<update>> to be ignored when it's inside of a <<live>> (or some similar limiter) in order to prevent an infinite regress of self-triggering updates.

greyelf commented 2 years ago

I'm also unsure about naming the target generation/identification macro <<live>>, both because it doesn't describe the purposed of that macro and because the name is too similar to Harlowe's (live:) macro, which could cause confusion to anyone switching between story formats as its functionality is totally unrelated.

Would a name like <<identify>> or <<target>> be more descriptive of that macro's purpose?

tmedwards commented 2 years ago

Re: Naming

I don't think I should be worried about what Harlowe is up to, especially considering the reverse has consistently been true.

I'm not married to <<live>>, however, it is used by Cyrus' set, so would be familiar to users of that.

Re: Repeating or delayed updates

Using <<repeat>> or <<timed>> with <<update>> should suffice for user-defined timing.

Re: Self-triggering updates

As long as updates are rate-limited, which they will be, self-triggering updates shouldn't hurt anything. That said, disallowing them wouldn't hurt anything either and would force usage of <<repeat>>.

ChapelR commented 2 years ago

I think one thing to consider is whether the <<live>> macro might have use beyond updating. Like could a custom macro use the target macro as a base to provide other effects? As a replacement for or in addition to targeting normal HTML elements.

I think <<live>> actually works either way, but <<target>> is more generic, and suggests a usage beyond just the <<update>> macro to me.

HiEv commented 2 years ago

Actually, <<identify>> isn't bad, but we could make it short and sweet with just <<id>>. It makes it pretty clear what it is, since it's just IDing a bit of code for other macros, and it's quick to type. It should also actually set the element's ID, so that you can use that ID with macros like <<replace>> and <<append>>. Having it also check to make sure that there aren't duplicate IDs would help out newbie coders, who may not know to make sure that their IDs are unique on the page like they're supposed to be.

If we go with that, though, should we then add a <<class>> macro too? Doesn't seem like it would hurt.

I'm cool with everything else discussed.

Another Side Note: If you replace the contents of the macro, whatever it ends up being named, using <<replace>>, and then do <<update>> on it, does it update using the original contents or the replaced contents? I'd assume the former, but I just want to make sure that the latter is considered if it might be useful.

greyelf commented 2 years ago

One reason I suggested <<identify>> instead of the shorter <<id>> is so a single macro could be used to assign both/either an element ID and/or CSS classes to the generated element, instead of needing specific macros for each identifier type. eg.

<<identifier "#now">><<= $now.toLocaleString()>><</identifier>>
<<identifier ".stats">>HP: <<= $player.HP>><</identifier>>
<<identifier "#magic .stats">>MP: <<= $player.MP>><</identifier>>

/* Specific identifiers / targets. */
<<update "#now">>
<<update ".stats">>

/* Pseudo identifiers / targets */
<<update ":all">>
tmedwards commented 2 years ago

Re: Multiple use cases.

Like could a custom macro use the target macro as a base to provide other effects?

I don't really like this at all. In general, I don't like Swiss Army knife macros. In specific, this macro will have to store its original raw contents and attach an event handler and there's no reason to do that for most any other purpose.

Re: Naming.

After hunting through thesauruses I still haven't found a name for the base macro that I like better than <<live>>. I think that it, in the alive sense, describes the macro adequately. Still not married to it though.

As to the companion macro. I'm not sure about <<rerun>> due to the similarity to <<run>>. <<update>> works, though it's maybe a bit too generic.

Actually. How does <<do>> and <<redo>> sound?

Re: Replacing <<live>> macro contents.

If you replace the contents of the macro, whatever it ends up being named, using <>, and then do <> on it, does it update using the original contents or the replaced contents? I'd assume the former, but I just want to make sure that the latter is considered if it might be useful.

The original contents.

Re: Why tags instead of selectors?

Because HTML elements are generally non-updatable. E.g., the following would do nothing:

<span id="pcname">$pcName</span>
…
<<update "#pcname">>

Using tags unique to the macro set solves users attempting to update/rerun everything.

Usage (as of time of posting)

<<live [tag tags] [class classes] [element tag] [id ID]>><</live>>

<<live>>
    Updates on <<update>>
<</live>>

<<live tag "foo">>
    Updates on <<update "foo">>
<</live>>

<<live tag "top bottom">>
    Updates on <<update "top">> or <<update "bottom">>
<</live>>

<<update [tags]>>

<<update>>
    Updates <<live>> macros that are untagged

<<update "foo">>
    Updates <<live>> macros that are tagged "foo"

<<update "top bottom">>
    Updates <<live>> macros that are tagged "top" or "bottom"
ChapelR commented 2 years ago

I like this. I'm also a bigger fan of <<do>> and <<redo>> than <<live>> and <<update>>. I think <<live>> is fine but it's a very generic terms that's bound to mean different things to different people compared to the vast majority of other macros. Doesn't help that Harlowe also uses it for timed intervals. I know you can't care about what Harlowe is doing when you name this stuff, but the name <<live>> is just ambiguous enough anyway that I think it's worth staying away from, the fact that Harlowe uses it is just the cherry on top. <<update>> is much less ambiguous in my opinion.

Edit: You could also, if you go with <<do>> make <<while expression>> a child tag 🙃 just to really cross all the streams.

I don't exactly get what the class, id and element tag options are for. I don't think they shouldn't be there, but I'm thinking you could wrap <<live>> around an HTML tag or vice versa if this matters. I only point this out because you already stated you don't want this to be a generic "target" macro for use in or with other macros or features, which after reading your reasoning, I agree with. On some level, generic macro arguments that let you define classes/ids/attributes/display-style of the macro output would be neat for all/most macros that generate output. Maybe something for v3. But for this macro, I'm not sure what the use case you envision is.

tmedwards commented 2 years ago

I like this. I'm also a bigger fan of <<do>> and <<redo>> than <<live>> and <<update>>.

Then let's go with <<do>> and <<redo>> for now.

I don't exactly get what the class, id and element tag options are for.

The <<do>> macro creates a wrapper element to (re)render its contents into, a <span> by default. The element option allows changing the element used.

The id and class options allow setting the wrapper's ID and classes.

On some level, generic macro arguments that let you define classes/ids/attributes/display-style of the macro output would be neat for all/most macros that generate output.

I figured that including such options for the macro's wrapper might be better than requiring users to add their own wrapper just for that purpose.

Though, I do admit to a small worry that some might use the macro simply to create wrappers, ignoring its main purpose. Maybe more than a small worry.

Maybe something for v3. But for this macro, I'm not sure what the use case you envision is.

It's definitely going to be a v3 thing. Unfortunately, the existing syntax of older v2 macros preclude its addition to many of those that could use it.

I suppose that in itself is an argument against including such options here, since they're largely alien to current v2 macros.

greyelf commented 2 years ago

and <> for now.

I was going to suggest <<refresh>> instead of <<redo>>, as the term "Redo" is generally associated with re-applying the outcome of an "Undo" action. But I can see how the pairing of <<do>> and <<redo>> makes grammatical sense.

tmedwards commented 1 year ago

The longer I mull it over, the more I sour on <<redo>> due to the undo/redo angle. I do think I'll switch to <<refresh>> for the update macro.

tmedwards commented 1 year ago

As the commit message notes, it's in.

<<do [tag tags] [element tag]>>…<</do>>
<<refresh [tags]>>

The documentation feels like a train wreck though. For example, the description of tags for both macros are awkward. 🫤

PS: The docs can finally be built using only NPM packages. No need for an external binary now.