1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 21:39:57 +00:00

Add wikitext shortcut for new-style function definitions

This commit is contained in:
jeremy@jermolene.com 2022-04-30 12:44:26 +01:00
parent 886c8620f5
commit c9bd1b5274
6 changed files with 169 additions and 5 deletions

View File

@ -0,0 +1,93 @@
/*\
title: $:/core/modules/parsers/wikiparser/rules/functiondef.js
type: application/javascript
module-type: wikirule
Wiki pragma rule for function definitions
```
\function name(param:defaultvalue,param2:defaultvalue)
definition text
\end
```
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.name = "functiondef";
exports.types = {pragma: true};
/*
Instantiate parse rule
*/
exports.init = function(parser) {
this.parser = parser;
// Regexp to match
this.matchRegExp = /^\\function\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 paramString = this.match[2],
params = [];
if(paramString !== "") {
var reParam = /\s*([A-Za-z0-9\-_]+)(?:\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);
}
}
// Is this a multiline definition?
var reEnd;
if(this.match[3]) {
// If so, the end of the body is marked with \end
reEnd = /(\r?\n\\end[^\S\n\r]*(?:$|\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
return [{
type: "set",
attributes: {
name: {type: "string", value: this.match[1]},
value: {type: "string", value: text}
},
children: [],
variableParams: params,
isFunctionDefinition: true
}];
};
})();

View File

@ -48,7 +48,13 @@ SetWidget.prototype.execute = function() {
this.setValue = this.getAttribute("value"); this.setValue = this.getAttribute("value");
this.setEmptyValue = this.getAttribute("emptyValue"); this.setEmptyValue = this.getAttribute("emptyValue");
// Set context variable // Set context variable
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,!!this.parseTreeNode.isMacroDefinition);
} else if(this.parseTreeNode.isFunctionDefinition) {
this.setVariable(this.setName,this.getValue(),undefined,undefined,{isFunctionDefinition: this.parseTreeNode.isFunctionDefinition,variableParams: this.parseTreeNode.variableParams});
} else {
this.setVariable(this.setName,this.getValue());
}
// Construct the child widgets // Construct the child widgets
this.makeChildWidgets(); this.makeChildWidgets();
}; };

View File

@ -1,3 +1,5 @@
const { parse } = require("../parsers/wikiparser/rules/transcludeinline");
/*\ /*\
title: $:/core/modules/widgets/transclude.js title: $:/core/modules/widgets/transclude.js
type: application/javascript type: application/javascript
@ -169,7 +171,26 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
} }
var parser; var parser;
if(this.transcludeVariable) { if(this.transcludeVariable) {
parser = this.wiki.parseText(this.transcludeType,this.getVariable(this.transcludeVariable,""),{parseAsInline: !this.parseTreeNode.isBlock}); var variableInfo = this.getVariableInfo(this.transcludeVariable).srcVariable;
parser = this.wiki.parseText(this.transcludeType,variableInfo.value || "",{parseAsInline: !this.parseTreeNode.isBlock});
if(parser && variableInfo.isFunctionDefinition) {
parser = {
tree: [
{
type: "parameters",
name: "$parameters",
children: parser.tree,
attributes: {},
orderedAttributes: []
}
]
}
$tw.utils.each(variableInfo.variableParams,function(param,index) {
var attr = {name: param.name, type: "string", value: param["default"]};
parser.tree[0].attributes[param.name] = attr;
parser.tree[0].orderedAttributes.push(attr);
});
}
} else { } else {
parser = this.wiki.parseTextReference( parser = this.wiki.parseTextReference(
this.transcludeTitle, this.transcludeTitle,

View File

@ -88,9 +88,19 @@ name: name of the variable
value: value of the variable 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:
isFunctionDefinition: true if the variable is set via a \function pragma (and hence should not have variable substitution performed)
variableParams: array of {name:, default:} for each function parameter
*/ */
Widget.prototype.setVariable = function(name,value,params,isMacroDefinition) { Widget.prototype.setVariable = function(name,value,params,isMacroDefinition,options) {
this.variables[name] = {value: value, params: params, isMacroDefinition: !!isMacroDefinition}; options = options || {};
this.variables[name] = {
value: value,
params: params,
isMacroDefinition: !!isMacroDefinition,
isFunctionDefinition: !!options.isFunctionDefinition,
variableParams: options.variableParams
};
}; };
/* /*

View File

@ -0,0 +1,18 @@
title: Transclude/Parameterised/Shortcut
description: Simple parameterised transclusion
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\function test(one:'Jaguar')
{<$text text=<<one>>/>}
\end
<$transclude $variable='test' one='Ferret'/>
<$transclude $variable='test'/>
_
title: ExpectedResult
<p>{Ferret}{Jaguar}</p>

View File

@ -119,6 +119,22 @@ describe("WikiText parser tests", function() {
); );
}); });
it("should parse function definitions", function() {
expect(parse("\\function 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 : [ ], variableParams : [ { name: 'one' }, { name: 'two' }, { name: 'three' }, { name: 'four', default: 'elephant' } ], isFunctionDefinition : true } ]
);
});
it("should parse function definitions", function() {
expect(parse("\\function 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 : [ ], variableParams : [ { name: 'one', "default": 'Jaguar' } ], isFunctionDefinition : true } ]
);
});
it("should parse comment in pragma area. Comment will be invisible", function() { it("should parse comment in pragma area. Comment will be invisible", function() {
expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual( expect(parse("<!-- comment in pragma area -->\n\\define aMacro()\nnothing\n\\end\n")).toEqual(