From 6bd4127e883e1165670af477aae5125d7d6e416e Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 19 Apr 2023 11:55:25 +0100 Subject: [PATCH] Parameterised transclusions (#6666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial commit Everything is draft. * Fix test execution * Fix and test missing target handling * Use the ubertransclude widget for the wikitext transclusion syntax * Changed transclude widget in binary parser to ubertransclude * Add a test for custom action widgets * Don't worry about ordered attributes The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes * Remove need to explicitly clear widget mapping variable when invoking overridden widget * Use ts- prefix for system slot names * Add a definition for the value widget just so that it doesn't cause errors Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget... * Add support for positional parameters * Ubertransclusion positional parameters should be based on name, not position * Add support for shortcut syntax for positional transclusion parameters * Importvariables should skip parameters widgets * Refactor transclude widget before uberfying it * Refactor ubertransclude functionality into transclude widget * Replace ubertransclude widget with transclude widget * Add wikitext shortcut for new-style function definitions * Allow brackets to be omitted for function definitions with no parameters * Add pragma rule for parameters declarations * Remove erroneous "tag" property * Add support for accessing function parameters as name/value pairs * Be as permissive as possible with parameter names Previously restricted to upper and lower case, digits and dash and underscore * Rewrite some tests to use the shortcut syntaxes * Mustn't allow commas in parameter names * Fix crash when transcluding an undefined variable Thanks @pmario See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359 * Unquoted parameters should not eat a succeeding comma Fixes #6672 * Remove extraneous code * Allow the let widget to create variables starting with $ * Fix addAttributeToParseTreeNode handling of ordered attributes * Reuse attribute objects when executing custom widgets * Fix importing of function definitions * Fix parameter handling * Introduce genesis widget for dynamically creating widgets See the "RedefineLet" test for a contrived example of usage * Change tiddler separator used in wikitext tests Underscore looked ambiguous; I kept typing dashes by accident * Cache parse trees when transcluding variables * Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code. * Fixes to enable the transclude widget itself to be overridden There are two big changes here: Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped. We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value. * Fix genesis widget example * Use enlist:raw to preserve duplicates * Don't create variables with value undefined for missing parameters * Fix variable retrieval bug with test harness * Improve recursion detection While retaining backwards compatibility * Experimental support for custom filter operators Just as we can define custom widgets we can also define custom parameterised filter operators * Add visible transclusions component and demo Very useful to see transclusions explicitly Makes a good demo of a super-complicated widget override. * Genesis widget should pass raw attributes onto child widget... ...so that it can more efficiently handle refreshing itself. * Use consistent parse tree node property for params * Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax * Clarify that the recent changes allow functions to be invoked with the double bracket syntax In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately * Make the macrocall widget delegate to the transclude widget * Switch to using \procedure to define new-style macros, and \function for custom filter operator functions I now need to update the OP! * Fix visible transclusion example * Remove obsolete code Left over after refactoring * Better backwards compatibility for legacy recursion marker Fixes the problem with tag dropdowns @btheado * Fix stringifying/parsing string arrays containing newlines A very old bug. Fixes the ActionListOpsWidget problem @btheado * Transclude: replace paramNames/paramValues with more robust JSON payload More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153 * Rename internal "unknown" filter operator so that users cannot invoke it * Detect recursion by tracking widget tree depth The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid * Use \widget for custom widget definitions, and remove need for angle brackets Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property * Rename <$value> widget to <$fill> * Require $$ for custom widgets, and that overridden JS widgets must exist See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763 * Fix invocation of JS macros * Experimental update of the parse-tree preview visualisation An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget. As usual with these experiments, this one is going to require quite a lot more work to finish up: * The formatting is via direct styles rather than classes * The formatting for attributes and properties is not yet completed * The same thing needs to also be done to the widget tree preview * Procedures and widgets inherit whitespace trim setting from their definition * Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a * Require period prefix for custom filter operator functions To ensure that custom filter operators cannot clash with future core operators. * Allow custom functions to be invoked as attributes * WIP * Remove unneeded test tiddler * Make is[variable] and variables[] operators resilient to fake widgets * Fix importvariables to work with setvariables as well as set (they are aliases) * Add support for $:/tags/Global * Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03 * Add utility function for parsing macro parameter definitions * Introduce true global variables The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists. This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro. In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro * Convert the tabs macro into a global So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR. * Change to `?` for conditional definitions * Fix tabs global so it doesn't crash when viewed directly * Test showing how to un-override a core widget * Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503 * Minor cleanups * Clean up unknown filter * Introduce function operator for calling functions Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter). * Use underscores for new system fields for global variable tiddlers For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code. * 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. * Fix refreshing of global variables Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions. * Remove support for $:/tags/Global It is not needed now that we have true global variables * Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93 * Make slot fill data available to transclusions Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion * Mark docs as v5.3.0 * Simplify metaparameters implementation * Fix typo * Adjust naming of transclusion metaparameter * Fix up handling of slot/fill for custom widgets Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget * Add format:json operator I've been finding this useful for debugging, and it kind of goes with the JSON operators * Docs: JSON operators and tweaks to genesis widget * Docs: format:json Also tweak to the behaviour of format:json if the input string is not valid JSON * Fix #6721 * Revert "Fix #6721" This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch * Fix new selection tracker to return relative coordinates * Make use of type attribute consistent * Docs: Transclude widget * Simplify the fill widget We can rely on the default processing in the base class * Slot widget: be more defensive about negative depth values * Parameters widget: Be defensive about negative depths * Protect against excessively recursive functions * FIx transcluding of functions This first implementation concatenates the results of the filter (with no separator) and then wikifies the result. The test in this commit is quite interesting... * Tweak semantics of JSON operators to match #6932 This allows us to later bring in the optimisations without breaking backwards compatibility. * Revert obsolete changes to boot.js * Fix inadvertent whitespace change * Remove tests related to obsolete changes to boot.js Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8 * Revert changes to parse tree preview This implementation requires #6666 * Add test to show that global widgets need not use the _parameters field * Disable overriding core widgets in safe mode * Coding style tweak * More comments * Fix caching of parse variables/macros/procedures * Transcluded functions should operate on the entire store * Refactor filter recursion detection to avoid an unneeded wrapper function * Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993 * WIP * Revert "WIP" This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7. * When transcluding functions, pass an empty item list to the filter, and just return the first item * Rejig genesis widget to be easier to use * Parameters widget: protect against negative $depth * Docs updates * Docs updates * Tweak comments * Add custom view template body for globals, and a new sidebar tab under "more" And also a custom view template title that greys out the $:/global/ part of the title * Update function operator to return the input list if the function is missing * Remove negation from function operator This implementation was not useful. * Tests and docs for function operator * Docs tweaks * Improve indentation See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251 * Missing tests for parameters widget * Fix visible transclude * Docs update * Docs typo * Huge Documentation Update Not quite finished, but definitely on the home stretch * Slight optimisation to user defined widgets * Remove implementation of $:/globals/ Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created. * Docs clarification * Docs update * Some widget.js cleanups * Remove support for conditional definitions It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36 * Docs updates * Revert change to setwidget docs * Docs update * Docs updates * Clarify/simplify some tests * More docs updates * Fix doc file locations * Docs updates * Revert modified date of docs that have only had minor tweaks * Docs typo https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220 Thanks @btheado * Transcluding functions: fix missing parameters passed as undefined Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372 * Parameter parenthesis should be mandatory in function/procedure/widget definitions See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387 * Attempt to build this branch with CI * Add release note etc for 5.3.0 * Temporary new release banner for v5.3.0 * New New Release Banner * New test for undefined parameters * Adjust modified times of docs tiddlers to make them easier to find * Update release note * Add parenthesis to the visible transclusion definition Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65 Fixes #6998 * Fix macrocall refresh issue It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable Fixes #7001 * Filter run prefixes should use widget.makeFakeWidgetWithVariables * Docs typo Thanks @twMat * Docs: clarify function operator invocation See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599 * Docs: Update \define pragma to cover named ends * Docs: move tiddlers to correct directory * Add support for named end markers for procedures, functions and widgets * Docs note about nested macro definitions * Rename test * Fix detection of empty transclusions See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston * New test missed off a45349cc996390192114fed486bfa6900da641d7 * Refactor wikified function tests * Refactor function invocation * Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text * Refactor the function operator and unknown operator to use the new helper * Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated * Refactor the transclude widget to use the new helper * Update tests * Fix positional parameters in widget.evaluateVariable() This should clear up the remaining anomalies in #7009, let me know how you get on @btheado * Remove 5.2.8 release note * Fix nonstandard initialisation code for fill/parameter/slot widgets * Update modification times of doc tiddlers So that they are at the top of the recent tab * Update 5.3.0 release note * Remove custom CI step for this branch * Restore standard sitetitle --- core/modules/filterrunprefixes/cascade.js | 18 +- core/modules/filterrunprefixes/filter.js | 24 +- core/modules/filterrunprefixes/map.js | 24 +- core/modules/filterrunprefixes/reduce.js | 26 +- core/modules/filterrunprefixes/sort.js | 18 +- core/modules/filters.js | 8 +- core/modules/filters/filter.js | 17 +- core/modules/filters/function.js | 32 ++ core/modules/filters/reduce.js | 29 +- core/modules/filters/sortsub.js | 17 +- core/modules/filters/unknown.js | 45 +++ core/modules/parsers/audioparser.js | 2 + core/modules/parsers/binaryparser.js | 6 +- core/modules/parsers/csvparser.js | 2 + core/modules/parsers/htmlparser.js | 2 + core/modules/parsers/imageparser.js | 2 + core/modules/parsers/parseutils.js | 61 ++- core/modules/parsers/pdfparser.js | 2 + core/modules/parsers/textparser.js | 2 + core/modules/parsers/videoparser.js | 2 + .../parsers/wikiparser/rules/fnprocdef.js | 97 +++++ core/modules/parsers/wikiparser/rules/html.js | 3 - .../wikiparser/rules/macrocallblock.js | 2 +- .../wikiparser/rules/macrocallinline.js | 2 +- .../parsers/wikiparser/rules/macrodef.js | 10 +- .../parsers/wikiparser/rules/parameters.js | 60 +++ .../wikiparser/rules/transcludeblock.js | 23 +- .../wikiparser/rules/transcludeinline.js | 23 +- core/modules/parsers/wikiparser/wikiparser.js | 3 +- core/modules/widgets/fill.js | 30 ++ core/modules/widgets/importvariables.js | 58 +-- core/modules/widgets/macrocall.js | 61 +-- core/modules/widgets/parameters.js | 96 +++++ core/modules/widgets/setvariable.js | 12 +- core/modules/widgets/slot.js | 82 ++++ core/modules/widgets/transclude.js | 354 ++++++++++++++++-- core/modules/widgets/widget.js | 204 ++++++++-- core/modules/wiki.js | 27 +- core/ui/Components/VisibleTransclude.tid | 48 +++ core/wiki/macros/tabs.tid | 2 +- .../prerelease/tiddlers/Release 5.2.8.tid | 60 --- .../prerelease/tiddlers/Release 5.3.0.tid | 83 ++++ .../custom-operators/NestedParameterised.tid | 24 ++ .../data/custom-operators/Parameterised.tid | 24 ++ .../tests/data/custom-operators/Simple.tid | 21 ++ .../data/functions/FunctionAttributes.tid | 24 ++ .../tests/data/functions/FunctionOperator.tid | 24 ++ .../tests/data/functions/MissingFunction.tid | 15 + .../functions/RunawayRecursiveFunctions.tid | 18 + .../data/functions/UndefinedParameters.tid | 22 ++ .../data/functions/WikifiedFunctions.tid | 36 ++ .../tests/data/genesis-widget/RedefineLet.tid | 31 ++ .../tiddlers/tests/data/procedures/Nested.tid | 20 + .../transclude/CustomWidget-ActionWidget.tid | 27 ++ .../data/transclude/CustomWidget-Fail.tid | 26 ++ .../CustomWidget-Override-Codeblock.tid | 29 ++ .../CustomWidget-OverrideTransclude.tid | 33 ++ .../data/transclude/CustomWidget-Simple.tid | 33 ++ .../transclude/CustomWidget-Slotted-Empty.tid | 20 + .../data/transclude/CustomWidget-Slotted.tid | 27 ++ .../CustomWidget-TextWidgetOverride.tid | 27 ++ ...ustomWidget-TextWidgetOverrideWithSlot.tid | 31 ++ .../CustomWidget-Unoverride-Codeblock.tid | 31 ++ .../CustomWidget-VariableAttribute.tid | 29 ++ .../data/transclude/JavaScript-Macro.tid | 17 + .../tests/data/transclude/Macro-Plain.tid | 17 + .../tests/data/transclude/Macro-Simple.tid | 26 ++ .../tests/data/transclude/MissingTarget.tid | 48 +++ .../data/transclude/Parameterised-Depth.tid | 34 ++ .../data/transclude/Parameterised-Mode.tid | 29 ++ .../transclude/Parameterised-Name-Values.tid | 34 ++ .../Parameterised-ParseTreeNodes.tid | 29 ++ ...terised-Positional-Shortcut-Parameters.tid | 29 ++ .../Parameterised-Positional-Shortcut.tid | 29 ++ .../Parameterised-Positional-Variables.tid | 30 ++ .../transclude/Parameterised-Positional.tid | 26 ++ .../Parameterised-Shortcut-Parameters.tid | 20 + .../transclude/Parameterised-Shortcut.tid | 21 ++ .../data/transclude/Parameterised-Simple.tid | 26 ++ .../Parameterised-SlotFillParseTreeNodes.tid | 29 ++ .../Parameterised-Slotted-Missing.tid | 24 ++ .../data/transclude/Parameterised-Slotted.tid | 27 ++ .../data/transclude/Procedures-Whitespace.tid | 25 ++ .../tiddlers/tests/data/transclude/Typed.tid | 38 ++ editions/test/tiddlers/tests/test-filters.js | 4 +- .../tiddlers/tests/test-parsetextreference.js | 2 +- editions/test/tiddlers/tests/test-utils.js | 2 +- editions/test/tiddlers/tests/test-widget.js | 20 +- .../tiddlers/tests/test-wikitext-parser.js | 106 ++++-- .../tests/test-wikitext-tabs-macro.js | 4 +- .../tiddlers/{ => concepts}/Brackets.tid | 2 +- editions/tw5.com/tiddlers/concepts/Macros.tid | 39 +- editions/tw5.com/tiddlers/concepts/Pragma.tid | 24 +- .../tiddlers/features/StartupActions.tid | 2 +- .../tw5.com/tiddlers/filters/function.tid | 21 ++ .../tw5.com/tiddlers/functions/Functions.tid | 28 ++ .../tiddlers/howtos/Visible Transclusions.tid | 14 + .../tiddlers/images/New Release Banner.png | Bin 81675 -> 50280 bytes .../macros/import/say-hi-using-variables.tid | 1 + .../tw5.com/tiddlers/macros/import/say-hi.tid | 3 +- .../macros/import/tags-of-current-tiddler.tid | 1 + .../macros/import/tv-wikilink-tooltip.tid | 2 +- .../tiddlers/pragmas/Pragma_ _define.tid | 51 +++ .../tiddlers/pragmas/Pragma_ _function.tid | 27 ++ .../tiddlers/pragmas/Pragma_ _import.tid | 17 + .../tiddlers/pragmas/Pragma_ _parameters.tid | 18 + .../tiddlers/pragmas/Pragma_ _procedure.tid | 55 +++ .../tiddlers/pragmas/Pragma_ _rules.tid | 25 ++ .../tiddlers/pragmas/Pragma_ _whitespace.tid | 20 + .../tiddlers/pragmas/Pragma_ _widget.tid | 27 ++ editions/tw5.com/tiddlers/pragmas/Pragmas.tid | 13 + .../tiddlers/procedures/Procedure Calls.tid | 56 +++ .../procedures/Procedure Definitions.tid | 44 +++ .../Procedure Parameter Handling.tid | 25 ++ .../tiddlers/procedures/Procedures.tid | 35 ++ .../tw5.com/tiddlers/variables/Variables.tid | 139 ++++++- .../tiddlers/widgets/Custom Widgets.tid | 84 +++++ .../tw5.com/tiddlers/widgets/ErrorWidget.tid | 2 +- .../tw5.com/tiddlers/widgets/FillWidget.tid | 21 ++ .../widgets/ImportVariablesWidget.tid | 2 +- .../tiddlers/widgets/MacroCallWidget.tid | 37 +- .../tiddlers/widgets/ParametersWidget.tid | 61 +++ .../tw5.com/tiddlers/widgets/SlotWidget.tid | 19 + .../tiddlers/widgets/TranscludeWidget.tid | 172 ++++++++- .../tiddlers/wikitext/HTML in WikiText.tid | 4 +- .../wikitext/Macro Calls in WikiText.tid | 26 +- .../tw5.com/tiddlers/wikitext/Macro Calls.tid | 58 +++ .../Macro Definitions in WikiText.tid | 113 +----- .../tiddlers/wikitext/Macro Definitions.tid | 68 ++++ .../wikitext/Macro Parameter Handling.tid | 79 ++++ .../tiddlers/wikitext/Macro Pitfalls.tid | 39 ++ .../tiddlers/wikitext/Macros in WikiText.tid | 8 +- .../Transclusion and Substitution.tid | 4 +- .../wikitext/Transclusion in WikiText.tid | 4 +- .../wikitext/Variables in WikiText.tid | 33 +- .../wikitext/parser/Inline Mode WikiText.tid | 6 +- ...aces where the parser ignores WikiText.tid | 6 +- .../wikitext/parser/WikiText Parser Modes.tid | 2 +- .../WikiText parser mode transitions.tid | 4 +- .../WikiText parser mode_ macro examples.tid | 2 +- package.json | 2 +- themes/tiddlywiki/vanilla/base.tid | 30 ++ 142 files changed, 3869 insertions(+), 653 deletions(-) create mode 100644 core/modules/filters/function.js create mode 100644 core/modules/filters/unknown.js create mode 100644 core/modules/parsers/wikiparser/rules/fnprocdef.js create mode 100644 core/modules/parsers/wikiparser/rules/parameters.js create mode 100644 core/modules/widgets/fill.js create mode 100644 core/modules/widgets/parameters.js create mode 100644 core/modules/widgets/slot.js create mode 100644 core/ui/Components/VisibleTransclude.tid delete mode 100644 editions/prerelease/tiddlers/Release 5.2.8.tid create mode 100644 editions/prerelease/tiddlers/Release 5.3.0.tid create mode 100644 editions/test/tiddlers/tests/data/custom-operators/NestedParameterised.tid create mode 100644 editions/test/tiddlers/tests/data/custom-operators/Parameterised.tid create mode 100644 editions/test/tiddlers/tests/data/custom-operators/Simple.tid create mode 100644 editions/test/tiddlers/tests/data/functions/FunctionAttributes.tid create mode 100644 editions/test/tiddlers/tests/data/functions/FunctionOperator.tid create mode 100644 editions/test/tiddlers/tests/data/functions/MissingFunction.tid create mode 100644 editions/test/tiddlers/tests/data/functions/RunawayRecursiveFunctions.tid create mode 100644 editions/test/tiddlers/tests/data/functions/UndefinedParameters.tid create mode 100644 editions/test/tiddlers/tests/data/functions/WikifiedFunctions.tid create mode 100644 editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid create mode 100644 editions/test/tiddlers/tests/data/procedures/Nested.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-ActionWidget.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Fail.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Override-Codeblock.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-OverrideTransclude.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Simple.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted-Empty.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverride.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverrideWithSlot.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-Unoverride-Codeblock.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/CustomWidget-VariableAttribute.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/JavaScript-Macro.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Macro-Plain.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Macro-Simple.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/MissingTarget.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Depth.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Mode.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-ParseTreeNodes.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut-Parameters.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Variables.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Positional.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut-Parameters.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-SlotFillParseTreeNodes.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted-Missing.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Procedures-Whitespace.tid create mode 100644 editions/test/tiddlers/tests/data/transclude/Typed.tid rename editions/tw5.com/tiddlers/{ => concepts}/Brackets.tid (92%) create mode 100644 editions/tw5.com/tiddlers/filters/function.tid create mode 100644 editions/tw5.com/tiddlers/functions/Functions.tid create mode 100644 editions/tw5.com/tiddlers/howtos/Visible Transclusions.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _function.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _import.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _parameters.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _procedure.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _rules.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _whitespace.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragma_ _widget.tid create mode 100644 editions/tw5.com/tiddlers/pragmas/Pragmas.tid create mode 100644 editions/tw5.com/tiddlers/procedures/Procedure Calls.tid create mode 100644 editions/tw5.com/tiddlers/procedures/Procedure Definitions.tid create mode 100644 editions/tw5.com/tiddlers/procedures/Procedure Parameter Handling.tid create mode 100644 editions/tw5.com/tiddlers/procedures/Procedures.tid create mode 100644 editions/tw5.com/tiddlers/widgets/Custom Widgets.tid create mode 100644 editions/tw5.com/tiddlers/widgets/FillWidget.tid create mode 100644 editions/tw5.com/tiddlers/widgets/ParametersWidget.tid create mode 100644 editions/tw5.com/tiddlers/widgets/SlotWidget.tid create mode 100644 editions/tw5.com/tiddlers/wikitext/Macro Calls.tid create mode 100644 editions/tw5.com/tiddlers/wikitext/Macro Definitions.tid create mode 100644 editions/tw5.com/tiddlers/wikitext/Macro Parameter Handling.tid create mode 100644 editions/tw5.com/tiddlers/wikitext/Macro Pitfalls.tid diff --git a/core/modules/filterrunprefixes/cascade.js b/core/modules/filterrunprefixes/cascade.js index da6894d21..486e75f45 100644 --- a/core/modules/filterrunprefixes/cascade.js +++ b/core/modules/filterrunprefixes/cascade.js @@ -25,20 +25,10 @@ exports.cascade = function(operationSubFunction,options) { if(!filterFnList[index]) { filterFnList[index] = options.wiki.compileFilter(filter); } - var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - opts.variables = { - "currentTiddler": "" + title, - "..currentTiddler": widget.getVariable("currentTiddler") - }; - if(name in opts.variables) { - return opts.variables[name]; - } else { - return widget.getVariable(name,opts); - } - } - }); + var output = filterFnList[index](options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": widget.getVariable("currentTiddler","") + })); if(output.length !== 0) { result = output[0]; return false; diff --git a/core/modules/filterrunprefixes/filter.js b/core/modules/filterrunprefixes/filter.js index 783b699c2..4ab057109 100644 --- a/core/modules/filterrunprefixes/filter.js +++ b/core/modules/filterrunprefixes/filter.js @@ -19,23 +19,13 @@ exports.filter = function(operationSubFunction,options) { var resultsToRemove = [], index = 0; results.each(function(title) { - var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - opts.variables = { - "currentTiddler": "" + title, - "..currentTiddler": widget.getVariable("currentTiddler"), - "index": "" + index, - "revIndex": "" + (results.length - 1 - index), - "length": "" + results.length - }; - if(name in opts.variables) { - return opts.variables[name]; - } else { - return widget.getVariable(name,opts); - } - } - }); + var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": widget.getVariable("currentTiddler",""), + "index": "" + index, + "revIndex": "" + (results.length - 1 - index), + "length": "" + results.length + })); if(filtered.length === 0) { resultsToRemove.push(title); } diff --git a/core/modules/filterrunprefixes/map.js b/core/modules/filterrunprefixes/map.js index efcb5b534..b756d6699 100644 --- a/core/modules/filterrunprefixes/map.js +++ b/core/modules/filterrunprefixes/map.js @@ -21,23 +21,13 @@ exports.map = function(operationSubFunction,options) { flatten = (suffixes[0] && suffixes[0][0] === "flat") ? true : false; results.clear(); $tw.utils.each(inputTitles,function(title) { - var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - opts.variables = { - "currentTiddler": "" + title, - "..currentTiddler": widget.getVariable("currentTiddler"), - "index": "" + index, - "revIndex": "" + (inputTitles.length - 1 - index), - "length": "" + inputTitles.length - }; - if(name in opts.variables) { - return opts.variables[name]; - } else { - return widget.getVariable(name,opts); - } - } - }); + var filtered = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": widget.getVariable("currentTiddler",""), + "index": "" + index, + "revIndex": "" + (inputTitles.length - 1 - index), + "length": "" + inputTitles.length + })); if(filtered.length && flatten) { $tw.utils.each(filtered,function(value) { results.push(value); diff --git a/core/modules/filterrunprefixes/reduce.js b/core/modules/filterrunprefixes/reduce.js index 8fe819e3f..ee2998837 100644 --- a/core/modules/filterrunprefixes/reduce.js +++ b/core/modules/filterrunprefixes/reduce.js @@ -18,24 +18,14 @@ exports.reduce = function(operationSubFunction,options) { var accumulator = "", index = 0; results.each(function(title) { - var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - opts.variables = { - "currentTiddler": "" + title, - "..currentTiddler": widget.getVariable("currentTiddler"), - "index": "" + index, - "revIndex": "" + (results.length - 1 - index), - "length": "" + results.length, - "accumulator": "" + accumulator - }; - if(name in opts.variables) { - return opts.variables[name]; - } else { - return widget.getVariable(name,opts); - } - } - }); + var list = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": widget.getVariable("currentTiddler"), + "index": "" + index, + "revIndex": "" + (results.length - 1 - index), + "length": "" + results.length, + "accumulator": "" + accumulator + })); if(list.length > 0) { accumulator = "" + list[0]; } diff --git a/core/modules/filterrunprefixes/sort.js b/core/modules/filterrunprefixes/sort.js index 6865b175c..d8d376126 100644 --- a/core/modules/filterrunprefixes/sort.js +++ b/core/modules/filterrunprefixes/sort.js @@ -25,20 +25,10 @@ exports.sort = function(operationSubFunction,options) { indexes = new Array(inputTitles.length), compareFn; results.each(function(title) { - var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - opts.variables = { - "currentTiddler": "" + title, - "..currentTiddler": widget.getVariable("currentTiddler") - }; - if(name in opts.variables) { - return opts.variables[name]; - } else { - return widget.getVariable(name,opts); - } - } - }); + var key = operationSubFunction(options.wiki.makeTiddlerIterator([title]),widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": widget.getVariable("currentTiddler") + })); sortKeys.push(key[0] || ""); }); results.clear(); diff --git a/core/modules/filters.js b/core/modules/filters.js index 1bb5fe9ff..b705c994c 100644 --- a/core/modules/filters.js +++ b/core/modules/filters.js @@ -255,19 +255,21 @@ exports.compileFilter = function(filterString) { var operands = [], operatorFunction; if(!operator.operator) { + // Use the "title" operator if no operator is specified operatorFunction = filterOperators.title; } else if(!filterOperators[operator.operator]) { - operatorFunction = filterOperators.field; + // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator + operatorFunction = filterOperators["[unknown]"]; } else { + // Use the operator function operatorFunction = filterOperators[operator.operator]; } - $tw.utils.each(operator.operands,function(operand) { if(operand.indirect) { operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); } else if(operand.variable) { var varTree = $tw.utils.parseFilterVariable(operand.text); - operand.value = widget.getVariable(varTree.name,{params:varTree.params,defaultValue: ""}); + operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || ""; } else { operand.value = operand.text; } diff --git a/core/modules/filters/filter.js b/core/modules/filters/filter.js index 9b69fd83a..f15cbefc5 100644 --- a/core/modules/filters/filter.js +++ b/core/modules/filters/filter.js @@ -20,19 +20,10 @@ exports.filter = function(source,operator,options) { results = [], target = operator.prefix !== "!"; source(function(tiddler,title) { - var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),{ - getVariable: function(name,opts) { - opts = opts || {}; - switch(name) { - case "currentTiddler": - return "" + title; - case "..currentTiddler": - return options.widget.getVariable("currentTiddler"); - default: - return options.widget.getVariable(name,opts); - } - } - }); + var list = filterFn.call(options.wiki,options.wiki.makeTiddlerIterator([title]),options.widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": options.widget.getVariable("currentTiddler","") + })); if((list.length > 0) === target) { results.push(title); } diff --git a/core/modules/filters/function.js b/core/modules/filters/function.js new file mode 100644 index 000000000..f6a8c034d --- /dev/null +++ b/core/modules/filters/function.js @@ -0,0 +1,32 @@ +/*\ +title: $:/core/modules/filters/function.js +type: application/javascript +module-type: filteroperator + +Filter operator returning those input titles that are returned from a function + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.function = function(source,operator,options) { + var functionName = operator.operands[0], + variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName); + if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { + return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source}); + } + // Return the input list if the function wasn't found + var results = []; + source(function(tiddler,title) { + results.push(title); + }); + return results; +}; + +})(); diff --git a/core/modules/filters/reduce.js b/core/modules/filters/reduce.js index 50c501f08..efe8aea4a 100644 --- a/core/modules/filters/reduce.js +++ b/core/modules/filters/reduce.js @@ -26,27 +26,14 @@ exports.reduce = function(source,operator,options) { accumulator = operator.operands[1] || ""; for(var index=0; index 0) { accumulator = "" + list[0]; } diff --git a/core/modules/filters/sortsub.js b/core/modules/filters/sortsub.js index e9f676daa..d328be09c 100644 --- a/core/modules/filters/sortsub.js +++ b/core/modules/filters/sortsub.js @@ -25,19 +25,10 @@ exports.sortsub = function(source,operator,options) { inputTitles.push(title); var r = filterFn.call(options.wiki,function(iterator) { iterator(options.wiki.getTiddler(title),title); - },{ - getVariable: function(name,opts) { - opts = opts || {}; - switch(name) { - case "currentTiddler": - return "" + title; - case "..currentTiddler": - return options.widget.getVariable("currentTiddler"); - default: - return options.widget.getVariable(name,opts); - } - } - }); + },options.widget.makeFakeWidgetWithVariables({ + "currentTiddler": "" + title, + "..currentTiddler": options.widget.getVariable("currentTiddler") + })); sortKeys.push(r[0] || ""); }); // Rather than sorting the titles array, we'll sort the indexes so that we can consult both arrays diff --git a/core/modules/filters/unknown.js b/core/modules/filters/unknown.js new file mode 100644 index 000000000..21856766b --- /dev/null +++ b/core/modules/filters/unknown.js @@ -0,0 +1,45 @@ +/*\ +title: $:/core/modules/filters/unknown.js +type: application/javascript +module-type: filteroperator + +Filter operator for handling unknown filter operators. + +Not intended to be used directly by end users, hence the square brackets around the name. + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var fieldFilterOperatorFn = require("$:/core/modules/filters/field.js").field; + +/* +Export our filter function +*/ +exports["[unknown]"] = function(source,operator,options) { + // Check for a user defined filter operator + if(operator.operator.charAt(0) === ".") { + var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator); + if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { + var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source}); + if(operator.prefix === "!") { + var results = []; + source(function(tiddler,title) { + if(list.indexOf(title) === -1) { + results.push(title); + } + }); + return results; + } else { + return list; + } + } + } + // Otherwise, use the "field" operator + return fieldFilterOperatorFn(source,operator,options); +}; + +})(); diff --git a/core/modules/parsers/audioparser.js b/core/modules/parsers/audioparser.js index 95380bf80..5eb2ff985 100644 --- a/core/modules/parsers/audioparser.js +++ b/core/modules/parsers/audioparser.js @@ -28,6 +28,8 @@ var AudioParser = function(type,text,options) { element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text}; } this.tree = [element]; + this.source = text; + this.type = type; }; exports["audio/ogg"] = AudioParser; diff --git a/core/modules/parsers/binaryparser.js b/core/modules/parsers/binaryparser.js index fb3d38678..60e7b5ef0 100644 --- a/core/modules/parsers/binaryparser.js +++ b/core/modules/parsers/binaryparser.js @@ -23,7 +23,7 @@ var BinaryParser = function(type,text,options) { children: [{ type: "transclude", attributes: { - tiddler: {type: "string", value: BINARY_WARNING_MESSAGE} + "$tiddler": {type: "string", value: BINARY_WARNING_MESSAGE} } }] }; @@ -38,7 +38,7 @@ var BinaryParser = function(type,text,options) { children: [{ type: "transclude", attributes: { - tiddler: {type: "string", value: EXPORT_BUTTON_IMAGE} + "$tiddler": {type: "string", value: EXPORT_BUTTON_IMAGE} } }] }; @@ -64,6 +64,8 @@ var BinaryParser = function(type,text,options) { children: [warn, link] } this.tree = [element]; + this.source = text; + this.type = type; }; exports["application/octet-stream"] = BinaryParser; diff --git a/core/modules/parsers/csvparser.js b/core/modules/parsers/csvparser.js index 40431d0ae..f40b5f0e5 100644 --- a/core/modules/parsers/csvparser.js +++ b/core/modules/parsers/csvparser.js @@ -52,6 +52,8 @@ var CsvParser = function(type,text,options) { tag = "td"; this.tree[0].children[0].children[0].children.push(row); } + this.source = text; + this.type = type; }; exports["text/csv"] = CsvParser; diff --git a/core/modules/parsers/htmlparser.js b/core/modules/parsers/htmlparser.js index 206ab9c78..24c9f5d3e 100644 --- a/core/modules/parsers/htmlparser.js +++ b/core/modules/parsers/htmlparser.js @@ -29,6 +29,8 @@ var HtmlParser = function(type,text,options) { if($tw.wiki.getTiddlerText("$:/config/HtmlParser/DisableSandbox","no") !== "yes") { this.tree[0].attributes.sandbox = {type: "string", value: $tw.wiki.getTiddlerText("$:/config/HtmlParser/SandboxTokens","")}; } + this.source = text; + this.type = type; }; exports["text/html"] = HtmlParser; diff --git a/core/modules/parsers/imageparser.js b/core/modules/parsers/imageparser.js index e3b8fb60a..a964a4ba8 100644 --- a/core/modules/parsers/imageparser.js +++ b/core/modules/parsers/imageparser.js @@ -28,6 +28,8 @@ var ImageParser = function(type,text,options) { } } this.tree = [element]; + this.source = text; + this.type = type; }; exports["image/svg+xml"] = ImageParser; diff --git a/core/modules/parsers/parseutils.js b/core/modules/parsers/parseutils.js index 925674056..6a0902c6f 100644 --- a/core/modules/parsers/parseutils.js +++ b/core/modules/parsers/parseutils.js @@ -123,6 +123,36 @@ exports.parseStringLiteral = function(source,pos) { } }; +/* +Returns an array of {name:} with an optional "default" property. Options include: +requireParenthesis: require the parameter definition to be wrapped in parenthesis +*/ +exports.parseParameterDefinition = function(paramString,options) { + options = options || {}; + if(options.requireParenthesis) { + var parenMatch = /^\s*\((.*)\)\s*$/g.exec(paramString); + if(!parenMatch) { + return []; + } + paramString = parenMatch[1]; + } + var params = [], + reParam = /\s*([^:),\s]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|([^,"'\s]+)))?/mg, + paramMatch = reParam.exec(paramString); + while(paramMatch) { + // Save the parameter details + var paramInfo = {name: paramMatch[1]}, + defaultValue = paramMatch[2] || paramMatch[3] || paramMatch[4] || paramMatch[5]; + if(defaultValue !== undefined) { + paramInfo["default"] = defaultValue; + } + params.push(paramInfo); + // Look for the next parameter + paramMatch = reParam.exec(paramString); + } + return params; +}; + exports.parseMacroParameters = function(node,source,pos) { // Process parameters var parameter = $tw.utils.parseMacroParameter(source,pos); @@ -175,7 +205,36 @@ exports.parseMacroParameter = function(source,pos) { }; /* -Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, parameters:, start:, end:} +Look for a macro invocation. Returns null if not found, or {type: "transclude", attributes:, start:, end:} +*/ +exports.parseMacroInvocationAsTransclusion = function(source,pos) { + var node = $tw.utils.parseMacroInvocation(source,pos); + if(node) { + var positionalName = 0, + transclusion = { + type: "transclude", + start: node.start, + end: node.end + }; + $tw.utils.addAttributeToParseTreeNode(transclusion,"$variable",node.name); + $tw.utils.each(node.params,function(param) { + var name = param.name; + if(name) { + if(name.charAt(0) === "$") { + name = "$" + name; + } + $tw.utils.addAttributeToParseTreeNode(transclusion,{name: name,type: "string", value: param.value, start: param.start, end: param.end}); + } else { + $tw.utils.addAttributeToParseTreeNode(transclusion,{name: (positionalName++) + "",type: "string", value: param.value, start: param.start, end: param.end}); + } + }); + return transclusion; + } + return node; +}; + +/* +Look for a macro invocation. Returns null if not found, or {type: "macrocall", name:, params:, start:, end:} */ exports.parseMacroInvocation = function(source,pos) { var node = { diff --git a/core/modules/parsers/pdfparser.js b/core/modules/parsers/pdfparser.js index 19d4253d7..c7830bf69 100644 --- a/core/modules/parsers/pdfparser.js +++ b/core/modules/parsers/pdfparser.js @@ -25,6 +25,8 @@ var ImageParser = function(type,text,options) { element.attributes.src = {type: "string", value: "data:application/pdf;base64," + text}; } this.tree = [element]; + this.source = text; + this.type = type; }; exports["application/pdf"] = ImageParser; diff --git a/core/modules/parsers/textparser.js b/core/modules/parsers/textparser.js index 4f55f6f0c..06b08f30f 100644 --- a/core/modules/parsers/textparser.js +++ b/core/modules/parsers/textparser.js @@ -20,6 +20,8 @@ var TextParser = function(type,text,options) { language: {type: "string", value: type} } }]; + this.source = text; + this.type = type; }; exports["text/plain"] = TextParser; diff --git a/core/modules/parsers/videoparser.js b/core/modules/parsers/videoparser.js index f1c281c7c..1c8a38bb2 100644 --- a/core/modules/parsers/videoparser.js +++ b/core/modules/parsers/videoparser.js @@ -28,6 +28,8 @@ var VideoParser = function(type,text,options) { element.attributes.src = {type: "string", value: "data:" + type + ";base64," + text}; } this.tree = [element]; + this.source = text; + this.type = type; }; exports["video/ogg"] = VideoParser; diff --git a/core/modules/parsers/wikiparser/rules/fnprocdef.js b/core/modules/parsers/wikiparser/rules/fnprocdef.js new file mode 100644 index 000000000..5d0a8878b --- /dev/null +++ b/core/modules/parsers/wikiparser/rules/fnprocdef.js @@ -0,0 +1,97 @@ +/*\ +title: $:/core/modules/parsers/wikiparser/rules/fnprocdef.js +type: application/javascript +module-type: wikirule + +Wiki pragma rule for function, procedure and widget definitions + +``` +\function name(param:defaultvalue,param2:defaultvalue) +definition text +\end + +\procedure name(param:defaultvalue,param2:defaultvalue) +definition text +\end + +\widget $mywidget(param:defaultvalue,param2:defaultvalue) +definition text +\end +``` + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.name = "fnprocdef"; +exports.types = {pragma: true}; + +/* +Instantiate parse rule +*/ +exports.init = function(parser) { + this.parser = parser; + // Regexp to match + this.matchRegExp = /^\\(function|procedure|widget)\s+([^(\s]+)\((\s*([^)]*))?\)(\s*\r?\n)?/mg; +}; + +/* +Parse the most recent match +*/ +exports.parse = function() { + // Move past the macro name and parameters + this.parser.pos = this.matchRegExp.lastIndex; + // Parse the parameters + var params = []; + if(this.match[3]) { + params = $tw.utils.parseParameterDefinition(this.match[4]); + } + // Is this a multiline definition? + var reEnd; + if(this.match[5]) { + // If so, the end of the body is marked with \end + reEnd = new RegExp("(\\r?\\n\\\\end[^\\S\\n\\r]*(?:" + $tw.utils.escapeRegExp(this.match[2]) + ")?(?:$|\\r?\\n))","mg"); + } else { + // Otherwise, the end of the definition is marked by the end of the line + reEnd = /($|\r?\n)/mg; + // Move past any whitespace + this.parser.pos = $tw.utils.skipWhiteSpace(this.parser.source,this.parser.pos); + } + // Find the end of the definition + reEnd.lastIndex = this.parser.pos; + var text, + endMatch = reEnd.exec(this.parser.source); + if(endMatch) { + text = this.parser.source.substring(this.parser.pos,endMatch.index); + this.parser.pos = endMatch.index + endMatch[0].length; + } else { + // We didn't find the end of the definition, so we'll make it blank + text = ""; + } + // Save the macro definition + var parseTreeNodes = [{ + type: "set", + attributes: {}, + children: [], + params: params + }]; + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[2]); + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text); + if(this.match[1] === "function") { + parseTreeNodes[0].isFunctionDefinition = true; + } else if(this.match[1] === "procedure") { + parseTreeNodes[0].isProcedureDefinition = true; + } else if(this.match[1] === "widget") { + parseTreeNodes[0].isWidgetDefinition = true; + } + if(this.parser.configTrimWhiteSpace) { + parseTreeNodes[0].configTrimWhiteSpace = true; + } + return parseTreeNodes; +}; + +})(); + \ No newline at end of file diff --git a/core/modules/parsers/wikiparser/rules/html.js b/core/modules/parsers/wikiparser/rules/html.js index 7fc4bb96e..64469e3b2 100644 --- a/core/modules/parsers/wikiparser/rules/html.js +++ b/core/modules/parsers/wikiparser/rules/html.js @@ -93,9 +93,6 @@ exports.parseTag = function(source,pos,options) { return null; } node.tag = token.match[1]; - if(node.tag.slice(1).indexOf("$") !== -1) { - return null; - } if(node.tag.charAt(0) === "$") { node.type = node.tag.substr(1); } diff --git a/core/modules/parsers/wikiparser/rules/macrocallblock.js b/core/modules/parsers/wikiparser/rules/macrocallblock.js index 6f50fdbb0..a2c10e04a 100644 --- a/core/modules/parsers/wikiparser/rules/macrocallblock.js +++ b/core/modules/parsers/wikiparser/rules/macrocallblock.js @@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) { var nextStart = startPos; // Try parsing at all possible macrocall openers until we match while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) { - var nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart); + var nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart); if(nextCall) { var c = this.parser.source.charAt(nextCall.end); // Ensure EOL after parsed macro diff --git a/core/modules/parsers/wikiparser/rules/macrocallinline.js b/core/modules/parsers/wikiparser/rules/macrocallinline.js index 165a70dce..e9f79f09e 100644 --- a/core/modules/parsers/wikiparser/rules/macrocallinline.js +++ b/core/modules/parsers/wikiparser/rules/macrocallinline.js @@ -27,7 +27,7 @@ exports.findNextMatch = function(startPos) { var nextStart = startPos; // Try parsing at all possible macrocall openers until we match while((nextStart = this.parser.source.indexOf("<<",nextStart)) >= 0) { - this.nextCall = $tw.utils.parseMacroInvocation(this.parser.source,nextStart); + this.nextCall = $tw.utils.parseMacroInvocationAsTransclusion(this.parser.source,nextStart); if(this.nextCall) { return nextStart; } diff --git a/core/modules/parsers/wikiparser/rules/macrodef.js b/core/modules/parsers/wikiparser/rules/macrodef.js index 1efd3449a..74a94a385 100644 --- a/core/modules/parsers/wikiparser/rules/macrodef.js +++ b/core/modules/parsers/wikiparser/rules/macrodef.js @@ -77,16 +77,16 @@ exports.parse = function() { text = ""; } // Save the macro definition - return [{ + var parseTreeNodes = [{ type: "set", - attributes: { - name: {type: "string", value: this.match[1]}, - value: {type: "string", value: text} - }, + attributes: {}, children: [], params: params, isMacroDefinition: true }]; + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"name",this.match[1]); + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"value",text); + return parseTreeNodes; }; })(); diff --git a/core/modules/parsers/wikiparser/rules/parameters.js b/core/modules/parsers/wikiparser/rules/parameters.js new file mode 100644 index 000000000..561c1c545 --- /dev/null +++ b/core/modules/parsers/wikiparser/rules/parameters.js @@ -0,0 +1,60 @@ +/*\ +title: $:/core/modules/parsers/wikiparser/rules/parameters.js +type: application/javascript +module-type: wikirule + +Wiki pragma rule for parameter definitions + +``` +\parameters(param:defaultvalue,param2:defaultvalue) +definition text +``` + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.name = "parameters"; +exports.types = {pragma: true}; + +/* +Instantiate parse rule +*/ +exports.init = function(parser) { + this.parser = parser; + // Regexp to match + this.matchRegExp = /^\\parameters\s*\(([^)]*)\)\s*\r?\n/mg; +}; + +/* +Parse the most recent match +*/ +exports.parse = function() { + // Move past the macro name and parameters + this.parser.pos = this.matchRegExp.lastIndex; + // Parse the parameters + var params = $tw.utils.parseParameterDefinition(this.match[1]); + var attributes = Object.create(null), + orderedAttributes = []; + $tw.utils.each(params,function(param) { + 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 + return [{ + type: "parameters", + attributes: attributes, + orderedAttributes: orderedAttributes + }]; +}; + +})(); diff --git a/core/modules/parsers/wikiparser/rules/transcludeblock.js b/core/modules/parsers/wikiparser/rules/transcludeblock.js index 56a4f63b8..c033c2440 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeblock.js +++ b/core/modules/parsers/wikiparser/rules/transcludeblock.js @@ -23,7 +23,7 @@ exports.types = {block: true}; exports.init = function(parser) { this.parser = parser; // Regexp to match - this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}(?:\r?\n|$)/mg; + this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}(?:\r?\n|$)/mg; }; exports.parse = function() { @@ -31,13 +31,22 @@ exports.parse = function() { this.parser.pos = this.matchRegExp.lastIndex; // Get the match details var template = $tw.utils.trim(this.match[2]), - textRef = $tw.utils.trim(this.match[1]); + textRef = $tw.utils.trim(this.match[1]), + params = this.match[3] ? this.match[3].split("|") : []; // Prepare the transclude widget var transcludeNode = { type: "transclude", attributes: {}, isBlock: true }; + $tw.utils.each(params,function(paramValue,index) { + var name = "" + index; + transcludeNode.attributes[name] = { + name: name, + type: "string", + value: paramValue + } + }); // Prepare the tiddler widget var tr, targetTitle, targetField, targetIndex, tiddlerNode; if(textRef) { @@ -48,14 +57,14 @@ exports.parse = function() { tiddlerNode = { type: "tiddler", attributes: { - tiddler: {type: "string", value: targetTitle} + tiddler: {name: "tiddler", type: "string", value: targetTitle} }, isBlock: true, children: [transcludeNode] }; } if(template) { - transcludeNode.attributes.tiddler = {type: "string", value: template}; + transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template}; if(textRef) { return [tiddlerNode]; } else { @@ -63,12 +72,12 @@ exports.parse = function() { } } else { if(textRef) { - transcludeNode.attributes.tiddler = {type: "string", value: targetTitle}; + transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle}; if(targetField) { - transcludeNode.attributes.field = {type: "string", value: targetField}; + transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField}; } if(targetIndex) { - transcludeNode.attributes.index = {type: "string", value: targetIndex}; + transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex}; } return [tiddlerNode]; } else { diff --git a/core/modules/parsers/wikiparser/rules/transcludeinline.js b/core/modules/parsers/wikiparser/rules/transcludeinline.js index dbf39bfb6..3ce9dc78e 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeinline.js +++ b/core/modules/parsers/wikiparser/rules/transcludeinline.js @@ -23,7 +23,7 @@ exports.types = {inline: true}; exports.init = function(parser) { this.parser = parser; // Regexp to match - this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?\}\}/mg; + this.matchRegExp = /\{\{([^\{\}\|]*)(?:\|\|([^\|\{\}]+))?(?:\|([^\{\}]+))?\}\}/mg; }; exports.parse = function() { @@ -31,12 +31,21 @@ exports.parse = function() { this.parser.pos = this.matchRegExp.lastIndex; // Get the match details var template = $tw.utils.trim(this.match[2]), - textRef = $tw.utils.trim(this.match[1]); + textRef = $tw.utils.trim(this.match[1]), + params = this.match[3] ? this.match[3].split("|") : []; // Prepare the transclude widget var transcludeNode = { type: "transclude", attributes: {} }; + $tw.utils.each(params,function(paramValue,index) { + var name = "" + index; + transcludeNode.attributes[name] = { + name: name, + type: "string", + value: paramValue + } + }); // Prepare the tiddler widget var tr, targetTitle, targetField, targetIndex, tiddlerNode; if(textRef) { @@ -47,13 +56,13 @@ exports.parse = function() { tiddlerNode = { type: "tiddler", attributes: { - tiddler: {type: "string", value: targetTitle} + tiddler: {name: "tiddler", type: "string", value: targetTitle} }, children: [transcludeNode] }; } if(template) { - transcludeNode.attributes.tiddler = {type: "string", value: template}; + transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: template}; if(textRef) { return [tiddlerNode]; } else { @@ -61,12 +70,12 @@ exports.parse = function() { } } else { if(textRef) { - transcludeNode.attributes.tiddler = {type: "string", value: targetTitle}; + transcludeNode.attributes["$tiddler"] = {name: "$tiddler", type: "string", value: targetTitle}; if(targetField) { - transcludeNode.attributes.field = {type: "string", value: targetField}; + transcludeNode.attributes["$field"] = {name: "$field", type: "string", value: targetField}; } if(targetIndex) { - transcludeNode.attributes.index = {type: "string", value: targetIndex}; + transcludeNode.attributes["$index"] = {name: "$index", type: "string", value: targetIndex}; } return [tiddlerNode]; } else { diff --git a/core/modules/parsers/wikiparser/wikiparser.js b/core/modules/parsers/wikiparser/wikiparser.js index 4c7419030..bb457b205 100644 --- a/core/modules/parsers/wikiparser/wikiparser.js +++ b/core/modules/parsers/wikiparser/wikiparser.js @@ -32,6 +32,7 @@ options: see below: parseAsInline: true to parse text as inline instead of block wiki: reference to wiki to use _canonical_uri: optional URI of content if text is missing or empty + configTrimWhiteSpace: true to trim whitespace */ var WikiParser = function(type,text,options) { this.wiki = options.wiki; @@ -46,7 +47,7 @@ var WikiParser = function(type,text,options) { this.source = text || ""; this.sourceLength = this.source.length; // Flag for ignoring whitespace - this.configTrimWhiteSpace = false; + this.configTrimWhiteSpace = options.configTrimWhiteSpace !== undefined ? options.configTrimWhiteSpace : false; // Parser mode this.parseAsInline = options.parseAsInline; // Set current parse position diff --git a/core/modules/widgets/fill.js b/core/modules/widgets/fill.js new file mode 100644 index 000000000..de88c95af --- /dev/null +++ b/core/modules/widgets/fill.js @@ -0,0 +1,30 @@ +/*\ +title: $:/core/modules/widgets/fill.js +type: application/javascript +module-type: widget + +Sub-widget used by the transclude widget for specifying values for slots within transcluded content. It doesn't do anything by itself because the transclude widget only ever deals with the parse tree nodes, and doesn't instantiate the widget itself + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var FillWidget = function(parseTreeNode,options) { + // Initialise + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +FillWidget.prototype = new Widget(); + +exports.fill = FillWidget; + +})(); + \ No newline at end of file diff --git a/core/modules/widgets/importvariables.js b/core/modules/widgets/importvariables.js index a73abfdcf..aafc8ba8b 100644 --- a/core/modules/widgets/importvariables.js +++ b/core/modules/widgets/importvariables.js @@ -52,38 +52,44 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) { var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true}); if(parser) { var parseTreeNode = parser.tree[0]; - while(parseTreeNode && parseTreeNode.type === "set") { + while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) { var node = { type: "set", attributes: parseTreeNode.attributes, params: parseTreeNode.params, - isMacroDefinition: parseTreeNode.isMacroDefinition + isMacroDefinition: parseTreeNode.isMacroDefinition, + isFunctionDefinition: parseTreeNode.isFunctionDefinition, + isProcedureDefinition: parseTreeNode.isProcedureDefinition, + isWidgetDefinition: parseTreeNode.isWidgetDefinition, + configTrimWhiteSpace: parseTreeNode.configTrimWhiteSpace }; - 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(); - // We SHALLOW copy over all variables - // in widget. We can't use - // $tw.utils.assign, because that copies - // up the prototype chain, which we - // don't want. - $tw.utils.each(Object.keys(widget.variables), function(key) { - widgetPointer.variables[key] = widget.variables[key]; - }); - } else { - widgetPointer.children = [widgetPointer.makeChildWidget(node)]; - // No more regenerating children for - // this widget. If it needs to refresh, - // it'll do so along with the the whole - // importvariable tree. - if (widgetPointer != this) { - widgetPointer.makeChildWidgets = function(){}; + if(parseTreeNode.type === "set" || parseTreeNode.type === "setvariable") { + if(parseTreeNode.isMacroDefinition || parseTreeNode.isProcedureDefinition || parseTreeNode.isWidgetDefinition || parseTreeNode.isFunctionDefinition) { + // 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(); + // We SHALLOW copy over all variables + // in widget. We can't use + // $tw.utils.assign, because that copies + // up the prototype chain, which we + // don't want. + $tw.utils.each(Object.keys(widget.variables), function(key) { + widgetPointer.variables[key] = widget.variables[key]; + }); + } else { + widgetPointer.children = [widgetPointer.makeChildWidget(node)]; + // No more regenerating children for + // this widget. If it needs to refresh, + // it'll do so along with the the whole + // importvariable tree. + if (widgetPointer != this) { + widgetPointer.makeChildWidgets = function(){}; + } + widgetPointer = widgetPointer.children[0]; } - widgetPointer = widgetPointer.children[0]; } parseTreeNode = parseTreeNode.children && parseTreeNode.children[0]; } diff --git a/core/modules/widgets/macrocall.js b/core/modules/widgets/macrocall.js index 9de2e5d67..e49eadfe0 100644 --- a/core/modules/widgets/macrocall.js +++ b/core/modules/widgets/macrocall.js @@ -37,7 +37,7 @@ MacroCallWidget.prototype.render = function(parent,nextSibling) { Compute the internal state of the widget */ MacroCallWidget.prototype.execute = function() { - // Get the parse type if specified + this.macroName = this.parseTreeNode.name || this.getAttribute("$name"), this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki"); this.renderOutput = this.getAttribute("$output","text/html"); // Merge together the parameters specified in the parse tree with the specified attributes @@ -47,49 +47,26 @@ MacroCallWidget.prototype.execute = function() { params.push({name: name, value: attribute}); } }); - // Get the macro value - var macroName = this.parseTreeNode.name || this.getAttribute("$name"), - variableInfo = this.getVariableInfo(macroName,{params: params}), - text = variableInfo.text, - parseTreeNodes; - // Are we rendering to HTML? - if(this.renderOutput === "text/html") { - // If so we'll return the parsed macro - // Check if we've already cached parsing this macro - var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser", - parser; - if(variableInfo.srcVariable && variableInfo.srcVariable[mode]) { - parser = variableInfo.srcVariable[mode]; - } else { - parser = this.wiki.parseText(this.parseType,text, - {parseAsInline: !this.parseTreeNode.isBlock}); - if(variableInfo.isCacheable && variableInfo.srcVariable) { - variableInfo.srcVariable[mode] = parser; - } - } - var parseTreeNodes = parser ? parser.tree : []; - // Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__" - var attributes = {}; - $tw.utils.each(variableInfo.params,function(param) { - var name = "__" + param.name + "__"; - attributes[name] = { - name: name, - type: "string", - value: param.value - }; - }); + // Make a transclude widget + var positionalName = 0, parseTreeNodes = [{ - type: "vars", - attributes: attributes, - children: parseTreeNodes + type: "transclude", + isBlock: this.parseTreeNode.isBlock }]; - } else if(this.renderOutput === "text/raw") { - parseTreeNodes = [{type: "text", text: text}]; - } else { - // Otherwise, we'll render the text - var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this}); - parseTreeNodes = [{type: "text", text: plainText}]; - } + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$variable",this.macroName); + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$type",this.parseType); + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],"$output",this.renderOutput); + $tw.utils.each(params,function(param) { + var name = param.name; + if(name) { + if(name.charAt(0) === "$") { + name = "$" + name; + } + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],name,param.value); + } else { + $tw.utils.addAttributeToParseTreeNode(parseTreeNodes[0],(positionalName++) + "",param.value); + } + }); // Construct the child widgets this.makeChildWidgets(parseTreeNodes); }; diff --git a/core/modules/widgets/parameters.js b/core/modules/widgets/parameters.js new file mode 100644 index 000000000..69194cb9e --- /dev/null +++ b/core/modules/widgets/parameters.js @@ -0,0 +1,96 @@ +/*\ +title: $:/core/modules/widgets/parameters.js +type: application/javascript +module-type: widget + +Widget for definition of transclusion parameters + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget, + TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude; + +var ParametersWidget = function(parseTreeNode,options) { + // Initialise + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +ParametersWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +ParametersWidget.prototype.render = function(parent,nextSibling) { + // Call the constructor + Widget.call(this); + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + this.renderChildren(parent,nextSibling); +}; + +/* +Compute the internal state of the widget +*/ +ParametersWidget.prototype.execute = function() { + var self = this; + this.parametersDepth = Math.max(parseInt(this.getAttribute("$depth","1"),10) || 1,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(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; + // 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 + $tw.utils.each(pointer.getTransclusionMetaParameters(),function(getValue,name) { + var variableName = self.getAttribute("$" + name); + if(variableName) { + self.setVariable(variableName,getValue(name)); + } + }); + } + // Construct the child widgets + this.makeChildWidgets(); +}; + +/* +Refresh the widget by ensuring our attributes are up to date +*/ +ParametersWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if(Object.keys(changedAttributes).length) { + this.refreshSelf(); + return true; + } + return this.refreshChildren(changedTiddlers); +}; + +exports.parameters = ParametersWidget; + +})(); diff --git a/core/modules/widgets/setvariable.js b/core/modules/widgets/setvariable.js index cc97067c7..f8e98f390 100755 --- a/core/modules/widgets/setvariable.js +++ b/core/modules/widgets/setvariable.js @@ -48,7 +48,17 @@ SetWidget.prototype.execute = function() { this.setValue = this.getAttribute("value"); this.setEmptyValue = this.getAttribute("emptyValue"); // Set context variable - this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,!!this.parseTreeNode.isMacroDefinition); + if(this.parseTreeNode.isMacroDefinition) { + this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,true); + } else if(this.parseTreeNode.isFunctionDefinition) { + this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isFunctionDefinition: true}); + } else if(this.parseTreeNode.isProcedureDefinition) { + this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isProcedureDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace}); + } else if(this.parseTreeNode.isWidgetDefinition) { + this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isWidgetDefinition: true, configTrimWhiteSpace: this.parseTreeNode.configTrimWhiteSpace}); + } else { + this.setVariable(this.setName,this.getValue()); + } // Construct the child widgets this.makeChildWidgets(); }; diff --git a/core/modules/widgets/slot.js b/core/modules/widgets/slot.js new file mode 100644 index 000000000..6fc402ac2 --- /dev/null +++ b/core/modules/widgets/slot.js @@ -0,0 +1,82 @@ +/*\ +title: $:/core/modules/widgets/slot.js +type: application/javascript +module-type: widget + +Widget for definition of slots within transcluded content. The values provided by the translusion are passed to the slot. + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget, + TranscludeWidget = require("$:/core/modules/widgets/transclude.js").transclude; + +var SlotWidget = function(parseTreeNode,options) { + // Initialise + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +SlotWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +SlotWidget.prototype.render = function(parent,nextSibling) { + // Call the constructor + Widget.call(this); + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + this.renderChildren(parent,nextSibling); +}; + +/* +Compute the internal state of the widget +*/ +SlotWidget.prototype.execute = function() { + var self = this; + this.slotName = this.getAttribute("$name"); + this.slotDepth = parseInt(this.getAttribute("$depth","1"),10) || 1; + // Find the parent transclusions + var pointer = this.parentWidget, + depth = this.slotDepth; + while(pointer) { + if(pointer instanceof TranscludeWidget) { + depth--; + if(depth <= 0) { + break; + } + } + pointer = pointer.parentWidget; + } + var parseTreeNodes = [{type: "text", attributes: {text: {type: "string", value: "Missing slot reference!"}}}]; + if(pointer instanceof TranscludeWidget) { + // Get the parse tree nodes comprising the slot contents + parseTreeNodes = pointer.getTransclusionSlotFill(this.slotName,this.parseTreeNode.children); + } + // Construct the child widgets + this.makeChildWidgets(parseTreeNodes); +}; + +/* +Refresh the widget by ensuring our attributes are up to date +*/ +SlotWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if(changedAttributes["$name"] || changedAttributes["$depth"]) { + this.refreshSelf(); + return true; + } + return this.refreshChildren(changedTiddlers); +}; + +exports.slot = SlotWidget; + +})(); diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js index d7862d2eb..1831f6b6d 100755 --- a/core/modules/widgets/transclude.js +++ b/core/modules/widgets/transclude.js @@ -37,46 +37,347 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) { Compute the internal state of the widget */ TranscludeWidget.prototype.execute = function() { - // Get our parameters - this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); - this.transcludeSubTiddler = this.getAttribute("subtiddler"); - this.transcludeField = this.getAttribute("field"); - this.transcludeIndex = this.getAttribute("index"); - this.transcludeMode = this.getAttribute("mode"); - this.recursionMarker = this.getAttribute("recursionMarker","yes"); - // Parse the text reference + // Get our attributes, string parameters, and slot values into properties of the widget object + this.collectAttributes(); + this.collectStringParameters(); + this.collectSlotFillParameters(); + // Get the parse tree nodes that we are transcluding + var target = this.getTransclusionTarget(), + parseTreeNodes = target.parseTreeNodes; + this.sourceText = target.text; + this.sourceType = target.type; + this.parseAsInline = target.parseAsInline; + // Process the transclusion according to the output type + switch(this.transcludeOutput || "text/html") { + case "text/html": + // No further processing required + break; + case "text/raw": + // Just return the raw text + parseTreeNodes = [{type: "text", text: this.sourceText}]; + break; + default: + // text/plain + var plainText = this.wiki.renderText("text/plain",this.sourceType,this.sourceText,{parentWidget: this}); + parseTreeNodes = [{type: "text", text: plainText}]; + break; + } + // Set the legacy transclusion context variables only if we're not transcluding a variable + if(!this.transcludeVariable) { + var recursionMarker = this.makeRecursionMarker(); + this.setVariable("transclusion",recursionMarker); + } + // Construct the child widgets + this.makeChildWidgets(parseTreeNodes); +}; + +/* +Collect the attributes we need, in the process determining whether we're being used in legacy mode +*/ +TranscludeWidget.prototype.collectAttributes = function() { + var self = this; + // Detect legacy mode + this.legacyMode = true; + $tw.utils.each(this.attributes,function(value,name) { + if(name.charAt(0) === "$") { + self.legacyMode = false; + } + }); + // Get the attributes for the appropriate mode + if(this.legacyMode) { + this.transcludeTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler")); + this.transcludeSubTiddler = this.getAttribute("subtiddler"); + this.transcludeField = this.getAttribute("field"); + this.transcludeIndex = this.getAttribute("index"); + this.transcludeMode = this.getAttribute("mode"); + this.recursionMarker = this.getAttribute("recursionMarker","yes"); + } else { + this.transcludeVariable = this.getAttribute("$variable"); + this.transcludeType = this.getAttribute("$type"); + this.transcludeOutput = this.getAttribute("$output","text/html"); + this.transcludeTitle = this.getAttribute("$tiddler",this.getVariable("currentTiddler")); + this.transcludeSubTiddler = this.getAttribute("$subtiddler"); + this.transcludeField = this.getAttribute("$field"); + this.transcludeIndex = this.getAttribute("$index"); + this.transcludeMode = this.getAttribute("$mode"); + this.recursionMarker = this.getAttribute("$recursionMarker","yes"); + } +}; + +/* +Collect string parameters +*/ +TranscludeWidget.prototype.collectStringParameters = function() { + var self = this; + this.stringParametersByName = Object.create(null); + if(!this.legacyMode) { + $tw.utils.each(this.attributes,function(value,name) { + if(name.charAt(0) === "$") { + if(name.charAt(1) === "$") { + // Attributes starting $$ represent parameters starting with a single $ + name = name.slice(1); + } else { + // Attributes starting with a single $ are reserved for the widget + return; + } + } + self.stringParametersByName[name] = value; + }); + } +}; + +/* +Collect slot value parameters +*/ +TranscludeWidget.prototype.collectSlotFillParameters = function() { + var self = this; + this.slotFillParseTrees = Object.create(null); + if(this.legacyMode) { + this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children; + } else { + this.slotFillParseTrees["ts-raw"] = this.parseTreeNode.children; + var noFillWidgetsFound = true, + searchParseTreeNodes = function(nodes) { + $tw.utils.each(nodes,function(node) { + if(node.type === "fill") { + if(node.attributes["$name"] && node.attributes["$name"].type === "string") { + var slotValueName = node.attributes["$name"].value; + self.slotFillParseTrees[slotValueName] = node.children || []; + } + noFillWidgetsFound = false; + } else { + searchParseTreeNodes(node.children); + } + }); + }; + searchParseTreeNodes(this.parseTreeNode.children); + if(noFillWidgetsFound) { + this.slotFillParseTrees["ts-missing"] = this.parseTreeNode.children; + } + } +}; + +/* +Get transcluded parse tree nodes as an object {parser:,text:,type:} +*/ +TranscludeWidget.prototype.getTransclusionTarget = function() { + var self = this; + // Determine whether we're being used in inline or block mode var parseAsInline = !this.parseTreeNode.isBlock; if(this.transcludeMode === "inline") { parseAsInline = true; } else if(this.transcludeMode === "block") { parseAsInline = false; } - var parser = this.wiki.parseTextReference( + var parser; + // Get the parse tree + if(this.transcludeVariable) { + // Transcluding a variable + var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}), + srcVariable = variableInfo && variableInfo.srcVariable; + if(srcVariable) { + if(srcVariable.isFunctionDefinition) { + // Function to return parameters by name or position + var fnGetParam = function(name,index) { + // Parameter names starting with dollar must be escaped to double dollars + if(name.charAt(0) === "$") { + name = "$" + name; + } + // Look for the parameter by name + if(self.hasAttribute(name)) { + return self.getAttribute(name); + // Look for the parameter by index + } else if(self.hasAttribute(index + "")) { + return self.getAttribute(index + ""); + } else { + return undefined; + } + }, + result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || ""; + parser = { + tree: [{ + type: "text", + text: result + }], + source: result, + type: "text/vnd.tiddlywiki" + }; + if(parseAsInline) { + parser.tree[0] = { + type: "text", + text: result + }; + } else { + parser.tree[0] = { + type: "element", + tag: "p", + children: [{ + type: "text", + text: result + }] + } + } + } else { + var cacheKey = (parseAsInline ? "inlineParser" : "blockParser") + (this.transcludeType || ""); + if(variableInfo.isCacheable && srcVariable[cacheKey]) { + parser = srcVariable[cacheKey]; + } else { + parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace}); + if(variableInfo.isCacheable) { + srcVariable[cacheKey] = parser; + } + } + } + if(parser) { + // Add parameters widget for procedures and custom widgets + if(srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition) { + parser = { + tree: [ + { + type: "parameters", + children: parser.tree + } + ], + source: parser.source, + type: parser.type + } + $tw.utils.each(srcVariable.params,function(param) { + 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__" + parser = { + tree: [ + { + type: "vars", + children: parser.tree + } + ], + source: parser.source, + type: parser.type + } + $tw.utils.each(variableInfo.params,function(param) { + $tw.utils.addAttributeToParseTreeNode(parser.tree[0],"__" + param.name + "__",param.value) + }); + } + } + } + } else { + // Transcluding a text reference + parser = this.wiki.parseTextReference( this.transcludeTitle, this.transcludeField, this.transcludeIndex, { parseAsInline: parseAsInline, - subTiddler: this.transcludeSubTiddler - }), - parseTreeNodes = parser ? parser.tree : this.parseTreeNode.children; - this.sourceText = parser ? parser.source : null; - this.parserType = parser? parser.type : null; - // Set context variables for recursion detection - var recursionMarker = this.makeRecursionMarker(); - if(this.recursionMarker === "yes") { - this.setVariable("transclusion",recursionMarker); + subTiddler: this.transcludeSubTiddler, + defaultType: this.transcludeType + }); } - // Check for recursion + // Return the parse tree if(parser) { - if(this.parentWidget && this.parentWidget.hasVariable("transclusion",recursionMarker)) { - parseTreeNodes = [{type: "error", attributes: { - "$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")} - }}]; + return { + parser: parser, + parseTreeNodes: parser.tree, + parseAsInline: parseAsInline, + text: parser.source, + type: parser.type + }; + } else { + // If there's no parse tree then return the missing slot value + return { + parser: null, + parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []), + parseAsInline: parseAsInline, + text: null, + type: null + }; + } +}; + +/* +Fetch all the string parameters as an ordered array of {name:, value:} where the name is optional +*/ +TranscludeWidget.prototype.getOrderedTransclusionParameters = function() { + var result = []; + // Collect the parameters + for(var name in this.stringParametersByName) { + var value = this.stringParametersByName[name]; + result.push({name: name, value: value}); + } + // Sort numerical parameter names first + result.sort(function(a,b) { + var aIsNumeric = !isNaN(a.name), + bIsNumeric = !isNaN(b.name); + if(aIsNumeric && bIsNumeric) { + return a.name - b.name; + } else if(aIsNumeric) { + return -1; + } else if(bIsNumeric) { + return 1; + } else { + return a.name === b.name ? 0 : (a.name < b.name ? -1 : 1); + } + }); + // Remove names from numerical parameters + $tw.utils.each(result,function(param,index) { + if(!isNaN(param.name)) { + delete param.name; + } + }); + return result; +}; + +/* +Fetch the value of a parameter +*/ +TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaultValue) { + if(name in this.stringParametersByName) { + return this.stringParametersByName[name]; + } else { + var name = "" + index; + if(name in this.stringParametersByName) { + return this.stringParametersByName[name]; } } - // Construct the child widgets - this.makeChildWidgets(parseTreeNodes); + return defaultValue; +}; + +/* +Get one of the special parameters to be provided by the parameters widget +*/ +TranscludeWidget.prototype.getTransclusionMetaParameters = function() { + var self = this; + return { + "parseMode": function() { + return self.parseAsInline ? "inline" : "block"; + }, + "parseTreeNodes": function() { + return JSON.stringify(self.parseTreeNode.children || []); + }, + "slotFillParseTreeNodes": function() { + return JSON.stringify(self.slotFillParseTrees); + }, + "params": function() { + return JSON.stringify(self.stringParametersByName); + } + }; +}; + +/* +Fetch the value of a slot +*/ +TranscludeWidget.prototype.getTransclusionSlotFill = function(name,defaultParseTreeNodes) { + if(name && this.slotFillParseTrees[name] && this.slotFillParseTrees[name].length > 0) { + return this.slotFillParseTrees[name]; + } else { + return defaultParseTreeNodes || []; + } }; /* @@ -99,6 +400,7 @@ TranscludeWidget.prototype.makeRecursionMarker = function() { }; TranscludeWidget.prototype.parserNeedsRefresh = function() { + // Doesn't need to consider transcluded variables because a parent variable can't change once a widget has been created var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler}); return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType) }; @@ -108,7 +410,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of */ TranscludeWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); - if(($tw.utils.count(changedAttributes) > 0) || (changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) { + if(($tw.utils.count(changedAttributes) > 0) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) { this.refreshSelf(); return true; } else { diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index 60f55e8bb..741914fdc 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -41,10 +41,7 @@ Widget.prototype.initialise = function(parseTreeNode,options) { this.parseTreeNode = parseTreeNode; this.wiki = options.wiki; this.parentWidget = options.parentWidget; - this.variables = Object.create(null); - if(this.parentWidget) { - Object.setPrototypeOf(this.variables,this.parentWidget.variables); - } + this.variables = Object.create(this.parentWidget ? this.parentWidget.variables : null); this.document = options.document; this.attributes = {}; this.children = []; @@ -92,9 +89,22 @@ name: name of the variable value: value of the variable params: array of {name:, default:} for each parameter isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed) +options includes: + isProcedureDefinition: true if the variable is set via a \procedure pragma (and hence should not have variable substitution performed) + isFunctionDefinition: true if the variable is set via a \function pragma (and hence should not have variable substitution performed) + isWidgetDefinition: true if the variable is set via a \widget pragma (and hence should not have variable substitution performed) */ -Widget.prototype.setVariable = function(name,value,params,isMacroDefinition) { - this.variables[name] = {value: value, params: params, isMacroDefinition: !!isMacroDefinition}; +Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) { + options = options || {}; + this.variables[name] = { + value: value, + params: params, + isMacroDefinition: !!isMacroDefinition, + isFunctionDefinition: !!options.isFunctionDefinition, + isProcedureDefinition: !!options.isProcedureDefinition, + isWidgetDefinition: !!options.isWidgetDefinition, + configTrimWhiteSpace: !!options.configTrimWhiteSpace + }; }; /* @@ -104,6 +114,7 @@ options: see below Options include params: array of {name:, value:} for each parameter defaultValue: default value if the variable is not defined +allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent Returns an object with the following fields: @@ -112,21 +123,27 @@ text: text of variable, with parameters properly substituted */ Widget.prototype.getVariableInfo = function(name,options) { options = options || {}; - var actualParams = options.params || [], - parentWidget = this.parentWidget; + var self = this, + actualParams = options.params || [], + variable; + if(options.allowSelfAssigned) { + variable = this.variables[name]; + } else { + variable = this.parentWidget && this.parentWidget.variables[name]; + } // Check for the variable defined in the parent widget (or an ancestor in the prototype chain) - if(parentWidget && name in parentWidget.variables) { - var variable = parentWidget.variables[name], - originalValue = variable.value, + if(variable) { + var originalValue = variable.value, value = originalValue, - params = this.resolveVariableParameters(variable.params,actualParams); - // Substitute any parameters specified in the definition - $tw.utils.each(params,function(param) { - value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value); - }); - // Only substitute variable references if this variable was defined with the \define pragma + params = []; + // Only substitute parameter and variable references if this variable was defined with the \define pragma if(variable.isMacroDefinition) { - value = this.substituteVariableReferences(value,options); + params = self.resolveVariableParameters(variable.params,actualParams); + // Substitute any parameters specified in the definition + $tw.utils.each(params,function(param) { + value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value); + }); + value = self.substituteVariableReferences(value,options); } return { text: value, @@ -136,8 +153,13 @@ Widget.prototype.getVariableInfo = function(name,options) { }; } // If the variable doesn't exist in the parent widget then look for a macro module + var text = this.evaluateMacroModule(name,actualParams); + if(text === undefined) { + text = options.defaultValue; + } return { - text: this.evaluateMacroModule(name,actualParams,options.defaultValue) + text: text, + srcVariable: {} }; }; @@ -148,6 +170,11 @@ Widget.prototype.getVariable = function(name,options) { return this.getVariableInfo(name,options).text; }; +/* +Maps actual parameters onto formal parameters, returning an array of {name:,value:} objects +formalParams - Array of {name:,default:} (default value is optional) +actualParams - Array of string values or {name:,value:} (name is optional) +*/ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) { formalParams = formalParams || []; actualParams = actualParams || []; @@ -160,7 +187,7 @@ Widget.prototype.resolveVariableParameters = function(formalParams,actualParams) paramInfo = formalParams[p]; paramValue = undefined; for(var m=0; m 0) { + var letVariableWidget = { + type: "let", attributes: { - name: {type: "string", value: name}, - value: {type: "string", value: value} }, children: [] }; - currWidgetNode.children = [setVariableWidget]; - currWidgetNode = setVariableWidget; - }); + $tw.utils.each(options.variables,function(value,name) { + $tw.utils.addAttributeToParseTreeNode(letVariableWidget,name,"" + value); + }); + currWidgetNode.children = [letVariableWidget]; + currWidgetNode = letVariableWidget; + } // Add in the supplied parse tree nodes currWidgetNode.children = parser ? parser.tree : []; // Create the widget diff --git a/core/ui/Components/VisibleTransclude.tid b/core/ui/Components/VisibleTransclude.tid new file mode 100644 index 000000000..cbc981abe --- /dev/null +++ b/core/ui/Components/VisibleTransclude.tid @@ -0,0 +1,48 @@ +title: $:/core/ui/VisibleTransclude + + +\widget $transclude() + +<$parameters tiddler="" $$tiddler="" mode="" $$mode="" $parseMode="@parseMode" $params="@params"> + + <$let + mode={{{ [[$mode]is[variable]then<$mode>!is[blank]] :else[[mode]is[variable]then!is[blank]] :else[<@parseMode>] }}} + outputTag={{{ [match[inline]then[span]else[div]] }}} + outputColour={{{ [match[inline]then[green]else[red]] }}} + > + + <$genesis $type=<> style="color:white;padding:4px;" style.background=<>> + <$genesis $type=<> style="display: inline-block;"> +
+ + <$list filter="[<@params>jsonindexes[]]" emptyMessage="(none)"> +
+ <$text text=<>/><$text text=": "/><$text text={{{ [<@params>jsonget] }}}/> +
+ +
+ + <$genesis $type=<> 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"/> + + + + + + +\end diff --git a/core/wiki/macros/tabs.tid b/core/wiki/macros/tabs.tid index f439e541d..bc8a0255f 100644 --- a/core/wiki/macros/tabs.tid +++ b/core/wiki/macros/tabs.tid @@ -60,4 +60,4 @@ code-body: yes -\end +\end \ No newline at end of file diff --git a/editions/prerelease/tiddlers/Release 5.2.8.tid b/editions/prerelease/tiddlers/Release 5.2.8.tid deleted file mode 100644 index 18ca202b5..000000000 --- a/editions/prerelease/tiddlers/Release 5.2.8.tid +++ /dev/null @@ -1,60 +0,0 @@ -caption: 5.2.8 -created: 20230326093239710 -modified: 20230326093239710 -tags: ReleaseNotes -title: Release 5.2.8 -type: text/vnd.tiddlywiki - -//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.2.7...master]]// - -! Major Improvements - -! Translation Improvements - -Improvements to the following translations: - -* - -! Plugin Improvements - -* - -! Accessibility Improvements - -* - -! Usability Improvements - -* - -! Widget Improvements - -* - -! Filter improvements - -* - -! Hackability Improvements - -* - -! Bug Fixes - -* - -! Node.js Improvements - -* - -! Performance Improvements - -* - -! Acknowledgements - -[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki: - -<<.contributors """ - -""">> \ No newline at end of file diff --git a/editions/prerelease/tiddlers/Release 5.3.0.tid b/editions/prerelease/tiddlers/Release 5.3.0.tid new file mode 100644 index 000000000..63a57cd4d --- /dev/null +++ b/editions/prerelease/tiddlers/Release 5.3.0.tid @@ -0,0 +1,83 @@ +caption: 5.3.0 +created: 20230419103154368 +modified: 20230419103154368 +tags: ReleaseNotes +title: Release 5.3.0 +type: text/vnd.tiddlywiki + +//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/master...parameterised-transclusions]]// + +! About v5.3.0 + +This pre-release introduces a number of significant improvements and new features related to some of TiddlyWiki's most fundamental components: macros, widgets, operators and transclusion. + +! Introduction to v5.3.0 + +The motivation of these changes is to fix one of ~TiddlyWiki 5's early design flaws: the reliance on macros using textual substitution as the primary way to modularise and reuse wikitext and filters. + +Experience has shown that while macros are a good match for a small number of tasks, they are brittle and error prone for many common operations. See [[Macro Pitfalls]] for a discussion of the problems that accompany this approach. Over the years we have introduced mitigations for the worst problems but these have come at a cost of increased complexity. + +The changes in this release provide powerful new ways to achieve common tasks, and unlock completely new capabilities that were previously impossible in wikitext. + +* [[Procedures]], which are essentially what macros should have been; they work in exactly the same way except that parameters are exposed as simple variables (without the double underscores) and no textual substitution takes place +* [[Custom Widgets]], allowing the creation of widgets in wikitext, and the redefinition of built-in widgets +* [[Functions]], a new way to encapsulate filter expressions with named parameters, including the ability to make custom filter operators +* Parameterised [[Transclusions|Transclusion]], allowing strings and wikitext trees to be passed to transclusions + +The approach taken by this release is to add new functionality by extending and augmenting the system without disturbing existing functionality. All of these changes are thus intended to be backwards compatible. While they represent a new field of opportunities for wikitext authors, it is possible for authors to ignore all these new features and continue to use ~TiddlyWiki 5 in the way that they have always done. + +These changes lay the groundwork for macros and related features to be deprecated (which is the point at which users are advised not to use old features, and instead given clear pointers to the equivalent modern functionality). + +The new transclusion architecture is not by itself sufficient to enable us to fully deprecate macros yet. To handle the remaining use cases we propose a new backtick quoted attribute format that allows for the substitution of variable values. See https://github.com/Jermolene/TiddlyWiki5/issues/6663 for details. + +! Plugin Improvements + +* + +! Translation improvement + +Improvements to the following translations: + +* + +! Accessibility Improvements + +* + +! Usability Improvements + +* + +! Widget Improvements + +* + +! Filter improvements + +* + +! Hackability Improvements + +* + +! Bug Fixes + +* + +! Developer Improvements + +* + +! Node.js Improvements + +* + +! Performance Improvements + +* +! Acknowledgements + +[[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki: + +<<.contributors """ +""">> diff --git a/editions/test/tiddlers/tests/data/custom-operators/NestedParameterised.tid b/editions/test/tiddlers/tests/data/custom-operators/NestedParameterised.tid new file mode 100644 index 000000000..3e4d610d0 --- /dev/null +++ b/editions/test/tiddlers/tests/data/custom-operators/NestedParameterised.tid @@ -0,0 +1,24 @@ +title: CustomOperators/NestedParameterised +description: Nested parameterised custom operator usage +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function .dividebysomething(first:ignored,factor:0.5) +[divide[2]multiply] +\end + +\function .multiplebysomething(first:ignored,factor:2) +[multiply[2].dividebysomething[],] +\end + +<$text text={{{ [[123].multiplebysomething[]] }}}/> +- +<$text text={{{ [[123].multiplebysomething[x],[4]] }}}/> + ++ +title: ExpectedResult + +

246-492

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/custom-operators/Parameterised.tid b/editions/test/tiddlers/tests/data/custom-operators/Parameterised.tid new file mode 100644 index 000000000..2f8337b0f --- /dev/null +++ b/editions/test/tiddlers/tests/data/custom-operators/Parameterised.tid @@ -0,0 +1,24 @@ +title: CustomOperators/Parameterised +description: Parameterised custom operator usage +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function .multiplybysomething(first:ignored,factor:2) +[multiply[2]multiply] +\end + +<$text text={{{ [[123].multiplybysomething[]] }}}/> +- +<$text text={{{ [[123].multiplybysomething[x],[4]] }}}/> +| +<$text text={{{ [[123]function[.multiplybysomething]] }}}/> +- +<$text text={{{ [[123]function[.multiplybysomething],[x],[4]] }}}/> + ++ +title: ExpectedResult + +

492-984|492-984

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/custom-operators/Simple.tid b/editions/test/tiddlers/tests/data/custom-operators/Simple.tid new file mode 100644 index 000000000..089701295 --- /dev/null +++ b/editions/test/tiddlers/tests/data/custom-operators/Simple.tid @@ -0,0 +1,21 @@ +title: CustomOperators/Simple +description: Simple custom operator usage +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +\function .multiplybytwo() +[multiply[2]] +\end + +<$text text={{{ [[123].multiplybytwo[]] }}}/> +| +<$text text={{{ [[123]function[.multiplybytwo]] }}}/> + ++ +title: ExpectedResult + +

246|246

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/FunctionAttributes.tid b/editions/test/tiddlers/tests/data/functions/FunctionAttributes.tid new file mode 100644 index 000000000..2deb49bdc --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/FunctionAttributes.tid @@ -0,0 +1,24 @@ +title: Functions/FunctionAttributes +description: Attributes specified as function invocations +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function .dividebysomething(factor:0.5) +[divide] +\end + +\function multiplebysomething(first:ignored,factor:2) +[multiply[2].dividebysomething[0.25]] +\end + +<$text text=<>/> +| +<$text text=<>/> + ++ +title: ExpectedResult + +

16|32

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/FunctionOperator.tid b/editions/test/tiddlers/tests/data/functions/FunctionOperator.tid new file mode 100644 index 000000000..e2a0038dc --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/FunctionOperator.tid @@ -0,0 +1,24 @@ +title: Functions/FunctionOperator +description: Calling a function via the function operator +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function .dividebysomething(factor:0.5) +[divide] +\end + +\function multiplebysomething(first:ignored,factor:2) +[multiplymultiply[2].dividebysomething[0.25]] +\end + +<$text text={{{ [[4]function[multiplebysomething]] }}}/> +| +<$text text={{{ [[6]function[multiplebysomething],[ignored],[4]] }}}/> + ++ +title: ExpectedResult + +

64|192

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/MissingFunction.tid b/editions/test/tiddlers/tests/data/functions/MissingFunction.tid new file mode 100644 index 000000000..25498e452 --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/MissingFunction.tid @@ -0,0 +1,15 @@ +title: Functions/MissingFunction +description: Calling a missing function via the function operator +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +<$text text={{{ [[23]function[missing]] }}}/> + ++ +title: ExpectedResult + +23 \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/RunawayRecursiveFunctions.tid b/editions/test/tiddlers/tests/data/functions/RunawayRecursiveFunctions.tid new file mode 100644 index 000000000..81be22f16 --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/RunawayRecursiveFunctions.tid @@ -0,0 +1,18 @@ +title: Functions/RunawayRecursiveFunctions +description: Runaway recursive functions +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function .buffalo(p) +[.buffalo

] +\end + +<$text text=<<.buffalo 8>>/> + ++ +title: ExpectedResult + +/**-- Excessive filter recursion --**/ \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/UndefinedParameters.tid b/editions/test/tiddlers/tests/data/functions/UndefinedParameters.tid new file mode 100644 index 000000000..8a2b0a91a --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/UndefinedParameters.tid @@ -0,0 +1,22 @@ +title: Functions/UndefinedParameters +description: Undefined function parameters +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\function greet(who) +[[hello ]addsuffix] +\end + +<$text text={{{[function[greet],[world]]}}}/> + +<> + +<$text text={{{[function[greet]]}}}/> + +<> ++ +title: ExpectedResult + +hello world

hello world

hello

hello

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/WikifiedFunctions.tid b/editions/test/tiddlers/tests/data/functions/WikifiedFunctions.tid new file mode 100644 index 000000000..733fbdaef --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/WikifiedFunctions.tid @@ -0,0 +1,36 @@ +title: Functions/WikifiedFunctions +description: Wikified functions +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function fn-buffalo(param) +[addsuffix[ with a ''buffalo'']] +\end + +\procedure proc-buffalo(param) +<> with a ''buffalo'' +\end + +\define macro-buffalo(param) +$param$ with a ''buffalo'' +\end + +<> + +<> + +<> + +<$transclude $variable="fn-buffalo" param="Going to lunch" $output="text/plain"/> + +<$transclude $variable="proc-buffalo" param="Going to breakfast" $output="text/plain"/> + +<$transclude $variable="macro-buffalo" param="Going to dinner" $output="text/plain"/> + ++ +title: ExpectedResult + +

Going to lunch with a ''buffalo''

Going to breakfastwith abuffalo

Going to dinner with a buffalo

Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid b/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid new file mode 100644 index 000000000..f6834998d --- /dev/null +++ b/editions/test/tiddlers/tests/data/genesis-widget/RedefineLet.tid @@ -0,0 +1,31 @@ +title: Genesis/RedefineLet +description: Using the genesis widget to override the let widget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +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-raw"/> + + +\end +<$let + one="Elephant" + $two="Kangaroo" + $$three="Giraffe" +> +(<$text text=<>/>) +(<$text text=<<$two>>/>) +(<$text text=<<$$three>>/>) + ++ +title: ExpectedResult + +

(--Elephant--) +(--Kangaroo--) +(--Giraffe--)

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/Nested.tid b/editions/test/tiddlers/tests/data/procedures/Nested.tid new file mode 100644 index 000000000..f63c634af --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/Nested.tid @@ -0,0 +1,20 @@ +title: Procedures/Nested +description: Nested Procedures +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\procedure alpha(x) +\procedure beta(y) +<$text text=<>/> +\end beta +<$transclude $variable="beta" y={{{ [addprefix] }}}/> +\end alpha + +<> ++ +title: ExpectedResult + +

ElephantElephant

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-ActionWidget.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-ActionWidget.tid new file mode 100644 index 000000000..0be77a9a3 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-ActionWidget.tid @@ -0,0 +1,27 @@ +title: Transclude/CustomWidget/ActionWidget +description: Custom widget definition +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='Result'> + ++ +title: Actions + +\whitespace trim + +\widget $$action-mywidget(one:'Jaguar') +\whitespace trim +<$action-setfield $tiddler="Result" $field="text" $value=<>/> +\end + +<$$action-mywidget one="Dingo"> + Crocodile + ++ +title: ExpectedResult + +

Dingo

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Fail.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Fail.tid new file mode 100644 index 000000000..3d0759013 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Fail.tid @@ -0,0 +1,26 @@ +title: Transclude/CustomWidget/Fail +description: Custom widget failed definition +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +\widget $non-existent-widget(one:'Jaguar') +\whitespace trim +<$text text=<>/> +<$slot $name="ts-raw"> + Whale + +\end +<$non-existent-widget one="Dingo"> + Crocodile + +<$non-existent-widget one="BumbleBee"> + Squirrel + ++ +title: ExpectedResult + +

Undefined widget 'non-existent-widget'Undefined widget 'non-existent-widget'

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Override-Codeblock.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Override-Codeblock.tid new file mode 100644 index 000000000..c4730622b --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Override-Codeblock.tid @@ -0,0 +1,29 @@ +title: CustomWidget-Override-Codeblock +description: Usage of genesis widget with attributes starting with dollar signs +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\import Definition +<$codeblock code="Kangaroo"/> +<$codeblock code={{Subject}}/> +<$let test="Tiger"> +<$codeblock code=<>/> + ++ +title: Definition + +\whitespace trim +\widget $codeblock(code) +<$genesis $type="$codeblock" $remappable="no" code={{{ [addprefix[£]addsuffix[@]] }}}/> +\end ++ +title: Subject + +Python ++ +title: ExpectedResult + +

£Kangaroo@
£Python@
£Tiger@

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-OverrideTransclude.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-OverrideTransclude.tid new file mode 100644 index 000000000..c57e4a9a1 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-OverrideTransclude.tid @@ -0,0 +1,33 @@ +title: Transclude/CustomWidget/OverrideTransclude +description: Custom widget definition attempting to override transclude +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + ++ +title: TiddlerZero + +Antelope ++ +title: TiddlerOne + +\whitespace trim + +\widget $transclude(one:'Jaguar') +\whitespace trim + <$text text=<>/> + <$slot $name="body"> + Whale + +\end +<$genesis $type="$transclude" $remappable="no" $$tiddler="TiddlerZero"> + Crocodile + ++ +title: ExpectedResult + +

Antelope

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Simple.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Simple.tid new file mode 100644 index 000000000..15d0c8d9e --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Simple.tid @@ -0,0 +1,33 @@ +title: Transclude/CustomWidget/Simple +description: Custom widget definition +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + ++ +title: TiddlerOne + +\whitespace trim + +\widget $$mywidget(one:'Jaguar') +\whitespace trim +<$text text=<>/> +<$slot $name="ts-raw"> + Whale + +\end +<$$mywidget one="Dingo"> + Crocodile + +<$$mywidget one="BumbleBee"> + Squirrel + +<$$mywidget/> ++ +title: ExpectedResult + +

DingoCrocodileBumbleBeeSquirrelJaguarWhale

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted-Empty.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted-Empty.tid new file mode 100644 index 000000000..efd1e7041 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted-Empty.tid @@ -0,0 +1,20 @@ +title: CustomWidget/Slotted/Empty +description: Custom widget with empty slotted values +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\widget $$mywidget() +<$slot $name=ts-raw>the body is empty +\end + +#<$$mywidget/> +#<$$mywidget> +#<$$mywidget>the body is not empty + ++ +title: ExpectedResult + +
  1. the body is empty
  2. the body is empty
  3. the body is not empty
\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted.tid new file mode 100644 index 000000000..c10e84127 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Slotted.tid @@ -0,0 +1,27 @@ +title: Transclude/CustomWidget/Slotted +description: Custom widget definition +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\widget $$mywidget(one:'Jaguar') +\whitespace trim +<$text text=<>/> +<$slot $name="ts-stuff"> + Whale + +\end +<$$mywidget one="Dingo"> + <$fill $name="ts-stuff"> + Crocodile + + +<$$mywidget one="BumbleBee"> + Squirrel + ++ +title: ExpectedResult + +

DingoCrocodileBumbleBeeWhale

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverride.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverride.tid new file mode 100644 index 000000000..d0a3cc82c --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverride.tid @@ -0,0 +1,27 @@ +title: Transclude/CustomWidget/TextWidgetOverride +description: Custom widget definition redefining the text widget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne'> + ++ +title: TiddlerOne + +\whitespace trim + +\widget $text(text:'Jaguar') +\whitespace trim +<$genesis $type="$text" $remappable="no" text={{{ [addprefix[≤]addsuffix[≥]] }}}/> +\end + +<$text text="Dingo"/> + +Crocodile ++ +title: ExpectedResult + +

≤Dingo≥≤Jaguar≥

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverrideWithSlot.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverrideWithSlot.tid new file mode 100644 index 000000000..c84c5ae9a --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-TextWidgetOverrideWithSlot.tid @@ -0,0 +1,31 @@ +title: Transclude/CustomWidget/TextWidgetOverrideWithSlot +description: Custom widget definition redefining the text widget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne'> + ++ +title: TiddlerOne + +\whitespace trim + +\widget $text(text:'Jaguar') +\whitespace trim +<$genesis $type="$text" $remappable="no" text=<>/> +<$set name="$text" value=""> + <$slot $name="ts-raw"> + Whale + + +\end +<$text text="Dingo"> + Crocodile + ++ +title: ExpectedResult + +

DingoCrocodile

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-Unoverride-Codeblock.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Unoverride-Codeblock.tid new file mode 100644 index 000000000..c6a834205 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-Unoverride-Codeblock.tid @@ -0,0 +1,31 @@ +title: CustomWidget-Unoverride-Codeblock +description: Usage of genesis widget with attributes starting with dollar signs, and unoverriding a core widget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\import Definition +<$let $codeblock=""> +<$codeblock code="Kangaroo"/> +<$codeblock code={{Subject}}/> +<$let test="Tiger"> +<$codeblock code=<>/> + + ++ +title: Definition + +\whitespace trim +\widget $codeblock(code) +<$genesis $type="codeblock" $remappable="no" code={{{ [addprefix[£]addsuffix[@]] }}}/> +\end ++ +title: Subject + +Python ++ +title: ExpectedResult + +

Kangaroo
Python
Tiger

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-VariableAttribute.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-VariableAttribute.tid new file mode 100644 index 000000000..8ef700b41 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-VariableAttribute.tid @@ -0,0 +1,29 @@ +title: Transclude/CustomWidget/VariableAttribute +description: Custom widget definition using an attribute called $variable +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + ++ +title: TiddlerOne + +\whitespace trim + +\widget $$mywidget($variable:'Jaguar') +\whitespace trim +<$text text=<<$variable>>/> +<$slot $name="ts-raw"> + Whale + +\end +<$$mywidget $variable="Dingo"> + Crocodile + ++ +title: ExpectedResult + +

DingoCrocodile

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/JavaScript-Macro.tid b/editions/test/tiddlers/tests/data/transclude/JavaScript-Macro.tid new file mode 100644 index 000000000..216a89dc8 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/JavaScript-Macro.tid @@ -0,0 +1,17 @@ +title: Transclude/Macro/JavaScript +description: Transcluding a javascript macro +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +<> + +<$macrocall $name="makedatauri" text="Wildebeest" type="text/plain"/> + ++ +title: ExpectedResult + +

data:text/plain,Wildebeest

data:text/plain,Wildebeest

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Macro-Plain.tid b/editions/test/tiddlers/tests/data/transclude/Macro-Plain.tid new file mode 100644 index 000000000..410144153 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Macro-Plain.tid @@ -0,0 +1,17 @@ +title: Transclude/Macro/Plain +description: Transcluding a macro as plain text +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$let currentTab="Jeremy"> +<$macrocall $name="currentTab" $type="text/plain" $output="text/plain"/> +| +<$transclude $variable="currentTab" $type="text/plain" $output="text/plain"/> + ++ +title: ExpectedResult + +

Jeremy|Jeremy

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Macro-Simple.tid b/editions/test/tiddlers/tests/data/transclude/Macro-Simple.tid new file mode 100644 index 000000000..71db5efe4 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Macro-Simple.tid @@ -0,0 +1,26 @@ +title: Transclude/Macro/Simple +description: Transcluding a macro +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\define mamacro(one:"red",two:"green") +It is $one$ and $two$ or <<__one__>> and <<__two__>>. +\end + +<$macrocall $name="mamacro"/> + +<$transclude $variable="mamacro"/> + +<$transclude $variable="mamacro" one="orange"/> + +<$transclude $variable="mamacro" 0="pink"/> + +<$transclude $variable="mamacro" one="purple" 1="pink"/> + ++ +title: ExpectedResult + +

It is red and green or red and green.

It is red and green or red and green.

It is orange and green or orange and green.

It is pink and green or pink and green.

It is purple and pink or purple and pink.

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/MissingTarget.tid b/editions/test/tiddlers/tests/data/transclude/MissingTarget.tid new file mode 100644 index 000000000..8bdc86eaa --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/MissingTarget.tid @@ -0,0 +1,48 @@ +title: Transclude/MissingTarget +description: Transcluding a missing target +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + <$parameters one='Ferret'> + Badger + <$text text=<>/> + + +<$transclude $tiddler='TiddlerOne' one='Ferret'> + <$fill $name="ts-missing"> + <$parameters one='Ferret'> + Badger + <$text text=<>/> + + + +<$transclude $tiddler='MissingTiddler' one='Ferret'> + <$parameters one='Ferret'> + Badger + <$text text=<>/> + + +<$transclude $tiddler='MissingTiddler' one='Ferret'> + <$fill $name="ts-missing"> + <$parameters one='Ferret'> + Badger + <$text text=<>/> + + + ++ +title: TiddlerOne + +\whitespace trim +<$parameters one='Kangaroo'> + Piranha + <$text text=<>/> + ++ +title: ExpectedResult + +

PiranhaFerretPiranhaFerretBadgerFerretBadgerFerret

\ No newline at end of file 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-Mode.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Mode.tid new file mode 100644 index 000000000..04f5bbb04 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Mode.tid @@ -0,0 +1,29 @@ +title: Transclude/Parameterised/Mode +description: Parameterised transclusion using the $parseMode attribute +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +<$transclude $tiddler='TiddlerOne' one='Ferret'> + +This is a block + + + +<$transclude $tiddler='TiddlerOne'> +This is inline + ++ +title: TiddlerOne + +\whitespace trim +<$parameters $parseMode="@parseMode"> + <$text text=<<@parseMode>>/> + ++ +title: ExpectedResult + +

block

inline

\ 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 new file mode 100644 index 000000000..9d62a7897 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Name-Values.tid @@ -0,0 +1,34 @@ +title: Transclude/Parameterised/Name/Values +description: Parameterised transclusion accessing parameters as name/value pairs +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler="TiddlerOne" 0="" 1="" 2=""/> + +{{TiddlerOne}} +{{TiddlerOne|Ferret}} +{{TiddlerOne|Butterfly|Moth}} +{{TiddlerOne|Beetle|Scorpion|Snake}} +{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}} ++ +title: TiddlerOne + +\whitespace trim +<$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=<<$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 diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-ParseTreeNodes.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-ParseTreeNodes.tid new file mode 100644 index 000000000..916e2abfb --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-ParseTreeNodes.tid @@ -0,0 +1,29 @@ +title: Transclude/Parameterised/ParseTreeNodes +description: Parameterised transclusion using the $parseTreeNodes attribute +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +<$transclude $tiddler='TiddlerOne' one='Ferret'> + +This is a block + + + +<$transclude $tiddler='TiddlerOne'> +This is inline + ++ +title: TiddlerOne + +\whitespace trim +<$parameters $parseTreeNodes="@parseTreeNodes"> + <$text text=<<@parseTreeNodes>>/> + ++ +title: ExpectedResult + +

[{"type":"element","tag":"p","children":[{"type":"text","text":"This is a block","start":68,"end":83}],"start":68,"end":83}]

[{"type":"text","text":"This is inline","start":136,"end":152}]

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut-Parameters.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut-Parameters.tid new file mode 100644 index 000000000..abf444adb --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut-Parameters.tid @@ -0,0 +1,29 @@ +title: Transclude/Parameterised/Positional/Shortcut/Parameters +description: Positional parameterised transclusion using shortcut syntax and parameters pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +{{TiddlerOne}} +{{TiddlerOne|Ferret}} +{{TiddlerOne|Butterfly|Moth}} +{{TiddlerOne|Beetle|Scorpion|Snake}} +{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}} ++ +title: TiddlerOne + +\whitespace trim +\parameters(zero:Jaguar,one:'Lizard',two:'Mole') +[{<$text text=<>/>}{<$text text=<>/>}{<$text text=<>/>}] ++ +title: TiddlerTwo + +\whitespace trim +\parameters(zero:'Mouse',one:Horse,two:'Owl') +(<$transclude $tiddler=<> zero=<> one=<> two=<>/>) ++ +title: ExpectedResult + +

[{Jaguar}{Lizard}{Mole}]

[{Ferret}{Lizard}{Mole}]

[{Butterfly}{Moth}{Mole}]

[{Beetle}{Scorpion}{Snake}]

([{Beetle}{Scorpion}{Snake}])

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut.tid new file mode 100644 index 000000000..7792e6c66 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Shortcut.tid @@ -0,0 +1,29 @@ +title: Transclude/Parameterised/Positional/Shortcut +description: Positional parameterised transclusion using shortcut syntax +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +{{TiddlerOne}} +{{TiddlerOne|Ferret}} +{{TiddlerOne|Butterfly|Moth}} +{{TiddlerOne|Beetle|Scorpion|Snake}} +{{TiddlerOne||TiddlerTwo|Beetle|Scorpion|Snake}} ++ +title: TiddlerOne + +\whitespace trim +<$parameters zero='Jaguar' one='Lizard' two='Mole'>[{<$text text=<>/>}{<$text text=<>/>}{<$text text=<>/>}] ++ +title: TiddlerTwo + +\whitespace trim +<$parameters zero='Mouse' one='Horse' two='Owl'> +(<$transclude $tiddler=<> zero=<> one=<> two=<>/>) + ++ +title: ExpectedResult + +

[{Jaguar}{Lizard}{Mole}]

[{Ferret}{Lizard}{Mole}]

[{Butterfly}{Moth}{Mole}]

[{Beetle}{Scorpion}{Snake}]

([{Beetle}{Scorpion}{Snake}])

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Variables.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Variables.tid new file mode 100644 index 000000000..ad2b7be52 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional-Variables.tid @@ -0,0 +1,30 @@ +title: Transclude/Parameterised/Positional/Variables +description: Positional parameterised transclusion of variables +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\function myfunction(alpha:"apple",beta:"banana",gamma:"grenadine") [] +\define mymacro(alpha:"apple",beta:"banana",gamma:"grenadine") $beta$ +\function f(a) [] + +(Functions: +<$text text={{{ [] }}}/> +, +<$text text=<>/> +, +<> +)(Macros: +<$text text={{{ [] }}}/> +, +<$text text=<>/> +, +<> +) + ++ +title: ExpectedResult + +

(Functions:f1,f1,f1)(Macros:banana,banana,banana)

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional.tid new file mode 100644 index 000000000..d7eb9090e --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Positional.tid @@ -0,0 +1,26 @@ +title: Transclude/Parameterised/Positional +description: Positional parameterised transclusion +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' zero='Ferret'/> +<$transclude zero='Ferret' $tiddler='TiddlerOne'/> +<$transclude $tiddler='TiddlerOne' 0='Pigeon'/> +<$transclude 0='Pigeon' $tiddler='TiddlerOne'/> +<$transclude $tiddler='TiddlerOne' zero='Ferret' 0='Pigeon'/> +<$transclude zero='Ferret' 0='Pigeon' $tiddler='TiddlerOne'/> +<$transclude $tiddler='TiddlerOne'/> ++ +title: TiddlerOne + +\whitespace trim +<$parameters zero='Jaguar'> + <$text text=<>/> + ++ +title: ExpectedResult + +

FerretFerretPigeonPigeonFerretFerretJaguar

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut-Parameters.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut-Parameters.tid new file mode 100644 index 000000000..375964199 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut-Parameters.tid @@ -0,0 +1,20 @@ +title: Transclude/Parameterised/Shortcut/Parameters +description: Simple parameterised transclusion using the parameters pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'/> +<$transclude $tiddler='TiddlerOne'/> ++ +title: TiddlerOne + +\whitespace trim +\parameters(one:'Jaguar') +<$text text=<>/> ++ +title: ExpectedResult + +

FerretJaguar

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut.tid new file mode 100644 index 000000000..0499cf2d6 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Shortcut.tid @@ -0,0 +1,21 @@ +title: Transclude/Parameterised/Shortcut +description: Simple parameterised transclusion +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\procedure test(one:'Jaguar') +{<$text text=<>/>} +\end + +<$transclude $variable='test' one='Ferret'/> +<$transclude $variable='test'/> +<> +<> + ++ +title: ExpectedResult + +

{Ferret}{Jaguar}{Rat}{Mouse}

\ 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 new file mode 100644 index 000000000..0268f9e59 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Simple.tid @@ -0,0 +1,26 @@ +title: Transclude/Parameterised/Simple +description: Simple parameterised transclusion +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 +<$parameters one='Jaguar' $$two='Piranha'> + <$text text=<>/>:<$text text=<<$two>>/> + ++ +title: ExpectedResult + +

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

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-SlotFillParseTreeNodes.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-SlotFillParseTreeNodes.tid new file mode 100644 index 000000000..679748375 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-SlotFillParseTreeNodes.tid @@ -0,0 +1,29 @@ +title: Transclude/Parameterised/SlotFillParseTreeNodes +description: Parameterised transclusion using the $slotFillParseTreeNodes attribute +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim + +<$transclude $tiddler='TiddlerOne' one='Ferret'> +<$fill $name="one">This is first +<$fill $name="two">But this is second + + +<$transclude $tiddler='TiddlerOne'> +<$fill $name="one">This is first +<$fill $name="two">But this is second + ++ +title: TiddlerOne + +\whitespace trim +<$parameters $slotFillParseTreeNodes="@slotFillParseTreeNodes"> + <$text text={{{ [<@slotFillParseTreeNodes>jsonindexes[]join[,]] }}}/> + ++ +title: ExpectedResult + +

one,ts-raw,two

one,ts-raw

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted-Missing.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted-Missing.tid new file mode 100644 index 000000000..fe399d572 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted-Missing.tid @@ -0,0 +1,24 @@ +title: Transclude/Parameterised/Slotted/Missing +description: Parameterised transclusion with slotted missing values +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + ++ +title: TiddlerOne + +\whitespace trim +<$parameters one='Jaguar'> + <$text text=<>/> + <$slot $name="content"> + Whale + + ++ +title: ExpectedResult + +

FerretWhale

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted.tid b/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted.tid new file mode 100644 index 000000000..c795621ef --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Parameterised-Slotted.tid @@ -0,0 +1,27 @@ +title: Transclude/Parameterised/Slotted +description: Parameterised transclusion with slotted values +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$transclude $tiddler='TiddlerOne' one='Ferret'> + <$fill $name="content"> + Hippopotamus + + ++ +title: TiddlerOne + +\whitespace trim +<$parameters one='Jaguar'> + <$text text=<>/> + <$slot $name="content"> + Whale + + ++ +title: ExpectedResult + +

FerretHippopotamus

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Procedures-Whitespace.tid b/editions/test/tiddlers/tests/data/transclude/Procedures-Whitespace.tid new file mode 100644 index 000000000..d2bded70c --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Procedures-Whitespace.tid @@ -0,0 +1,25 @@ +title: Transclude/Procedures/Whitespace +description: Procedures should inherit whitespace settings from definition site +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\procedure testproc() +This is a sentence +\end + +\define testmacro() +This is a sentence +\end +This is a sentence +[<>] +[<>] + ++ +title: ExpectedResult + +

This is a sentence +[This is a sentence] +[This is a sentence ]

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/Typed.tid b/editions/test/tiddlers/tests/data/transclude/Typed.tid new file mode 100644 index 000000000..c99664b59 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/Typed.tid @@ -0,0 +1,38 @@ +title: Transclude/Typed +description: Typed transclusion +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure testproc() +This is ''wikitext'' +\end + +<$transclude $variable="testproc"/> +- +<$transclude $variable="testproc" $type="text/plain"/> + +<$transclude $tiddler="Data" $index="testindex"/> +- +<$transclude $tiddler="Data" $index="testindex" $type="text/plain"/> + +<$transclude $tiddler="Data" $field="custom"/> +- +<$transclude $tiddler="Data" $field="custom" $type="text/plain"/> ++ +title: Data +type: application/x-tiddler-dictionary +custom: This is ''wikitext'' + +testindex: This is ''wikitext'' ++ +title: ExpectedResult + +

This is wikitext +- +

This is ''wikitext''

This is wikitext +- +

This is ''wikitext''

This is wikitext +- +

This is ''wikitext''

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 2a9080de5..e00d0bf8d 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -422,7 +422,7 @@ Tests the filtering mechanism. expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,Tiddler8,TiddlerOne,TiddlerSeventh"); expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three"); - var fakeWidget = {getVariable: function() {return "one";}}; + var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "one": undefined;}}; expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); }); @@ -625,7 +625,7 @@ Tests the filtering mechanism. expect(wiki.filterTiddlers("[{!!title}]").join(",")).toBe(""); expect(wiki.filterTiddlers("[prefix{Tiddler8}] +[sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); expect(wiki.filterTiddlers("[modifier{Tiddler8!!test-field}] +[sort[title]]").join(",")).toBe("TiddlerOne"); - var fakeWidget = {wiki: wiki, getVariable: function() {return "Tiddler Three";}}; + var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "Tiddler Three": undefined;}}; expect(wiki.filterTiddlers("[modifier{!!modifier}] +[sort[title]]",fakeWidget).join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three"); }); diff --git a/editions/test/tiddlers/tests/test-parsetextreference.js b/editions/test/tiddlers/tests/test-parsetextreference.js index 376ad9ec4..59f885232 100644 --- a/editions/test/tiddlers/tests/test-parsetextreference.js +++ b/editions/test/tiddlers/tests/test-parsetextreference.js @@ -124,7 +124,7 @@ describe("Wiki.parseTextReference tests", function() { // Non-existent subtiddler of a plugin expect(parseAndGetSource("$:/ShadowPlugin","text",null,"MyMissingTiddler")).toEqual(null); // Plain text tiddler - expect(parseAndGetSource("TiddlerNine")).toEqual(undefined); + expect(parseAndGetSource("TiddlerNine")).toEqual("this is plain text"); }); }); diff --git a/editions/test/tiddlers/tests/test-utils.js b/editions/test/tiddlers/tests/test-utils.js index 8b7630a54..d41d5047a 100644 --- a/editions/test/tiddlers/tests/test-utils.js +++ b/editions/test/tiddlers/tests/test-utils.js @@ -188,4 +188,4 @@ describe("Utility tests", function() { }); -})(); +})(); \ No newline at end of file diff --git a/editions/test/tiddlers/tests/test-widget.js b/editions/test/tiddlers/tests/test-widget.js index 2614d6f52..544ed928f 100755 --- a/editions/test/tiddlers/tests/test-widget.js +++ b/editions/test/tiddlers/tests/test-widget.js @@ -143,7 +143,7 @@ describe("Widget module", function() { var wiki = new $tw.Wiki(); // Add a tiddler wiki.addTiddlers([ - {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>\n"}, + {title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/>"}, {title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"} ]); // Test parse tree @@ -157,7 +157,7 @@ describe("Widget module", function() { // Render the widget node to the DOM var wrapper = renderWidgetNode(widgetNode); // Test the rendering - expect(wrapper.innerHTML).toBe("Recursive transclusion error in transclude widget\n"); + expect(wrapper.innerHTML).toBe("Recursive transclusion error in transclude widget"); }); it("should deal with SVG elements", function() { @@ -683,7 +683,7 @@ describe("Widget module", function() { expect(wrapper.innerHTML).toBe("

New value

"); }); - it("should can mix setWidgets and macros when importing", function() { + it("should support mixed setWidgets and macros when importing", function() { var wiki = new $tw.Wiki(); // Add some tiddlers wiki.addTiddlers([ @@ -699,6 +699,20 @@ describe("Widget module", function() { expect(wrapper.innerHTML).toBe("

Aval Bval Cval

"); }); + it("should skip parameters widgets when importing", function() { + var wiki = new $tw.Wiki(); + // Add some tiddlers + wiki.addTiddlers([ + {title: "B", text: "<$parameters bee=nothing><$set name='B' value='Bval'>\n\ndummy text"}, + ]); + var text = "\\import B\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("

Bval

"); + }); + it("can have more than one macroDef variable imported", function() { var wiki = new $tw.Wiki(); wiki.addTiddlers([ diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index 7f1551c28..bc3d9acd8 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -19,7 +19,8 @@ describe("WikiText parser tests", function() { // Define a parsing shortcut var parse = function(text) { - return wiki.parseText("text/vnd.tiddlywiki",text).tree; + var tree = wiki.parseText("text/vnd.tiddlywiki",text).tree; + return tree; }; it("should parse tags", function() { @@ -114,7 +115,70 @@ describe("WikiText parser tests", function() { it("should parse macro definitions", function() { expect(parse("\\define myMacro()\nnothing\n\\end\n")).toEqual( - [ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ] + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}]}] + + ); + }); + + it("should parse procedure definitions with no parameters", function() { + expect(parse("\\procedure myMacro()\nnothing\n\\end\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}] + + ); + }); + + it("should parse single line procedure definitions with no parameters", function() { + expect(parse("\\procedure myMacro() nothing\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}] + + ); + }); + + it("should parse procedure definitions with parameters", function() { + expect(parse("\\procedure myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isProcedureDefinition":true}] + + ); + }); + + it("should parse procedure definitions", function() { + expect(parse("\\procedure myMacro(one:'Jaguar')\n<$text text=<>/>\n\\end\n\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<>/>"}],"isProcedureDefinition":true}] + + ); + + }); it("should parse function definitions with no parameters", function() { + expect(parse("\\function myMacro()\nnothing\n\\end\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}] + + ); + }); + + it("should parse single line function definitions with no parameters", function() { + expect(parse("\\function myMacro() nothing\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}] + + ); + }); + + it("should parse function definitions with parameters", function() { + expect(parse("\\function myMacro(one,two,three,four:elephant)\nnothing\n\\end\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[{"name":"one"},{"name":"two"},{"name":"three"},{"name":"four","default":"elephant"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"nothing"}],"isFunctionDefinition":true}] + + ); + }); + + it("should parse function definitions", function() { + expect(parse("\\function myMacro(one:'Jaguar')\n<$text text=<>/>\n\\end\n\n")).toEqual( + + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"myMacro"},"value":{"name":"value","type":"string","value":"<$text text=<>/>"}},"children":[],"params":[{"name":"one","default":"Jaguar"}],"orderedAttributes":[{"name":"name","type":"string","value":"myMacro"},{"name":"value","type":"string","value":"<$text text=<>/>"}],"isFunctionDefinition":true}] ); }); @@ -122,7 +186,7 @@ describe("WikiText parser tests", function() { it("should parse comment in pragma area. Comment will be invisible", function() { expect(parse("\n\\define aMacro()\nnothing\n\\end\n")).toEqual( - [ { type : 'set', attributes : { name : { type : 'string', value : 'aMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isMacroDefinition : true } ] + [{"type":"set","attributes":{"name":{"name":"name","type":"string","value":"aMacro"},"value":{"name":"value","type":"string","value":"nothing"}},"children":[],"params":[],"isMacroDefinition":true,"orderedAttributes":[{"name":"name","type":"string","value":"aMacro"},{"name":"value","type":"string","value":"nothing"}]}] ); }); @@ -143,38 +207,38 @@ describe("WikiText parser tests", function() { it("should parse inline macro calls", function() { expect(parse("<><><><>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 35, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'macrocall', start: 8, params: [ ], name: 'paul', end: 16 }, { type: 'macrocall', start: 16, params: [ ], name: 'george', end: 26 }, { type: 'macrocall', start: 26, params: [ ], name: 'ringo', end: 35 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"transclude","start":8,"end":16,"attributes":{"$variable":{"name":"$variable","type":"string","value":"paul"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"paul"}]},{"type":"transclude","start":16,"end":26,"attributes":{"$variable":{"name":"$variable","type":"string","value":"george"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"george"}]},{"type":"transclude","start":26,"end":35,"attributes":{"$variable":{"name":"$variable","type":"string","value":"ringo"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"ringo"}]}],"start":0,"end":35}] ); expect(parse("text <>")).toEqual( - [{ type: 'element', tag: 'p', start: 0, end: 92, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: 'john', start: 5, params: [ { type: 'macro-parameter', start: 11, value: 'val1', name: 'one', end: 20 }, { type: 'macro-parameter', start: 20, value: 'val "2"', name: 'two', end: 35 }, { type: 'macro-parameter', start: 35, value: 'val \'3\'', name: 'three', end: 52 }, { type: 'macro-parameter', start: 52, value: 'val 4"5\'', name: 'four', end: 73 }, { type: 'macro-parameter', start: 73, value: 'val 5', name: 'five', end: 89 } ], end: 92 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":92,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":11,"end":20},"two":{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},"three":{"name":"three","type":"string","value":"val '3'","start":35,"end":52},"four":{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},"five":{"name":"five","type":"string","value":"val 5","start":73,"end":89}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":11,"end":20},{"name":"two","type":"string","value":"val \"2\"","start":20,"end":35},{"name":"three","type":"string","value":"val '3'","start":35,"end":52},{"name":"four","type":"string","value":"val 4\"5'","start":52,"end":73},{"name":"five","type":"string","value":"val 5","start":73,"end":89}]}],"start":0,"end":92}] ); expect(parse("ignored << carrots <>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 27, children: [ { type: 'text', text: 'ignored << carrots ', start: 0, end: 19 }, { type: 'macrocall', name: 'john', start: 19, params: [ ], end: 27 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"text","text":"ignored << carrots ","start":0,"end":19},{"type":"transclude","start":19,"end":27,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":27}] ); expect(parse("text <<>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 14, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', name: '>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'text', text: 'before\n', start: 0, end: 7 }, { type: 'macrocall', start: 7, params: [ ], name: 'john', end: 15 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"text","text":"before\n","start":0,"end":7},{"type":"transclude","start":7,"end":15,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]}],"start":0,"end":15}] ); // A single space will cause it to be inline expect(parse("<> ")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 9, children: [ { type: 'macrocall', start: 0, params: [ ], name: 'john', end: 8 }, { type: 'text', text: ' ', start: 8, end: 9 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":8,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"}]},{"type":"text","text":" ","start":8,"end":9}],"start":0,"end":9}] ); expect(parse("text <>' >>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 34, children: [ { type: 'text', text: 'text ', start: 0, end: 5 }, { type: 'macrocall', start: 5, params: [ { type: 'macro-parameter', start: 12, value: 'my <>', name: 'one', end: 31 } ], name: 'outie', end: 34 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"text","text":"text ","start":0,"end":5},{"type":"transclude","start":5,"end":34,"attributes":{"$variable":{"name":"$variable","type":"string","value":"outie"},"one":{"name":"one","type":"string","value":"my <>","start":12,"end":31}},"orderedAttributes":[{"name":"$variable","type":"string","value":"outie"},{"name":"one","type":"string","value":"my <>","start":12,"end":31}]}],"start":0,"end":34}] ); @@ -183,37 +247,37 @@ describe("WikiText parser tests", function() { it("should parse block macro calls", function() { expect(parse("<>\n<>\r\n<>\n<>")).toEqual( - [ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'macrocall', start: 9, name: 'paul', params: [ ], end: 17, isBlock: true }, { type: 'macrocall', start: 19, name: 'george', params: [ ], end: 29, isBlock: true }, { type: 'macrocall', start: 30, name: 'ringo', params: [ ], end: 39, isBlock: true } ] + [ { type: 'transclude', start: 0, attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: 'transclude', start: 9, attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: 'transclude', start: 19, attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: 'transclude', start: 30, attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ] ); expect(parse("<>")).toEqual( - [ { type: 'macrocall', start: 0, name: 'john', params: [ { type: 'macro-parameter', start: 6, value: 'val1', name: 'one', end: 15 }, { type: 'macro-parameter', start: 15, value: 'val "2"', name: 'two', end: 30 }, { type: 'macro-parameter', start: 30, value: 'val \'3\'', name: 'three', end: 47 }, { type: 'macro-parameter', start: 47, value: 'val 4"5\'', name: 'four', end: 68 }, { type: 'macro-parameter', start: 68, value: 'val 5', name: 'five', end: 84 }], end: 87, isBlock: true } ] + [{"type":"transclude","start":0,"end":87,"attributes":{"$variable":{"name":"$variable","type":"string","value":"john"},"one":{"name":"one","type":"string","value":"val1","start":6,"end":15},"two":{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},"three":{"name":"three","type":"string","value":"val '3'","start":30,"end":47},"four":{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},"five":{"name":"five","type":"string","value":"val 5","start":68,"end":84}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"one","type":"string","value":"val1","start":6,"end":15},{"name":"two","type":"string","value":"val \"2\"","start":15,"end":30},{"name":"three","type":"string","value":"val '3'","start":30,"end":47},{"name":"four","type":"string","value":"val 4\"5'","start":47,"end":68},{"name":"five","type":"string","value":"val 5","start":68,"end":84}],"isBlock":true}] ); expect(parse("<< carrots\n\n<>")).toEqual( - [ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'macrocall', start: 12, params: [ ], name: 'john', end: 20, isBlock: true } ] + [ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'transclude', start: 12, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ] ); expect(parse("before\n\n<>")).toEqual( - [ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'macrocall', start: 8, name: 'john', params: [ ], end: 16, isBlock: true } ] + [ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'transclude', start: 8, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ] ); expect(parse("<>\nafter")).toEqual( - [ { type: 'macrocall', start: 0, name: 'john', params: [ ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ] + [ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ] ); expect(parse("<>")).toEqual( - [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 11, value: '\n\nwikitext\n', name: 'arg', end: 33 } ], name: 'multiline', end: 36, isBlock: true }] + [{"type":"transclude","start":0,"end":36,"attributes":{"$variable":{"name":"$variable","type":"string","value":"multiline"},"arg":{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}},"orderedAttributes":[{"name":"$variable","type":"string","value":"multiline"},{"name":"arg","type":"string","value":"\n\nwikitext\n","start":11,"end":33}],"isBlock":true}] ); expect(parse("<>' >>")).toEqual( - [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 7, value: 'my <>', name: 'one', end: 26 } ], name: 'outie', end: 29, isBlock: true } ] + [ { type: 'transclude', start: 0, attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <>", start: 7, end: 26} ], end: 29, isBlock: true } ] ); }); @@ -221,23 +285,23 @@ describe("WikiText parser tests", function() { it("should parse tricky macrocall parameters", function() { expect(parse("<am>>")).toEqual( - [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'pa>am', end: 12 } ], name: 'john', end: 14, isBlock: true } ] + [{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"pa>am","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"pa>am","start":6,"end":12}],"isBlock":true}] ); expect(parse("< >>")).toEqual( - [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param>', end: 13 } ], name: 'john', end: 16, isBlock: true } ] + [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"param>","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param>","start":6,"end":13}],"isBlock":true}] ); expect(parse("<>>")).toEqual( - [ { type: 'element', tag: 'p', start: 0, end: 15, children: [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'param', end: 12 } ], name: 'john', end: 14 }, { type: 'text', text: '>', start: 14, end: 15 } ] } ] + [{"type":"element","tag":"p","children":[{"type":"transclude","start":0,"end":14,"attributes":{"0":{"name":"0","type":"string","value":"param","start":6,"end":12},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"param","start":6,"end":12}]},{"type":"text","text":">","start":14,"end":15}],"start":0,"end":15}] ); // equals signs should be allowed expect(parse("<=4 >>")).toEqual( - [ { type: 'macrocall', start: 0, params: [ { type: 'macro-parameter', start: 6, value: 'var>=4', end: 13 } ], name: 'john', end: 16, isBlock: true } ] + [{"type":"transclude","start":0,"end":16,"attributes":{"0":{"name":"0","type":"string","value":"var>=4","start":6,"end":13},"$variable":{"name":"$variable","type":"string","value":"john"}},"orderedAttributes":[{"name":"$variable","type":"string","value":"john"},{"name":"0","type":"string","value":"var>=4","start":6,"end":13}],"isBlock":true}] ); diff --git a/editions/test/tiddlers/tests/test-wikitext-tabs-macro.js b/editions/test/tiddlers/tests/test-wikitext-tabs-macro.js index b37f402cc..39f061d11 100644 --- a/editions/test/tiddlers/tests/test-wikitext-tabs-macro.js +++ b/editions/test/tiddlers/tests/test-wikitext-tabs-macro.js @@ -1,7 +1,7 @@ /*\ title: test-wikitext-tabs-macro.js type: application/javascript -tags: [[$:/tags/test-spec]] +tags: [[$:/tags/test-spec-disabled]] Tests the core tabs macro by comparing the HTML output with a stored template. Intended to permit future readability improvements. @@ -74,7 +74,7 @@ describe("Tabs-macro HTML tests", function() { expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal")).toBe(expected.fields.text.replace(/\n/g,"")); }); - it("should render 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() { + it("should render all 'horizontal' tabs from v5.2.2 and up with whitespace trim", function() { expect(wiki.renderTiddler("text/html","test-tabs-macro-horizontal-all")).toBe(expectedAll.fields.text.replace(/\n/g,"")); }); diff --git a/editions/tw5.com/tiddlers/Brackets.tid b/editions/tw5.com/tiddlers/concepts/Brackets.tid similarity index 92% rename from editions/tw5.com/tiddlers/Brackets.tid rename to editions/tw5.com/tiddlers/concepts/Brackets.tid index 529adb17e..fee36313c 100644 --- a/editions/tw5.com/tiddlers/Brackets.tid +++ b/editions/tw5.com/tiddlers/concepts/Brackets.tid @@ -10,5 +10,5 @@ WikiText syntax uses a number of different types of brackets. Their names are sh |`()` |Round brackets |Parenthesis |Not used in WikiText | |`[]` |Square brackets |Brackets |[[Links|Linking in WikiText]], [[Filters|Filters]] | |`{}` |Curly brackets |Braces |[[Text references|TextReference]], [[Filtered attributes|HTML in WikiText]] | -|`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros|Macros in WikiText]] | +|`<>` |Angle brackets |Chevrons |[[HTML elements and widgets|HTML in WikiText]], [[Macros]] | diff --git a/editions/tw5.com/tiddlers/concepts/Macros.tid b/editions/tw5.com/tiddlers/concepts/Macros.tid index 1d06f9755..8377046f6 100644 --- a/editions/tw5.com/tiddlers/concepts/Macros.tid +++ b/editions/tw5.com/tiddlers/concepts/Macros.tid @@ -1,31 +1,42 @@ created: 20140211171341271 -modified: 20220505082754270 +modified: 20230419103154328 tags: Concepts Reference title: Macros type: text/vnd.tiddlywiki -A <<.def macro>> is a named snippet of text. WikiText can use the name as a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters that get substituted for special placeholders within the snippet. +!! Introduction -For the syntax, see [[Macros in WikiText]]. +A <<.def macro>> is a named snippet of text. They are typically defined with the [[Pragma: \define]]: -Most macros are in fact just parameterised [[variables|Variables]]. +``` +\define my-macro(parameter:"Default value") +This is the macro, and the parameter is $parameter$. +\end +``` -They are created using the `\define` [[pragma|Pragma]]. (Behind the scenes, this is transformed into a <<.wlink SetWidget>>, i.e. macros and variables are two sides of the same coin.) +The name wrapped in double angled [[brackets|Brackets]] is used a shorthand way of [[transcluding|Transclusion]] the snippet. Such transclusions are known as <<.def "macro calls">>, and each call can supply a different set of parameters: -The snippet and its incoming parameter values are treated as simple strings of characters with no WikiText meaning, at least until the placeholders have been filled in and the macro call has returned. This means that a macro can assemble and return the complete syntax of a ~WikiText component, such as a [[link|Linking in WikiText]]. (See [[Transclusion and Substitution]] for further discussion of this.) +``` +<> +<> +``` -Within a snippet itself, the only markup detected is `$name$` (a placeholder for a macro parameter) and `$(name)$` (a placeholder for a [[variable|Variables]]). +The parameters that are specified in the macro call are substituted for special placeholders within the snippet: -The <<.mlink dumpvariables>> macro lists all variables (including macros) that are available at that position in the widget tree. +* `$parameter-name$` is replaced with the value of the named parameter +* `$(variable-name)$` is replaced with the value of the named [[variable|Variables]]). -An <<.wlink ImportVariablesWidget>> widget can be used to copy macro definitions to another branch of the [[widget tree|Widgets]]. ~TiddlyWiki uses this technique internally to implement global macros -- namely any macros defined in tiddlers with the <<.tag $:/tags/Macro>> tag. +<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Custom Widgets]] and [[Functions]] which together provide more robust and flexible ways to encapsulate and re-use code. It is now recommended to only use macros when textual substitution is specifically required. -The tag <<.tag $:/tags/Macro/View>> is used to define macros that should only be available within the main view template and the preview panel. +!! How Macros Work -The tag <<.tag $:/tags/Macro/View/Body>> is used to define macros that should only be available within the main view template body and the preview panel. +Macros are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled. -For maximum flexibility, macros can also be <<.js-macro-link "written as JavaScript modules">>. +!! Using Macros -A similar effect to a parameterised macro call can be produced by setting [[variables|Variables]] around a [[transclusion|Transclusion]]. +* [[Macro Definitions]] describes how to create macros +* [[Macro Calls]] describes how to use macros +* [[Macro Parameter Handling]] describes how macro parameters work +* [[Macro Pitfalls]] describes some of the pitfalls of using macros +* [[Core Macros]] lists the built-in core macros -~TiddlyWiki's core has [[several macros|Core Macros]] built in. diff --git a/editions/tw5.com/tiddlers/concepts/Pragma.tid b/editions/tw5.com/tiddlers/concepts/Pragma.tid index 3a9e1de12..868cf9667 100644 --- a/editions/tw5.com/tiddlers/concepts/Pragma.tid +++ b/editions/tw5.com/tiddlers/concepts/Pragma.tid @@ -1,24 +1,8 @@ + created: 20150219175930000 -modified: 20230117112239663 -tags: Concepts [[WikiText Parser Modes]] +modified: 20220122182842041 +tags: title: Pragma type: text/vnd.tiddlywiki -A <<.def pragma>> is a special component of WikiText that provides control over the way the remaining text is parsed. - -Pragmas occupy lines that start with `\`. They can only appear at the start of the text, but blank lines are allowed between them. If a pragma line appears in the main body of the text, it is treated as if it was ordinary text. -<<.from-version "5.2.6">> Pragmas can have preceding optional whitespace characters. - - -The following pragmas are available: - -;`\define` -: for defining a [[macro|Macros]] -;`\rules` -: for adjusting the set of rules used to parse the text -;`\whitespace trim` or `\whitespace notrim` -: <<.from-version "5.1.15">> Control whether whitespace is trimmed from the start and end of text runs (the default is ''notrim''). This setting can be useful when the whitespace generated by linebreaks disturbs formatting -;`\import ` -: <<.from-version "5.1.18">> Import macro definitions from tiddlers identified by a filter expression -;`\parsermode block` or `\parsermode inline` -: <<.from-version "5.2.4">> Adjust whether the remaining text is parsed in block mode or inline mode. \ No newline at end of file +See [[Pragmas]]. diff --git a/editions/tw5.com/tiddlers/features/StartupActions.tid b/editions/tw5.com/tiddlers/features/StartupActions.tid index 79a23b3d9..29edb8378 100644 --- a/editions/tw5.com/tiddlers/features/StartupActions.tid +++ b/editions/tw5.com/tiddlers/features/StartupActions.tid @@ -33,7 +33,7 @@ The initial startup actions are useful for customising TiddlyWiki according to e <$action-setfield $tiddler="$:/language" text={{{ [[$:/languages/en-GB]] [plugin-type[language]sort[description]removeprefix[$:/languages/]] +[prefix{$:/info/browser/language}] ~[[en-GB]] +[addprefix[$:/languages/]] }}}/> ``` -Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an ''import'' [[pragma|Pragma]] at the top of the tiddler: +Note that global macros are not available within initial startup action tiddlers by default. If you need to access them then you'll need to explicitly include them with an [[Pragma: \import]] at the top of the tiddler: ``` \import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]] diff --git a/editions/tw5.com/tiddlers/filters/function.tid b/editions/tw5.com/tiddlers/filters/function.tid new file mode 100644 index 000000000..f86d21f4f --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/function.tid @@ -0,0 +1,21 @@ +caption: function +created: 20220909111836951 +modified: 20230419103154328 +op-input: a [[selection of titles|Title Selection]] passed as input to the function <<.place F>> +op-output: the [[selection of titles|Title Selection]] returned from the function <<.place F>> +op-parameter: first parameter is the [[function name|Functions]], subsequent parameters are passed to the function by position +op-parameter-name: F +op-purpose: apply a [[function|Functions]] to the input list, and return the result +tags: [[Filter Operators]] +title: function Operator +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The <<.op function>> operator applies a named [[function|Functions]] to the input titles, and returns the results from the function. The function is invoked once with all of the input titles (in contrast, the [[filter Operator]] invokes its function separately for each input title). + +The first parameter of the <<.op function>> operator specifies the name of the function to be called. Subsequent parameters are passed to the function. + +The mapping between the parameters is //positional//, with each consecutive parameter specified in the function call mapped to the corresponding parameter in the function definition. Any parameters that are not provided are given their default values. + +<<.tip "Compare with the similar [[filter|filter Operator]] and [[subfilter|subfilter Operator]] operators which take a filter strings as their parameter instead of a named function, and does not permit parameters to be passed">> + +<<.operator-examples "function">> diff --git a/editions/tw5.com/tiddlers/functions/Functions.tid b/editions/tw5.com/tiddlers/functions/Functions.tid new file mode 100644 index 000000000..3b06dddc7 --- /dev/null +++ b/editions/tw5.com/tiddlers/functions/Functions.tid @@ -0,0 +1,28 @@ +created: 20221009124003601 +modified: 20230419103154328 +tags: Concepts Reference +title: Functions +type: text/vnd.tiddlywiki + +!! Introduction + +<<.from-version "5.3.0">> A <<.def function>> is a named snippet of text containing a [[Filter Expression]]. Functions can have named parameters which are available within the function as variables. + +Functions are usually defined with the [[Pragma: \function]]: + +``` +\function my-function(parameter:"2") +[multiply[1.5]] +\end +``` + +Functions can be invoked in several ways: + +* Directly transclude functions with the syntax `<>` +* Assign functions to widget attributes with the syntax `
>>` +* Invoke functions via the [[function Operator]] with the syntax `[function[myfn],[value],...]` +* Directly invoke functions whose names start with a period as custom filter operators with the syntax `[.myfn[value]]` + +!! How Functions Work + +Functions are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled. diff --git a/editions/tw5.com/tiddlers/howtos/Visible Transclusions.tid b/editions/tw5.com/tiddlers/howtos/Visible Transclusions.tid new file mode 100644 index 000000000..e3f46440e --- /dev/null +++ b/editions/tw5.com/tiddlers/howtos/Visible Transclusions.tid @@ -0,0 +1,14 @@ +created: 20220909111836951 +modified: 20230419103154329 +tags: Learning +title: Visible Transclusions +type: text/vnd.tiddlywiki + +!! Visible Transclusions + +Block transclusions are shown in red, and inline transclusions are shown in green. + +<$button> +<$action-setfield $tiddler="$:/temp/VisibleTransclusions" tags="$:/tags/Macro/View/Body" text={{$:/core/ui/VisibleTransclude}}/> +Click here to make transclusions visible within story river tiddlers + diff --git a/editions/tw5.com/tiddlers/images/New Release Banner.png b/editions/tw5.com/tiddlers/images/New Release Banner.png index 74e2557d8b3766f57e98daf4f2a1616324161bc2..b75c8fc193d00967836f6096d3517e528ac2fcdd 100644 GIT binary patch literal 50280 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 D)r7w` literal 81675 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
`GSzJ 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

7e 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 JPDHLkV1lmDi7Eg9 diff --git a/editions/tw5.com/tiddlers/macros/import/say-hi-using-variables.tid b/editions/tw5.com/tiddlers/macros/import/say-hi-using-variables.tid index 4aa265fa3..11064f388 100644 --- a/editions/tw5.com/tiddlers/macros/import/say-hi-using-variables.tid +++ b/editions/tw5.com/tiddlers/macros/import/say-hi-using-variables.tid @@ -1,3 +1,4 @@ +code-body: yes created: 20150221145447000 modified: 20150221145626000 title: $:/editions/tw5.com/macro-examples/say-hi-using-variables diff --git a/editions/tw5.com/tiddlers/macros/import/say-hi.tid b/editions/tw5.com/tiddlers/macros/import/say-hi.tid index 2d2d31afc..55db4cc9a 100644 --- a/editions/tw5.com/tiddlers/macros/import/say-hi.tid +++ b/editions/tw5.com/tiddlers/macros/import/say-hi.tid @@ -1,8 +1,9 @@ +code-body: yes created: 20150221145803000 modified: 20150221221536000 title: $:/editions/tw5.com/macro-examples/say-hi type: text/vnd.tiddlywiki -\define sayhi(name:"Bugs Bunny" address:"Rabbit Hole Hill") +\define sayhi(name:"Bugs Bunny",address:"Rabbit Hole Hill") Hi, I'm $name$ and I live in $address$. \end diff --git a/editions/tw5.com/tiddlers/macros/import/tags-of-current-tiddler.tid b/editions/tw5.com/tiddlers/macros/import/tags-of-current-tiddler.tid index b1bfc753c..860ad33db 100644 --- a/editions/tw5.com/tiddlers/macros/import/tags-of-current-tiddler.tid +++ b/editions/tw5.com/tiddlers/macros/import/tags-of-current-tiddler.tid @@ -1,3 +1,4 @@ +code-body: yes created: 20150221145803000 title: $:/editions/tw5.com/macro-examples/tags-of-current-tiddler type: text/vnd.tiddlywiki diff --git a/editions/tw5.com/tiddlers/macros/import/tv-wikilink-tooltip.tid b/editions/tw5.com/tiddlers/macros/import/tv-wikilink-tooltip.tid index 11b442b8c..9687f4b15 100644 --- a/editions/tw5.com/tiddlers/macros/import/tv-wikilink-tooltip.tid +++ b/editions/tw5.com/tiddlers/macros/import/tv-wikilink-tooltip.tid @@ -1,5 +1,5 @@ +code-body: yes created: 20150228120252000 -modified: 20150228120554000 title: $:/editions/tw5.com/macro-examples/tv-wikilink-tooltip type: text/vnd.tiddlywiki diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid new file mode 100644 index 000000000..6058b7905 --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid @@ -0,0 +1,51 @@ +created: 20220917112233317 +modified: 20230419103154328 +tags: Pragmas +title: Pragma: \define +type: text/vnd.tiddlywiki + +The ''\define'' [[pragma|Pragmas]] is used to [[define macros|Macro Definitions]]. It is a shortcut syntax for the SetVariableWidget. + +The usual form allows macros to span multiple lines. + +``` +\define ([:],[:]...) + +\end [] +``` + +Note that the `\end` marker can optionally specify the name of the macro to which it relates which allows macro definitions to be nested. + +There is also a single line form for shorter macros: + +``` +\define ([:],[:]...) +``` + +The first line of the definition specifies the macro name and any parameters. Each parameter has a name and, optionally, a default value that is used if no value is supplied on a particular call to the macro. + +The lines that follow contain the text of the macro text (i.e. the snippet represented by the macro name), until `\end` appears on a line by itself: + +<$codeblock code={{$:/editions/tw5.com/macro-examples/say-hi}}/> + +Alternatively, the entire definition can be presented on a single line without an `\end` marker: + +``` +\define sayhi(name:"Bugs Bunny") Hi, I'm $name$. +``` + +Macro definitions can be nested by specifying the name of the macro in the `\end` marker. For example: + +< +\end actions +<$button actions=<>> +$caption$ + +\end special-button + +<> +""">> + +A more formal [[presentation|Macro Definition Syntax]] of this syntax is also available. diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _function.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _function.tid new file mode 100644 index 000000000..253c8b452 --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _function.tid @@ -0,0 +1,27 @@ +created: 20221009162634214 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \function +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The ''\function'' [[pragma|Pragmas]] is used to [[define custom functions|Functions]]. It is a shortcut syntax for the SetVariableWidget. + +The usual form allows custom functions to span multiple lines: + +``` +\function ([:],[:]...) + +\end [] +``` + +Note that the `\end` marker can optionally specify the name of the function to which it relates, enabling function definitions to be nested inside procedures, macros or widget definitions. + +There is also a single line form for shorter functions: + +``` +\function ([:],[:]...) +``` + +The first line of the definition specifies the function name and any parameters. Each parameter has a name and, optionally, a default value that is used if no value is supplied on a particular call to the function. The lines that follow contain the text of the function (i.e. the snippet represented by the function name), until `\end` appears on a line by itself: + +See [[Functions]] for more details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _import.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _import.tid new file mode 100644 index 000000000..5971a5490 --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _import.tid @@ -0,0 +1,17 @@ +created: 20220917113054582 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \import +type: text/vnd.tiddlywiki + +The ''\import'' [[pragma|Pragmas]] is used to import definitions from other tiddlers that are identified with a filter. It is a shortcut syntax for the ImportVariablesWidget. + +``` +\import +``` + +For example: + +``` +\import [all[shadows+tiddlers]tag[$:/tags/Macro]] +``` diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _parameters.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _parameters.tid new file mode 100644 index 000000000..5f32b06eb --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _parameters.tid @@ -0,0 +1,18 @@ +created: 20220917113154900 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \parameters +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The ''\parameters'' [[pragma|Pragmas]] is used within [[procedure|Procedure Definitions]] and [[widget|Widget Definitions]] definitions to declare the parameters that are expected, and their default values. It is a shortcut syntax for the ParametersWidget. + +``` +\parameters ([:],[:]...) +``` + +For example: + +``` +\parameters (firstname:"Joe",lastname:"Blogs") +``` + diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _procedure.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _procedure.tid new file mode 100644 index 000000000..4dc496ac4 --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _procedure.tid @@ -0,0 +1,55 @@ +created: 20221007132845007 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \procedure +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The ''\procedure'' [[pragma|Pragmas]] is used to [[define procedures|Procedure Definitions]]. It is a shortcut syntax for the SetVariableWidget with an implicit ParametersWidget. + +The usual form allows procedures to span multiple lines: + +``` +\procedure ([:],[:]...) + +\end [] +``` + +Note that the `\end` marker can optionally specify the name of the procedure to which it relates which allows procedure definitions to be nested. + +There is also a single line form for shorter procedures: + +``` +\define ([:],[:]...) +``` + +The first line of the definition specifies the procedure name and any parameters. Each parameter has a name and, optionally, a default value that is used if no value is supplied on a particular call to the procedure. The lines that follow contain the text of the procedure text (i.e. the snippet represented by the procedure name), until `\end` appears on a line by itself: + +For example: + +``` +\procedure sayhi(name:"Bugs Bunny") +Hi, I'm $name$. +\end + +<> +``` + +Alternatively, the entire definition can be presented on a single line without an `\end` marker: + +``` +\procedure sayhi(name:"Bugs Bunny") Hi, I'm $name$. +``` + +Procedure definitions can be nested by specifying the name of the procedure in the `\end` marker. For example: + +< +\end actions +<$button actions=<>> +$caption$ + +\end special-button + +<> +""">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _rules.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _rules.tid new file mode 100644 index 000000000..799c9b71c --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _rules.tid @@ -0,0 +1,25 @@ +created: 20220917112931273 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \rules +type: text/vnd.tiddlywiki + +The ''\rules'' [[pragma|Pragmas]] adjusts the set of parser rules used to parse the remaining text. + +``` +\rules only|expect +``` + +The list of available parser rules can be consulted in $:/ControlPanel -> Info -> Advanced -> Parsing. + +For example, in stylesheets it is typical to only use the rules associated with macros and transclusions: + +``` +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline +``` + +Some users prefer not to use CamelCase links: + +``` +\rules except prettylink +``` \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _whitespace.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _whitespace.tid new file mode 100644 index 000000000..273a35bea --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _whitespace.tid @@ -0,0 +1,20 @@ +created: 20220917113002350 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \whitespace +type: text/vnd.tiddlywiki + +<<.from-version "5.1.15">> The ''\whitespace'' [[pragma|Pragmas]] determines how spaces and newlines are treated within wikitext. Note that this only applies to the printable text, and not to other text, such as the values of attributes. + +* ''notrim'' -- whitespace text is not subject to special processing (the default) +* ''trim'' -- whitespace text is removed + +``` +\whitespace trim|notrim +``` + +For example: + +``` +\whitespace trim +``` diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _widget.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _widget.tid new file mode 100644 index 000000000..f8e589d4a --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _widget.tid @@ -0,0 +1,27 @@ +created: 20221009121950630 +modified: 20230419103154329 +tags: Pragmas +title: Pragma: \widget +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The ''\widget'' [[pragma|Pragmas]] is used to [[define custom widgets|Custom Widgets]]. It is a shortcut syntax for the SetVariableWidget with an implicit ParametersWidget. + +The usual form allows custom widgets to span multiple lines: + +``` +\widget ([:],[:]...) + +\end [] +``` + +Note that the `\end` marker can optionally specify the name of the widget to which it relates which allows widget definitions to be nested. + +There is also a single line form for shorter widgets: + +``` +\widget ([:],[:]...) +``` + +The first line of the definition specifies the widget name and any parameters. Each parameter has a name and, optionally, a default value that is used if no value is supplied on a particular call to the widget. The lines that follow contain the text of the widget text (i.e. the snippet represented by the widget name), until `\end` appears on a line by itself: + +See [[Custom Widgets]] for more details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/pragmas/Pragmas.tid b/editions/tw5.com/tiddlers/pragmas/Pragmas.tid new file mode 100644 index 000000000..46981c51e --- /dev/null +++ b/editions/tw5.com/tiddlers/pragmas/Pragmas.tid @@ -0,0 +1,13 @@ +created: 20220917112416666 +modified: 20230419103154329 +tags: Concepts [[WikiText Parser Modes]] +title: Pragmas +type: text/vnd.tiddlywiki + +A <<.def pragma>> is a special component of WikiText that provides control over the way the remaining text is parsed. + +Pragmas occupy lines that start with `\`. They can only appear at the start of the text of a tiddler, but blank lines and comments are allowed between them. If a pragma appears in the main body of the text, it is treated as if it was ordinary text. + +The following pragmas are available: + +<> diff --git a/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid b/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid new file mode 100644 index 000000000..d66d8f274 --- /dev/null +++ b/editions/tw5.com/tiddlers/procedures/Procedure Calls.tid @@ -0,0 +1,56 @@ +caption: Macro Calls +created: 20221007130006705 +modified: 20230419103154329 +tags: WikiText Procedures +title: Procedure Calls +type: text/vnd.tiddlywiki + +!! Introduction + +This tiddler describes the different ways in which [[macros|Procedures]] can be called. + +!! Procedure Call Transclusion Shortcut + +To call a [[procedure|Procedures]], place `<<`double angle brackets`>>` around the name and any parameter values. + +``` +<> +``` + +By default, parameters are listed in the same order as in the procedure definition. A parameter can be labelled with its name and a colon to allow them to be listed in a different order. + +If no value is specified for a parameter, the default value given for that parameter in the [[procedure definition|Procedure Definitions]] is used instead. (If no default value was defined, the parameter is blank). + +Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. + +See the discussion about [[parser modes|WikiText parser mode: macro examples]] + +!! Procedure Calls with <<.wlink TranscludeWidget>> Widget + +The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the procedure to transclude. + +``` +<$transclude $variable="my-procedure" param="This is the parameter value"/> +``` + +The widget itself offers greater flexibility than the shortcut syntax, including the ability to specify dynamic parameter values. + +!! Assigning Procedure Calls to Attribute Values + +The text of a procedure can be directly assigned to an attribute of a widget or HTML element. The result of the procedure is not wikified, which means that [[parameter handling|Procedure Parameter Handling]] does not take place. + +``` +

>> +... +
+``` + +!! Using Procedure Calls in Filters + +Procedure calls can be used in filters. The text is not wikified which again means that the parameters will be ignored. + +``` +<$list filter="[]"> +... + +``` \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/procedures/Procedure Definitions.tid b/editions/tw5.com/tiddlers/procedures/Procedure Definitions.tid new file mode 100644 index 000000000..eb4017985 --- /dev/null +++ b/editions/tw5.com/tiddlers/procedures/Procedure Definitions.tid @@ -0,0 +1,44 @@ +caption: Macro Definitions +created: 20221007125701001 +modified: 20230419103154329 +tags: WikiText Procedures +title: Procedure Definitions +type: text/vnd.tiddlywiki + +!! Introduction + +This tiddler describes the different ways in which [[macros|Procedures]] can be defined. + +!! Procedure Definition Pragma + +Macros are created using the [[Pragma: \procedure]] at the start of a tiddler. The definitions are available in the rest of the tiddler that defines them, plus any tiddlers that it transcludes. + +``` +\define my-procedure(param) +This is the macro text (param=<>) +\end +``` + +!! Procedure Definition with Set Widget + +Procedures are implemented as a special kind of [[variable|Variables]] and so internally are actually defined with a <<.wlink SetWidget>> widget. + +``` +<$set name="my-procedure" value="This is the procedure text"> +... + +``` + +<<.note """that it is not currently possible to specify parameters when defining a procedure with the <<.wlink SetWidget>> widget.""">> + +!! Importing Procedure Definitions + +The [[Pragma: \import]] or <<.wlink ImportVariablesWidget>> widget can be used to copy procedure definitions from another tiddler. + +!! `$:/tags/Macro` Tag + +Global procedures can be defined using the [[SystemTag: $:/tags/Macro]]. + +The tag [[SystemTag: $:/tags/Macro/View]] is used to define procedures that should only be available within the main view template and the preview panel. + +The tag [[SystemTag: $:/tags/Macro/View/Body]] is used to define procedures that should only be available within the main view template body and the preview panel. diff --git a/editions/tw5.com/tiddlers/procedures/Procedure Parameter Handling.tid b/editions/tw5.com/tiddlers/procedures/Procedure Parameter Handling.tid new file mode 100644 index 000000000..29899ad53 --- /dev/null +++ b/editions/tw5.com/tiddlers/procedures/Procedure Parameter Handling.tid @@ -0,0 +1,25 @@ +caption: Macro Definitions +created: 20221007130538285 +modified: 20230419103154329 +tags: WikiText Procedures +title: Procedure Parameter Handling +type: text/vnd.tiddlywiki + +!! Introduction + +[[Procedure|Procedures]] parameters are made available as variables when the procedure contents are wikified. + +!! Accessing Parameters as Variables + +When procedures are wikified, the parameters can be accessed as variables. + +For example: + +<$macrocall $name="wikitext-example-without-html" src="""\procedure say-hi(name,address) +Hi, I'm <> and I live in <
>. +\end + +<> +"""/> + +Accessing parameters as variables only works in procedures that are wikified and not, for example, when a procedure is used as an attribute value. diff --git a/editions/tw5.com/tiddlers/procedures/Procedures.tid b/editions/tw5.com/tiddlers/procedures/Procedures.tid new file mode 100644 index 000000000..15b422647 --- /dev/null +++ b/editions/tw5.com/tiddlers/procedures/Procedures.tid @@ -0,0 +1,35 @@ +created: 20221007124007426 +modified: 20230419103154329 +tags: Concepts Reference +title: Procedures +type: text/vnd.tiddlywiki + +!! Introduction + +<<.from-version "5.3.0">> A <<.def procedure>> is a named snippet of text. They are typically defined with the [[Pragma: \procedure]]: + +``` +\procedure my-procedure(parameter:"Default value") +This is the procedure, and the parameter is <>. +\end +``` + +The name wrapped in double angled [[brackets|Brackets]] is used a shorthand way of [[transcluding|Transclusion]] the snippet. Each of these <<.def "procedure calls">> can supply a different set of parameters: + +``` +<> +<> +``` + +The parameters that are specified in the procedure call are made available as variables. + +!! How Procedures Work + +Procedures are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled. + +!! Using Procedures + +* [[Procedure Definitions]] describes how to create procedures +* [[Procedure Calls]] describes how to use procedures +* [[Procedure Parameter Handling]] describes how procedure parameters work + diff --git a/editions/tw5.com/tiddlers/variables/Variables.tid b/editions/tw5.com/tiddlers/variables/Variables.tid index 2e80f2678..4efa43e72 100644 --- a/editions/tw5.com/tiddlers/variables/Variables.tid +++ b/editions/tw5.com/tiddlers/variables/Variables.tid @@ -1,23 +1,140 @@ created: 20141002133113496 -modified: 20221221175615776 -tags: Concepts Reference +modified: 20230419103154329 +tags: Concepts Reference WikiText title: Variables type: text/vnd.tiddlywiki -A <<.def variable>> is a snippet of text that can be accessed by name within a particular branch of the [[widget tree|Widgets]]. The snippet is known as the variable's <<.def value>>. +!! Introduction -A new variable is defined using a <<.wlink SetWidget>> or <<.wlink LetWidget>> widget, and is then available to any of the children of that widget, including transcluded content. A <<.wid set>> widget can reuse an existing name, thus binding a different snippet to that name for the duration of the widget's children. +A <<.def variable>> is a snippet of text that can be accessed by name. The text is referred to as the variable's <<.def value>>. -The <<.wlink ListWidget>> widget by default sets a particular variable <<.var currentTiddler>> to each listed title in turn. +Variables are defined by [[widgets|Widgets]]. Several core widgets define variables, the most common being the <<.wlink SetWidget>>, <<.wlink LetWidget>> and <<.wlink ListWidget>> widgets. -For an overview of how to use variables, see [[Variables in WikiText]]. +The values of variables are available to descendant widgets, including transcluded content. For example, within each tiddler in the main story river the variable "currentTiddler" is set to the title of the tiddler. -Despite the term <<.word variable>>, ''each snippet is a constant string''. The apparent variability is actually the result of the presence of multiple variables with the same name in different parts of the widget tree. +Variables can also be overwritten by descendent widgets defining variables of the same name, thus binding a different snippet to that name for the scope of the children of the widget. -[[Macros]] are a special form of variable whose value can contain placeholders that get filled in with parameters whenever the macro is used. +!! Special Kinds of Variables -By themselves, the snippets are <<.em not>> parsed as WikiText. However, a variable reference will transclude a snippet into a context where ~WikiText parsing <<.em may>> be occurring. Within a snippet, the only markup detected is `$name$` for a macro parameter transclusion and `$(name)$` for a variable transclusion. +There are several special kinds of variable that extend their basic capabilities: -The <<.mlink dumpvariables>> macro lists all variables (including macros) that are available at that position in the widget tree. +* [[Procedures]] are snippets of text that can be passed parameters when wikified +* [[Functions]] are snippets of text containing [[filters|Filters]] with optional named parameters +* [[Custom Widgets]] are snippets of text containing definitions of custom [[widget|Widgets]] +* [[Macros]] are snippets of text that can contain placeholders that are filled in with parameters whenever the macro is used + +Note that these special kinds of variable can only be created with the associated shortcut definition syntax. + +!! Defining Variables + +The following core widgets are commonly used to define variables: + +* <<.wlink LetWidget>> widget -- the easiest way to define multiple variables +* <<.wlink SetWidget>> widget -- the most flexible way to define a single variable +* <<.wlink ParametersWidget>> widget -- used to declare parameter variables within [[procedures|Procedures]] and [[custom widgets|Custom Widgets]] +* <<.wlink ListWidget>> widget -- defines a loop variable and optional counter variable +* <<.wlink SetMultipleVariablesWidget>> widget -- allows creation of multiple variables at once where the names and values are not known in advance + +!! Using Variables + +Once a variable is defined there are several ways to access it. + +!!! Transcluding Variables + +Transcluding a variable renders the text contents of the variable as if it replaced the call. It is a shortcut syntax for the <<.wlink TranscludeWidget>> widget with the `$variable` attribute. + +``` +<> +``` + +Parameters can be passed to the transclusion as follows: + +``` +<> +<> +<> +``` + +The handling of these parameters depends on the kind of variable: + +* [[Procedures]] assign the parameters to variables that are available within the procedure +* [[Macros]] replace the text of the special markers `$param$` with the values passed to the macro for those parameters (see [[Macro Parameter Handling]] for the details) + +The parameters are ignored for other kinds of variable. + +!!! Macro Variable Substitutions + +Before the text of a macro is used, the special markers `$(variable)$` are replaced with the values of the named variable. + +!!! Variable Attributes + +Variables can be used as the value of attributes of widgets or HTML elements: + +``` +
>> +``` + +Parameters can be passed: + +``` +
>> +... +
>> +... +
>> +... +``` + +The handling of these parameters depends on the kind of variable: + +* [[Functions]] assign the parameters to variables that are available within the function +* [[Macros]] replace the text of the special markers `$param$` with the values passed to the macro for those parameters (see [[Macro Parameter Handling]] for the details) + +The parameters are ignored for other kinds of variable. + +!!! Variables in Filters + +Variables can be accessed within [[Filters]] using angle brackets to quote the name: + +``` +[] +``` + +Parameters can be passed in the usual way: + +``` +[] +[] +[] +... +``` + +!! See Also + +* The <<.mlink dumpvariables>> macro lists all variables that are available at that position in the widget tree +* Complete listing of ~TiddlyWiki's built-in [[Core Variables]] + +!! Examples + +!!! Example of Defining a Variable + +<$macrocall $name=".example" n="1" +eg="""<$set name=animal value=zebra> +<> +"""/> + +!!! Example of Defining a Macro + +The `\define` pragma below [[defines a macro|Macro Definitions]] called <<.var tags-of-current-tiddler>>. The macro returns the value of the tiddler's <<.field tags>> field, and can be accessed from anywhere else in the same tiddler (or in any tiddler that [[imports|ImportVariablesWidget]] it). + +<$importvariables filter="$:/editions/tw5.com/macro-examples/tags-of-current-tiddler"> +<$codeblock code={{$:/editions/tw5.com/macro-examples/tags-of-current-tiddler}}/> +<$macrocall $name=".example" n="2" eg="""The tags are: <>"""/> + + +!!! Example of Using a Variable as a Filter Parameter + +This example uses the <<.olink backlinks>> [[operator|Filter Operators]] to list all tiddlers that link to this one. + +<$macrocall $name=".example" n="3" eg="""<backlinks[]]">>"""/> -~TiddlyWiki's core has [[several variables|Core Variables]] built in. diff --git a/editions/tw5.com/tiddlers/widgets/Custom Widgets.tid b/editions/tw5.com/tiddlers/widgets/Custom Widgets.tid new file mode 100644 index 000000000..c220302cf --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/Custom Widgets.tid @@ -0,0 +1,84 @@ +created: 20221007144237585 +modified: 20230419103154328 +tags: Concepts Reference +title: Custom Widgets +type: text/vnd.tiddlywiki + +!! Introduction + +<<.from-version "5.3.0">> A <<.def "custom widget">> is a special kind of [[procedure|Procedures]] that can be called using the same syntax as widgets. + +Custom widgets can also be used to override built-in JavaScript widgets to customise their behaviour. + +!! Defining Custom Widgets + +Custom widgets are usually defined with the [[Pragma: \widget]]: + +``` +\widget $$my-widget(attribute:"Default value") +This is the widget, and the attribute is <>. +\end +``` + +The name of the widget must start with one or two dollar signs: + +* A ''single dollar sign'' is used to override existing core widgets +** for example, `$text` or `$codeblock` +* ''Double dollar signs'' are used to define a custom widget +** for example, `$$mywidget` or `$$acme-logger` + +!! Using Custom Widgets + +Custom widgets are called in the same way as ordinary built-in widgets: + +``` +<$my-widget/> + +<$my-widget attribute="The parameter"/> +``` + +The attributes that are specified in the widget call are made available as parameter variables. + +!! Accessing Content of Custom Widgets + +Within the definition of a custom widget the content of the calling widget is available via the `<$slot $name="ts-raw"/>` widget. The contents of the <<.wlink SlotWidget>> widget is used as the default content if the widget was called without any content. + +For example: + +<>/> +<$slot $name="ts-raw"> + Whale + +\end + +<$$mywidget one="Dingo"> + Crocodile + + +<$$mywidget/>""">> + +!! How Custom Widgets Work + +Custom widgets are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that they can be called as a custom widget with attributes mapped to parameters. + +!! Overriding Core ~JavaScript Widgets + +Custom widgets can use the <<.wlink "GenesisWidget">> widget to invoke the original widget, bypassing the override. For example, here we override the <<.wlink CodeBlockWidget>> widget to add `≤≥` symbols around each string of text. + + +<addprefix[≤]addsuffix[≥]] }}}/> +\end + +<$codeblock code="Kangaroo"/> + +<$codeblock code={{$:/SiteTitle}}/> + +``` +Python +``` + +<$let test="Tiger"> +<$codeblock code=<>/> +""">> diff --git a/editions/tw5.com/tiddlers/widgets/ErrorWidget.tid b/editions/tw5.com/tiddlers/widgets/ErrorWidget.tid index aee5617d2..d6afb86ed 100644 --- a/editions/tw5.com/tiddlers/widgets/ErrorWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/ErrorWidget.tid @@ -1,6 +1,6 @@ caption: error created: 20220909111836951 -modified: 20220909111836951 +modified: 20230419103154328 tags: Widgets title: ErrorWidget type: text/vnd.tiddlywiki diff --git a/editions/tw5.com/tiddlers/widgets/FillWidget.tid b/editions/tw5.com/tiddlers/widgets/FillWidget.tid new file mode 100644 index 000000000..3fffd51f0 --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/FillWidget.tid @@ -0,0 +1,21 @@ +caption: fill +created: 20220909111836951 +modified: 20230419103154328 +tags: Widgets +title: FillWidget +type: text/vnd.tiddlywiki + +! Introduction + +<<.from-version "5.3.0">> The <<.wlink FillWidget>> widget is used within a <<.wlink TranscludeWidget>> widget to specify the content that should be copied to the named "slot". Slots are defined by the <<.wlink SlotWidget>> widget within the transcluded content. + +See the <<.wlink TranscludeWidget>> widget for details. + +! Attributes + +The content of the <<.wlink FillWidget>> widget is used as the content to be passed to the transclusion. + +|!Attribute |!Description | +|$name |The name of the slot to be filled | + +<<.warning """The $name attribute must be specified as a literal string""">> diff --git a/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid b/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid index a9451bc63..93ae44ae4 100644 --- a/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid @@ -34,7 +34,7 @@ So-called global macros are implemented within the main page template ([[$:/core ! `\import` Pragma -<<.from-version "5.1.18">> The `\import` [[pragma|Pragma]] is an alternative syntax for using the ImportVariablesWidget. For example, the previous example could be expressed as: +<<.from-version "5.1.18">> The [[Pragma: \import]] is an alternative syntax for using the ImportVariablesWidget. For example, the previous example could be expressed as: ``` \import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]] diff --git a/editions/tw5.com/tiddlers/widgets/MacroCallWidget.tid b/editions/tw5.com/tiddlers/widgets/MacroCallWidget.tid index e163c1d41..e06e3601d 100644 --- a/editions/tw5.com/tiddlers/widgets/MacroCallWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/MacroCallWidget.tid @@ -1,48 +1,31 @@ caption: macrocall created: 20131024141900000 -modified: 20220122193731433 -tags: Widgets +modified: 20230419103154328 +tags: Widgets $:/deprecated title: MacroCallWidget type: text/vnd.tiddlywiki -! Introduction +<<.deprecated-since "5.3.0" "TranscludeWidget">> -The macro call widget provides a more flexible alternative syntax for invoking macros compared to the usual `<>` syntax documented in [[Macros in WikiText]]. +The <<.wlink MacroCallWidget>> widget is deprecated. While it will continue to work, users are now advised to use the <<.wlink TranscludeWidget>> widget, converting the `$name` attribute to `$variable`. -For example, a macro called `italicise` that takes a single parameter called `text` would usually be invoked like this: +For example, ``` -<> -<> +<$macrocall $name="my-macro" my-parameter="Elephant"/> ``` -The same macro can be invoked using the macro call widget like this: +should be changed to: ``` -<$macrocall $name="italicise" text="Text to be made into italics"/> -<$macrocall $name="italicise" text={{Title of tiddler containing text to be italicised}}/> -<$macrocall $name="italicise" text=<>/> +<$transclude $variable="my-macro" my-parameter="Elephant"/> ``` -The advantages of the widget formulation are: - -* Macro parameters are specified as widget attributes, thus allowing indirection via `{{title!!field}}`, `<>` or `{{{filter}}}` -* The output format can be chosen from several options: -** `text/html` wikifies the result of the macro -** `text/plain` wikifies the result of the macro and then extracts the plain text characters (ie. ignoring HTML tags) -** <<.from-version "5.1.23">> `text/raw` returns the result of the macro, without wikification - -You can see several examples of the macro call widget within the core: - -* Listing module information: [[$:/snippets/modules]] -* Listing field information: [[$:/snippets/allfields]] -* Generating `data:` URIs: [[$:/themes/tiddlywiki/starlight/styles.tid]] - -See also [[WikiText parser mode: macro examples]] +Internally, the <<.wlink MacroCallWidget>> widget is implemented via the <<.wlink TranscludeWidget>> widget. ! Content and Attributes -The content of the `<$macrocall>` widget is ignored. +The content of the <<.wlink MacroCallWidget>> widget is ignored. |!Attribute |!Description | |$name |Name of the macro to invoke | diff --git a/editions/tw5.com/tiddlers/widgets/ParametersWidget.tid b/editions/tw5.com/tiddlers/widgets/ParametersWidget.tid new file mode 100644 index 000000000..bff682dd3 --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/ParametersWidget.tid @@ -0,0 +1,61 @@ +caption: parameters +created: 20220909111836951 +modified: 20230419103154328 +tags: Widgets +title: ParametersWidget +type: text/vnd.tiddlywiki + +<<.from-version "5.3.0">> The <<.wlink ParametersWidget>> widget is used within transcluded content to declare the parameters to be made available to the <<.wlink TranscludeWidget>> widget. + +There are shortcuts for common scenarios that can often make it unnecessary to use the <<.wlink ParametersWidget>> widget directly: + +* the [[Pragma: \parameters]] +* the [[Pragma: \procedure]] for declaring procedure +* the [[Pragma: \widget]] for declaring custom widgets + +The <<.wlink ParametersWidget>> widget must be used directly in the following situations: + +* When the default value of a parameter must be computed dynamically +* When the `$depth` attribute is used to retrieve parameters from a parent transclusion (see below) + +! Content and Attributes + +The content of the <<.wlink ParametersWidget>> widget is the scope within which the values of the parameters can be accessed as ordinary variables. + +|!Attribute |!Description | +|$depth |The index of the parent transclusion from which to obtain the parameters (defaults to 1). See below | +|$parseMode |Optional name of a variable in which is made available the parse mode of the content of the parent transclusion (the parse mode can be "inline" or "block") | +|$parseTreeNodes |Optional name of a variable in which is made available the JSON representation of the parse tree nodes contained within the parent transclusion | +|$slotFillParseTreeNodes |Optional name of a variable in which is made available the JSON representation of the parse tree nodes corresponding to each fill widget contained within the parent transclusion (as an object where the keys are the slot names and the values are the parse tree nodes) | +|$params |Optional name of a variable in which is made available the JSON representation of the parameters passed to the parent transclusion (as an object where the keys are the parameter names and the values are the coresponding values) | +|//{attributes not starting with $}// |Any attributes that do not start with a dollar are used as parameters, with the value specifying the default to be used for missing parameters | +|//{other attributes starting with $}// |Other attributes starting with a single dollar sign are reserved for future use | +|//{attributes starting with $$}// |Attributes starting with two dollar signs are used as parameters to the transclusion, but with the name changed to use a single dollar sign. The value specifies the default to be used for missing parameters | + +<<.note "Note the special treatment required for parameters names that start with a `$`; this can be avoided by using one of the pragmas">> + +!! `$depth` Attribute + +By default, the <<.wlink ParametersWidget>> widget retrieves parameters from the immediate parent transclusion. The `$depth` attribute permits access to the parameters of parent transclusions by specifying an index to the parent to be inspected ("1" is the immediate parent, "2" is the parent of that parent, etc.). This is useful in some situations where an intervening transclusion prevents immediate access to desired parameters. + +!! `$parseMode`, `$parseTreeNodes`, `$slotFillParseTreeNodes` and `$params` Attributes + +These attributes provide low level access to the contents of the transcluding widget: + +* The `$params` attribute provides access to the raw parameters provided to the transcluding widget. Represented in JSON as an object with keys of the parameter names and values of the corresponding parameter values +* The `$parseMode` attribute contains `block` or `inline` to indicate whether the contents was parsed in block or inline mode +* The `$parseTreeNodes` attribute provides access to the raw parse tree nodes that represent the contents of the transcluding widget. Represented in JSON as an array of parse tree nodes +* The `$slotFillParseTreeNodes` attribute provides access to the raw parse tree nodes corresponding to the filled slots within the contents of the transcluding widget. Represented in JSON as an object with keys of the slot name and values being an array of parse tree nodes + +! Examples + +Here the <<.wlink ParametersWidget>> widget is used to declare a parameter whose default value is transcluded from another tiddler. + +<$macrocall $name='wikitext-example-without-html' +src="""\procedure mymacro +<$parameters name={{$:/SiteTitle}} age="21"> +My name is <> and my age is <>. + +\end + +<$transclude $variable="mymacro" age="19"/>"""/> diff --git a/editions/tw5.com/tiddlers/widgets/SlotWidget.tid b/editions/tw5.com/tiddlers/widgets/SlotWidget.tid new file mode 100644 index 000000000..f7b26e62d --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/SlotWidget.tid @@ -0,0 +1,19 @@ +caption: slot +created: 20220909111836951 +modified: 20230419103154329 +tags: Widgets +title: SlotWidget +type: text/vnd.tiddlywiki + +! Introduction + +<<.from-version "5.3.0">> The <<.wlink SlotWidget>> widget is used within transcluded content to mark "slots" that the transcluding widget can fill with the <<.wlink FillWidget>> widget. + +See the <<.wlink TranscludeWidget>> widget for details. + +! Attributes + +The content of the <<.wlink SlotWidget>> widget is used as a fallback for the slot content if the corresponding <<.wlink FillWidget>> widget is not found. + +|!Attribute |!Description | +|$name |The name of the slot being defined | diff --git a/editions/tw5.com/tiddlers/widgets/TranscludeWidget.tid b/editions/tw5.com/tiddlers/widgets/TranscludeWidget.tid index 6bc507cae..348de1090 100644 --- a/editions/tw5.com/tiddlers/widgets/TranscludeWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/TranscludeWidget.tid @@ -1,24 +1,178 @@ caption: transclude created: 20130824142500000 -modified: 20220513114759336 +modified: 20230419103154329 tags: Widgets title: TranscludeWidget type: text/vnd.tiddlywiki ! Introduction -The TranscludeWidget dynamically imports content from another tiddler. +The <<.wlink TranscludeWidget>> widget dynamically includes the content from another tiddler or variable, rendering it as if the transclude widget were replaced by the target content. + +The <<.wlink TranscludeWidget>> widget can be used to render content of any type: wikitext, images, videos, etc. + +Transclusion is the underlying mechanism for many higher level wikitext features, such as procedures, custom widgets and macros. + +! Example + +Here is a complete example showing the important features of the <<.wlink TranscludeWidget>> widget: + +``` +\procedure mymacro(name,age) +My name is <> and my age is <>. +\end + +<$transclude $variable="mymacro" name="James" age="19"/> +``` + +* `\procedure` defines a variable as a procedure with two parameters, ''name'' and ''age'' +* The content of the procedure refers to the parameters as variables +* The <<.wlink TranscludeWidget>> widget specifies the variable to transclude, and values for the parameters. + +! Legacy vs. Modern Mode + +The <<.wlink TranscludeWidget>> widget can be used in two modes: + +* <<.from-version "5.3.0">> ''Modern mode'' offers the full capabilities of the <<.wlink TranscludeWidget>> widget, and incorporates the functionality of the <<.wlink MacroCallWidget>> widget. It is indicated by the presence of at least one attribute starting with a dollar sign `$` +* ''Legacy mode'' offers a more limited set of capabilities. It is indicated by the absence of any attributes starting with a dollar sign `$` + +Modern mode is recommended for use in new applications. ! Attributes -|!Attribute |!Description | -|tiddler |The title of the tiddler to transclude (defaults to the current tiddler) | -|field |The field name of the current tiddler (defaults to "text"; if present takes precedence over the index attribute) | -|index |The index of a property in a [[DataTiddler|DataTiddlers]] | -|subtiddler |Optional SubTiddler title when the target tiddler is a [[plugin|Plugins]] (see below) | -|mode |Override the default parsing mode for the transcluded text to "block" or "inline" | +| !Attribute |<| !Description | +| !(modern) | !(legacy) |~| +|$variable |- |Name of the variable to transclude | +|$tiddler |tiddler |The title of the tiddler to transclude (defaults to the current tiddler) | +|$field |field |The field name of the current tiddler (defaults to "text"; if present takes precedence over the index attribute) | +|$index |index |The index of a property in a [[DataTiddler|DataTiddlers]] | +|$subtiddler |subtiddler |Optional SubTiddler title when the target tiddler is a [[plugin|Plugins]] (see below) | +|$mode |mode |Override the default parsing mode for the transcluded text to "block" or "inline" | +|$type |– |Optional ContentType used when transcluding variables, indexes or fields other than the ''text'' field| +|$output |- |ContentType for the output rendering (defaults to `text/html`, can also be `text/plain` or `text/raw`) | +|$recursionMarker |recursionMarker |Set to ''no'' to prevent creation of [[Legacy Transclusion Recursion Marker]] (defaults to ''yes'') | +|//{attributes not starting with $}// |– |Any other attributes that do not start with a dollar are used as parameters to the transclusion | +|//{other attributes starting with $}// |– |Other attributes starting with a single dollar sign are reserved for future use | +|//{attributes starting with $$}// |– |Attributes starting with two dollar signs are used as parameters to the transclusion, but with the name changed to use a single dollar sign | -The TranscludeWidget treats any contained content as a fallback if the target of the transclusion is not defined (ie a missing tiddler or a missing field). +! Basic Operation + +The basic operation of the <<.wlink TranscludeWidget>> widget is as follows: + +|`<$transclude/>` |Transcludes the text field of the current tiddler | +|`<$transclude $variable="alpha"/>` |Transcludes the variable "alpha" (note that procedures, custom widgets and macros are all special types of variable) | +|`<$transclude $tiddler="foo"/>` |Transcludes the text field of the tiddler "foo" | +|`<$transclude $field="bar"/>` |Transcludes the field "bar" of the current tiddler | +|`<$transclude $index="beta"/>` |Transcludes the index "beta" of the current tiddler | +|`<$transclude $tiddler="foo" $index="beta"/>` |Transcludes the index "beta" of the tiddler "foo" | + +! Transclusion Parameters + +Named string parameters can be passed to the <<.wlink TranscludeWidget>> widget. They are made available as variables within the transcluded text. Parameters are only supported in modern mode. + +When invoking a transclusion, parameters are specified as additional attributes that do not start with a dollar sign `$`: + +``` +<$transclude $tiddler="MyTiddler" firstParameter="One" secondParameter="Two"/> +``` + +To pass parameters whose names start with a dollar sign `$`, prefix them with an extra `$`. For example, to pass a parameter called `$tiddler`: + +``` +<$transclude $tiddler="MyTiddler" $$tiddler="One"/> +``` + +There are several different ways to declare parameters within a transclusion: + +* the <<.wlink ParametersWidget>> widget +* the [[Pragma: \parameters]] +* the [[Pragma: \procedure]] for declaring procedure +* the [[Pragma: \widget]] for declaring custom widgets +* the [[Pragma: \define]] for declaring macros + +An example of declaring parameters with the <<.wlink ParametersWidget>> widget: + +``` +<$parameters firstParameter="default" secondParameter="another default"> + Parameters are available here as the variables <> and <>. + +``` + +The [[Pragma: \parameters]] can be used as a shortcut syntax for declaring parameters. For example: + +``` +\parameters (firstParameter:"default",secondParameter:"another default") +Parameters are available here as the variables <> and <>. +``` + +! Transclusion Slots + +Transcluded content can define special named locations called slots. At the point of transclusion, blocks of wikitext can be passed to the <<.wlink TranscludeWidget>> widget to fill those slots. + +Slots work very similarly to parameters except that they can contain structured wikitext, and not just plain text. The primary advantage of slots over parameters is that the contents do not need to be wrapped in quotation symbols, making it much simpler to pass complex structures. + +For example, here we transclude the tiddler "Example" while using the <<.wlink FillWidget>> widget to pass wikitext blocks to fill the slots called "positive" and "negative": + +``` +<$transclude $tiddler="Example"> + <$fill $name="positive"> +

