GPQAPP
308 строк · 8.3 Кб
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
17CoreVersion parameter is needed for TiddlyWiki only!
18***/
19
20(function(mod) {21if (typeof exports == "object" && typeof module == "object") // CommonJS22mod(require("../../lib/codemirror"));23else if (typeof define == "function" && define.amd) // AMD24define(["../../lib/codemirror"], mod);25else // Plain browser env26mod(CodeMirror);27})(function(CodeMirror) {28"use strict";29
30CodeMirror.defineMode("tiddlywiki", function () {31// Tokenizer32var textwords = {};33
34var 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": true43};44
45var isSpaceName = /[\w_\-]/i,46reHR = /^\-\-\-\-+$/, // <hr>47reWikiCommentStart = /^\/\*\*\*$/, // /***48reWikiCommentStop = /^\*\*\*\/$/, // ***/49reBlockQuote = /^<<<$/,50
51reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start52reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop53reXmlCodeStart = /^<!--\{\{\{-->$/, // xml block start54reXmlCodeStop = /^<!--\}\}\}-->$/, // xml stop55
56reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start57reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop58
59reUntilCodeStop = /.*?\}\}\}/;60
61function chain(stream, state, f) {62state.tokenize = f;63return f(stream, state);64}65
66function tokenBase(stream, state) {67var sol = stream.sol(), ch = stream.peek();68
69state.block = false; // indicates the start of a code block.70
71// check start of blocks72if (sol && /[<\/\*{}\-]/.test(ch)) {73if (stream.match(reCodeBlockStart)) {74state.block = true;75return chain(stream, state, twTokenCode);76}77if (stream.match(reBlockQuote))78return 'quote';79if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop))80return 'comment';81if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop))82return 'comment';83if (stream.match(reHR))84return 'hr';85}86
87stream.next();88if (sol && /[\/\*!#;:>|]/.test(ch)) {89if (ch == "!") { // tw header90stream.skipToEnd();91return "header";92}93if (ch == "*") { // tw list94stream.eatWhile('*');95return "comment";96}97if (ch == "#") { // tw numbered list98stream.eatWhile('#');99return "comment";100}101if (ch == ";") { // definition list, term102stream.eatWhile(';');103return "comment";104}105if (ch == ":") { // definition list, description106stream.eatWhile(':');107return "comment";108}109if (ch == ">") { // single line quote110stream.eatWhile(">");111return "quote";112}113if (ch == '|')114return 'header';115}116
117if (ch == '{' && stream.match('{{'))118return chain(stream, state, twTokenCode);119
120// rudimentary html:// file:// link matching. TW knows much more ...121if (/[hf]/i.test(ch) &&122/[ti]/i.test(stream.peek()) &&123stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i))124return "link";125
126// just a little string indicator, don't want to have the whole string covered127if (ch == '"')128return 'string';129
130if (ch == '~') // _no_ CamelCase indicator should be bold131return 'brace';132
133if (/[\[\]]/.test(ch) && stream.match(ch)) // check for [[..]]134return 'brace';135
136if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting137stream.eatWhile(isSpaceName);138return "link";139}140
141if (/\d/.test(ch)) { // numbers142stream.eatWhile(/\d/);143return "number";144}145
146if (ch == "/") { // tw invisible comment147if (stream.eat("%")) {148return chain(stream, state, twTokenComment);149} else if (stream.eat("/")) { //150return chain(stream, state, twTokenEm);151}152}153
154if (ch == "_" && stream.eat("_")) // tw underline155return chain(stream, state, twTokenUnderline);156
157// strikethrough and mdash handling158if (ch == "-" && stream.eat("-")) {159// if strikethrough looks ugly, change CSS.160if (stream.peek() != ' ')161return chain(stream, state, twTokenStrike);162// mdash163if (stream.peek() == ' ')164return 'brace';165}166
167if (ch == "'" && stream.eat("'")) // tw bold168return chain(stream, state, twTokenStrong);169
170if (ch == "<" && stream.eat("<")) // tw macro171return chain(stream, state, twTokenMacro);172
173// core macro handling174stream.eatWhile(/[\w\$_]/);175return textwords.propertyIsEnumerable(stream.current()) ? "keyword" : null176}177
178// tw invisible comment179function twTokenComment(stream, state) {180var maybeEnd = false, ch;181while (ch = stream.next()) {182if (ch == "/" && maybeEnd) {183state.tokenize = tokenBase;184break;185}186maybeEnd = (ch == "%");187}188return "comment";189}190
191// tw strong / bold192function twTokenStrong(stream, state) {193var maybeEnd = false,194ch;195while (ch = stream.next()) {196if (ch == "'" && maybeEnd) {197state.tokenize = tokenBase;198break;199}200maybeEnd = (ch == "'");201}202return "strong";203}204
205// tw code206function twTokenCode(stream, state) {207var sb = state.block;208
209if (sb && stream.current()) {210return "comment";211}212
213if (!sb && stream.match(reUntilCodeStop)) {214state.tokenize = tokenBase;215return "comment";216}217
218if (sb && stream.sol() && stream.match(reCodeBlockStop)) {219state.tokenize = tokenBase;220return "comment";221}222
223stream.next();224return "comment";225}226
227// tw em / italic228function twTokenEm(stream, state) {229var maybeEnd = false,230ch;231while (ch = stream.next()) {232if (ch == "/" && maybeEnd) {233state.tokenize = tokenBase;234break;235}236maybeEnd = (ch == "/");237}238return "em";239}240
241// tw underlined text242function twTokenUnderline(stream, state) {243var maybeEnd = false,244ch;245while (ch = stream.next()) {246if (ch == "_" && maybeEnd) {247state.tokenize = tokenBase;248break;249}250maybeEnd = (ch == "_");251}252return "underlined";253}254
255// tw strike through text looks ugly256// change CSS if needed257function twTokenStrike(stream, state) {258var maybeEnd = false, ch;259
260while (ch = stream.next()) {261if (ch == "-" && maybeEnd) {262state.tokenize = tokenBase;263break;264}265maybeEnd = (ch == "-");266}267return "strikethrough";268}269
270// macro271function twTokenMacro(stream, state) {272if (stream.current() == '<<') {273return 'macro';274}275
276var ch = stream.next();277if (!ch) {278state.tokenize = tokenBase;279return null;280}281if (ch == ">") {282if (stream.peek() == '>') {283stream.next();284state.tokenize = tokenBase;285return "macro";286}287}288
289stream.eatWhile(/[\w\$_]/);290return keywords.propertyIsEnumerable(stream.current()) ? "keyword" : null291}292
293// Interface294return {295startState: function () {296return {tokenize: tokenBase};297},298
299token: function (stream, state) {300if (stream.eatSpace()) return null;301var style = state.tokenize(stream, state);302return style;303}304};305});306
307CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki");308});309