topical media & game development

talk show tell print

mobile-query-three-www-vendor-CodeMirror2-mode-tiddlywiki-tiddlywiki.js / js



  
* |''Name''|tiddlywiki.js| |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror2| |''Author''|PMario| |''Version''|0.1.6| |''Status''|''beta''| |''Source''|[[GitHub|github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]| |''Documentation''|http://codemirror.tiddlyspace.com/| |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]| |''CoreVersion''|2.5.0| |''Requires''|codemirror.js| |''Keywords''|syntax highlighting color code mirror codemirror| ! Info CoreVersion parameter is needed for TiddlyWiki only! *

  
  //{{{
  CodeMirror.defineMode("tiddlywiki", function (config, parserConfig) {
          var indentUnit = config.indentUnit;
  
          // Tokenizer
          var textwords = function () {
                  function kw(type) {
                          return {
                                  type: type,
                                  style: "text"
                          };
                  }
                  return {};
          }();
  
          var keywords = function () {
                  function kw(type) {
                          return { type: type, style: "macro"};
                  }
                  return {
                          "allTags": kw('allTags'), "closeAll": kw('closeAll'), "list": kw('list'),
                          "newJournal": kw('newJournal'), "newTiddler": kw('newTiddler'),
                          "permaview": kw('permaview'), "saveChanges": kw('saveChanges'),
                          "search": kw('search'), "slider": kw('slider'),        "tabs": kw('tabs'),
                          "tag": kw('tag'), "tagging": kw('tagging'),        "tags": kw('tags'),
                          "tiddler": kw('tiddler'), "timeline": kw('timeline'),
                          "today": kw('today'), "version": kw('version'),        "option": kw('option'),
  
                          "with": kw('with'),
                          "filter": kw('filter')
                  };
          }();
  
          var isSpaceName = /[\w_\-]/i,
                  reHR = /^\-\-\-\-+/,
                  reWikiCommentStart = /^\/\*\*\*/,                // 
* reWikiCommentStop = /^\*\*\*\//, // **

  
                  reBlockQuote = /^<<</,
  
                  reJsCodeStart = /^\/\/\{\{\{/,                        // //{{
                  reJsCodeStop = /^\/\/\}\}\}/,                        // //}}}
                  reXmlCodeStart = /^<!--\{\{\{-->/,
                  reXmlCodeStop = /^<!--\}\}\}-->/,
  
                  reCodeBlockStart = /^\{\{\{/,
                  reCodeBlockStop = /^\}\}\}/,
  
                  reCodeStart = /\{\{\{/,
                  reUntilCodeStop = /.*?\}\}\}/;
  
          function chain(stream, state, f) {
                  state.tokenize = f;
                  return f(stream, state);
          }
  
          // used for strings
          function nextUntilUnescaped(stream, end) {
                  var escaped = false,
                          next;
                  while ((next = stream.next()) != null) {
                          if (next == end && !escaped) return false;
                          escaped = !escaped && next == "\\";
                  }
                  return escaped;
          }
  
          // Used as scratch variables to communicate multiple values without
          // consing up tons of objects.
          var type, content;
  
          function ret(tp, style, cont) {
                  type = tp;
                  content = cont;
                  return style;
          }
  
          function jsTokenBase(stream, state) {
                  var sol = stream.sol(), 
                          ch, tch;
                          
                  state.block = false;        // indicates the start of a code block.
  
                  ch = stream.peek(); // don't eat, to make match simpler
                  
                  // check start of  blocks    
                  if (sol && /[<\/\*{}\-]/.test(ch)) {
                          if (stream.match(reCodeBlockStart)) {
                                  state.block = true;
                                  return chain(stream, state, twTokenCode);
                          }
                          if (stream.match(reBlockQuote)) {
                                  return ret('quote', 'quote');
                          }
                          if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) {
                                  return ret('code', 'code');
                          }
                          if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) {
                                  return ret('code', 'code');
                          }
                          if (stream.match(reHR)) {
                                  return ret('hr', 'hr');
                          }
                  } // sol
                  var ch = stream.next();
  
                  if (sol && /[\/\*!#;:>|]/.test(ch)) {
                          if (ch == "!") { // tw header
                                  stream.skipToEnd();
                                  return ret("header", "header");
                          }
                          if (ch == "*") { // tw list
                                  stream.eatWhile('*');
                                  return ret("list", "list");
                          }
                          if (ch == "#") { // tw numbered list
                                  stream.eatWhile('#');
                                  return ret("list", "list");
                          }
                          if (ch == ";") { // tw list
                                  stream.eatWhile(';');
                                  return ret("list", "list");
                          }
                          if (ch == ":") { // tw list
                                  stream.eatWhile(':');
                                  return ret("list", "list");
                          }
                          if (ch == ">") { // single line quote
                                  stream.eatWhile(">");
                                  return ret("quote", "quote");
                          }
                          if (ch == '|') {
                                  return ret('table', 'table');
                          }
                  }
  
                  if (ch == '{' && stream.match(/\{\{/)) {
                          return chain(stream, state, twTokenCode);
                  }
  
                  // rudimentary html:// file:// link matching. TW knows much more ...
                  if (/[hf]/i.test(ch)) {
                          if (/[ti]/i.test(stream.peek()) && stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|!:,.;]*[A-Z0-9+&@#\/%=~_|]/i)) {
                                  return ret("link-external", "link-external");
                          }
                  }
                  // just a little string indicator, don't want to have the whole string covered
                  if (ch == '"') {
                          return ret('string', 'string');
                  }
                  if (/[\[\]]/.test(ch)) { // check for [[..]]
                          if (stream.peek() == ch) {
                                  stream.next();
                                  return ret('brace', 'brace');
                          }
                  }
                  if (ch == "@") {        // check for space link. TODO fix @@...@@ highlighting
                          stream.eatWhile(isSpaceName);
                          return ret("link-external", "link-external");
                  }
                  if (/\d/.test(ch)) {        // numbers
                          stream.eatWhile(/\d/);
                          return ret("number", "number");
                  }
                  if (ch == "/") { // tw invisible comment
                          if (stream.eat("%")) {
                                  return chain(stream, state, twTokenComment);
                          }
                          else if (stream.eat("/")) { // 
                                  return chain(stream, state, twTokenEm);
                          }
                  }
                  if (ch == "_") { // tw underline
                          if (stream.eat("_")) {
                                  return chain(stream, state, twTokenUnderline);
                          }
                  }
                  if (ch == "-") { // tw strikethrough TODO looks ugly .. different handling see below;
                          if (stream.eat("-")) {
                                  return chain(stream, state, twTokenStrike);
                          }
                  }
                  if (ch == "'") { // tw bold
                          if (stream.eat("'")) {
                                  return chain(stream, state, twTokenStrong);
                          }
                  }
                  if (ch == "<") { // tw macro
                          if (stream.eat("<")) {
                                  return chain(stream, state, twTokenMacro);
                          }
                  }
                  else {
                          return ret(ch);
                  }
  
                  stream.eatWhile(/[\w$_]/);
                  var word = stream.current(),
                          known = textwords.propertyIsEnumerable(word) && textwords[word];
  
                  return known ? ret(known.type, known.style, word) : ret("text", null, word);
  
          } // jsTokenBase()
  
          function twTokenString(quote) {
                  return function (stream, state) {
                          if (!nextUntilUnescaped(stream, quote)) state.tokenize = jsTokenBase;
                          return ret("string", "string");
                  };
          }
  
          // tw invisible comment
          function twTokenComment(stream, state) {
                  var maybeEnd = false,
                          ch;
                  while (ch = stream.next()) {
                          if (ch == "/" && maybeEnd) {
                                  state.tokenize = jsTokenBase;
                                  break;
                          }
                          maybeEnd = (ch == "%");
                  }
                  return ret("comment", "comment");
          }
  
          // tw strong / bold
          function twTokenStrong(stream, state) {
                  var maybeEnd = false,
                          ch;
                  while (ch = stream.next()) {
                          if (ch == "'" && maybeEnd) {
                                  state.tokenize = jsTokenBase;
                                  break;
                          }
                          maybeEnd = (ch == "'");
                  }
                  return ret("text", "strong");
          }
  
          // tw code
          function twTokenCode(stream, state) {
                  var ch, sb = state.block;
                  
                  if (sb && stream.current()) {
                          return ret("code", "code");
                  }
  
                  if (!sb && stream.match(reUntilCodeStop)) {
                          state.tokenize = jsTokenBase;
                          return ret("code", "code-inline");
                  }
  
                  if (sb && stream.sol() && stream.match(reCodeBlockStop)) {
                          state.tokenize = jsTokenBase;
                          return ret("code", "code");
                  }
  
                  ch = stream.next();
                  return (sb) ? ret("code", "code") : ret("code", "code-inline");
          }
  
          // tw em / italic
          function twTokenEm(stream, state) {
                  var maybeEnd = false,
                          ch;
                  while (ch = stream.next()) {
                          if (ch == "/" && maybeEnd) {
                                  state.tokenize = jsTokenBase;
                                  break;
                          }
                          maybeEnd = (ch == "/");
                  }
                  return ret("text", "em");
          }
  
          // tw underlined text
          function twTokenUnderline(stream, state) {
                  var maybeEnd = false,
                          ch;
                  while (ch = stream.next()) {
                          if (ch == "_" && maybeEnd) {
                                  state.tokenize = jsTokenBase;
                                  break;
                          }
                          maybeEnd = (ch == "_");
                  }
                  return ret("text", "underlined");
          }
  
          // tw strike through text looks ugly 
          // TODO just strike through the first and last 2 chars if possible.
          function twTokenStrike(stream, state) {
                  var maybeEnd = false,
                          ch, nr;
                          
                  while (ch = stream.next()) {
                          if (ch == "-" && maybeEnd) {
                                  state.tokenize = jsTokenBase;
                                  break;
                          }
                          maybeEnd = (ch == "-");
                  }
                  return ret("text", "line-through");
          }
  
          // macro
          function twTokenMacro(stream, state) {
                  var ch, tmp, word, known;
  
                  if (stream.current() == '<<') {
                          return ret('brace', 'macro');
                  }
  
                  ch = stream.next();
                  if (!ch) {
                          state.tokenize = jsTokenBase;
                          return ret(ch);
                  }
                  if (ch == ">") {
                          if (stream.peek() == '>') {
                                  stream.next();
                                  state.tokenize = jsTokenBase;
                                  return ret("brace", "macro");
                          }
                  }
  
                  stream.eatWhile(/[\w$_]/);
                  word = stream.current();
                  known = keywords.propertyIsEnumerable(word) && keywords[word];
  
                  if (known) {
                          return ret(known.type, known.style, word);
                  }
                  else {
                          return ret("macro", null, word);
                  }
          }
  
          // Interface
          return {
                  startState: function (basecolumn) {
                          return {
                                  tokenize: jsTokenBase,
                                  indented: 0,
                                  level: 0
                          };
                  },
  
                  token: function (stream, state) {
                          if (stream.eatSpace()) return null;
                          var style = state.tokenize(stream, state);
                          return style;
                  },
  
                  electricChars: ""
          };
  });
  
  CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");
  //}}}
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.