r93717 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r93716‎ | r93717 | r93718 >
Date:09:40, 2 August 2011
Author:brion
Status:deferred
Tags:
Comment:
ParserPlayground -- checking in my airplane work :D

Starting on primitive parser environment with extension points for parser functions & tag hooks, start on moving the <ref> support out to a tag hook extension.

Doing this one as a 'pure-wiki-tree' extension which expands to additional nodes within the tree which can be rendered by the core renderer.
Modified paths:
  • /trunk/extensions/ParserPlayground/ParserPlayground.php (modified) (history)
  • /trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js (added) (history)
  • /trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js (modified) (history)
  • /trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js (modified) (history)
  • /trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js (added) (history)
  • /trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt (modified) (history)
  • /trunk/extensions/ParserPlayground/tests/parserTests.js (modified) (history)
  • /trunk/extensions/ParserPlayground/tests/parserTests.pegjs (modified) (history)

Diff [purge]

Index: trunk/extensions/ParserPlayground/tests/parserTests.pegjs
@@ -33,7 +33,8 @@
3434 comment /
3535 article /
3636 test /
37 - line
 37+ line /
 38+ hooks
3839
3940
4041
@@ -112,3 +113,18 @@
113114 end_test =
114115 "!!" ws? "end" ws? eol
115116
 117+
 118+hooks =
 119+ start_hooks text:text end_hooks
 120+{
 121+ return {
 122+ type: 'hooks',
 123+ text: text
 124+ }
 125+}
 126+
 127+start_hooks =
 128+ "!!" ws? "hooks" ":"? ws? eol
 129+
 130+end_hooks =
 131+ "!!" ws? "endhooks" ws? eol
Index: trunk/extensions/ParserPlayground/tests/parserTests.js
@@ -39,6 +39,9 @@
4040 global.PEG = _require('lib.pegjs.js');
4141
4242 // Our code...
 43+_import('mediawiki.parser.environment.js', ['MWParserEnvironment']);
 44+_import('ext.cite.taghook.ref.js', ['MWRefTagHook']);
 45+
4346 _import('ext.parserPlayground.serializer.js', ['MWTreeSerializer']);
4447 _import('ext.parserPlayground.renderer.js', ['MWTreeRenderer']);
4548 _import('ext.parserPlayground.pegParser.js', ['PegParser']);
@@ -47,10 +50,16 @@
4851 PegParser.src = fs.readFileSync(path.join(basePath, 'pegParser.pegjs.txt'), 'utf8');
4952
5053 var parser = new PegParser();
51 -var renderer = new MWTreeRenderer();
5254
 55+var testFileName = '../../../tests/parser/parserTests.txt'; // default
 56+if (process.argv.length > 2) {
 57+ // hack :D
 58+ testFileName = process.argv[2];
 59+ console.log(testFileName);
 60+}
 61+
5362 var testParser = PEG.buildParser(fs.readFileSync('parserTests.pegjs', 'utf8'));
54 -var testFile = fs.readFileSync('../../../tests/parser/parserTests.txt', 'utf8');
 63+var testFile = fs.readFileSync(testFileName, 'utf8');
