1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-06-03 15:14:07 +00:00

Switch to using \procedure to define new-style macros, and \function for custom filter operator functions

I now need to update the OP!
This commit is contained in:
jeremy@jermolene.com 2022-05-09 18:00:09 +01:00
parent 64448ae774
commit e092113f9f
15 changed files with 80 additions and 29 deletions

View File

@ -20,7 +20,7 @@ Export our filter function
exports.unknown = function(source,operator,options) { exports.unknown = function(source,operator,options) {
var customDefinitionTitle = "[" + operator.operator + "[]]", var customDefinitionTitle = "[" + operator.operator + "[]]",
customDefinition = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(customDefinitionTitle); customDefinition = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(customDefinitionTitle);
if(customDefinition && customDefinition.srcVariable) { if(customDefinition && customDefinition.srcVariable && customDefinition.srcVariable.isFunctionDefinition) {
var variables = Object.create(null); var variables = Object.create(null);
$tw.utils.each(customDefinition.srcVariable.params,function(param,index) { $tw.utils.each(customDefinition.srcVariable.params,function(param,index) {
var value = operator.operands[index]; var value = operator.operands[index];
@ -37,7 +37,13 @@ exports.unknown = function(source,operator,options) {
}; };
}; };
var getVariableInfo = function(name,opts) { var getVariableInfo = function(name,opts) {
return options.widget.getVariableInfo(name,opts); if(name in variables) {
return {
text: variables[name]
};
} else {
return options.widget.getVariableInfo(name,opts);
};
} }
var list = options.wiki.filterTiddlers(customDefinition.srcVariable.value,{getVariable: getVariable,getVariableInfo: getVariableInfo},source); var list = options.wiki.filterTiddlers(customDefinition.srcVariable.value,{getVariable: getVariable,getVariableInfo: getVariableInfo},source);
if(operator.prefix === "!") { if(operator.prefix === "!") {

View File

@ -1,14 +1,18 @@
/*\ /*\
title: $:/core/modules/parsers/wikiparser/rules/functiondef.js title: $:/core/modules/parsers/wikiparser/rules/fnprocdef.js
type: application/javascript type: application/javascript
module-type: wikirule module-type: wikirule
Wiki pragma rule for function definitions Wiki pragma rule for function and procedure definitions
``` ```
\function name(param:defaultvalue,param2:defaultvalue) \function name(param:defaultvalue,param2:defaultvalue)
definition text definition text
\end \end
\procedure name(param:defaultvalue,param2:defaultvalue)
definition text
\end
``` ```
\*/ \*/
@ -18,7 +22,7 @@ definition text
/*global $tw: false */ /*global $tw: false */
"use strict"; "use strict";
exports.name = "functiondef"; exports.name = "fnprocdef";
exports.types = {pragma: true}; exports.types = {pragma: true};
/* /*
@ -27,7 +31,7 @@ Instantiate parse rule
exports.init = function(parser) { exports.init = function(parser) {
this.parser = parser; this.parser = parser;
// Regexp to match // Regexp to match
this.matchRegExp = /^\\function\s+([^(\s]+)(\(\s*([^)]*)\))?(\s*\r?\n)?/mg; this.matchRegExp = /^\\(function|procedure)\s+([^(\s]+)(\(\s*([^)]*)\))?(\s*\r?\n)?/mg;
}; };
/* /*
@ -37,9 +41,9 @@ exports.parse = function() {
// Move past the macro name and parameters // Move past the macro name and parameters
this.parser.pos = this.matchRegExp.lastIndex; this.parser.pos = this.matchRegExp.lastIndex;
// Parse the parameters // Parse the parameters
var paramString = this.match[3], var paramString = this.match[4],
params = []; params = [];
if(this.match[2]) { if(this.match[3]) {
var reParam = /\s*([^:),\s]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|([^,"'\s]+)))?/mg, var reParam = /\s*([^:),\s]+)(?:\s*:\s*(?:"""([\s\S]*?)"""|"([^"]*)"|'([^']*)'|([^,"'\s]+)))?/mg,
paramMatch = reParam.exec(paramString); paramMatch = reParam.exec(paramString);
while(paramMatch) { while(paramMatch) {
@ -56,7 +60,7 @@ exports.parse = function() {
} }
// Is this a multiline definition? // Is this a multiline definition?
var reEnd; var reEnd;
if(this.match[4]) { if(this.match[5]) {
// If so, the end of the body is marked with \end // If so, the end of the body is marked with \end
reEnd = /(\r?\n\\end[^\S\n\r]*(?:$|\r?\n))/mg; reEnd = /(\r?\n\\end[^\S\n\r]*(?:$|\r?\n))/mg;
} else { } else {
@ -77,16 +81,21 @@ exports.parse = function() {
text = ""; text = "";
} }
// Save the macro definition // Save the macro definition
return [{ var parseTreeNodes = [{
type: "set", type: "set",
attributes: { attributes: {
name: {type: "string", value: this.match[1]}, name: {type: "string", value: this.match[2]},
value: {type: "string", value: text} value: {type: "string", value: text}
}, },
children: [], children: [],
params: params, params: params
isFunctionDefinition: true
}]; }];
if(this.match[1] === "function") {
parseTreeNodes[0].isFunctionDefinition = true;
} else if(this.match[1] === "procedure") {
parseTreeNodes[0].isProcedureDefinition = true;
}
return parseTreeNodes;
}; };
})(); })();

View File

@ -56,9 +56,10 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
attributes: parseTreeNode.attributes, attributes: parseTreeNode.attributes,
params: parseTreeNode.params, params: parseTreeNode.params,
isMacroDefinition: parseTreeNode.isMacroDefinition, isMacroDefinition: parseTreeNode.isMacroDefinition,
isFunctionDefinition: parseTreeNode.isFunctionDefinition isFunctionDefinition: parseTreeNode.isFunctionDefinition,
isProcedureDefinition: parseTreeNode.isProcedureDefinition
}; };
if (parseTreeNode.isMacroDefinition || parseTreeNode.isFunctionDefinition) { if (parseTreeNode.isMacroDefinition || parseTreeNode.isProcedureDefinition) {
// Macro definitions can be folded into // Macro definitions can be folded into
// current widget instead of adding // current widget instead of adding
// another link to the chain. // another link to the chain.

View File

@ -49,9 +49,11 @@ SetWidget.prototype.execute = function() {
this.setEmptyValue = this.getAttribute("emptyValue"); this.setEmptyValue = this.getAttribute("emptyValue");
// Set context variable // Set context variable
if(this.parseTreeNode.isMacroDefinition) { if(this.parseTreeNode.isMacroDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,!!this.parseTreeNode.isMacroDefinition); this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,true);
} else if(this.parseTreeNode.isFunctionDefinition) { } else if(this.parseTreeNode.isFunctionDefinition) {
this.setVariable(this.setName,this.getValue(),this.parseTreeNode.params,undefined,{isFunctionDefinition: 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});
} else { } else {
this.setVariable(this.setName,this.getValue()); this.setVariable(this.setName,this.getValue());
} }

View File

@ -197,8 +197,8 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
} }
} }
if(parser) { if(parser) {
// Add parameters widget for functions // Add parameters widget for procedures
if(srcVariable.isFunctionDefinition) { if(srcVariable.isProcedureDefinition) {
parser = { parser = {
tree: [ tree: [
{ {

View File

@ -89,6 +89,7 @@ value: value of the variable
params: array of {name:, default:} for each parameter 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) isMacroDefinition: true if the variable is set via a \define macro pragma (and hence should have variable substitution performed)
options includes: 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) isFunctionDefinition: true if the variable is set via a \function pragma (and hence should not have variable substitution performed)
*/ */
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) { Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
@ -97,7 +98,8 @@ Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,opti
value: value, value: value,
params: params, params: params,
isMacroDefinition: !!isMacroDefinition, isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition isFunctionDefinition: !!options.isFunctionDefinition,
isProcedureDefinition: !!options.isProcedureDefinition
}; };
}; };

View File

@ -6,7 +6,7 @@ tags: [[$:/tags/wiki-test-spec]]
title: Output title: Output
\whitespace trim \whitespace trim
\function <$let> \procedure <$let>
\whitespace trim \whitespace trim
<$setmultiplevariables $names="[enlist:raw<paramNames>]" $values="[enlist:raw<paramValues>addprefix[--]addsuffix[--]]"> <$setmultiplevariables $names="[enlist:raw<paramNames>]" $values="[enlist:raw<paramValues>addprefix[--]addsuffix[--]]">
<$slot $name="ts-body"/> <$slot $name="ts-body"/>

View File

@ -13,7 +13,7 @@ title: Actions
\whitespace trim \whitespace trim
<!-- Define the <$action-mywidget> widget by defining a transcludable variable with that name --> <!-- Define the <$action-mywidget> widget by defining a transcludable variable with that name -->
\function <$action-mywidget>(one:'Jaguar') \procedure <$action-mywidget>(one:'Jaguar')
\whitespace trim \whitespace trim
<$action-setfield $tiddler="Result" $field="text" $value=<<one>>/> <$action-setfield $tiddler="Result" $field="text" $value=<<one>>/>
\end \end

View File

@ -16,7 +16,7 @@ title: Output
title: Definition title: Definition
\whitespace trim \whitespace trim
\function <$codeblock>(code) \procedure <$codeblock>(code)
<$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/> <$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/>
\end \end
+ +

View File

@ -17,7 +17,7 @@ title: TiddlerOne
\whitespace trim \whitespace trim
<!-- Redefine the <$transclude> widget by defining a transcludable variable with that name --> <!-- Redefine the <$transclude> widget by defining a transcludable variable with that name -->
\function <$transclude>(one:'Jaguar') \procedure <$transclude>(one:'Jaguar')
\whitespace trim \whitespace trim
<$text text=<<one>>/> <$text text=<<one>>/>
<$slot $name="body"> <$slot $name="body">

View File

@ -13,7 +13,7 @@ title: TiddlerOne
\whitespace trim \whitespace trim
<!-- Define the <$mywidget> widget by defining a transcludable variable with that name --> <!-- Define the <$mywidget> widget by defining a transcludable variable with that name -->
\function <$mywidget>(one:'Jaguar') \procedure <$mywidget>(one:'Jaguar')
\whitespace trim \whitespace trim
<$text text=<<one>>/> <$text text=<<one>>/>
<$slot $name="ts-body"> <$slot $name="ts-body">

View File

@ -13,7 +13,7 @@ title: TiddlerOne
\whitespace trim \whitespace trim
<!-- Redefine the <$text> widget by defining a transcludable variable with that name --> <!-- Redefine the <$text> widget by defining a transcludable variable with that name -->
\function <$text>(text:'Jaguar') \procedure <$text>(text:'Jaguar')
\whitespace trim \whitespace trim
<$genesis $type="text" $remappable="no" text=<<text>>/> <$genesis $type="text" $remappable="no" text=<<text>>/>
<$set name="<$text>" value=""> <$set name="<$text>" value="">

View File

@ -13,7 +13,7 @@ title: TiddlerOne
\whitespace trim \whitespace trim
<!-- Redefine the <$mywidget> widget by defining a transcludable variable with that name --> <!-- Redefine the <$mywidget> widget by defining a transcludable variable with that name -->
\function <$mywidget>($variable:'Jaguar') \procedure <$mywidget>($variable:'Jaguar')
\whitespace trim \whitespace trim
<$text text=<<$variable>>/> <$text text=<<$variable>>/>
<$slot $name="ts-body"> <$slot $name="ts-body">

View File

@ -6,7 +6,7 @@ tags: [[$:/tags/wiki-test-spec]]
title: Output title: Output
\whitespace trim \whitespace trim
\function test(one:'Jaguar') \procedure test(one:'Jaguar')
{<$text text=<<one>>/>} {<$text text=<<one>>/>}
\end \end

View File

@ -119,7 +119,38 @@ describe("WikiText parser tests", function() {
); );
}); });
it("should parse function definitions with no parameters", function() { it("should parse procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isProcedureDefinition : true } ]
);
});
it("should parse single line procedure definitions with no parameters", function() {
expect(parse("\\procedure myMacro nothing\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], 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 : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ { name: 'one' }, { name: 'two' }, { name: 'three' }, { name: 'four', default: 'elephant' } ], isProcedureDefinition : true } ]
);
});
it("should parse procedure definitions", function() {
expect(parse("\\procedure myMacro(one:'Jaguar')\n<$text text=<<one>>/>\n\\end\n\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : '<$text text=<<one>>/>' } }, children : [ ], params : [ { name: 'one', "default": 'Jaguar' } ], isProcedureDefinition : true } ]
);
}); it("should parse function definitions with no parameters", function() {
expect(parse("\\function myMacro\nnothing\n\\end\n")).toEqual( expect(parse("\\function myMacro\nnothing\n\\end\n")).toEqual(
[ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isFunctionDefinition : true } ] [ { type : 'set', attributes : { name : { type : 'string', value : 'myMacro' }, value : { type : 'string', value : 'nothing' } }, children : [ ], params : [ ], isFunctionDefinition : true } ]