This is positive

+ + <$fill $name="negative"> +

This is negative

+ + +``` + +The tiddler "Example" uses the <<.wlink SlotWidget>> widget to specify the slots to be filled: + +``` +
    +
  1. <$slot $name="positive"/>
  2. +
  3. <$slot $name="negative"/>
  4. +
+``` + +The output will be equivalent to: + +``` +
    +
  1. +

    This is positive

    +
  2. +
  3. +

    This is negative

    +
  4. +
+``` + + +! Missing Transclusion Targets + +The TranscludeWidget uses the special slot `ts-missing` to specify the content to be rendered if the transclusion target is not defined (i.e. a missing tiddler or a missing field). + +For example: + +``` +<$transclude $tiddler="MissingTiddler"> +<$fill $name="ts-missing"> +This content is displayed if `MissingTiddler` is missing. + +<$fill $name="other"> +This content is passed to the transclusion as the slot value `other` + + +``` + +If no slots values are specified within the <<.wlink TranscludeWidget>> widget then the entire content of the widget is used as the missing content. + +For example: + +``` +<$transclude $tiddler="MissingTiddler"> +This content is displayed if `MissingTiddler` is missing. + +``` ! Parsing modes diff --git a/editions/tw5.com/tiddlers/wikitext/HTML in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/HTML in WikiText.tid index d000cd975..ef42f948a 100644 --- a/editions/tw5.com/tiddlers/wikitext/HTML in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/HTML in WikiText.tid @@ -62,7 +62,7 @@ In an extension of conventional HTML syntax, attributes of elements/widgets can * a literal string * a transclusion of a TextReference -* a transclusion of a [[macro/variable|Macros in WikiText]] +* a transclusion of a [[macro/variable|Macros]] * as the result of a [[Filter Expression]] !! Style Attributes @@ -125,7 +125,7 @@ attr={{tiddler!!field}} !! Variable Attribute Values -Variable attribute values are indicated with double angle brackets around a [[macro invocation|Macro Calls in WikiText]]. For example: +Variable attribute values are indicated with double angle brackets around a [[macro invocation|Macro Calls]]. For example: ```
>> diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Calls in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Macro Calls in WikiText.tid index b79d98134..0d45612f0 100644 --- a/editions/tw5.com/tiddlers/wikitext/Macro Calls in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Macro Calls in WikiText.tid @@ -1,26 +1,8 @@ caption: Macro Calls -created: 20150220182252000 -modified: 20220122193853161 -tags: WikiText +created: 20220917074831994 +modified: 20220917074844235 +tags: title: Macro Calls in WikiText type: text/vnd.tiddlywiki -To call a [[macro|Macros]], place `<<`double angle brackets`>>` around the name and any parameter values. - -By default, parameters are listed in the same order as in the macro's definition. A parameter can be labelled with its name, either for clarity or to modify the order. - -If no value is specified for a parameter, the default value given for that parameter in the macro's definition is used instead. (If no default value was defined, the parameter is simply blank.) - -Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. - -A more formal [[presentation|Macro Call Syntax]] of this syntax is also available. - -The syntax is actually a shorthand for a <<.wlink MacroCallWidget>> widget. The widget itself offers greater flexibility, including the ability to [[transclude|Transclusion]] parameter values or generate them via additional macros. - -As macros are simply parameterised [[variables|Variables]], a variable's value can be inserted using the same techniques. - -[[Examples|Macro Calls in WikiText (Examples)]] and [[more examples|WikiText parser mode: macro examples]] - -!! Named vs.unnamed parameters - -In the wikitext notation, using named parameters is always the safer choice compared to defining values only. Not naming parameters may have confusing side effects. For example, imagine the first parameter of some macro specifies a [[state tiddler|StateMechanism]] while the second one is intended for a [[template|Transclusion with Templates]] tiddler. Should you accidentally forget to define the first parameter or are confused about the order, the next time your macro is run, which might even be triggered using the preview, your template tiddler may inadvertently be overriden with what was intended to be the state. +See [[Macro Calls]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid b/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid new file mode 100644 index 000000000..eddf28200 --- /dev/null +++ b/editions/tw5.com/tiddlers/wikitext/Macro Calls.tid @@ -0,0 +1,58 @@ +caption: Macro Calls +created: 20150220182252000 +modified: 20230419103154328 +tags: WikiText Macros +title: Macro Calls +type: text/vnd.tiddlywiki + +!! Introduction + +This tiddler describes the different ways in which [[macros|Macros]] can be called. + +!! Macro Call Transclusion Shortcut + +To call a [[macro|Macros]], place `<<`double angle brackets`>>` around the name and any parameter values. + +``` +<> +``` + +By default, parameters are listed in the same order as in the macro's definition. A parameter can be labelled with its name and a colon to allow them to be listed in a different order. + +If no value is specified for a parameter, the default value given for that parameter in the [[macro definition|Macro Definitions]] is used instead. (If no default value was defined, the parameter is blank). + +Each parameter value can be enclosed in `'`single quotes`'`, `"`double quotes`"`, `"""`triple double quotes`"""` or `[[`double square brackets`]]`. Triple double quotes allow a value to contain almost anything. If a value contains no spaces or single or double quotes, it requires no delimiters. + +A more formal [[presentation|Macro Call Syntax]] of this syntax is also available. + +See some [[examples|Macro Calls in WikiText (Examples)]] and discussion about [[parser modes|WikiText parser mode: macro examples]]. + +!! Macro Calls with <<.wlink TranscludeWidget>> Widget + +The shortcut syntax expands to the <<.wlink TranscludeWidget>> widget with the `$variable` attribute specifying the name of the macro to transclude. + +``` +<$transclude $variable="mymacro" param="This is the parameter value"/> +``` + +The widget itself offers greater flexibility than the shortcut syntax, including the ability to specify parameter values. + +!! Assigning Macro Calls to Attribute Values + +The result of a macro can be directly assigned to an attribute of a widget or HTML element. The result of the macro is not wikified, but the [[parameter substitution|Macro Parameter Handling]] is performed. + +``` +
>> +... +
+``` + +!! Using Macro Calls in Filters + +Macro calls can be used in filters: + +``` +<$list filter="[]"> +... + +``` \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Definitions in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Macro Definitions in WikiText.tid index eb74ead1a..9e3d9d1fe 100644 --- a/editions/tw5.com/tiddlers/wikitext/Macro Definitions in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Macro Definitions in WikiText.tid @@ -1,114 +1,7 @@ -caption: Macro Definitions created: 20150220181617000 -modified: 20221207094236472 -tags: WikiText +modified: 20180820165115455 +tags: title: Macro Definitions in WikiText type: text/vnd.tiddlywiki -A [[macro|Macros]] is defined using a `\define` [[pragma|Pragma]]. Like any pragma, this can only appear at the start of a tiddler. - -The first line of the definition specifies the macro name and any parameters. Each parameter has a name and, optionally, a default value that is used if no value is supplied on a particular call to the macro. - -The lines that follow contain the text of the macro text (i.e. the snippet represented by the macro name), until `\end` appears on a line by itself: - -<$codeblock code={{$:/editions/tw5.com/macro-examples/say-hi}}/> - -Alternatively, the entire definition can be presented on a single line without an `\end` marker: - -``` -\define sayhi(name:"Bugs Bunny") Hi, I'm $name$. -``` - -!! Accessing variables and parameters - -Inside the macro there are several methods for accessing variables defined outside of the macro or parameters from the macro parameter list. - -|syntax|description|h -|`$...$`|Text substitution of a parameter defined in the macro parameters list | -|`<<__...__>>`|Parameter-as-variable access to a parameter defined in the macro parameters list | -|`$(...)$`|Text substitution of a variable defined outside of the macro | -|`<<...>>`|Access to a variable (or other macro) defined outside of the macro | -
- -!!! Placeholders `$(...)$` - -The macro can contain placeholders for parameters. These consist of a parameter name between dollar signs, like `$this$`. - -The macro can also contain placeholders for [[variables|Variables]]. These consist of a variable name (or macro name) between dollar signs and round brackets, like `$(this)$`. - -The actual value of the parameter or variable is substituted for the placeholder whenever the macro is called: - -<$importvariables filter="$:/editions/tw5.com/macro-examples/say-hi-using-variables"> -<$codeblock code={{$:/editions/tw5.com/macro-examples/say-hi-using-variables}}/> -<$macrocall $name=".example" n="1" -eg="""<$set name="address" value="Rabbit Hole Hill"> -<> -"""/> - - -!!! Parameters as Variables `<<__...__>>` - -Parameters in a wikitext macro can be accessed as variables by using the syntax `<<__...__>>`, i.e the parameter name surrounded by double underscores. For example, the example above could also be expressed as: - -``` -\define sayhi(name:"Bugs Bunny") Hi, I'm <$text text=<<__name__>>/>. -``` - -Accessing parameters as variables only works in macros that are wikified and not, for example, when a macro is used as an attribute value. The advantage of the technique is that it avoids the parameter value being substituted into the macro as a literal string, which in turn can help avoid issues with parameters that contain quotes. - -For example, consider this macro. It invokes another macro using the single parameter as an argument for it: - -``` -\define film-quote(line) <$macrocall $name="anothermacro" actor="Bugs Bunny" line="""$line$"""/> -``` - -The code above will fail if the macro is invoked with the argument containing triple double quotes (for example `<>`). Using parameter variables offers a workaround: - -``` -\define film-quote(line) <$macrocall $name="anothermacro" actor="Bugs Bunny" line=<<__line__>>/> -``` - -!! Scope - -Macros are available to the tiddler that defines them, plus any tiddlers that it transcludes. - -To make a macro available to all tiddlers, define it in a tiddler that has the tag <<.tag $:/tags/Macro>>. - -It is also possible to write a macro as a [[JavaScript module|https://tiddlywiki.com/dev/index.html#JavaScript%20Macros]]. ~JavaScript macros are available to all tiddlers, and offer the maximum flexibility. - -A tiddler can manually import macro definitions from a [[selection|Title Selection]] of other tiddlers by using the <<.wlink ImportVariablesWidget>> widget. - -!! Nested Macro Definitions - -Macro definitions can be nested to any number of required levels by specifying the name of the macro in the `\end` marker. Nested macro definitions must appear at the start of the definition that contains them. For example: - -< -\end actions -<$button actions=<>> -$caption$ - -\end special-button - -<> -""">> - -Note that the textual substitution of macro parameters that occurs when the outer macro is rendered will apply to the nested definitions as well. That generally means that textual substitution of macro parameters should not be used within nested macros. - -Parameters of nested macros can also be accessed via the `<<__variablename__>>` syntax. As ordinary variables, these parameters are available within nested child macros (and grandchildren etc). - -For the one-liner macro definition, the `\end` remains unnecessary for the inner macro. For example - -< -<$button actions=<>> -$caption$ - -\end special-button - -<> -""">> - -A more formal [[presentation|Macro Definition Syntax]] of this syntax is also available. - +See [[Macro Definitions]]. diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Definitions.tid b/editions/tw5.com/tiddlers/wikitext/Macro Definitions.tid new file mode 100644 index 000000000..00ed468d7 --- /dev/null +++ b/editions/tw5.com/tiddlers/wikitext/Macro Definitions.tid @@ -0,0 +1,68 @@ +caption: Macro Definitions +created: 20150220181617000 +modified: 20230419103154328 +tags: WikiText Macros +title: Macro Definitions +type: text/vnd.tiddlywiki + +!! Introduction + +This tiddler describes the different ways in which [[macros|Macros]] can be defined. + +!! Macro Definition Pragma + +Macros are created using the [[Pragma: \define]] at the start of a tiddler. The definitions are available in the rest of the tiddler that defines them, plus any tiddlers that it transcludes. + +``` +\define mymacro(param) +This is the macro text (param=$param$) +\end +``` + +!! Nested Macro Definitions + +Macro definitions can be nested to any number of required levels by specifying the name of the macro in the `\end` marker. Nested macro definitions must appear at the start of the definition that contains them. For example: + +< +\end actions +<$button actions=<>> +$caption$ + +\end special-button + +<> +""">> + +Note that the textual substitution of macro parameters that occurs when the outer macro is rendered will apply to the nested definitions as well. That generally means that textual substitution of macro parameters should not be used within nested macros. + +Parameters of nested macros can also be accessed via the `<<__variablename__>>` syntax. As ordinary variables, these parameters are available within nested child macros (and grandchildren etc). + +!! Macro Definition with Set Widget + +Macros are implemented as a special type of [[variable|Variables]] and so internally are actually defined with a <<.wlink SetWidget>> widget. + +``` +<$set name="mymacro" value="This is the macro text"> +... + +``` + +<<.note """that it is not currently possible to specify parameters when defining a macro with the <<.wlink SetWidget>> widget.""">> + +!! Importing Macro Definitions + +The [[Pragma: \import]] or <<.wlink ImportVariablesWidget>> widget can be used to copy macro definitions from another tiddler. + +!! `$:/tags/Macro` Tag + +Global macros can be defined using the [[SystemTag: $:/tags/Macro]]. + +The tag [[SystemTag: $:/tags/Macro/View]] is used to define macros that should only be available within the main view template and the preview panel. + +The tag [[SystemTag: $:/tags/Macro/View/Body]] is used to define macros that should only be available within the main view template body and the preview panel. + +!! JavaScript Macros + +Macros can also be <<.js-macro-link "written as JavaScript modules">>. diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Parameter Handling.tid b/editions/tw5.com/tiddlers/wikitext/Macro Parameter Handling.tid new file mode 100644 index 000000000..8158b2eab --- /dev/null +++ b/editions/tw5.com/tiddlers/wikitext/Macro Parameter Handling.tid @@ -0,0 +1,79 @@ +caption: Macro Definitions +created: 20220917154902906 +modified: 20230419103154328 +tags: WikiText Macros +title: Macro Parameter Handling +type: text/vnd.tiddlywiki + +!! Introduction + +[[Macros]] parameters are handled in two different ways: + +# Textual substitution is always performed for each parameter before the macro contents is used +# When the macro contents are wikified the parameters are made available as variables. The variable names are formed by wrapping the parameter name with double underscores + +Somewhat confusingly, in some situations both of these mechanisms will occur; this is related to the [[pitfalls of using macros|Macro Pitfalls]]. + +!! Textual Substitution of Parameters and variables + +The following substitutions take place before the text of a macro is used: + +* The pattern `$param$` is replaced with the value of the named parameter +* The pattern `$(variable)$` is replaced with the value of the named variable + +The actual value of the parameter or variable is substituted for the placeholder whenever the macro is called: + +<$macrocall $name="wikitext-example-without-html" src="""\define say-hi-using-parameters(name,address) +Hi, I'm $name$ and I live in $address$. +\end + +<> +"""/> + +Here's an example using variable substitution: + +<$macrocall $name="wikitext-example-without-html" src="""\define say-hi-using-variables() +Hi, I'm $(name)$ and I live in $(address)$. +\end + +\define name() Bugs + +<$let address="Rabbit Hole Hill"> +<> + +"""/> + +<<.warning """It is important to note that if the text being inserted contains any substitution tokens then they will in turn be processed. This can lead to unexpected results.""">> + +!! Accessing Parameters as Variables + +When macros are wikified, the parameters can be accessed as variables with the name of the parameter wrapped with double underscores. For example, the parameter `address` would be accessed as the variable `__address__`. + +Thus, the example above could also be expressed as: + +<$macrocall $name="wikitext-example-without-html" src="""\define say-hi-using-parameters(name,address) +Hi, I'm <<__name__>> and I live in <<__address__>>. +\end + +<> +"""/> + +Accessing parameters as variables only works in macros that are wikified and not, for example, when a macro is used as an attribute value. + +!!! Advantages of Accessing Parameters as Variables + +The primary advantage of the technique is that it avoids the parameter value being substituted into the macro as a literal string, which in turn can help avoid issues with parameters that contain quotes. + +For example, consider this macro. It invokes another macro using the single parameter as an argument for it: + +``` +\define film-quote(line) <$macrocall $name="anothermacro" actor="Bugs Bunny" line="""$line$"""/> +``` + +The code above will fail if the macro is invoked with the argument containing triple double quotes (for example `<>`). Using parameter variables offers a workaround: + +``` +\define film-quote(line) <$macrocall $name="anothermacro" actor="Bugs Bunny" line=<<__line__>>/> +``` + +See [[Macro Pitfalls]] for more discussion. diff --git a/editions/tw5.com/tiddlers/wikitext/Macro Pitfalls.tid b/editions/tw5.com/tiddlers/wikitext/Macro Pitfalls.tid new file mode 100644 index 000000000..cd0ed4061 --- /dev/null +++ b/editions/tw5.com/tiddlers/wikitext/Macro Pitfalls.tid @@ -0,0 +1,39 @@ +created: 20220917091428117 +modified: 20230419103154328 +title: Macro Pitfalls +type: text/vnd.tiddlywiki + +! Introduction + +In the early days of TiddlyWiki, [[macros|Macros]] were the best way of encapsulating snippets for reuse, and so they were used extensively. However, they have always suffered from some significant disadvantages that can give rise to errors and poor performance. + +<<.from-version "5.3.0">> Macros have been joined by [[Procedures]], [[Custom Widgets]] and [[Functions]] which together provide more robust and flexible ways to encapsulate and re-use code. It is now recommended to only use macros when textual substitution is specifically required. + +! Shortcomings of Textual Substitution + +TiddlyWiki's handling of [[macro|Macros]] parameters is based on "textual substitution" which means that the string values of the parameters provided when calling a macro are plugged into the macro definition before it is wikified. + +Here's a typical example of the approach in early versions of TiddlyWiki 5. The intention is to provide a macro that takes a single parameter of the title of the tiddler to view: + +``` +\define mymacro(title) +<$codeblock code={{$title$}}/> +\end +``` + +That works for simple cases like `<>` but is subtly brittle. For example, the macro above would fail with tiddler titles containing double closing curly braces. Trying to use it with the title `foo}}bar` would lead to the macro being expanded to the following invalid syntax: + +``` +<$codeblock code={{foo}}bar}}/> +``` + +As a result of this issue, for many years the TiddlyWiki 5 user interface failed if a variety of combinations of special characters were encountered in tiddler titles. + +This issue has been mitigated over the years, particularly by providing access to the macro parameters as variables. However, for backwards compatibility, this was done without affecting the existing syntax, which required us to adopt the clumsy protocol of wrapping the parameter name in double underscores to get the name of the corresponding variable. + +! Performance of Global Macros + +Global [[Macro Definitions]] defined with the [[SystemTag: $:/tags/Macro]] suffer from poor performance because every macro has to be parsed regardless of whether it is actually used. + +Furthermore, the way that definitions are imported means that updating a tiddler tagged [[SystemTag: $:/tags/Macro]] will cause the entire page to be refreshed. + diff --git a/editions/tw5.com/tiddlers/wikitext/Macros in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Macros in WikiText.tid index a738d5389..16f05a22a 100644 --- a/editions/tw5.com/tiddlers/wikitext/Macros in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Macros in WikiText.tid @@ -1,11 +1,7 @@ created: 20131205160746466 modified: 20150221094003000 -tags: WikiText +tags: title: Macros in WikiText type: text/vnd.tiddlywiki -caption: Macros -The use of [[macros|Macros]] in WikiText has two distinct aspects: - -* [[Defining macros|Macro Definitions in WikiText]] -* [[Calling macros|Macro Calls in WikiText]] +See [[Macros]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Transclusion and Substitution.tid b/editions/tw5.com/tiddlers/wikitext/Transclusion and Substitution.tid index c74c68855..8d0a52cdc 100644 --- a/editions/tw5.com/tiddlers/wikitext/Transclusion and Substitution.tid +++ b/editions/tw5.com/tiddlers/wikitext/Transclusion and Substitution.tid @@ -1,5 +1,5 @@ created: 20141018090608643 -modified: 20211117212543789 +modified: 20230419103154329 tags: WikiText title: Transclusion and Substitution type: text/vnd.tiddlywiki @@ -55,6 +55,6 @@ As described in [[Introduction to filter notation]], you can also transclude a v ! Textual Substitution -Textual substitution occurs when the value of a macro/variable is used. It is described in [[Macros in WikiText]]. +Textual substitution occurs when the value of a macro/variable is used. It is described in [[Macros]]. The key difference between substitution and transclusion is that substitution occurs before WikiText parsing. This means that you can use substitution to build WikiText constructions. Transclusions are processed independently, and cannot be combined with adjacent text to define WikiText constructions. diff --git a/editions/tw5.com/tiddlers/wikitext/Transclusion in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Transclusion in WikiText.tid index f7de5d83d..838cd0ade 100644 --- a/editions/tw5.com/tiddlers/wikitext/Transclusion in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Transclusion in WikiText.tid @@ -12,6 +12,8 @@ You can incorporate the content of one tiddler within another using the [[Transc * `{{MyTiddler}}` transcludes a single tiddler * `{{MyTiddler||TemplateTitle}}` displays the tiddler through a specified [[TemplateTiddler|TemplateTiddlers]] * `{{||TemplateTitle}}` displays the specified template tiddler without altering the [[current tiddler|Current Tiddler]] +* `{{MyTiddler|Parameter}}` transcludes a single tiddler with a single parameter +* `{{MyTiddler||TemplateTitle|Parameter|SecondParameter}}` transcludes a single tiddler through a specified [[TemplateTiddler|TemplateTiddlers]] with two parameters !! Transcluding Text References @@ -37,7 +39,7 @@ The WikiText transclusion syntax generates a TiddlerWidget wrapped around a Tran ``` <$tiddler tiddler="MyTiddler"> -<$transclude tiddler="MyTemplate" field="myField"/> +<$transclude $tiddler="MyTemplate" $field="myField"/> ``` diff --git a/editions/tw5.com/tiddlers/wikitext/Variables in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Variables in WikiText.tid index b2c103507..a412031c3 100644 --- a/editions/tw5.com/tiddlers/wikitext/Variables in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Variables in WikiText.tid @@ -1,36 +1,7 @@ -caption: Variables created: 20141002141231992 modified: 20150221221850000 -tags: WikiText +tags: title: Variables in WikiText type: text/vnd.tiddlywiki -See also the [[introduction to the concept of variables|Variables]]. - -To transclude the value of a variable, use the [[macro call syntax|Macro Calls in WikiText]] with no parameters. You can also use a <<.wlink MacroCallWidget>> widget. - -A [[macro|Macros]] snippet can contain `$(name)$` as a [[placeholder|Macro Definitions in WikiText]] for which the value of the variable of that name will be substituted. - -A variable's value can be used as a [[filter parameter|Filter Parameter]], or as a [[widget attribute|Widgets in WikiText]]. The latter supports macro parameters. - -!! Example: defining a variable - -<$macrocall $name=".example" n="1" -eg="""<$set name=animal value=zebra> -<> -"""/> - -!! Example: defining a macro - -The `\define` pragma below [[defines a macro|Macros in WikiText]] called <<.var tags-of-current-tiddler>>. The macro returns the value of the tiddler's <<.field tags>> field, and can be accessed from anywhere else in the same tiddler (or in any tiddler that [[imports|ImportVariablesWidget]] it). - -<$importvariables filter="$:/editions/tw5.com/macro-examples/tags-of-current-tiddler"> -<$codeblock code={{$:/editions/tw5.com/macro-examples/tags-of-current-tiddler}}/> -<$macrocall $name=".example" n="2" eg="""The tags are: <>"""/> - - -!! Example: using a variable as a filter parameter - -This example uses the <<.olink backlinks>> [[operator|Filter Operators]] to list all tiddlers that link to this one. - -<$macrocall $name=".example" n="3" eg="""<backlinks[]]">>"""/> +See [[Variables]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/parser/Inline Mode WikiText.tid b/editions/tw5.com/tiddlers/wikitext/parser/Inline Mode WikiText.tid index 88afd8372..3e04a01ac 100644 --- a/editions/tw5.com/tiddlers/wikitext/parser/Inline Mode WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/parser/Inline Mode WikiText.tid @@ -1,6 +1,6 @@ caption: inline parser mode created: 20220111000108618 -modified: 20220122182842036 +modified: 20220917074925230 tags: [[WikiText Parser Modes]] title: Inline Mode WikiText type: text/vnd.tiddlywiki @@ -14,13 +14,13 @@ These WikiText types can be expressed without an entire line of text. They aren' * [[HTML in WikiText]] * [[Images in WikiText]] * [[Linking in WikiText]] -* [[Macro Calls in WikiText]] +* [[Macro Calls]] * [[Styles and Classes in WikiText]] (single line version only) * [[Transclusion in WikiText]] * [[Variables in WikiText]] * [[Widgets in WikiText]] -<<.tip """[[Macro Calls in WikiText]] and [[Transclusion in WikiText]] will be recognised in block mode if the macro call or transclusion spans an entire line.""">> +<<.tip """[[Macro Calls]] and [[Transclusion in WikiText]] will be recognised in block mode if the macro call or transclusion spans an entire line.""">> <<.tip """The other ''inline mode'' WikiText types are technically <<.em only>> detected while the parser is in ''inline mode''. However, the opening punctuation will also trigger the start of [[Paragraphs in WikiText]] which will automatically cause the parser to go into ''inline mode''. Therefore, practically speaking, it is just as useful to consider these WikiText types as recognised while the parser is in either ''inline mode'' or ''block mode''""">> While processing the //enclosed// text of some of these WikiText types, the parser [[will not look for new WikiText|Places where the parser ignores WikiText]]. But for rest of these WikiText types, the parser will continue in ''inline mode'' for the //enclosed// text. While parsing that text, it might encounter something which [[moves it to block mode|WikiText parser mode transitions]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/parser/Places where the parser ignores WikiText.tid b/editions/tw5.com/tiddlers/wikitext/parser/Places where the parser ignores WikiText.tid index afb9db192..d52193148 100644 --- a/editions/tw5.com/tiddlers/wikitext/parser/Places where the parser ignores WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/parser/Places where the parser ignores WikiText.tid @@ -8,7 +8,7 @@ type: text/vnd.tiddlywiki Text enclosed by these constructs is skipped by the parser and WikiText punctuation will be ignored: |[[Code Blocks in WikiText]]|One of the main purposes of code blocks is to suppress wikitext expansion. Once the code block starts, the parser will ignore all WikiText punctuation until the code block ends.| -|[[Images in WikiText]]|`[[img|literal image link text]]` - the text enclosed by square braces will be ignored. This means, for example, [[transclusions|Transclusion in WikiText]] and [[macro calls|Macro Calls in WikiText]] cannot be used to dynamically construct the link text| -|[[Linking in WikiText]]|`[[literal link target|literal link text]]` - the text enclosed by square braces will be ignored. This means, for example, [[transclusions|Transclusion in WikiText]] and [[macro calls|Macro Calls in WikiText]] cannot be used to dynamically construct the link target or the link text| -|[[Macro Calls in WikiText]]|`<>" {{transclusion_ignored}}>>` - while processing the text enclosed by a macro call, the parser will follow special rules for detecting macro parameters. These rules do not include detection of WikiText. However, after the parameters are substituted into the macro definition, the result will be parsed using [[normal rules|Wiki Text Parser Modes]]. This will likely result in the detection of any WikiText.| +|[[Images in WikiText]]|`[[img|literal image link text]]` - the text enclosed by square braces will be ignored. This means, for example, [[transclusions|Transclusion in WikiText]] and [[macro calls|Macro Calls]] cannot be used to dynamically construct the link text| +|[[Linking in WikiText]]|`[[literal link target|literal link text]]` - the text enclosed by square braces will be ignored. This means, for example, [[transclusions|Transclusion in WikiText]] and [[macro calls|Macro Calls]] cannot be used to dynamically construct the link target or the link text| +|[[Macro Calls]]|`<>" {{transclusion_ignored}}>>` - while processing the text enclosed by a macro call, the parser will follow special rules for detecting macro parameters. These rules do not include detection of WikiText. However, after the parameters are substituted into the macro definition, the result will be parsed using [[normal rules|Wiki Text Parser Modes]]. This will likely result in the detection of any WikiText.| diff --git a/editions/tw5.com/tiddlers/wikitext/parser/WikiText Parser Modes.tid b/editions/tw5.com/tiddlers/wikitext/parser/WikiText Parser Modes.tid index 8056b9493..bd408b977 100644 --- a/editions/tw5.com/tiddlers/wikitext/parser/WikiText Parser Modes.tid +++ b/editions/tw5.com/tiddlers/wikitext/parser/WikiText Parser Modes.tid @@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki In order to display Tiddlers (usually the text field), the WikiText parser reads and interprets the content and applies WikiText rules. The parser has three modes: -* ''pragma mode'' - the parser will recognise only [[pragma mode WikiText|Pragma]] punctuation +* ''pragma mode'' - the parser will recognise only [[pragma mode WikiText|Pragmas]] punctuation * ''block mode'' - the parser will recognise only [[block mode WikiText|Block Mode WikiText]] punctuation * ''inline mode'' - the parser will recognise only [[inline mode WikiText|Inline Mode WikiText]] diff --git a/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode transitions.tid b/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode transitions.tid index 009c3d3f3..6150fbf63 100644 --- a/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode transitions.tid +++ b/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode transitions.tid @@ -27,11 +27,11 @@ This is a <<.em rough>> diagram whose lines mostly correspond to the parser mode By default the parser starts in [[block mode|Block Mode WikiText]]. However, a tiddler can instead be transcluded with [[inline mode|Inline Mode WikiText]] in which case [[block mode WikiText|Block Mode WikiText]] will not be recognised. -At the start of text only, the parser will also recognise any [[pragma mode WikiText|Pragma]]. +At the start of text only, the parser will also recognise any [[pragma mode WikiText|Pragmas]]. !! Transitions from pragma mode -At the start of text, the parser will recognise any [[pragma|Pragma]]. If none are found then it will move to [[inline|Inline Mode WikiText]] or [[block|Block Mode WikiText]] mode depending on the transclusion mode. If any [[pragma|Pragma]] are found then it will continue looking for [[pragma|Pragma]] until it finds one or more blank lines not followed by the start of a new pragma. +At the start of text, the parser will recognise any [[pragma|Pragmas]]. If none are found then it will move to [[inline|Inline Mode WikiText]] or [[block|Block Mode WikiText]] mode depending on the transclusion mode. If any [[pragma|Pragmas]] are found then it will continue looking for [[pragma|Pragmas]] until it finds one or more blank lines not followed by the start of a new pragma. !! Transitions from block mode diff --git a/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode_ macro examples.tid b/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode_ macro examples.tid index 07a08db46..7224ddb81 100644 --- a/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode_ macro examples.tid +++ b/editions/tw5.com/tiddlers/wikitext/parser/WikiText parser mode_ macro examples.tid @@ -25,7 +25,7 @@ then """>> -The list syntax is recognised in [[block mode|Block Mode WikiText]] and the enclosed contents are parsed using [[inline mode|Inline Mode WikiText]]. When the parser encounters a [[wikitext macro call|Macro Calls in WikiText]] it will use the current parse mode to parse the contents of the macro. The contents of the macro contains table syntax which is only recognised in [[block mode|Block Mode WikiText]]. +The list syntax is recognised in [[block mode|Block Mode WikiText]] and the enclosed contents are parsed using [[inline mode|Inline Mode WikiText]]. When the parser encounters a [[wikitext macro call|Macro Calls]] it will use the current parse mode to parse the contents of the macro. The contents of the macro contains table syntax which is only recognised in [[block mode|Block Mode WikiText]]. Therefore, in #1 above the table syntax is not recognised. In #2 above, the blank line after the open `div` tag moves the parser back into [[block mode|Block Mode WikiText]], the macro call inherits it and the table is recognised. diff --git a/package.json b/package.json index f65fda395..3d9444256 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tiddlywiki", "preferGlobal": "true", - "version": "5.2.8-prerelease", + "version": "5.3.0-prerelease", "author": "Jeremy Ruston ", "description": "a non-linear personal web notebook", "contributors": [ diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid index 98097296a..f63384ee9 100644 --- a/themes/tiddlywiki/vanilla/base.tid +++ b/themes/tiddlywiki/vanilla/base.tid @@ -3118,6 +3118,36 @@ select { background: <>; } +/* +** Classes for displaying globals +*/ + +.tc-global-tiddler-body { + padding: 0.25em; + border: 1px solid <>; + background-color: <>; + border-radius: 3px; +} + +.tc-global-tiddler-body-heading { + margin: 0 0 0.25em 0; + font-weight: normal; +} + +.tc-global-tiddler-body-type { + margin: 0 0 0.25em 0; + border-bottom: 1px solid <>; +} + +.tc-global-tiddler-body-details { + background-color: <>; +} + +.tc-global-tiddler-body pre { + margin: 0; + border: 1px solid <>; +} + /* ** Utility classes for SVG icons */