From aa817f66d2851206d79dff415a46b9cadb927fd8 Mon Sep 17 00:00:00 2001 From: Cameron Fischer Date: Thu, 30 Jan 2020 07:53:26 -0500 Subject: [PATCH] Changed importVariable to store its own variables (#4108) * Changed importVariable to store its ownvariables Before, importVariables was creating a setWidget for every single variable it would find in its tiddlers, and it would create a long-ass call tree. Now, instead, it just accumulates the variables in itself. * Can't use Object.assign Learned the hardway while working on tw5-relink that Object.assign doesn't exist in IE11. Using $tw.utils.extend instead. * Retaining setWidget transclusion flexibility * One more test to verify mixing sets and macros --- core/modules/widgets/importvariables.js | 44 ++++++++--------- editions/test/tiddlers/tests/test-widget.js | 52 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/core/modules/widgets/importvariables.js b/core/modules/widgets/importvariables.js index 86559ef25..e720dc07b 100644 --- a/core/modules/widgets/importvariables.js +++ b/core/modules/widgets/importvariables.js @@ -37,49 +37,45 @@ ImportVariablesWidget.prototype.render = function(parent,nextSibling) { Compute the internal state of the widget */ ImportVariablesWidget.prototype.execute = function(tiddlerList) { - var self = this; + var widgetPointer = this; // Get our parameters this.filter = this.getAttribute("filter"); // Compute the filter this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this); // Accumulate the <$set> widgets from each tiddler - var widgetStackStart,widgetStackEnd; - function addWidgetNode(widgetNode) { - if(widgetNode) { - if(!widgetStackStart && !widgetStackEnd) { - widgetStackStart = widgetNode; - widgetStackEnd = widgetNode; - } else { - widgetStackEnd.children = [widgetNode]; - widgetStackEnd = widgetNode; - } - } - } $tw.utils.each(this.tiddlerList,function(title) { - var parser = self.wiki.parseTiddler(title); + var parser = widgetPointer.wiki.parseTiddler(title); if(parser) { var parseTreeNode = parser.tree[0]; while(parseTreeNode && parseTreeNode.type === "set") { - addWidgetNode({ + var node = { type: "set", attributes: parseTreeNode.attributes, params: parseTreeNode.params, isMacroDefinition: parseTreeNode.isMacroDefinition - }); + }; + if (parseTreeNode.isMacroDefinition) { + // Macro definitions can be folded into + // current widget instead of adding + // another link to the chain. + var widget = widgetPointer.makeChildWidget(node); + widget.computeAttributes(); + widget.execute(); + $tw.utils.extend(widgetPointer.variables,widget.variables); + } else { + widgetPointer.makeChildWidgets([node]); + widgetPointer = widgetPointer.children[0]; + } parseTreeNode = parseTreeNode.children && parseTreeNode.children[0]; } } }); - // Add our own children to the end of the pile - var parseTreeNodes; - if(widgetStackStart && widgetStackEnd) { - parseTreeNodes = [widgetStackStart]; - widgetStackEnd.children = this.parseTreeNode.children; + + if (widgetPointer != this) { + widgetPointer.parseTreeNode.children = this.parseTreeNode.children; } else { - parseTreeNodes = this.parseTreeNode.children; + widgetPointer.makeChildWidgets(); } - // Construct the child widgets - this.makeChildWidgets(parseTreeNodes); }; /* diff --git a/editions/test/tiddlers/tests/test-widget.js b/editions/test/tiddlers/tests/test-widget.js index 5875a3b44..5983b97dd 100755 --- a/editions/test/tiddlers/tests/test-widget.js +++ b/editions/test/tiddlers/tests/test-widget.js @@ -465,6 +465,58 @@ describe("Widget module", function() { expect(wrapper.innerHTML).toBe("

nothing

"); }); + /**This test confirms that imported set variables properly refresh + * if they use transclusion for their value. This relates to PR #4108. + */ + it("should refresh imported <$set> widgets", function() { + var wiki = new $tw.Wiki(); + // Add some tiddlers + wiki.addTiddlers([ + {title: "Raw", text: "Initial value"}, + {title: "Macro", text: "<$set name='test' value={{Raw}}>\n\ndummy text"}, + {title: "Caller", text: text} + ]); + var text = "\\import Macro\n<>"; + var widgetNode = createWidgetNode(parseText(text,wiki),wiki); + // Render the widget node to the DOM + var wrapper = renderWidgetNode(widgetNode); + // Test the rendering + expect(wrapper.innerHTML).toBe("

Initial value

"); + wiki.addTiddler({title: "Raw", text: "New value"}); + // Refresh + refreshWidgetNode(widgetNode,wrapper,["Raw"]); + expect(wrapper.innerHTML).toBe("

New value

"); + }); + + it("should can mix setWidgets and macros when importing", function() { + var wiki = new $tw.Wiki(); + // Add some tiddlers + wiki.addTiddlers([ + {title: "A", text: "\\define A() Aval"}, + {title: "B", text: "<$set name='B' value='Bval'>\n\ndummy text"}, + {title: "C", text: "\\define C() Cval"} + ]); + var text = "\\import A B C\n<> <> <>"; + var widgetNode = createWidgetNode(parseText(text,wiki),wiki); + // Render the widget node to the DOM + var wrapper = renderWidgetNode(widgetNode); + // Test the rendering + expect(wrapper.innerHTML).toBe("

Aval Bval Cval

"); + }); + + /** Special case. <$importvariables> has different handling if + * it doesn't end up importing any variables. Make sure it + * doesn't forget its childrenNodes. + */ + it("should work when import widget imports nothing", function() { + var wiki = new $tw.Wiki(); + var text = "\\import [prefix[XXX]]\nDon't forget me."; + var widgetNode = createWidgetNode(parseText(text,wiki),wiki); + // Render the widget node to the DOM + var wrapper = renderWidgetNode(widgetNode); + // Test the rendering + expect(wrapper.innerHTML).toBe("

Don't forget me.

"); + }); }); })();