~ubuntu-branches/ubuntu/utopic/codemirror-js/utopic

« back to all changes in this revision

Viewing changes to mode/tiddlywiki/tiddlywiki.js

  • Committer: Package Import Robot
  • Author(s): David Paleino
  • Date: 2012-04-12 12:25:28 UTC
  • Revision ID: package-import@ubuntu.com-20120412122528-8xp5a8frj4h1d3ee
Tags: upstream-2.23
ImportĀ upstreamĀ versionĀ 2.23

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***
 
2
|''Name''|tiddlywiki.js|
 
3
|''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror|
 
4
|''Author''|PMario|
 
5
|''Version''|0.1.7|
 
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|
 
13
! Info
 
14
CoreVersion parameter is needed for TiddlyWiki only!
 
15
***/
 
16
//{{{
 
17
CodeMirror.defineMode("tiddlywiki", function (config, parserConfig) {
 
18
        var indentUnit = config.indentUnit;
 
19
 
 
20
        // Tokenizer
 
21
        var textwords = function () {
 
22
                function kw(type) {
 
23
                        return {
 
24
                                type: type,
 
25
                                style: "text"
 
26
                        };
 
27
                }
 
28
                return {};
 
29
        }();
 
30
 
 
31
        var keywords = function () {
 
32
                function kw(type) {
 
33
                        return { type: type, style: "macro"};
 
34
                }
 
35
                return {
 
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'),
 
43
 
 
44
                        "with": kw('with'),
 
45
                        "filter": kw('filter')
 
46
                };
 
47
        }();
 
48
 
 
49
        var isSpaceName = /[\w_\-]/i,
 
50
                reHR = /^\-\-\-\-+$/,                                   // <hr>
 
51
                reWikiCommentStart = /^\/\*\*\*$/,              // /***
 
52
                reWikiCommentStop = /^\*\*\*\/$/,               // ***/
 
53
                reBlockQuote = /^<<<$/,
 
54
 
 
55
                reJsCodeStart = /^\/\/\{\{\{$/,                 // //{{{ js block start
 
56
                reJsCodeStop = /^\/\/\}\}\}$/,                  // //}}} js stop
 
57
                reXmlCodeStart = /^<!--\{\{\{-->$/,             // xml block start
 
58
                reXmlCodeStop = /^<!--\}\}\}-->$/,              // xml stop
 
59
 
 
60
                reCodeBlockStart = /^\{\{\{$/,                  // {{{ TW text div block start
 
61
                reCodeBlockStop = /^\}\}\}$/,                   // }}} TW text stop
 
62
 
 
63
                reCodeStart = /\{\{\{/,                                 // {{{ code span start
 
64
                reUntilCodeStop = /.*?\}\}\}/;
 
65
 
 
66
        function chain(stream, state, f) {
 
67
                state.tokenize = f;
 
68
                return f(stream, state);
 
69
        }
 
70
 
 
71
        // used for strings
 
72
        function nextUntilUnescaped(stream, end) {
 
73
                var escaped = false,
 
74
                        next;
 
75
                while ((next = stream.next()) != null) {
 
76
                        if (next == end && !escaped) return false;
 
77
                        escaped = !escaped && next == "\\";
 
78
                }
 
79
                return escaped;
 
80
        }
 
81
 
 
82
        // Used as scratch variables to communicate multiple values without
 
83
        // consing up tons of objects.
 
84
        var type, content;
 
85
 
 
86
        function ret(tp, style, cont) {
 
87
                type = tp;
 
88
                content = cont;
 
89
                return style;
 
90
        }
 
91
 
 
92
        function jsTokenBase(stream, state) {
 
93
                var sol = stream.sol(), 
 
94
                        ch, tch;
 
95
                        
 
96
                state.block = false;    // indicates the start of a code block.
 
97
 
 
98
                ch = stream.peek();     // don't eat, to make matching simpler
 
99
                
 
100
                // check start of  blocks
 
101
                if (sol && /[<\/\*{}\-]/.test(ch)) {
 
102
                        if (stream.match(reCodeBlockStart)) {
 
103
                                state.block = true;
 
104
                                return chain(stream, state, twTokenCode);
 
105
                        }
 
106
                        if (stream.match(reBlockQuote)) {
 
107
                                return ret('quote', 'quote');
 
108
                        }
 
109
                        if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) {
 
110
                                return ret('code', 'code');
 
111
                        }
 
112
                        if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) {
 
113
                                return ret('code', 'code');
 
114
                        }
 
115
                        if (stream.match(reHR)) {
 
116
                                return ret('hr', 'hr');
 
117
                        }
 
118
                } // sol
 
119
                ch = stream.next();
 
120
 
 
121
                if (sol && /[\/\*!#;:>|]/.test(ch)) {
 
122
                        if (ch == "!") { // tw header
 
123
                                stream.skipToEnd();
 
124
                                return ret("header", "header");
 
125
                        }
 
126
                        if (ch == "*") { // tw list
 
127
                                stream.eatWhile('*');
 
128
                                return ret("list", "list");
 
129
                        }
 
130
                        if (ch == "#") { // tw numbered list
 
131
                                stream.eatWhile('#');
 
132
                                return ret("list", "list");
 
133
                        }
 
134
                        if (ch == ";") { // definition list, term
 
135
                                stream.eatWhile(';');
 
136
                                return ret("list", "list");
 
137
                        }
 
138
                        if (ch == ":") { // definition list, description
 
139
                                stream.eatWhile(':');
 
140
                                return ret("list", "list");
 
141
                        }
 
142
                        if (ch == ">") { // single line quote
 
143
                                stream.eatWhile(">");
 
144
                                return ret("quote", "quote");
 
145
                        }
 
146
                        if (ch == '|') {
 
147
                                return ret('table', 'table');
 
148
                        }
 
149
                }
 
150
 
 
151
                if (ch == '{' && stream.match(/\{\{/)) {
 
152
                        return chain(stream, state, twTokenCode);
 
153
                }
 
154
 
 
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");
 
159
                        }
 
160
                }
 
161
                // just a little string indicator, don't want to have the whole string covered
 
162
                if (ch == '"') {
 
163
                        return ret('string', 'string');
 
164
                }
 
165
                if (ch == '~') {        // _no_ CamelCase indicator should be bold
 
166
                        return ret('text', 'brace');
 
167
                }
 
168
                if (/[\[\]]/.test(ch)) { // check for [[..]]
 
169
                        if (stream.peek() == ch) {
 
170
                                stream.next();
 
171
                                return ret('brace', 'brace');
 
172
                        }
 
173
                }
 
174
                if (ch == "@") {        // check for space link. TODO fix @@...@@ highlighting
 
175
                        stream.eatWhile(isSpaceName);
 
176
                        return ret("link-external", "link-external");
 
177
                }
 
178
                if (/\d/.test(ch)) {    // numbers
 
179
                        stream.eatWhile(/\d/);
 
180
                        return ret("number", "number");
 
181
                }
 
182
                if (ch == "/") { // tw invisible comment
 
183
                        if (stream.eat("%")) {
 
184
                                return chain(stream, state, twTokenComment);
 
185
                        }
 
186
                        else if (stream.eat("/")) { // 
 
187
                                return chain(stream, state, twTokenEm);
 
188
                        }
 
189
                }
 
190
                if (ch == "_") { // tw underline
 
191
                        if (stream.eat("_")) {
 
192
                                return chain(stream, state, twTokenUnderline);
 
193
                        }
 
194
                }
 
195
                // strikethrough and mdash handling
 
196
                if (ch == "-") {
 
197
                        if (stream.eat("-")) {
 
198
                                // if strikethrough looks ugly, change CSS.
 
199
                                if (stream.peek() != ' ')
 
200
                                        return chain(stream, state, twTokenStrike);
 
201
                                // mdash
 
202
                                if (stream.peek() == ' ')
 
203
                                        return ret('text', 'brace');
 
204
                        }
 
205
                }
 
206
                if (ch == "'") { // tw bold
 
207
                        if (stream.eat("'")) {
 
208
                                return chain(stream, state, twTokenStrong);
 
209
                        }
 
210
                }
 
211
                if (ch == "<") { // tw macro
 
212
                        if (stream.eat("<")) {
 
213
                                return chain(stream, state, twTokenMacro);
 
214
                        }
 
215
                }
 
216
                else {
 
217
                        return ret(ch);
 
218
                }
 
219
 
 
220
                // core macro handling
 
221
                stream.eatWhile(/[\w\$_]/);
 
222
                var word = stream.current(),
 
223
                        known = textwords.propertyIsEnumerable(word) && textwords[word];
 
224
 
 
225
                return known ? ret(known.type, known.style, word) : ret("text", null, word);
 
226
 
 
227
        } // jsTokenBase()
 
228
 
 
229
        function twTokenString(quote) {
 
230
                return function (stream, state) {
 
231
                        if (!nextUntilUnescaped(stream, quote)) state.tokenize = jsTokenBase;
 
232
                        return ret("string", "string");
 
233
                };
 
234
        }
 
235
 
 
236
        // tw invisible comment
 
237
        function twTokenComment(stream, state) {
 
238
                var maybeEnd = false,
 
239
                        ch;
 
240
                while (ch = stream.next()) {
 
241
                        if (ch == "/" && maybeEnd) {
 
242
                                state.tokenize = jsTokenBase;
 
243
                                break;
 
244
                        }
 
245
                        maybeEnd = (ch == "%");
 
246
                }
 
247
                return ret("comment", "comment");
 
248
        }
 
249
 
 
250
        // tw strong / bold
 
251
        function twTokenStrong(stream, state) {
 
252
                var maybeEnd = false,
 
253
                        ch;
 
254
                while (ch = stream.next()) {
 
255
                        if (ch == "'" && maybeEnd) {
 
256
                                state.tokenize = jsTokenBase;
 
257
                                break;
 
258
                        }
 
259
                        maybeEnd = (ch == "'");
 
260
                }
 
261
                return ret("text", "strong");
 
262
        }
 
263
 
 
264
        // tw code
 
265
        function twTokenCode(stream, state) {
 
266
                var ch, sb = state.block;
 
267
                
 
268
                if (sb && stream.current()) {
 
269
                        return ret("code", "code");
 
270
                }
 
271
 
 
272
                if (!sb && stream.match(reUntilCodeStop)) {
 
273
                        state.tokenize = jsTokenBase;
 
274
                        return ret("code", "code-inline");
 
275
                }
 
276
 
 
277
                if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
 
278
                        state.tokenize = jsTokenBase;
 
279
                        return ret("code", "code");
 
280
                }
 
281
 
 
282
                ch = stream.next();
 
283
                return (sb) ? ret("code", "code") : ret("code", "code-inline");
 
284
        }
 
