Rejigged macro implementation

Now each macro is in a separate file, and is implemented as a function,
rather than being inlined into the compiled tiddler rendering function
This commit is contained in:
Jeremy Ruston 2012-01-07 17:33:42 +00:00
parent 3412580af3
commit 9a73b0a6aa
10 changed files with 227 additions and 199 deletions

View File

@ -79,8 +79,13 @@ var App = function() {
} else {
this.store.jsParser = new JavaScriptParser(require("fs").readFileSync("parsers/javascript.pegjs","utf8"));
}
// Hack to install standard macros
this.store.installMacros();
// Bit of a hack to set up the macros
this.store.installMacro(require("./macros/echo.js").macro);
this.store.installMacro(require("./macros/info.js").macro);
this.store.installMacro(require("./macros/list.js").macro);
this.store.installMacro(require("./macros/tiddler.js").macro);
this.store.installMacro(require("./macros/version.js").macro);
this.store.installMacro(require("./macros/view.js").macro);
// Set up navigation if we're in the browser
if(this.isBrowser) {
// Install the standard navigators

View File

@ -19,6 +19,7 @@ var WikiStore = function WikiStore(options) {
options = options || {};
this.tiddlers = {};
this.parsers = {};
this.macros = {};
this.tiddlerSerializers = {};
this.tiddlerDeserializers = {};
this.sandbox = options.sandbox;
@ -162,26 +163,6 @@ WikiStore.prototype.deserializeTiddlers = function(type,text,srcFields) {
}
};
WikiStore.prototype.getFormattedTiddlerField = function(title,field,format,template) {
format = format || "text";
var tiddler = this.getTiddler(title);
if(tiddler && tiddler.fields[field]) {
switch(format) {
case "text":
return utils.htmlEncode(tiddler.fields[field]);
case "link":
// xxx: Attribute encoding is wrong
return "<a href='" + utils.htmlEncode(tiddler.fields[field]) + "'" + this.classesForLink(tiddler.fields[field]) + ">" + utils.htmlEncode(tiddler.fields[field]) + "</a>";
case "wikified":
return this.renderTiddler("text/html",tiddler.fields.title);
case "date":
template = template || "DD MMM YYYY";
return utils.htmlEncode(utils.formatDateString(tiddler.fields[field],template));
}
}
return "";
};
WikiStore.prototype.classesForLink = function(target) {
var className = "",
externalRegExp = /(?:file|http|https|mailto|ftp|irc|news|data):[^\s'"]+(?:\/|\b)/i;
@ -195,90 +176,6 @@ WikiStore.prototype.classesForLink = function(target) {
return className !== "" ? " class=\"" + className + "\"" : "";
};
WikiStore.prototype.listTiddlers = function(type,template,emptyMessage) {
return "<span>Listing!</span>";
};
WikiStore.prototype.tiddlerInfo = function(title) {
var tiddler = this.getTiddler(title),
parseTree = this.parseTiddler(title);
if(tiddler && parseTree) {
var d = parseTree.dependencies;
if(d === null) {
return "Dependencies: *";
} else {
return "Dependencies: " + d.join(", ");
}
} else {
return "";
}
};
/*
argOptions: {defaultName:"type"},
handler: function(macroNode,args,title) {
var type = args.getValueByName("type","all"),
template = args.getValueByName("template",null),
templateType = "text/x-tiddlywiki", templateText = "<<view title link>>",
emptyMessage = args.getValueByName("emptyMessage",null);
// Get the template to use
template = template ? this.store.getTiddler(template) : null;
if(template) {
templateType = template.fields.type;
templateText = template.fields.text;
}
// Get the handler and the tiddlers
var handler = WikiTextRenderer.macros.list.types[type];
handler = handler || WikiTextRenderer.macros.list.types.all;
var tiddlers = handler.call(this);
// Render them as a list
var ul = {type: "ul", children: []};
for(var t=0; t<tiddlers.length; t++) {
var li = {
type: "li",
children: [ {
type: "context",
tiddler: tiddlers[t],
children: []
} ]
};
li.children[0].children = this.store.parseText(templateType,templateText).children;
ul.children.push(li);
}
if(ul.children.length > 0) {
macroNode.output.push(ul);
this.executeMacros(macroNode.output,title);
} else if (emptyMessage) {
macroNode.output.push({type: "text", value: emptyMessage});
}
},
types: {
all: function() {
return this.store.getTitles("title","excludeLists");
},
missing: function() {
return this.store.getMissingTitles();
},
orphans: function() {
return this.store.getOrphanTitles();
},
shadowed: function() {
return this.store.getShadowTitles();
},
touched: function() {
// Server syncing isn't implemented yet
return [];
},
filter: function() {
// Filters aren't implemented yet
return [];
}
}
},
*/
WikiStore.prototype.parseText = function(type,text) {
var parser = this.parsers[type];
if(!parser) {
@ -343,94 +240,10 @@ WikiStore.prototype.renderTiddler = function(type,title,asTitle) {
return null;
};
WikiStore.prototype.installMacros = function() {
this.macros = {
echo: {
params: {
text: {byPos: 0, type: "text", optional: false}
},
code: {
"text/html": this.jsParser.parse("return utils.htmlEncode(params.text);"),
"text/plain": this.jsParser.parse("return params.text;")
}
},
view: {
params: {
field: {byPos: 0, type: "text", optional: false},
format: {byPos: 1, type: "text", optional: true},
template: {byPos: 2, type: "text", optional: true}
},
code: {
"text/html": this.jsParser.parse("return store.getFormattedTiddlerField(tiddler.fields.title,params.field,params.format,params.template);"),
"text/plain": this.jsParser.parse("return store.getFormattedTiddlerField(tiddler.fields.title,params.field,params.format,params.template);")
}
},
list: {
dependantAll: true, // Tiddlers containing <<list>> macro are dependent on every tiddler
params: {
type: {byName: "default", type: "text", optional: false},
template: {byName: true, type: "tiddler", optional: true},
emptyMessage: {byName: true, type: "text", optional: true}
},
code: {
"text/html": this.jsParser.parse("return store.listTiddlers(params.type,params.template,params.emptyMessage);"),
"text/plain": this.jsParser.parse("return store.listTiddlers(params.type,params.template,params.emptyMessage);")
}
},
version: {
params: {
},
code: {
"text/html": this.jsParser.parse("return utils.htmlEncode('5.0.0');"),
"text/plain": this.jsParser.parse("return '5.0.0';")
}
},
tiddler: {
cascadeParams: true, // Cascade names of named parameters to following anonymous parameters
params: {
target: {byName: "default", type: "tiddler", optional: false},
"with": {byName: true, type: "text", optional: true}
},
code: {
"text/html": this.jsParser.parse("return store.renderTiddler('text/html',params.target);"),
"text/plain": this.jsParser.parse("return store.renderTiddler('text/plain',params.target);")
}
},
info: {
params: {
},
code: {
"text/html": this.jsParser.parse("return utils.htmlEncode(store.tiddlerInfo(tiddler.fields.title));"),
"text/plain": this.jsParser.parse("return store.tiddlerInfo(tiddler.fields.title);")
}
}
};
WikiStore.prototype.installMacro = function(macro) {
this.macros[macro.name] = macro;
};
/*
tiddler: {
argOptions: {defaultName:"name",cascadeDefaults:true},
handler: function(macroNode,args,title) {
var targetTitle = args.getValueByName("name",null),
withTokens = args.getValuesByName("with",[]),
tiddler = this.store.getTiddler(targetTitle),
text = this.store.getTiddlerText(targetTitle,""),
t;
for(t=0; t<withTokens.length; t++) {
var placeholderRegExp = new RegExp("\\$"+(t+1),"mg");
text = text.replace(placeholderRegExp,withTokens[t]);
}
macroNode.output = this.store.parseText(tiddler.fields.type,text).children;
// Execute any macros in the copy
this.executeMacros(macroNode.output,title);
}
},
*/
exports.WikiStore = WikiStore;
})();

View File

@ -84,23 +84,48 @@ WikiTextParseTree.prototype.compileMacroCall = function(type,name,params) {
var macroCall = {
type: "FunctionCall",
name: {
type: "Function",
name: null,
params: ["params"],
elements: []},
"base": {
"base": {
"base": {
"name": "store",
"type": "Variable"
},
"name": "macros",
"type": "PropertyAccess"
},
"name": {
"type": "StringLiteral",
"value": name
},
"type": "PropertyAccess"
},
"name": "code",
"type": "PropertyAccess"
},
"arguments": [ {
"type": "StringLiteral",
"value": type
},
{
"type": "Variable",
"name": "tiddler"
},
{
"type": "Variable",
"name": "store"
},
{
type: "ObjectLiteral",
properties: []
}]
};
macroCall.name.elements = macro.code[type].tree.elements;
for(p in params) {
if(params[p].type === "string") {
n = {type: "StringLiteral", value: params[p].value};
} else {
n = this.store.jsParser.parse(params[p].value).tree.elements[0];
}
macroCall["arguments"][0].properties.push({
macroCall["arguments"][3].properties.push({
type: "PropertyAssignment",
name: p,
value: n
@ -108,7 +133,7 @@ WikiTextParseTree.prototype.compileMacroCall = function(type,name,params) {
}
this.output.push(macroCall);
} else {
this.pushString("<span class='error errorUnknownMacro'>Unknown macro '" + name + "'</span>");
this.pushString("{{** Unknown macro '" + name + "' **}}");
}
};

27
js/macros/echo.js Normal file
View File

@ -0,0 +1,27 @@
/*\
title: js/macros/echo.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "echo",
types: ["text/html","text/plain"],
params: {
text: {byPos: 0, type: "text", optional: false}
},
code: function(type,tiddler,store,params) {
if(type === "text/html") {
return utils.htmlEncode(params.text);
} else {
return params.text;
}
}
};
})();

33
js/macros/info.js Normal file
View File

@ -0,0 +1,33 @@
/*\
title: js/macros/info.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "info",
types: ["text/html","text/plain"],
params: {
},
code: function(type,tiddler,store,params) {
var encoder = type === "text/html" ? utils.htmlEncode : function(x) {return x;},
parseTree = store.parseTiddler(tiddler.fields.title);
if(parseTree) {
var d = parseTree.dependencies;
if(d === null) {
return encoder("Dependencies: *");
} else {
return encoder("Dependencies: " + d.join(", "));
}
} else {
return "";
}
}
};
})();

26
js/macros/list.js Normal file
View File

@ -0,0 +1,26 @@
/*\
title: js/macros/list.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "list",
types: ["text/html","text/plain"],
dependantAll: true, // Tiddlers containing <<list>> macro are dependent on every tiddler
params: {
type: {byName: "default", type: "text", optional: false},
template: {byName: true, type: "tiddler", optional: true},
emptyMessage: {byName: true, type: "text", optional: true}
},
code: function(type,tiddler,store,params) {
return "Listing!";
}
};
})();

25
js/macros/tiddler.js Normal file
View File

@ -0,0 +1,25 @@
/*\
title: js/macros/tiddler.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "tiddler",
types: ["text/html","text/plain"],
cascadeParams: true, // Cascade names of named parameters to following anonymous parameters
params: {
target: {byName: "default", type: "tiddler", optional: false},
"with": {byName: true, type: "text", optional: true}
},
code: function(type,tiddler,store,params) {
return store.renderTiddler(type,params.target);
}
};
})();

22
js/macros/version.js Normal file
View File

@ -0,0 +1,22 @@
/*\
title: js/macros/version.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "version",
types: ["text/html","text/plain"],
params: {
},
code: function(type,tiddler,store,params) {
return "5.0.0";
}
};
})();

45
js/macros/view.js Normal file
View File

@ -0,0 +1,45 @@
/*\
title: js/macros/view.js
\*/
(function(){
/*jslint node: true */
"use strict";
var utils = require("../Utils.js");
exports.macro = {
name: "view",
types: ["text/html","text/plain"],
params: {
field: {byPos: 0, type: "text", optional: false},
format: {byPos: 1, type: "text", optional: true},
template: {byPos: 2, type: "text", optional: true}
},
code: function(type,tiddler,store,params) {
var v = tiddler.fields[params.field],
encoder = type === "text/html" ? utils.htmlEncode : function(x) {return x;};
if(v) {
switch(params.format) {
case "link":
if(type === "text/html") {
return "<a href='" + encoder(v) + "'" + store.classesForLink(v) + ">" + encoder(v) + "</a>";
} else {
return v;
}
case "wikified":
return store.renderTiddler(type,tiddler.fields.title);
case "date":
var template = params.template || "DD MMM YYYY";
return encoder(utils.formatDateString(v,template));
default: // "text"
return encoder(v);
}
}
return "";
}
};
})();

View File

@ -23,6 +23,13 @@ jsmodule: ../js/Navigators.js
jsmodule: ../js/StoryNavigator.js
jsmodule: ../js/App.js
jsmodule: ../js/macros/echo.js
jsmodule: ../js/macros/info.js
jsmodule: ../js/macros/list.js
jsmodule: ../js/macros/tiddler.js
jsmodule: ../js/macros/version.js
jsmodule: ../js/macros/view.js
jsmodule: ../node_modules/pegjs/lib/peg.js
title: pegjs