From 92353d37b20cba9f7cff9f3d6f959951a83f1b05 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sat, 26 May 2012 18:30:32 +0100 Subject: [PATCH] First pass at a new wiki text parser This one respects HTML paragraphs properly --- .../newwikitextparser/blockrules/class.js | 55 ++++++ .../newwikitextparser/blockrules/heading.js | 25 +++ .../newwikitextparser/blockrules/html.js | 54 ++++++ .../newwikitextparser/blockrules/list.js | 87 +++++++++ .../newwikitextparser/blockrules/rule.js | 24 +++ .../newwikitextparser/newwikitextparser.js | 182 ++++++++++++++++++ .../newwikitextparser/runrules/wikilink.js | 54 ++++++ core/modules/treenodes/element.js | 8 + readme.md | 4 +- tw5.com/tiddlers/HelloThere.tid | 2 +- tw5.com/tiddlers/Introduction.tid | 3 +- tw5.com/tiddlers/TestingNewWikiText.tid | 77 ++++++++ 12 files changed, 570 insertions(+), 5 deletions(-) create mode 100644 core/modules/parsers/newwikitextparser/blockrules/class.js create mode 100644 core/modules/parsers/newwikitextparser/blockrules/heading.js create mode 100644 core/modules/parsers/newwikitextparser/blockrules/html.js create mode 100644 core/modules/parsers/newwikitextparser/blockrules/list.js create mode 100644 core/modules/parsers/newwikitextparser/blockrules/rule.js create mode 100644 core/modules/parsers/newwikitextparser/newwikitextparser.js create mode 100644 core/modules/parsers/newwikitextparser/runrules/wikilink.js create mode 100644 tw5.com/tiddlers/TestingNewWikiText.tid diff --git a/core/modules/parsers/newwikitextparser/blockrules/class.js b/core/modules/parsers/newwikitextparser/blockrules/class.js new file mode 100644 index 000000000..ff95bf6aa --- /dev/null +++ b/core/modules/parsers/newwikitextparser/blockrules/class.js @@ -0,0 +1,55 @@ +/*\ +title: $:/core/modules/parsers/newwikitextparser/blockrules/class.js +type: application/javascript +module-type: wikitextblockrule + +Wiki text block rule for assigning classes to paragraphs and other blocks + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.name = "class"; + +exports.regExpString = "\\{\\{(?:[^\\{\\r\\n]*)\\{$"; + +exports.parse = function(match) { + var tree = [], + reStart = /\{\{([^\{\r\n]*){(?:\r?\n)?/mg, + reEnd = /(\}\}\}$(?:\r?\n)?)/mg, + endMatch; + reStart.lastIndex = this.pos; + match = reStart.exec(this.source); + if(match) { + this.pos = match.index + match[0].length; + // Skip any whitespace + this.skipWhitespace(); + // Check if we've got the end marker + reEnd.lastIndex = this.pos; + endMatch = reEnd.exec(this.source); + // Parse the text into blocks + while(this.pos < this.sourceLength && !(endMatch && endMatch.index === this.pos)) { + var blocks = this.parseBlock(); + for(var t=0; t]*)>/mg, + reAttr = /\s*([A-Za-z\-_]+)(?:\s*=\s*(?:("[^"]*")|('[^']*')|([^"'\s]+)))?/mg; + reStart.lastIndex = this.pos; + var startMatch = reStart.exec(this.source); + if(startMatch && startMatch.index === this.pos) { + var attrMatch = reAttr.exec(startMatch[2]), + attributes = {}; + while(attrMatch) { + var name = attrMatch[1], + value; + if(attrMatch[2]) { // Double quoted + value = attrMatch[2].substring(1,attrMatch[2].length-1); + } else if(attrMatch[3]) { // Single quoted + value = attrMatch[3].substring(1,attrMatch[3].length-1); + } else if(attrMatch[4]) { // Unquoted + value = attrMatch[4]; + } else { // Valueless + value = true; // TODO: We should have a way of indicating we want an attribute without a value + } + attributes[name] = value; + attrMatch = reAttr.exec(startMatch[2]); + } + this.pos = startMatch.index + startMatch[0].length; + var reEnd = new RegExp("()","mg"), + element = $tw.Tree.Element(startMatch[1],attributes,this.parseRun(reEnd)); + reEnd.lastIndex = this.pos; + match = reEnd.exec(this.source); + if(match && match.index === this.pos) { + this.pos = match.index + match[0].length; + } + return [element]; + } +}; + +})(); diff --git a/core/modules/parsers/newwikitextparser/blockrules/list.js b/core/modules/parsers/newwikitextparser/blockrules/list.js new file mode 100644 index 000000000..d5f250ca0 --- /dev/null +++ b/core/modules/parsers/newwikitextparser/blockrules/list.js @@ -0,0 +1,87 @@ +/*\ +title: $:/core/modules/parsers/newwikitextparser/blockrules/list.js +type: application/javascript +module-type: wikitextblockrule + +Wiki text block rule for lists. + + + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.name = "list"; + +exports.regExpString = "[\\*#;:]+"; + +var listTypes = { + "*": {listTag: "ul", itemTag: "li"}, + "#": {listTag: "ol", itemTag: "li"}, + ";": {listTag: "dl", itemTag: "dt"}, + ":": {listTag: "dl", itemTag: "dd"} +}; + +/* + +*/ +exports.parse = function(match) { + var listStack = [], // Array containing list elements for the previous row in the list + t, listInfo, listElement, itemElement, previousRootListTag; + // Cycle through the rows in the list + do { + // Walk through the list markers for the current row + for(t=0; t t && listStack[t].type !== listInfo.listTag) { + listStack.splice(t,listStack.length - t); + } + // Construct the list element or reuse the previous one at this level + if(listStack.length <= t) { + listElement = $tw.Tree.Element(listInfo.listTag,{},[$tw.Tree.Element(listInfo.itemTag,{},[])]); + // Link this list element into the last child item of the parent list item + if(t) { + var prevListItem = listStack[t-1].children[listStack[t-1].children.length-1]; + prevListItem.children.push(listElement); + } + // Save this element in the stack + listStack[t] = listElement; + } else if(t === (match[0].length - 1)) { + listStack[t].children.push($tw.Tree.Element(listInfo.itemTag,{},[])); + } + } + if(listStack.length > match[0].length) { + listStack.splice(match[0].length,listStack.length - match[0].length); + } + // Skip the list markers + this.pos = match.index + match[0].length; + // Process the body of the list item into the last list item + var lastListInfo = listTypes[match[0].charAt(match[0].length-1)], + lastListChildren = listStack[listStack.length-1].children, + lastListItem = lastListChildren[lastListChildren.length-1], + classedRun = this.parseClassedRun(/(\r?\n)/mg); + for(t=0; t terminatorMatch.index) { + if(terminatorMatch.index > this.pos) { + tree.push($tw.Tree.Text(this.source.substring(this.pos,terminatorMatch.index))); + } + this.pos = terminatorMatch.index; + return tree; + } + } + // Process any run rule, along with the text preceding it + if(runRuleMatch) { + // Preceding text + if(runRuleMatch.index > this.pos) { + tree.push($tw.Tree.Text(this.source.substring(this.pos,runRuleMatch.index))); + this.pos = runRuleMatch.index; + } + // Process the run rule + var rule; + for(var t=0; t 0) { + var preRegExp = new RegExp(textPrimitives.anyLetterStrict,"mg"); + preRegExp.lastIndex = match.index-1; + var preMatch = preRegExp.exec(this.source); + if(preMatch && preMatch.index === match.index-1) { + return [$tw.Tree.Text(match[0])]; + } + } + var macroNode = $tw.Tree.Macro("link",{to: match[0]},[$tw.Tree.Text(match[0])],this.wiki); + this.dependencies.mergeDependencies(macroNode.dependencies); + return [macroNode]; +}; + +})(); diff --git a/core/modules/treenodes/element.js b/core/modules/treenodes/element.js index eb1d44aa2..095936243 100644 --- a/core/modules/treenodes/element.js +++ b/core/modules/treenodes/element.js @@ -144,6 +144,14 @@ Element.prototype.broadcastEvent = function(event) { return true; }; +Element.prototype.addClass = function(className) { + if(typeof this.attributes["class"] === "string") { + this.attributes["class"] = this.attributes["class"].split(" "); + } + this.attributes["class"] = this.attributes["class"] || []; + this.attributes["class"].push(className); +}; + exports.Element = Element; })(); diff --git a/readme.md b/readme.md index e7efdfde8..37dbb3806 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,4 @@ -

Welcome to TiddlyWiki5

Welcome to TiddlyWiki5, a reboot of TiddlyWiki, the venerable, reusable non-linear personal web notebook first released in 2004. It is a complete interactive wiki that can run from a single HTML file in the browser or as a powerful [[node.js application|What is node.js?]]. - -TiddlyWiki5 is currently at version <<version>> and is under active development, which is to say that it is useful but incomplete. You can try out the online prototype at http://tiddlywiki.com/tiddlywiki5, [[try out the command line incarnation|TryingOutTiddlyWiki]], get involved in the [[development on GitHub|https://github.com/Jermolene/TiddlyWiki5]] or join the discussions on [[the TiddlyWikiDev Google Group|http://groups.google.com/group/TiddlyWikiDev]].

Usage

TiddlyWiki5 can be used on the command line to perform an extensive set of operations based on tiddlers, TiddlerFiles and TiddlyWikiFiles. For example, this loads the tiddlers from a TiddlyWiki HTML file and then saves one of them in HTML:
node core/boot.js --verbose --load mywiki.html --savetiddler ReadMe ./readme.html
+

Welcome to TiddlyWiki5

Welcome to TiddlyWiki5, a reboot of TiddlyWiki, the venerable, reusable non-linear personal web notebook first released in 2004. It is a complete interactive wiki that can run from a single HTML file in the browser or as a powerful node.js application.

TiddlyWiki5 is currently at version 5.0.0.a2 and is under active development, which is to say that it is useful but incomplete. You can try out the online prototype at http://tiddlywiki.com/tiddlywiki5, try out the command line incarnation, get involved in the development on GitHub or join the discussions on the TiddlyWikiDev Google Group.

Usage

TiddlyWiki5 can be used on the command line to perform an extensive set of operations based on tiddlers, TiddlerFiles and TiddlyWikiFiles. For example, this loads the tiddlers from a TiddlyWiki HTML file and then saves one of them in HTML:
node core/boot.js --verbose --load mywiki.html --savetiddler ReadMe ./readme.html
 

Usage

Running boot.js from the command line boots the TiddlyWiki kernel, loads the core plugins and establishes an empty wiki store. It then sequentially processes the command line arguments from left to right. The arguments are separated with spaces. The commands are identified by the prefix --.
node core/boot.js [--<option> [<arg>[,<arg>]]]
 

Commands

The following commands are available.

load

Load tiddlers from 2.x.x TiddlyWiki files (.html), .tiddler, .tid, .json or other files
--load <filepath>
 

savetiddler

Save an individual tiddler as a specified MIME type, defaults to text/html
--savetiddler <title> <filename> [<type>]
diff --git a/tw5.com/tiddlers/HelloThere.tid b/tw5.com/tiddlers/HelloThere.tid
index b32c5d4bf..4f0fde0f9 100644
--- a/tw5.com/tiddlers/HelloThere.tid
+++ b/tw5.com/tiddlers/HelloThere.tid
@@ -1,7 +1,7 @@
 title: HelloThere
 modifier: JeremyRuston
 tags: introduction
-type: text/x-tiddlywiki-new
+type: text/x-tiddlywiki
 
 Welcome to TiddlyWiki5, a reboot of TiddlyWiki, the venerable, reusable non-linear personal web notebook first released in 2004. It is a complete interactive wiki that can run from a single HTML file in the browser or as a powerful [[node.js application|What is node.js?]].
 
diff --git a/tw5.com/tiddlers/Introduction.tid b/tw5.com/tiddlers/Introduction.tid
index 3aab403b6..6ec2da34b 100644
--- a/tw5.com/tiddlers/Introduction.tid
+++ b/tw5.com/tiddlers/Introduction.tid
@@ -19,6 +19,7 @@ Learning more about TiddlyWiki5:
 
 Some useful tiddlers for feature testing:
 * HelloThere
+* TestingNewWikiText shows off the embryonic new wiki text engine
 * ImageTests showing different ways of embedding images
 * SampleData showing how JSON tiddlers are handled
 * SampleJavaScript and SampleJavaScriptWithError showing how JavaScript code is displayed
@@ -33,7 +34,7 @@ Technical documentation includes:
 * Overview of TiddlyWikiArchitecture
 ** MacroInternals
 * Information about TiddlerFiles and RecipeFiles
-* NewWikiTextFeatures
+* A discussion of potential NewWikiTextFeatures
 
 All tiddlers:
 <>
\ No newline at end of file
diff --git a/tw5.com/tiddlers/TestingNewWikiText.tid b/tw5.com/tiddlers/TestingNewWikiText.tid
new file mode 100644
index 000000000..386622126
--- /dev/null
+++ b/tw5.com/tiddlers/TestingNewWikiText.tid
@@ -0,0 +1,77 @@
+title: TestingNewWikiText
+type: text/x-tiddlywiki-new
+
+! This is a heading
+
+HelloThere
+
+One two three four. With a link to HelloThere. And a link to TiddlyWiki and TiddlyWiki5. And a suppressed link to ~HelloThere.
+
+! This is a new heading
+This is a paragraph
+immediately after
+that heading
+
+----
+* This is a list
+* Of lots of items
+** And subitems
+*# And nested numbered lists inside ordinary lists
+*# Yes
+*## Definitely
+*##* And then
+*##** back
+*##*** to items
+*## And back to numbers once more
+*# More numbering
+* And back to items
+
+----
+
+; definitionile
+: definitionate
+; definitionilisation
+: definitionatisative
+
+----
+
+This is a list with a class wrapped around it:
+
+{{myclass andanotherone{
+* One and one
+** Two and three
+* Four and five
+** Six and Seven
+}}}
+
+And here's another one:
+
+{{class1 class2{
+* Un et deux
+**{{class}} Two and three
+* Four and five
+** Trois et cinq
+}}}
+
+And here's a class wrapped around a heading:
+
+{{class1{
+! My heading
+}}}
+
+And here's a class assigned directly to a heading:
+
+!!!!{{class1}} My very beautiful heading
+
+---
+
+Here are some HTML paragraph blocks:
+
+
+This is my nice and simple block of text +
+ +And another: + +
This time the text is all squashed up, without line breaks
+