285
 
 
286
        // tw em / italic
 
287
        function twTokenEm(stream, state) {
 
288
                var maybeEnd = false,
 
289
                        ch;
 
290
                while (ch = stream.next()) {
 
291
                        if (ch == "/" && maybeEnd) {
 
292
                                state.tokenize = jsTokenBase;
 
293
                                break;
 
294
                        }
 
295
                        maybeEnd = (ch == "/");
 
296
                }
 
297
                return ret("text", "em");
 
298
        }
 
299
 
 
300
        // tw underlined text
 
301
        function twTokenUnderline(stream, state) {
 
302
                var maybeEnd = false,
 
303
                        ch;
 
304
                while (ch = stream.next()) {
 
305
                        if (ch == "_" && maybeEnd) {
 
306
                                state.tokenize = jsTokenBase;
 
307
                                break;
 
308
                        }
 
309
                        maybeEnd = (ch == "_");
 
310
                }
 
311
                return ret("text", "underlined");
 
312
        }
 
313
 
 
314
        // tw strike through text looks ugly
 
315
        // change CSS if needed
 
316
        function twTokenStrike(stream, state) {
 
317
                var maybeEnd = false,
 
318
                        ch, nr;
 
319
                        
 
320
                while (ch = stream.next()) {
 
321
                        if (ch == "-" && maybeEnd) {
 
322
                                state.tokenize = jsTokenBase;
 
323
                                break;
 
324
                        }
 
325
                        maybeEnd = (ch == "-");
 
326
                }
 
327
                return ret("text", "line-through");
 
328
        }
 
