From 006ae6e759bbe797c57b47df07fbf883301312a4 Mon Sep 17 00:00:00 2001 From: "jeremy@jermolene.com" Date: Tue, 31 May 2022 09:03:20 +0100 Subject: [PATCH] Refactor $parameters widget The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself. --- .../parsers/wikiparser/rules/parameters.js | 9 ++- core/modules/widgets/parameters.js | 40 +++++++--- core/modules/widgets/transclude.js | 26 +++++-- core/ui/Components/VisibleTransclude.tid | 73 ++++++++++--------- .../tests/data/genesis-widget/RedefineLet.tid | 2 + .../data/transclude/Parameterised-Depth.tid | 34 +++++++++ .../transclude/Parameterised-Name-Values.tid | 9 ++- .../data/transclude/Parameterised-Simple.tid | 11 ++- 8 files changed, 142 insertions(+), 62 deletions(-) create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Depth.tid diff --git a/core/modules/parsers/wikiparser/rules/parameters.js b/core/modules/parsers/wikiparser/rules/parameters.js index 745d7b7dd..561c1c545 100644 --- a/core/modules/parsers/wikiparser/rules/parameters.js +++ b/core/modules/parsers/wikiparser/rules/parameters.js @@ -40,8 +40,13 @@ exports.parse = function() { var attributes = Object.create(null), orderedAttributes = []; $tw.utils.each(params,function(param) { - var attribute = {name: param.name, type: "string", value: param["default"] || ""}; - attributes[param.name] = attribute; + var name = param.name; + // Parameter names starting with dollar must be escaped to double dollars for the parameters widget + if(name.charAt(0) === "$") { + name = "$" + name; + } + var attribute = {name: name, type: "string", value: param["default"] || ""}; + attributes[name] = attribute; orderedAttributes.push(attribute); }); // Save the macro definition diff --git a/core/modules/widgets/parameters.js b/core/modules/widgets/parameters.js index d741ffe2c..ce65ac101 100644 --- a/core/modules/widgets/parameters.js +++ b/core/modules/widgets/parameters.js @@ -42,21 +42,41 @@ Compute the internal state of the widget */ ParametersWidget.prototype.execute = function() { var self = this; - // Find the parent transclusion - var transclusionWidget = this.parentWidget; - while(transclusionWidget && !(transclusionWidget instanceof TranscludeWidget)) { - transclusionWidget = transclusionWidget.parentWidget; + this.parametersDepth = parseInt(this.getAttribute("$depth","1"),10) || 1; + // Find the parent transclusions + var pointer = this.parentWidget, + depth = this.parametersDepth; + while(pointer) { + if(pointer instanceof TranscludeWidget) { + depth--; + if(depth === 0) { + break; + } + } + pointer = pointer.parentWidget; } // Process each parameter - if(transclusionWidget) { + if(pointer instanceof TranscludeWidget) { + // Get the value for each defined parameter $tw.utils.each($tw.utils.getOrderedAttributesFromParseTreeNode(self.parseTreeNode),function(attr,index) { - var name = attr.name, - value = transclusionWidget.getTransclusionParameter(name,index,self.getAttribute(name,"")); - self.setVariable(name,value); - }); - $tw.utils.each(transclusionWidget.getTransclusionMetaVariables(),function(value,name) { + var name = attr.name; + // If the attribute name starts with $$ then reduce to a single dollar + if(name.substr(0,2) === "$$") { + name = name.substr(1); + } + var value = pointer.getTransclusionParameter(name,index,self.getAttribute(attr.name,"")); self.setVariable(name,value); }); + // Assign any metaparameters + var assignMetaParameter = function(name) { + var variableName = self.getAttribute("$" + name); + if(variableName !== undefined) { + self.setVariable(variableName,pointer.getTransclusionMetaParameter(name)); + } + }; + assignMetaParameter("parseAsInline"); + assignMetaParameter("parseTreeNodes"); + assignMetaParameter("params"); } // Construct the child widgets this.makeChildWidgets(); diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js index c4c4c0321..d98e6b7e4 100755 --- a/core/modules/widgets/transclude.js +++ b/core/modules/widgets/transclude.js @@ -198,7 +198,12 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { type: parser.type } $tw.utils.each(srcVariable.params,function(param) { - $tw.utils.addAttributeToParseTreeNode(parser.tree[0],param.name,param["default"]) + var name = param.name; + // Parameter names starting with dollar must be escaped to double dollars + if(name.charAt(0) === "$") { + name = "$" + name; + } + $tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"]) }); } else { // For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__" @@ -299,14 +304,19 @@ TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaul }; /* -Get a hashmap of the special variables to be provided by the parameters widget +Get one of the special parameters to be provided by the parameters widget */ -TranscludeWidget.prototype.getTransclusionMetaVariables = function() { - var variables = { - "@parseAsInline": this.parseAsInline ? "yes" : "no", - "@params": JSON.stringify(this.stringParametersByName) - }; - return variables; +TranscludeWidget.prototype.getTransclusionMetaParameter = function(name) { + switch(name) { + case "parseAsInline": + return this.parseAsInline ? "yes" : "no"; + case "parseTreeNodes": + return JSON.stringify(this.parseTreeNode); + case "params": + return JSON.stringify(this.stringParametersByName); + default: + return ""; + } }; /* diff --git a/core/ui/Components/VisibleTransclude.tid b/core/ui/Components/VisibleTransclude.tid index f654c0520..57ba0a05f 100644 --- a/core/ui/Components/VisibleTransclude.tid +++ b/core/ui/Components/VisibleTransclude.tid @@ -5,41 +5,44 @@ Import this component to make all the child transclusions visible. Block transclusions are shown in red, and inline transclusions are shown in green. --> -\widget $transclude(tiddler,$tiddler,mode,$mode) - -<$let - mode={{{ [[$mode]is[variable]then<$mode>!is[blank]] :else[[mode]is[variable]then!is[blank]] :else[match[yes]then[inline]else[block]] }}} - outputTag={{{ [match[inline]then[span]else[div]] }}} - outputColour={{{ [match[inline]then[green]else[red]] }}} -> - - <$genesis $type="element" $tag=<> style="color:white;padding:4px;" style.background=<>> - <$genesis $type="element" $tag=<> style="display: inline-block;"> -
- - <$list filter="[<@params>jsonindexes[]]" emptyMessage="(none)"> -
- <$text text=<>/><$text text=": "/><$text text={{{ [<@params>jsonget] }}}/> -
+\widget $transclude + +<$parameters tiddler="" $$tiddler="" mode="" $$mode="" $params="@params"> + + <$let + mode={{{ [[$mode]is[variable]then<$mode>!is[blank]] :else[[mode]is[variable]then!is[blank]] :else[match[yes]then[inline]else[block]] }}} + outputTag={{{ [match[inline]then[span]else[div]] }}} + outputColour={{{ [match[inline]then[green]else[red]] }}} + > + + <$genesis $type="element" $tag=<> style="color:white;padding:4px;" style.background=<>> + <$genesis $type="element" $tag=<> style="display: inline-block;"> +
+ + <$list filter="[<@params>jsonindexes[]]" emptyMessage="(none)"> +
+ <$text text=<>/><$text text=": "/><$text text={{{ [<@params>jsonget] }}}/> +
+ +
+ + <$genesis $type="element" $tag=<> style="background:white;color:black;padding:4px;"> + + <$list filter="[<@params>jsonindexes[]] :filter[prefix[$]] +[limit[1]]" variable="ignore" emptyMessage=""" + + <$genesis $type="transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget]" recursionMarker="no" mode=<>> + + <$slot $name="ts-raw" $depth="2"/> + + """> + + <$genesis $type="transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget]" $$recursionMarker="no" $$mode=<>> + + <$slot $name="ts-raw" $depth="2"/> + -
+ - <$genesis $type="element" $tag=<> style="background:white;color:black;padding:4px;"> - - <$list filter="[<@params>jsonindexes[]] :filter[prefix[$]] +[limit[1]]" variable="ignore" emptyMessage=""" - - <$genesis $type="transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget]" recursionMarker="no" mode=<>> - - <$slot $name="ts-raw" $depth="2"/> - - """> - - <$genesis $type="transclude" $remappable="no" $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsonget]" $$recursionMarker="no" $$mode=<>> - - <$slot $name="ts-raw" $depth="2"/> - - - - -<$let> + + \end diff --git a/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid b/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid index 126d1bf33..99bf18e24 100644 --- a/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid +++ b/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid @@ -8,9 +8,11 @@ title: Output \whitespace trim \widget $let \whitespace trim +<$parameters $params="@params"> <$setmultiplevariables $names="[<@params>jsonindexes[]]" $values="[<@params>jsonindexes[]] :map[<@params>jsongetaddprefix[--]addsuffix[--]]"> <$slot $name="ts-body"/> + \end <$let one="Elephant" diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Depth.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Depth.tid new file mode 100644 index 000000000..064e225c8 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Depth.tid @@ -0,0 +1,34 @@ +title: Transclude/Parameterised/Depth +description: Parameterised transclusion using the $depth attribute +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'/> +| +<$transclude $tiddler='TiddlerOne'/> +| +<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/> +| +<$transclude $tiddler='TiddlerOne' $$two="Falcon"/> ++ +title: TiddlerOne + +\whitespace trim +{{TiddlerTwo}} ++ +title: TiddlerTwo + +\whitespace trim +<$parameters one='Jaguar' $$two='Piranha' $depth="2"> + <$text text=<>/>:<$text text=<<$two>>/> + +<$parameters one='Leopard' $$two='Coelacanth'> + (<$text text=<>/>|<$text text=<<$two>>/>) + ++ +title: ExpectedResult + +

Ferret:Piranha(Leopard|Coelacanth)|Jaguar:Piranha(Leopard|Coelacanth)|Ferret:Osprey(Leopard|Coelacanth)|Jaguar:Falcon(Leopard|Coelacanth)

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid index a80abc00c..9d62a7897 100644 --- a/editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid @@ -17,17 +17,18 @@ title: Output title: TiddlerOne \whitespace trim -\parameters(zero:'Jaguar',one:'Lizard',two:'Mole') +<$parameters zero='Jaguar' $$one='Lizard' two='Mole' $params="@params"> <$list filter="[<@params>jsonindexes[]]"> {<$text text=<>/>: <$text text={{{ [<@params>jsonget] }}}/>} + + title: TiddlerTwo \whitespace trim -\parameters(zero:'Mouse',one:'Horse',two:'Owl') -(<$transclude $tiddler=<> zero=<> one=<> two=<>/>) +\parameters(zero:'Mouse',$one:'Horse',two:'Owl') +(<$transclude $tiddler=<> zero=<> $$one=<<$one>> two=<>/>) + title: ExpectedResult -

{0:}{1:}{2:}

{0:Ferret}

{0:Butterfly}{1:Moth}

{0:Beetle}{1:Scorpion}{2:Snake}

({one:Scorpion}{two:Snake}{zero:Beetle})

\ No newline at end of file +

{0:}{1:}{2:}

{0:Ferret}

{0:Butterfly}{1:Moth}

{0:Beetle}{1:Scorpion}{2:Snake}

({$one:Scorpion}{two:Snake}{zero:Beetle})

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid index a6228d625..0268f9e59 100644 --- a/editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid @@ -7,15 +7,20 @@ title: Output \whitespace trim <$transclude $tiddler='TiddlerOne' one='Ferret'/> +| <$transclude $tiddler='TiddlerOne'/> +| +<$transclude $tiddler='TiddlerOne' one='Ferret' $$two="Osprey"/> +| +<$transclude $tiddler='TiddlerOne' $$two="Falcon"/> + title: TiddlerOne \whitespace trim -<$parameters one='Jaguar'> - <$text text=<>/> +<$parameters one='Jaguar' $$two='Piranha'> + <$text text=<>/>:<$text text=<<$two>>/> + title: ExpectedResult -

FerretJaguar

\ No newline at end of file +

Ferret:Piranha|Jaguar:Piranha|Ferret:Osprey|Jaguar:Falcon

\ No newline at end of file