all repos — NoPaste @ 3e3c433f026c49e04d0efa0f0f39fc688ed32bb1

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/tiddlywiki/tiddlywiki.js (view raw)

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://codemirror.net/LICENSE
  3
  4/***
  5    |''Name''|tiddlywiki.js|
  6    |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror|
  7    |''Author''|PMario|
  8    |''Version''|0.1.7|
  9    |''Status''|''stable''|
 10    |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|
 11    |''Documentation''|https://codemirror.tiddlyspace.com/|
 12    |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
 13    |''CoreVersion''|2.5.0|
 14    |''Requires''|codemirror.js|
 15    |''Keywords''|syntax highlighting color code mirror codemirror|
 16    ! Info
 17    CoreVersion parameter is needed for TiddlyWiki only!
 18***/
 19
 20(function(mod) {
 21  if (typeof exports == "object" && typeof module == "object") // CommonJS
 22    mod(require("../../lib/codemirror"));
 23  else if (typeof define == "function" && define.amd) // AMD
 24    define(["../../lib/codemirror"], mod);
 25  else // Plain browser env
 26    mod(CodeMirror);
 27})(function(CodeMirror) {
 28"use strict";
 29
 30CodeMirror.defineMode("tiddlywiki", function () {
 31  // Tokenizer
 32  var textwords = {};
 33
 34  var keywords = {
 35    "allTags": true, "closeAll": true, "list": true,
 36    "newJournal": true, "newTiddler": true,
 37    "permaview": true, "saveChanges": true,
 38    "search": true, "slider": true, "tabs": true,
 39    "tag": true, "tagging": true, "tags": true,
 40    "tiddler": true, "timeline": true,
 41    "today": true, "version": true, "option": true,
 42    "with": true, "filter": true
 43  };
 44
 45  var isSpaceName = /[\w_\-]/i,
 46      reHR = /^\-\-\-\-+$/,                                 // <hr>
 47      reWikiCommentStart = /^\/\*\*\*$/,            // /***
 48      reWikiCommentStop = /^\*\*\*\/$/,             // ***/
 49      reBlockQuote = /^<<<$/,
 50
 51      reJsCodeStart = /^\/\/\{\{\{$/,                       // //{{{ js block start
 52      reJsCodeStop = /^\/\/\}\}\}$/,                        // //}}} js stop
 53      reXmlCodeStart = /^<!--\{\{\{-->$/,           // xml block start
 54      reXmlCodeStop = /^<!--\}\}\}-->$/,            // xml stop
 55
 56      reCodeBlockStart = /^\{\{\{$/,                        // {{{ TW text div block start
 57      reCodeBlockStop = /^\}\}\}$/,                 // }}} TW text stop
 58
 59      reUntilCodeStop = /.*?\}\}\}/;
 60
 61  function chain(stream, state, f) {
 62    state.tokenize = f;
 63    return f(stream, state);
 64  }
 65
 66  function tokenBase(stream, state) {
 67    var sol = stream.sol(), ch = stream.peek();
 68
 69    state.block = false;        // indicates the start of a code block.
 70
 71    // check start of  blocks
 72    if (sol && /[<\/\*{}\-]/.test(ch)) {
 73      if (stream.match(reCodeBlockStart)) {
 74        state.block = true;
 75        return chain(stream, state, twTokenCode);
 76      }
 77      if (stream.match(reBlockQuote))
 78        return 'quote';
 79      if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop))
 80        return 'comment';
 81      if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop))
 82        return 'comment';
 83      if (stream.match(reHR))
 84        return 'hr';
 85    }
 86
 87    stream.next();
 88    if (sol && /[\/\*!#;:>|]/.test(ch)) {
 89      if (ch == "!") { // tw header
 90        stream.skipToEnd();
 91        return "header";
 92      }
 93      if (ch == "*") { // tw list
 94        stream.eatWhile('*');
 95        return "comment";
 96      }
 97      if (ch == "#") { // tw numbered list
 98        stream.eatWhile('#');
 99        return "comment";
100      }
101      if (ch == ";") { // definition list, term
102        stream.eatWhile(';');
103        return "comment";
104      }
105      if (ch == ":") { // definition list, description
106        stream.eatWhile(':');
107        return "comment";
108      }
109      if (ch == ">") { // single line quote
110        stream.eatWhile(">");
111        return "quote";
112      }
113      if (ch == '|')
114        return 'header';
115    }
116
117    if (ch == '{' && stream.match(/\{\{/))
118      return chain(stream, state, twTokenCode);
119
120    // rudimentary html:// file:// link matching. TW knows much more ...
121    if (/[hf]/i.test(ch) &&
122        /[ti]/i.test(stream.peek()) &&
123        stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i))
124      return "link";
125
126    // just a little string indicator, don't want to have the whole string covered
127    if (ch == '"')
128      return 'string';
129
130    if (ch == '~')    // _no_ CamelCase indicator should be bold
131      return 'brace';
132
133    if (/[\[\]]/.test(ch) && stream.match(ch)) // check for [[..]]
134      return 'brace';
135
136    if (ch == "@") {    // check for space link. TODO fix @@...@@ highlighting
137      stream.eatWhile(isSpaceName);
138      return "link";
139    }
140
141    if (/\d/.test(ch)) {        // numbers
142      stream.eatWhile(/\d/);
143      return "number";
144    }
145
146    if (ch == "/") { // tw invisible comment
147      if (stream.eat("%")) {
148        return chain(stream, state, twTokenComment);
149      } else if (stream.eat("/")) { //
150        return chain(stream, state, twTokenEm);
151      }
152    }
153
154    if (ch == "_" && stream.eat("_")) // tw underline
155        return chain(stream, state, twTokenUnderline);
156
157    // strikethrough and mdash handling
158    if (ch == "-" && stream.eat("-")) {
159      // if strikethrough looks ugly, change CSS.
160      if (stream.peek() != ' ')
161        return chain(stream, state, twTokenStrike);
162      // mdash
163      if (stream.peek() == ' ')
164        return 'brace';
165    }
166
167    if (ch == "'" && stream.eat("'")) // tw bold
168      return chain(stream, state, twTokenStrong);
169
170    if (ch == "<" && stream.eat("<")) // tw macro
171      return chain(stream, state, twTokenMacro);
172
173    // core macro handling
174    stream.eatWhile(/[\w\$_]/);
175    return textwords.propertyIsEnumerable(stream.current()) ? "keyword" : null
176  }
177
178  // tw invisible comment
179  function twTokenComment(stream, state) {
180    var maybeEnd = false, ch;
181    while (ch = stream.next()) {
182      if (ch == "/" && maybeEnd) {
183        state.tokenize = tokenBase;
184        break;
185      }
186      maybeEnd = (ch == "%");
187    }
188    return "comment";
189  }
190
191  // tw strong / bold
192  function twTokenStrong(stream, state) {
193    var maybeEnd = false,
194    ch;
195    while (ch = stream.next()) {
196      if (ch == "'" && maybeEnd) {
197        state.tokenize = tokenBase;
198        break;
199      }
200      maybeEnd = (ch == "'");
201    }
202    return "strong";
203  }
204
205  // tw code
206  function twTokenCode(stream, state) {
207    var sb = state.block;
208
209    if (sb && stream.current()) {
210      return "comment";
211    }
212
213    if (!sb && stream.match(reUntilCodeStop)) {
214      state.tokenize = tokenBase;
215      return "comment";
216    }
217
218    if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
219      state.tokenize = tokenBase;
220      return "comment";
221    }
222
223    stream.next();
224    return "comment";
225  }
226
227  // tw em / italic
228  function twTokenEm(stream, state) {
229    var maybeEnd = false,
230    ch;
231    while (ch = stream.next()) {
232      if (ch == "/" && maybeEnd) {
233        state.tokenize = tokenBase;
234        break;
235      }
236      maybeEnd = (ch == "/");
237    }
238    return "em";
239  }
240
241  // tw underlined text
242  function twTokenUnderline(stream, state) {
243    var maybeEnd = false,
244    ch;
245    while (ch = stream.next()) {
246      if (ch == "_" && maybeEnd) {
247        state.tokenize = tokenBase;
248        break;
249      }
250      maybeEnd = (ch == "_");
251    }
252    return "underlined";
253  }
254
255  // tw strike through text looks ugly
256  // change CSS if needed
257  function twTokenStrike(stream, state) {
258    var maybeEnd = false, ch;
259
260    while (ch = stream.next()) {
261      if (ch == "-" && maybeEnd) {
262        state.tokenize = tokenBase;
263        break;
264      }
265      maybeEnd = (ch == "-");
266    }
267    return "strikethrough";
268  }
269
270  // macro
271  function twTokenMacro(stream, state) {
272    if (stream.current() == '<<') {
273      return 'macro';
274    }
275
276    var ch = stream.next();
277    if (!ch) {
278      state.tokenize = tokenBase;
279      return null;
280    }
281    if (ch == ">") {
282      if (stream.peek() == '>') {
283        stream.next();
284        state.tokenize = tokenBase;
285        return "macro";
286      }
287    }
288
289    stream.eatWhile(/[\w\$_]/);
290    return keywords.propertyIsEnumerable(stream.current()) ? "keyword" : null
291  }
292
293  // Interface
294  return {
295    startState: function () {
296      return {tokenize: tokenBase};
297    },
298
299    token: function (stream, state) {
300      if (stream.eatSpace()) return null;
301      var style = state.tokenize(stream, state);
302      return style;
303    }
304  };
305});
306
307CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");
308});