File modules/editor/codemirror/mode/ruby/ruby.js

Last commit: Sun Dec 17 01:14:09 2017 +0100	Jan Dankert	Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://codemirror.net/LICENSE 3 4 (function(mod) { 5 if (typeof exports == "object" && typeof module == "object") // CommonJS 6 mod(require("../../lib/codemirror")); 7 else if (typeof define == "function" && define.amd) // AMD 8 define(["../../lib/codemirror"], mod); 9 else // Plain browser env 10 mod(CodeMirror); 11 })(function(CodeMirror) { 12 "use strict"; 13 14 CodeMirror.defineMode("ruby", function(config) { 15 function wordObj(words) { 16 var o = {}; 17 for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true; 18 return o; 19 } 20 var keywords = wordObj([ 21 "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else", 22 "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or", 23 "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", 24 "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc", 25 "caller", "lambda", "proc", "public", "protected", "private", "require", "load", 26 "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__" 27 ]); 28 var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then", 29 "catch", "loop", "proc", "begin"]); 30 var dedentWords = wordObj(["end", "until"]); 31 var matching = {"[": "]", "{": "}", "(": ")"}; 32 var curPunc; 33 34 function chain(newtok, stream, state) { 35 state.tokenize.push(newtok); 36 return newtok(stream, state); 37 } 38 39 function tokenBase(stream, state) { 40 if (stream.sol() && stream.match("=begin") && stream.eol()) { 41 state.tokenize.push(readBlockComment); 42 return "comment"; 43 } 44 if (stream.eatSpace()) return null; 45 var ch = stream.next(), m; 46 if (ch == "`" || ch == "'" || ch == '"') { 47 return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state); 48 } else if (ch == "/") { 49 if (regexpAhead(stream)) 50 return chain(readQuoted(ch, "string-2", true), stream, state); 51 else 52 return "operator"; 53 } else if (ch == "%") { 54 var style = "string", embed = true; 55 if (stream.eat("s")) style = "atom"; 56 else if (stream.eat(/[WQ]/)) style = "string"; 57 else if (stream.eat(/[r]/)) style = "string-2"; 58 else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; } 59 var delim = stream.eat(/[^\w\s=]/); 60 if (!delim) return "operator"; 61 if (matching.propertyIsEnumerable(delim)) delim = matching[delim]; 62 return chain(readQuoted(delim, style, embed, true), stream, state); 63 } else if (ch == "#") { 64 stream.skipToEnd(); 65 return "comment"; 66 } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { 67 return chain(readHereDoc(m[1]), stream, state); 68 } else if (ch == "0") { 69 if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/); 70 else if (stream.eat("b")) stream.eatWhile(/[01]/); 71 else stream.eatWhile(/[0-7]/); 72 return "number"; 73 } else if (/\d/.test(ch)) { 74 stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/); 75 return "number"; 76 } else if (ch == "?") { 77 while (stream.match(/^\\[CM]-/)) {} 78 if (stream.eat("\\")) stream.eatWhile(/\w/); 79 else stream.next(); 80 return "string"; 81 } else if (ch == ":") { 82 if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state); 83 if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state); 84 85 // :> :>> :< :<< are valid symbols 86 if (stream.eat(/[\<\>]/)) { 87 stream.eat(/[\<\>]/); 88 return "atom"; 89 } 90 91 // :+ :- :/ :* :| :& :! are valid symbols 92 if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) { 93 return "atom"; 94 } 95 96 // Symbols can't start by a digit 97 if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) { 98 stream.eatWhile(/[\w$\xa1-\uffff]/); 99 // Only one ? ! = is allowed and only as the last character 100 stream.eat(/[\?\!\=]/); 101 return "atom"; 102 } 103 return "operator"; 104 } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) { 105 stream.eat("@"); 106 stream.eatWhile(/[\w\xa1-\uffff]/); 107 return "variable-2"; 108 } else if (ch == "$") { 109 if (stream.eat(/[a-zA-Z_]/)) { 110 stream.eatWhile(/[\w]/); 111 } else if (stream.eat(/\d/)) { 112 stream.eat(/\d/); 113 } else { 114 stream.next(); // Must be a special global like $: or $! 115 } 116 return "variable-3"; 117 } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) { 118 stream.eatWhile(/[\w\xa1-\uffff]/); 119 stream.eat(/[\?\!]/); 120 if (stream.eat(":")) return "atom"; 121 return "ident"; 122 } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) { 123 curPunc = "|"; 124 return null; 125 } else if (/[\(\)\[\]{}\\;]/.test(ch)) { 126 curPunc = ch; 127 return null; 128 } else if (ch == "-" && stream.eat(">")) { 129 return "arrow"; 130 } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) { 131 var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/); 132 if (ch == "." && !more) curPunc = "."; 133 return "operator"; 134 } else { 135 return null; 136 } 137 } 138 139 function regexpAhead(stream) { 140 var start = stream.pos, depth = 0, next, found = false, escaped = false 141 while ((next = stream.next()) != null) { 142 if (!escaped) { 143 if ("[{(".indexOf(next) > -1) { 144 depth++ 145 } else if ("]})".indexOf(next) > -1) { 146 depth-- 147 if (depth < 0) break 148 } else if (next == "/" && depth == 0) { 149 found = true 150 break 151 } 152 escaped = next == "\\" 153 } else { 154 escaped = false 155 } 156 } 157 stream.backUp(stream.pos - start) 158 return found 159 } 160 161 function tokenBaseUntilBrace(depth) { 162 if (!depth) depth = 1; 163 return function(stream, state) { 164 if (stream.peek() == "}") { 165 if (depth == 1) { 166 state.tokenize.pop(); 167 return state.tokenize[state.tokenize.length-1](stream, state); 168 } else { 169 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1); 170 } 171 } else if (stream.peek() == "{") { 172 state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1); 173 } 174 return tokenBase(stream, state); 175 }; 176 } 177 function tokenBaseOnce() { 178 var alreadyCalled = false; 179 return function(stream, state) { 180 if (alreadyCalled) { 181 state.tokenize.pop(); 182 return state.tokenize[state.tokenize.length-1](stream, state); 183 } 184 alreadyCalled = true; 185 return tokenBase(stream, state); 186 }; 187 } 188 function readQuoted(quote, style, embed, unescaped) { 189 return function(stream, state) { 190 var escaped = false, ch; 191 192 if (state.context.type === 'read-quoted-paused') { 193 state.context = state.context.prev; 194 stream.eat("}"); 195 } 196 197 while ((ch = stream.next()) != null) { 198 if (ch == quote && (unescaped || !escaped)) { 199 state.tokenize.pop(); 200 break; 201 } 202 if (embed && ch == "#" && !escaped) { 203 if (stream.eat("{")) { 204 if (quote == "}") { 205 state.context = {prev: state.context, type: 'read-quoted-paused'}; 206 } 207 state.tokenize.push(tokenBaseUntilBrace()); 208 break; 209 } else if (/[@\$]/.test(stream.peek())) { 210 state.tokenize.push(tokenBaseOnce()); 211 break; 212 } 213 } 214 escaped = !escaped && ch == "\\"; 215 } 216 return style; 217 }; 218 } 219 function readHereDoc(phrase) { 220 return function(stream, state) { 221 if (stream.match(phrase)) state.tokenize.pop(); 222 else stream.skipToEnd(); 223 return "string"; 224 }; 225 } 226 function readBlockComment(stream, state) { 227 if (stream.sol() && stream.match("=end") && stream.eol()) 228 state.tokenize.pop(); 229 stream.skipToEnd(); 230 return "comment"; 231 } 232 233 return { 234 startState: function() { 235 return {tokenize: [tokenBase], 236 indented: 0, 237 context: {type: "top", indented: -config.indentUnit}, 238 continuedLine: false, 239 lastTok: null, 240 varList: false}; 241 }, 242 243 token: function(stream, state) { 244 curPunc = null; 245 if (stream.sol()) state.indented = stream.indentation(); 246 var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype; 247 var thisTok = curPunc; 248 if (style == "ident") { 249 var word = stream.current(); 250 style = state.lastTok == "." ? "property" 251 : keywords.propertyIsEnumerable(stream.current()) ? "keyword" 252 : /^[A-Z]/.test(word) ? "tag" 253 : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def" 254 : "variable"; 255 if (style == "keyword") { 256 thisTok = word; 257 if (indentWords.propertyIsEnumerable(word)) kwtype = "indent"; 258 else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent"; 259 else if ((word == "if" || word == "unless") && stream.column() == stream.indentation()) 260 kwtype = "indent"; 261 else if (word == "do" && state.context.indented < state.indented) 262 kwtype = "indent"; 263 } 264 } 265 if (curPunc || (style && style != "comment")) state.lastTok = thisTok; 266 if (curPunc == "|") state.varList = !state.varList; 267 268 if (kwtype == "indent" || /[\(\[\{]/.test(curPunc)) 269 state.context = {prev: state.context, type: curPunc || style, indented: state.indented}; 270 else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev) 271 state.context = state.context.prev; 272 273 if (stream.eol()) 274 state.continuedLine = (curPunc == "\\" || style == "operator"); 275 return style; 276 }, 277 278 indent: function(state, textAfter) { 279 if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0; 280 var firstChar = textAfter && textAfter.charAt(0); 281 var ct = state.context; 282 var closing = ct.type == matching[firstChar] || 283 ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter); 284 return ct.indented + (closing ? 0 : config.indentUnit) + 285 (state.continuedLine ? config.indentUnit : 0); 286 }, 287 288 electricInput: /^\s*(?:end|rescue|elsif|else|\})$/, 289 lineComment: "#", 290 fold: "indent" 291 }; 292 }); 293 294 CodeMirror.defineMIME("text/x-ruby", "ruby"); 295 296 });
Download modules/editor/codemirror/mode/ruby/ruby.js
History Sun, 17 Dec 2017 01:14:09 +0100 Jan Dankert Integration eines weiteren Code-Editors: Codemirror. Demnächst müssen wir hier mal aufräumen und andere Editoren rauswerfen.