From 3ae0f3666aed5c454ce3c69191d83b0bd81f1d07 Mon Sep 17 00:00:00 2001 From: Jermolene Date: Mon, 15 Feb 2016 11:38:55 +0000 Subject: [PATCH] Add Internals plugin Adds a live preview of the raw JSON of the parse tree or widget tree generated from a tiddler. --- .../prerelease/tiddlers/Release 5.1.12.tid | 8 +- editions/prerelease/tiddlywiki.info | 4 +- .../internals/editpreviews/parse-tree.tid | 9 ++ .../internals/editpreviews/shared.tid | 25 ++++ .../internals/editpreviews/widget-tree.tid | 9 ++ plugins/tiddlywiki/internals/plugin.info | 7 ++ plugins/tiddlywiki/internals/styles.tid | 4 + .../internals/widgets/parse-tree.js | 75 ++++++++++++ .../internals/widgets/widget-tree.js | 107 ++++++++++++++++++ 9 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 plugins/tiddlywiki/internals/editpreviews/parse-tree.tid create mode 100644 plugins/tiddlywiki/internals/editpreviews/shared.tid create mode 100644 plugins/tiddlywiki/internals/editpreviews/widget-tree.tid create mode 100644 plugins/tiddlywiki/internals/plugin.info create mode 100644 plugins/tiddlywiki/internals/styles.tid create mode 100644 plugins/tiddlywiki/internals/widgets/parse-tree.js create mode 100644 plugins/tiddlywiki/internals/widgets/widget-tree.js diff --git a/editions/prerelease/tiddlers/Release 5.1.12.tid b/editions/prerelease/tiddlers/Release 5.1.12.tid index ecaae0f15..68a82a4f4 100644 --- a/editions/prerelease/tiddlers/Release 5.1.12.tid +++ b/editions/prerelease/tiddlers/Release 5.1.12.tid @@ -13,6 +13,12 @@ type: text/vnd.tiddlywiki The experimental Evernote plugin allows notes and images from `.enex` files to be imported into TiddlyWiki. Install it through the "Plugins" tab of control panel. +!! "Internals" Plugin Introduction + +The new `tiddlywiki/internals` plugin provides features to help understand the internal operation of TiddlyWiki, including: + +* New preview modes in the editor showing both the parse and widget trees of the current tiddler + !! Text-Slicer Plugin Improvements * [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/176d2ccd76856b10aadd5e71af587574e7bcd447]] support for sticky notes within documents @@ -33,7 +39,7 @@ The experimental Evernote plugin allows notes and images from `.enex` files to b !! Hackability Improvements -* TBD +* [[Extended|https://github.com/Jermolene/TiddlyWiki5/commit/]] editor preview pane to allow plugins to add further types of preview (see the new `tiddlywiki/internals` plugin for an example) !! Bug Fixes diff --git a/editions/prerelease/tiddlywiki.info b/editions/prerelease/tiddlywiki.info index 632fc48f6..43d90dcb9 100644 --- a/editions/prerelease/tiddlywiki.info +++ b/editions/prerelease/tiddlywiki.info @@ -7,7 +7,9 @@ "tiddlywiki/browser-sniff", "tiddlywiki/help", "tiddlywiki/stacked-view", - "tiddlywiki/powered-by-tiddlywiki" + "tiddlywiki/powered-by-tiddlywiki", + "tiddlywiki/internals", + "tiddlywiki/highlight" ], "themes": [ "tiddlywiki/vanilla", diff --git a/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid b/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid new file mode 100644 index 000000000..803a67be5 --- /dev/null +++ b/plugins/tiddlywiki/internals/editpreviews/parse-tree.tid @@ -0,0 +1,9 @@ +title: $:/core/ui/EditTemplate/body/preview/parse-tree +tags: $:/tags/EditPreview +caption: parse tree + +\define preview(mode,output) +<$parse-tree mode="$mode$" output="$output$"/> +\end + +{{||$:/core/ui/EditTemplate/body/preview/shared}} diff --git a/plugins/tiddlywiki/internals/editpreviews/shared.tid b/plugins/tiddlywiki/internals/editpreviews/shared.tid new file mode 100644 index 000000000..3a8c05c6c --- /dev/null +++ b/plugins/tiddlywiki/internals/editpreviews/shared.tid @@ -0,0 +1,25 @@ +title: $:/core/ui/EditTemplate/body/preview/shared + +\define body() + +Mode: <$select tiddler="$(tv-mode-configuration)$" default="block"> + + + + +<$macrocall $name="preview" mode={{$(tv-mode-configuration)$}} output={{$(tv-output-configuration)$}}/> +\end + +
+ +
+ +<$vars tv-mode-configuration=<>> + +<> + + + +
+ +
diff --git a/plugins/tiddlywiki/internals/editpreviews/widget-tree.tid b/plugins/tiddlywiki/internals/editpreviews/widget-tree.tid new file mode 100644 index 000000000..57a75632a --- /dev/null +++ b/plugins/tiddlywiki/internals/editpreviews/widget-tree.tid @@ -0,0 +1,9 @@ +title: $:/core/ui/EditTemplate/body/preview/widget-tree +tags: $:/tags/EditPreview +caption: widget tree + +\define preview(mode,output) +<$widget-tree mode="$mode$" output="$output$"/> +\end + +{{||$:/core/ui/EditTemplate/body/preview/shared}} diff --git a/plugins/tiddlywiki/internals/plugin.info b/plugins/tiddlywiki/internals/plugin.info new file mode 100644 index 000000000..78634903c --- /dev/null +++ b/plugins/tiddlywiki/internals/plugin.info @@ -0,0 +1,7 @@ +{ + "title": "$:/plugins/tiddlywiki/internals", + "description": "Tools for exploring the internals of TiddlyWiki", + "author": "JeremyRuston", + "core-version": ">=5.0.0", + "list": "readme" +} diff --git a/plugins/tiddlywiki/internals/styles.tid b/plugins/tiddlywiki/internals/styles.tid new file mode 100644 index 000000000..73f3f068d --- /dev/null +++ b/plugins/tiddlywiki/internals/styles.tid @@ -0,0 +1,4 @@ +title: $:/plugins/tiddlywiki/internals/styles +tags: $:/tags/Stylesheet + +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock diff --git a/plugins/tiddlywiki/internals/widgets/parse-tree.js b/plugins/tiddlywiki/internals/widgets/parse-tree.js new file mode 100644 index 000000000..3de42464e --- /dev/null +++ b/plugins/tiddlywiki/internals/widgets/parse-tree.js @@ -0,0 +1,75 @@ +/*\ +title: $:/core/modules/widgets/parse-tree.js +type: application/javascript +module-type: widget + +Widget to render the parse tree of a tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var ParseTreeWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +ParseTreeWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +ParseTreeWidget.prototype.render = function(parent,nextSibling) { + var self = this; + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + this.renderChildren(parent,nextSibling); +}; + +/* +Compute the internal state of the widget +*/ +ParseTreeWidget.prototype.execute = function() { + // Get our parameters + this.parseTreeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); + this.parseTreeInlineMode = this.getAttribute("mode","block") === "inline"; + // Compute the parse tree + var parser = this.wiki.parseTiddler(this.parseTreeTitle,{parseAsInline: this.parseTreeInlineMode}), + parseTreeNodes = []; + if(parser) { + parseTreeNodes = [{ + type: "codeblock", + attributes: { + code: {type: "string", value: JSON.stringify(parser.tree,0,$tw.config.preferences.jsonSpaces)}, + language: {type: "string", value: "json"} + } + }]; + } + // Make the child widgets + this.makeChildWidgets(parseTreeNodes); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +ParseTreeWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + // Completely rerender if any of our attributes have changed + if(changedAttributes.tiddler || changedAttributes.mode || changedTiddlers[this.parseTreeTitle]) { + this.refreshSelf(); + return true; + } + return false; +}; + +exports["parse-tree"] = ParseTreeWidget; + +})(); diff --git a/plugins/tiddlywiki/internals/widgets/widget-tree.js b/plugins/tiddlywiki/internals/widgets/widget-tree.js new file mode 100644 index 000000000..00ca2b74e --- /dev/null +++ b/plugins/tiddlywiki/internals/widgets/widget-tree.js @@ -0,0 +1,107 @@ +/*\ +title: $:/core/modules/widgets/widget-tree.js +type: application/javascript +module-type: widget + +Widget to render the widget tree of a tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var WidgetTreeWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +WidgetTreeWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +WidgetTreeWidget.prototype.render = function(parent,nextSibling) { + var self = this; + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + this.renderChildren(parent,nextSibling); +}; + +/* +Compute the internal state of the widget +*/ +WidgetTreeWidget.prototype.execute = function() { + // Get our parameters + this.widgetTreeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); + this.widgetTreeInlineMode = this.getAttribute("mode","block") === "inline"; + // Compute the widget tree + var parser = this.wiki.parseTiddler(this.widgetTreeTitle,{parseAsInline: this.widgetTreeInlineMode}), + results; + if(parser) { + var widgetNode = this.wiki.makeWidget(parser,{ + parentWidget: this + }), + container = $tw.fakeDocument.createElement("div"), + copyNode = function(widgetNode,resultNode) { + var type = widgetNode.parseTreeNode.type; + resultNode.type = type; + switch(type) { + case "element": + resultNode.tag = widgetNode.parseTreeNode.tag; + break; + case "text": + resultNode.text = widgetNode.parseTreeNode.text; + break; + } + if(Object.keys(widgetNode.attributes || {}).length > 0) { + resultNode.attributes = {}; + $tw.utils.each(widgetNode.attributes,function(attr,attrName) { + resultNode.attributes[attrName] = widgetNode.getAttribute(attrName); + }); + } + if(Object.keys(widgetNode.children || {}).length > 0) { + resultNode.children = []; + $tw.utils.each(widgetNode.children,function(widgetChildNode) { + var node = {}; + resultNode.children.push(node); + copyNode(widgetChildNode,node); + }); + } + }; + widgetNode.render(container,null); + results = {}; + copyNode(widgetNode,results); + } + // Make the child widgets + this.makeChildWidgets([{ + type: "codeblock", + attributes: { + code: {type: "string", value: (results && results.children) ? JSON.stringify(results.children,0,$tw.config.preferences.jsonSpaces) : ""}, + language: {type: "string", value: "json"} + } + }]); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +WidgetTreeWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + // Completely rerender if any of our attributes have changed + if(changedAttributes.tiddler || changedAttributes.mode || changedTiddlers[this.widgetTreeTitle]) { + this.refreshSelf(); + return true; + } + return false; +}; + +exports["widget-tree"] = WidgetTreeWidget; + +})();