2
|''Name''|tiddlywiki.js|
3
|''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror|
6
|''Status''|''stable''|
7
|''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|
8
|''Documentation''|http://codemirror.tiddlyspace.com/|
9
|''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
10
|''CoreVersion''|2.5.0|
11
|''Requires''|codemirror.js|
12
|''Keywords''|syntax highlighting color code mirror codemirror|
14
CoreVersion parameter is needed for TiddlyWiki only!
17
CodeMirror.defineMode("tiddlywiki", function (config, parserConfig) {
18
var indentUnit = config.indentUnit;
21
var textwords = function () {
31
var keywords = function () {
33
return { type: type, style: "macro"};
36
"allTags": kw('allTags'), "closeAll": kw('closeAll'), "list": kw('list'),
37
"newJournal": kw('newJournal'), "newTiddler": kw('newTiddler'),
38
"permaview": kw('permaview'), "saveChanges": kw('saveChanges'),
39
"search": kw('search'), "slider": kw('slider'), "tabs": kw('tabs'),
40
"tag": kw('tag'), "tagging": kw('tagging'), "tags": kw('tags'),
41
"tiddler": kw('tiddler'), "timeline": kw('timeline'),
42
"today": kw('today'), "version": kw('version'), "option": kw('option'),
45
"filter": kw('filter')
49
var isSpaceName = /[\w_\-]/i,
50
reHR = /^\-\-\-\-+$/, // <hr>
51
reWikiCommentStart = /^\/\*\*\*$/, // /***
52
reWikiCommentStop = /^\*\*\*\/$/, // ***/
53
reBlockQuote = /^<<<$/,
55
reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start
56
reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop
57
reXmlCodeStart = /^<!--\{\{\{-->$/, // xml block start
58
reXmlCodeStop = /^<!--\}\}\}-->$/, // xml stop
60
reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start
61
reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop
63
reCodeStart = /\{\{\{/, // {{{ code span start
64
reUntilCodeStop = /.*?\}\}\}/;
66
function chain(stream, state, f) {
68
return f(stream, state);
72
function nextUntilUnescaped(stream, end) {
75
while ((next = stream.next()) != null) {
76
if (next == end && !escaped) return false;
77
escaped = !escaped && next == "\\";
82
// Used as scratch variables to communicate multiple values without
83
// consing up tons of objects.
86
function ret(tp, style, cont) {
92
function jsTokenBase(stream, state) {
93
var sol = stream.sol(),
96
state.block = false; // indicates the start of a code block.
98
ch = stream.peek(); // don't eat, to make matching simpler
100
// check start of blocks
101
if (sol && /[<\/\*{}\-]/.test(ch)) {
102
if (stream.match(reCodeBlockStart)) {
104
return chain(stream, state, twTokenCode);
106
if (stream.match(reBlockQuote)) {
107
return ret('quote', 'quote');
109
if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) {
110
return ret('code', 'code');
112
if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) {
113
return ret('code', 'code');
115
if (stream.match(reHR)) {
116
return ret('hr', 'hr');
121
if (sol && /[\/\*!#;:>|]/.test(ch)) {
122
if (ch == "!") { // tw header
124
return ret("header", "header");
126
if (ch == "*") { // tw list
127
stream.eatWhile('*');
128
return ret("list", "list");
130
if (ch == "#") { // tw numbered list
131
stream.eatWhile('#');
132
return ret("list", "list");
134
if (ch == ";") { // definition list, term
135
stream.eatWhile(';');
136
return ret("list", "list");
138
if (ch == ":") { // definition list, description
139
stream.eatWhile(':');
140
return ret("list", "list");
142
if (ch == ">") { // single line quote
143
stream.eatWhile(">");
144
return ret("quote", "quote");
147
return ret('table', 'table');
151
if (ch == '{' && stream.match(/\{\{/)) {
152
return chain(stream, state, twTokenCode);
155
// rudimentary html:// file:// link matching. TW knows much more ...
156
if (/[hf]/i.test(ch)) {
157
if (/[ti]/i.test(stream.peek()) && stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i)) {
158
return ret("link-external", "link-external");
161
// just a little string indicator, don't want to have the whole string covered
163
return ret('string', 'string');
165
if (ch == '~') { // _no_ CamelCase indicator should be bold
166
return ret('text', 'brace');
168
if (/[\[\]]/.test(ch)) { // check for [[..]]
169
if (stream.peek() == ch) {
171
return ret('brace', 'brace');
174
if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting
175
stream.eatWhile(isSpaceName);
176
return ret("link-external", "link-external");
178
if (/\d/.test(ch)) { // numbers
179
stream.eatWhile(/\d/);
180
return ret("number", "number");
182
if (ch == "/") { // tw invisible comment
183
if (stream.eat("%")) {
184
return chain(stream, state, twTokenComment);
186
else if (stream.eat("/")) { //
187
return chain(stream, state, twTokenEm);
190
if (ch == "_") { // tw underline
191
if (stream.eat("_")) {
192
return chain(stream, state, twTokenUnderline);
195
// strikethrough and mdash handling
197
if (stream.eat("-")) {
198
// if strikethrough looks ugly, change CSS.
199
if (stream.peek() != ' ')
200
return chain(stream, state, twTokenStrike);
202
if (stream.peek() == ' ')
203
return ret('text', 'brace');
206
if (ch == "'") { // tw bold
207
if (stream.eat("'")) {
208
return chain(stream, state, twTokenStrong);
211
if (ch == "<") { // tw macro
212
if (stream.eat("<")) {
213
return chain(stream, state, twTokenMacro);
220
// core macro handling
221
stream.eatWhile(/[\w\$_]/);
222
var word = stream.current(),
223
known = textwords.propertyIsEnumerable(word) && textwords[word];
225
return known ? ret(known.type, known.style, word) : ret("text", null, word);
229
function twTokenString(quote) {
230
return function (stream, state) {
231
if (!nextUntilUnescaped(stream, quote)) state.tokenize = jsTokenBase;
232
return ret("string", "string");
236
// tw invisible comment
237
function twTokenComment(stream, state) {
238
var maybeEnd = false,
240
while (ch = stream.next()) {
241
if (ch == "/" && maybeEnd) {
242
state.tokenize = jsTokenBase;
245
maybeEnd = (ch == "%");
247
return ret("comment", "comment");
251
function twTokenStrong(stream, state) {
252
var maybeEnd = false,
254
while (ch = stream.next()) {
255
if (ch == "'" && maybeEnd) {
256
state.tokenize = jsTokenBase;
259
maybeEnd = (ch == "'");
261
return ret("text", "strong");
265
function twTokenCode(stream, state) {
266
var ch, sb = state.block;
268
if (sb && stream.current()) {
269
return ret("code", "code");
272
if (!sb && stream.match(reUntilCodeStop)) {
273
state.tokenize = jsTokenBase;
274
return ret("code", "code-inline");
277
if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
278
state.tokenize = jsTokenBase;
279
return ret("code", "code");
283
return (sb) ? ret("code", "code") : ret("code", "code-inline");
287
function twTokenEm(stream, state) {
288
var maybeEnd = false,
290
while (ch = stream.next()) {
291
if (ch == "/" && maybeEnd) {
292
state.tokenize = jsTokenBase;
295
maybeEnd = (ch == "/");
297
return ret("text", "em");
300
// tw underlined text
301
function twTokenUnderline(stream, state) {
302
var maybeEnd = false,
304
while (ch = stream.next()) {
305
if (ch == "_" && maybeEnd) {
306
state.tokenize = jsTokenBase;
309
maybeEnd = (ch == "_");
311
return ret("text", "underlined");
314
// tw strike through text looks ugly
315
// change CSS if needed
316
function twTokenStrike(stream, state) {
317
var maybeEnd = false,
320
while (ch = stream.next()) {
321
if (ch == "-" && maybeEnd) {
322
state.tokenize = jsTokenBase;
325
maybeEnd = (ch == "-");
327
return ret("text", "line-through");
331
function twTokenMacro(stream, state) {
332
var ch, tmp, word, known;
334
if (stream.current() == '<<') {
335
return ret('brace', 'macro');
340
state.tokenize = jsTokenBase;
344
if (stream.peek() == '>') {
346
state.tokenize = jsTokenBase;
347
return ret("brace", "macro");
351
stream.eatWhile(/[\w\$_]/);
352
word = stream.current();
353
known = keywords.propertyIsEnumerable(word) && keywords[word];
356
return ret(known.type, known.style, word);
359
return ret("macro", null, word);
365
startState: function (basecolumn) {
367
tokenize: jsTokenBase,
373
token: function (stream, state) {
374
if (stream.eatSpace()) return null;
375
var style = state.tokenize(stream, state);
383
CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");