329
 
 
330
        // macro
 
331
        function twTokenMacro(stream, state) {
 
332
                var ch, tmp, word, known;
 
333
 
 
334
                if (stream.current() == '<<') {
 
335
                        return ret('brace', 'macro');
 
336
                }
 
337
 
 
338
                ch = stream.next();
 
339
                if (!ch) {
 
340
                        state.tokenize = jsTokenBase;
 
341
                        return ret(ch);
 
342
                }
 
343
                if (ch == ">") {
 
344
                        if (stream.peek() == '>') {
 
345
                                stream.next();
 
346
                                state.tokenize = jsTokenBase;
 
347
                                return ret("brace", "macro");
 
348
                        }
 
349
                }
 
350
 
 
351
                stream.eatWhile(/[\w\$_]/);
 
352
                word = stream.current();
 
353
                known = keywords.propertyIsEnumerable(word) && keywords[word];
 
354
 
 
355
                if (known) {
 
356
                        return ret(known.type, known.style, word);
 
357
                }
 
358
                else {
 
359
                        return ret("macro", null, word);
 
360
                }
 
361
        }
 
362
 
 
363
        // Interface
 
364
        return {
 
365
                startState: function (basecolumn) {
 
366
                        return {
 
367
                                tokenize: jsTokenBase,
 
368
                                indented: 0,
 
369
                                level: 0
 
370
                        };
 
371
                },
 
372
 
 
373
                token: function (stream, state) {
 
374
                        if (stream.eatSpace()) return null;
 
375
                        var style = state.tokenize(stream, state);
 
376
                        return style;
 
377
                },
 
378
 
 
379
                electricChars: ""
 
380
        };
 
381
});
 
382
 
 
383
CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");
 
384
//}}}