How to use the colour macro as an attribute value

I’m sure many of us have tried to write something like this:

<span style.color=<<colour muted-foreground>>>...</span>

only to find that it does take effect due to the fact that the macro call is not wikified.

The value you get for the style.color attribute is actually:

\whitespace trim <$transclude $tiddler={{$:/palette}} $index=`$(name)$`> <$transclude $tiddler="$:/palettes/Vanilla" $index=`$(name)$`> <$transclude $tiddler=`$:/config/DefaultColourMappings/$(name)$`/> </$transclude> </$transclude>

(Or, if you use <<color>> instead of <<colour>>, you will get <$macrocall $name=colour name=`$(name)$`/>.)

Instead of a macro, it would be nice to have a function that does the same thing, since functions are evaluated (\function under the same link above in the text not wikified).

Well, by looking at the macro definition, we can see it is just transcluding a tiddler index (from the palette), transcluding the same index from the vanilla palette as a backup, and finally transcluding the text from a default config tiddler for the given color as a final backup. We can do the same with a custom function:

\function getColor(c) [{$:/palette}getindex<c>!is[blank]] ~[[$:/palettes/Vanilla]getindex<c>!is[blank]] ~[[$:/config/DefaultColourMappings/]addsuffix<c>get[text]]

There is actually one little problem with this approach: sometimes the value in the palette is another call to the colour macro! This won’t be automatically wikified to the final value if called in a widget attribute.

To fix this, we can use regular expressions to detect if the value is another call to the colour macro, and recursively call the getColor function again if it is.

I bet someone with better regex and filter skills could write a cleaner or more efficient version, but this is how I implemented it (originally for another topic):

\define colorRegexp() <<colou?r .*>>

\define removeRegex() <<colou?r |"|'|>>

\function getColor(c) [{$:/palette}getindex<c>!is[blank]] ~[[$:/palettes/Vanilla]getindex<c>!is[blank]] ~[[$:/config/DefaultColourMappings/]addsuffix<c>get[text]] :map[function[recolor],<currentTiddler>]

\function recolor(c) [<c>regexp<colorRegexp>] :then[<c>search-replace:g:regexp<removeRegex>,[]split[ ]!is[blank]first[]] :map[function[getColor],<currentTiddler>] :else[<c>]

This allows you to do things like this:

<div style.color=<<getColor foreground>> style.background-color=<<getColor table-header-background>> >
(contents)
</div>

I’m American, so I used the American spelling “color” in the name of my implementation of the “getColor” function, but you can name the functions whatever you like.

Are you sure it’s not because you should be doing something like:

<span style="color:<<colour muted-foreground>>">...</span>

?

I’m 200% sure.

  1. It is valid (and encouraged) in TW to use style.xxx attributes as a shortcut. See the last section (Style Attributes) in this page of the documentation.
  2. A literal string as an attribute value won’t be wikified (except in highly specific situations such as action strings). (You could use substitution in the attribute value, but that would still result in the text definition of the colour macro being substituted, rather than the value it evaluates to–because it’s still not wikified.)

Interesting. Now I’ve learned something!

@bluepenguindeveloper, your solution is very interesting!

One small filter improvement in <<getColor>> is to omit the !is[blank] operator, since the preceding getindex<c> operator implicitly rejects blank index values. You could also define a “getColour” function to handle the British spelling.

\function getColor(c) [{$:/palette}getindex<c>] ~[[$:/palettes/Vanilla]getindex<c>] ~[[$:/config/DefaultColourMappings/]addsuffix<c>get[text]] :map[function[recolor],<currentTiddler>]
\function getColour(c) [function[getColor],<c>]

Another potential improvement would be to define recolor(c) like this:

\function recolor(c) [<c>regexp<colorRegexp>] :then[<c>split[ ]last[]trim[']trim["]trim[>>]] :map[function[getColor],<currentTiddler>] :else[<c>]

which eliminates the need for the removeRegex() definition by using <c>split[ ]last[]trim[']trim["]trim[>>] to isolate the argument value when handling recursive <<colour>> references.

Lastly, the colorRegexp() definition should probably be extended to permit recursive <<getColor>> references, like this:

\define colorRegexp() <<(get)?[cC]olou?r .*>>

See [BUG] · Issue #8326 · Jermolene/TiddlyWiki5 · GitHub for a related discussion regarding a backward-compatibility issue with the <<colour>> macro in v5.3.4

-e

@bluepenguindeveloper Your :then filter run contains split[] !is[blank] and first[] – Is / was there a reason, why they are there. I did some tests and non of them did influence the result. – But I may have missed something.

I don’t remember for sure (I should’ve posted this topic much closer to when I implemented it–then I would more likely have remembered), but it looks like I might have put that there to account for potential additional junk parameters (<<colour muted-foreground some-junk-param>>).

Alternatively, maybe it was simply written at a time when the “remove” regexp wasn’t as good; I definitely went through a few iterations of both those regular expressions before getting them as simple as they are now.

It’s also possible that there was never any reason for it other than me being a bit clumsy in the coding.

1 Like

My goal was to have something to use only for attribute values, and not to use in the palette (where I would just leave it as <<colour xxxx>>), but this is a good idea.

That said, I didn’t know there were any changes to the color macro in the new release until I got this notification and the mention on GH. I see in the GH issue you suggest adding something like what I did as an addition to the colour macro, but if we were going to change the colour macro at all, I wonder if it would be better to replace it with an equivalent function such as what I made. (Something like what I wrote, renamed to colour and with a color duplicate, plus with your other improvement suggestions and any other needed regexp improvements.) Then we also wouldn’t need to separately handle recursion for getColo(u)r.

I can’t see anyone relying on the lack of wikification of the colour macro for any use case, but if there is for some reason, then there would of course be backwards compatibility issues with replacing the macro with a function.

TiddlyWiki actually allows arbitrary wikitext in a palette colour definition. So something like this should work as things stand:

page-background: <%if {{$:/Something}} %>red<%else%>green<%endif%>

In practice, the core palettes only use embedded <<colour>> macros, and so I would be surprised if there were any palettes out there that do anything more elaborate.

So, if we were to adopt the regex approach we would need to document the resulting restrictions.

My instinct, though, is that there are so many situations where wikified attributes would be useful that I think it would be worth looking at a way to implement them generically.

For example, using triple single quotes:

<$let a='''The colour is <<colour background>>'''>

Perhaps triple single quotes are a bit hard to read, but they would be unlikely to appear in existing wikis.

That is a very significant restriction. Perhaps the conclusion is that the ideas in this thread are better suited to personal use than an addition to the core.

An alternative syntax idea: a prefix to a string literal, such as w (for wikify):

<$let a=w"The colour is <<colour background>>">

(This syntax idea is inspired by Python’s raw string syntax.)

Another alternative would be to have a wikify filter operator. Then, with no new syntax, you could do things like this:

<$let a={{{[[The colour is <<colour background>>]wikify[]]}}}>

or

<$let a={{{"The colour is <<colour background>>" +[wikify[]]}}}>

I love this idea. I have been bitten by this issue so very many times.

I like this a bit less, but could certainly live with it. I don’t know how customized the wikitext parser is, versus how much it is simply a generic HTML/XML/SGML parser. If the latter, it might be difficult to implement.

1 Like

Fascinating and helpful! I often play with making the sidebar background different from the general page background, but then certain sidebar contrasts are undermined, and I have to go tinker with the platte. Then I find the palette works in that one wiki, but not in another (because I may not be doing the same thing with the sidebar, etc.). I’ll want to play with this conditional approach so that my custom palettes are less brittle across projects.

I just realised that we should be able to apply wikification to attributes that are specified as transclusions of variables or tiddlers (ie attr=<<mything>> or attr={{anotherthing}}.

So, rather then being triggered by a new type of quotation, it occurs to me that we might use a double equals sign to indicate wikification. For example:

<$let
   a=="The time is <<now>>"
   b==<<colour foreground>>
   c=={{MyToolTip}}

This approach would allow us to apply separate optimisations to each type of wikification, in particular we would be able to take advantage of the cached parse tree when transcoding variables or tiddlers.

3 Likes

This strikes me as brilliant!

Just yesterday I was running into trouble getting the action-confirm widget to display the intended message, and wished for a simpler way to pass along the same string that had been working perfectly well when it was simply displaying in its own div.

Would this == solution be broadly available to widget attributes, etc.? If so, this would be revolutionary! (And would deserve not to be tucked into a thread about the colour macro!)

1 Like

Are we truly happy about == ?

There is a ton of prior art (baggage) where that means “equivalency”.

I just wonder how much of a stumbling point (reading code, I mean) this might be.

We have = for assignment.
We have : for assignment of defaults.
Do we need another type?

Might we consider a boolean attribute? (Although, in a multi-assignment <$let> that would not work.)

<$let wikify a="The time is <<now>>"/>
<$let wikify b=<<colour foreground>> />
<$let wikify c={{MyToolTip}} />

Or  :=  (“becomes”)

<$let
   a:="The time is <<now>>"
   b:=<<colour foreground>>
   c:={{MyToolTip}}
2 Likes

“==” is used as comparison in many programming languages, and therefore may confuse people (or bots)

I like ‘:=’ as this is used as assignment in some languages (eg pascal) as suggest by CodaCoder Talk TW - Community discussion forum about TiddlyWiki

1 Like

More grammar makes things more complicated. Should we concatenate strings and variables like "Time is <<now>>"?
I think we have a lot of rules and grammar now!

How so? Adding more doesn’t mean complicated. The growth/improvement of the grammar is a necessity assuming we want the language to become more expressive.

It may not be pretty, but I really like @bluepenguindeveloper’s idea: x=w"to-be-wikified-stuff". But I think  :=  is less of a shift in semantics (and bit more natural).

Earlier I was going to say that one advantage := has over a w prefix is that the former can be used on non-literal attributes (attr:={{somethingTranscluded}}). But then I realized that that’s not strictly true; <<>> or {{}} with w is just as much of a possibility as prefixing quotes with w. (attr=w{{somethingTranscluded}}).

But you’re right, it sure isn’t pretty. :sweat_smile:

Semantically, I think what we’re doing isn’t a different kind of assignment; it is assigning a different value, namely, the result of wikification; thus, I could see an argument from semantics for doing something to the attribute value (a prefix or surrounding it with something) rather than doing something to the = sign (a double == or := etc.). Ideas would include parentheses, slashes or backslashes, or some symbol for a prefix other than w (maybe $ or ! or \).

But personally, I think changing the equals sign to := or == to indicate wikification simply makes for cleaner code, and even == (despite its meaning in other languages) would be far from the worst abuse of notation in my opinion.

4 Likes

I think the “w” prefix is ugly and not consistent with TiddlyWiki’s use of symbols vs. letters elsewhere.

I like that == feels like a more intense version of =. I can see the argument for :=but I think the resemblance to other programming languages is unhelpful.

I don’t think we should shape the wikitext syntax to meet the expectations of experienced software developers. In any case, arguably using := does them a disservice because any prior understanding of the distinction between := and = will not apply here.

If we are going to consider the needs of experienced software developers, it makes sense to work on the assumption that they are smart. Such a developer encountering == in an assignment context can be assumed to realise fairly swiftly that this cannot be a comparison.

1 Like