5564
5665
5766 try {
@@ -108,6 +117,12 @@
109118 if (err) {
110119 console.log('PARSE FAIL', err);
111120 } else {
 121+ var environment = new MWParserEnvironment({
 122+ tagHooks: {
 123+ 'ref': MWRefTagHook
 124+ }
 125+ });
 126+ var renderer = new MWTreeRenderer(environment);
112127 renderer.treeToHtml(tree, function(node, err) {
113128 if (err) {
114129 console.log('RENDER FAIL', err);
Index: trunk/extensions/ParserPlayground/ParserPlayground.php
@@ -61,6 +61,9 @@
6262
6363 $wgResourceModules['ext.parserPlayground'] = array(
6464 'scripts' => array(
 65+ 'mediawiki.parser.environment.js',
 66+ 'ext.cite.taghook.ref.js',
 67+
6568 'lib.jsdiff.js',
6669 'lib.pegjs.js',
6770 'jquery.nodetree.js',
Index: trunk/extensions/ParserPlayground/modules/ext.parserPlayground.renderer.js
@@ -1,9 +1,9 @@
22 /**
33 * @param {ParserContext} context
44 */
5 -function MWTreeRenderer(context) {
 5+function MWTreeRenderer(env) {
66 // whee
7 - this.context = context || {};
 7+ this.env = env || {};
88 }
99
1010 /**
@@ -45,6 +45,7 @@
4646 // A sequence of block-level elements...
4747 var page = $('<div class="parseNode"></div>');
4848 subParseArray(tree.content, page);
 49+ /*
4950 if (self.context.refs) {
5051 // We're at the end; drop all the remaining refs!
5152 subParseArray([{
@@ -52,6 +53,7 @@
5354 name: 'references'
5455 }], page);
5556 }
 57+ */
5658 node = page[0];
5759 break;
5860 case 'para':
@@ -111,7 +113,42 @@
112114 t.append('}}');
113115 node = t[0];
114116 break;
 117+ case 'placeholder':
 118+ if ('content' in tree) {
 119+ var $place = $('<span>'); // hmmmm
 120+ subParseArray(tree.content, $place);
 121+ node = $place[0];
 122+ }
 123+ break;
 124+ case 'span':
 125+ var $span = $('<span>');
 126+ if ('attrs' in tree) {
 127+ $.map(tree.attrs, function(val, key) {
 128+ $span.attr(key, val); // @fixme safety!
 129+ });
 130+ if ('content' in tree) {
 131+ subParseArray(tree.content, $span);
 132+ }
 133+ }
 134+ node = $span[0];
 135+ break;
 136+ case 'hashlink':
 137+ var $a = $('<a>');
 138+ $a.attr('href', '#' + tree.target);
 139+ subParseArray(tree.content, $a);
 140+ node = $a[0];
 141+ break;
115142 case 'ext':
 143+ var hook = this.env.getTagHook(tree.name);
 144+ if (!hook) {
 145+ console.log('kabooom! no ext ' + tree.name)
 146+ }
 147+ var transformed = hook.execute(tree);
 148+ var $ext = $('<span>'); // hmmmm
 149+ subParseArray([transformed], $ext);
 150+ node = $ext[0];
 151+ // @fixme
 152+ /*
116153 if (tree.name == 'ref') {
117154 // Save the reference for later!
118155 // @fixme names etc?
@@ -176,6 +213,7 @@
177214 callback(null, 'Unrecognized extension in parse tree');
178215 return;
179216 }
 217+ */
180218 break;
181219 case 'comment':
182220 var h = $('<span class="parseNode comment"></span>').text('<!--' + tree.text + '-->');
Index: trunk/extensions/ParserPlayground/modules/ext.cite.taghook.ref.js
@@ -0,0 +1,160 @@
 2+/**
 3+ * The ref / references tags don't do any fancy HTML, so we can actually
 4+ * implement this in terms of parse tree manipulations, skipping the need
 5+ * for renderer-specific plugins as well.
 6+ *
 7+ * Pretty neat huh!
 8+ */
 9+
 10+MWRefTagHook = function( env ) {
 11+ if (!('cite' in env)) {
 12+ env.cite = {
 13+ refGroups: {}
 14+ };
 15+ }
 16+ var refGroups = env.cite.refGroups;
 17+
 18+ var getRefGroup = function(group) {
 19+ if (!(group in refGroups)) {
 20+ var refs = [],
 21+ byName = {};
 22+ refGroups[group] = {
 23+ refs: refs,
 24+ byName: byName,
 25+ add: function(node, options) {
 26+ var ref;
 27+ if (options.name && options.name in byName) {
 28+ ref = byName[options.name];
 29+ } else {
 30+ var n = refs.length;
 31+ var key = n + '';
 32+ if (options.name) {
 33+ key = options.name + '-' + key;
 34+ }
 35+ ref = {
 36+ node: node,
 37+ index: n,
 38+ groupIndex: n, // @fixme
 39+ name: options.name,
 40+ group: options.group,
 41+ key: key,
 42+ target: 'cite_note-' + key,
 43+ linkbacks: []
 44+ };
 45+ refs[n] = ref;
 46+ if (options.name) {
 47+ byName[options.name] = ref;
 48+ }
 49+ }
 50+ ref.linkbacks.push(
 51+ 'cite_ref-' + ref.key + '-' + ref.linkbacks.length
 52+ );
 53+ return ref;
 54+ }
 55+ }
 56+ }
 57+ return refGroups[group];
 58+ };
 59+
 60+ this.execute = function( node ) {
 61+ var options = $.extend({
 62+ name: null,
 63+ group: null
 64+ }, node.params);
 65+
 66+ var group = getRefGroup(options.group);
 67+ var ref = group.add(node, options);
 68+ var linkback = ref.linkbacks[ref.linkbacks.length - 1];
 69+
 70+ var bits = []
 71+ if (options.group) {
 72+ bits.push(options.group);
 73+ }
 74+ bits.push(env.formatNum( ref.groupIndex + 1 ));
 75+
 76+ return {
 77+ type: 'span',
 78+ attrs: {
 79+ id: linkback,
 80+ 'class': 'reference'
 81+ },
 82+ content: [
 83+ {
 84+ type: 'hashlink',
 85+ target: '#' + ref.target,
 86+ content: [
 87+ '[' + bits.join(' ') + ']'
 88+ ]
 89+ },
 90+ ],
 91+ origNode: node
 92+ };
 93+ };
 94+};
 95+
 96+MWReferencesTagHook = function( env ) {
 97+ var refGroups = env.cite.refGroups;
 98+
 99+ var arrow = '↑';
 100+ var renderLine = function( ref ) {
 101+ var out = {
 102+ type: 'li',
 103+ attrs: {
 104+ id: 'cite-note-' + ref.target
 105+ },
 106+ content: []
 107+ };
 108+ if (ref.linkbacks.length == 1) {
 109+ out.content.push({
 110+ type: 'hashlink',
 111+ target: '#' + ref.linkbacks[0],
 112+ content: [
 113+ arrow
 114+ ]
 115+ })
 116+ } else {
 117+ out.content.push(arrow)
 118+ $.each(ref.linkbacks, function(i, linkback) {
 119+ out.contents.push({
 120+ type: 'hashlink',
 121+ target: '#' + ref.linkbacks[0],
 122+ content: [
 123+ env.formatNum( ref.groupIndex + '.' + i)
 124+ ]
 125+ });
 126+ })
 127+ }
 128+ out.content.push(' ');
 129+ out.content.push({
 130+ type: 'placeholder',
 131+ content: ref.node.content
 132+ });
 133+ return out;
 134+ };
 135+ this.execute = function( node ) {
 136+ var options = $.extend({
 137+ name: null,
 138+ group: null
 139+ }, node.params);
 140+ if (options.group in refGroups) {
 141+ var group = refGroups[options.group];
 142+ return {
 143+ type: 'ol',
 144+ attrs: {
 145+ 'class': 'references'
 146+ },
 147+ content: $.each(group, renderLine),
 148+ origNode: node
 149+ }
 150+ } else {
 151+ return {
 152+ type: 'placeholder',
 153+ origNode: node
 154+ }
 155+ }
 156+ }
 157+}
 158+
 159+if (typeof module == "object") {
 160+ module.exports.MWRefTagHook = MWRefTagHook;
 161+}
Index: trunk/extensions/ParserPlayground/modules/ext.parserPlayground.js
@@ -124,12 +124,17 @@
125125 var pp = context.parserPlayground;
126126 pp.parser = new parserClass();
127127 // hack
 128+ pp.env = new MWParserEnvironment({
 129+ tagHooks: {
 130+ 'ref': MWRefTagHook
 131+ }
 132+ });
128133 if (pp.parser instanceof MediaWikiParser) {
129134 pp.serializer = pp.parser;
130135 pp.renderer = pp.parser;
131136 } else {
132137 pp.serializer = new MWTreeSerializer();
133 - pp.renderer = new MWTreeRenderer();
 138+ pp.renderer = new MWTreeRenderer(pp.env);
134139 }
135140 context.parserPlayground.fn.initDisplay();
136141 $.cookie('pp-editmode', className, {
Index: trunk/extensions/ParserPlayground/modules/mediawiki.parser.environment.js
@@ -0,0 +1,86 @@
 2+var MWParserEnvironment = function(opts) {
 3+ var options = {
 4+ tagHooks: {},
 5+ parserFunctions: {}
 6+ };
 7+ $.extend(options, opts);
 8+ this.tagHooks = options.tagHooks;
 9+ this.parserFunctions = options.parserFunctions;
 10+};
 11+
 12+$.extend(MWParserEnvironment.prototype, {
 13+ // Does this need separate UI/content inputs?
 14+ formatNum: function( num ) {
 15+ return num + '';
 16+ },
 17+
 18+ getVariable: function( varname, options ) {
 19+ //
 20+ },
 21+
 22+ /**
 23+ * @return MWParserFunction
 24+ */
 25+ getParserFunction: function( name ) {
 26+ if (name in this.parserFunctions) {
 27+ return new this.parserFunctions[name]( this );
 28+ } else {
 29+ return null;
 30+ }
 31+ },
 32+
 33+ /**
 34+ * @return MWParserTagHook
 35+ */
 36+ getTagHook: function( name ) {
 37+ if (name in this.tagHooks) {
 38+ return new this.tagHooks[name](this);
 39+ } else {
 40+ return null;
 41+ }
 42+ }
 43+
 44+});
 45+
 46+
 47+
 48+/**
 49+ * @parm MWParserEnvironment env
 50+ * @constructor
 51+ */
 52+MWParserTagHook = function( env ) {
 53+ if (!env) {
 54+ throw new Error( 'Tag hook requires a parser environment.' );
 55+ }
 56+ this.env = env;
 57+};
 58+
 59+/**
 60+ * @param string text (or a parse tree?)
 61+ * @param object params map of named parameters (strings or parse frames?)
 62+ * @return either a string or a parse frame -- finalize this?
 63+ */
 64+MWParserTagHook.execute = function( text, params ) {
 65+ return '';
 66+};
 67+
 68+
 69+MWParserFunction = function( env) {
 70+ if (!env) {
 71+ throw new Error( 'Parser funciton requires a parser environment.');
 72+ }
 73+ this.env = env;
 74+};
 75+
 76+/**
 77+ * @param string text (or a parse tree?)
 78+ * @param object params map of named parameters (strings or parse frames?)
 79+ * @return either a string or a parse frame -- finalize this?
 80+ */
 81+MWParserFunction.execute = function( text, params ) {
 82+ return '';
 83+};
 84+
 85+if (typeof module == "object") {
 86+ module.exports.MWParserEnvironment = MWParserEnvironment;
 87+}
Index: trunk/extensions/ParserPlayground/modules/pegParser.pegjs.txt
@@ -239,8 +239,9 @@
240240
241241 ref = ref_full / ref_empty
242242
 243+/* Can we do backreferences to genericize this? */
243244 ref_full
244 - = start:ref_start ">" content:ref_content+ close:ref_end {
 245+ = start:ref_start ">" content:ref_content* close:ref_end {
245246 return {
246247 type: 'ext',
247248 name: 'ref',

Status & tagging log