github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/app/public/codemirror/mode/xquery/xquery.js (about) 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("xquery", function() { 15 16 // The keywords object is set to the result of this self executing 17 // function. Each keyword is a property of the keywords object whose 18 // value is {type: atype, style: astyle} 19 var keywords = function(){ 20 // conveinence functions used to build keywords object 21 function kw(type) {return {type: type, style: "keyword"};} 22 var A = kw("keyword a") 23 , B = kw("keyword b") 24 , C = kw("keyword c") 25 , operator = kw("operator") 26 , atom = {type: "atom", style: "atom"} 27 , punctuation = {type: "punctuation", style: null} 28 , qualifier = {type: "axis_specifier", style: "qualifier"}; 29 30 // kwObj is what is return from this function at the end 31 var kwObj = { 32 'if': A, 'switch': A, 'while': A, 'for': A, 33 'else': B, 'then': B, 'try': B, 'finally': B, 'catch': B, 34 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C, 35 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C, 36 ',': punctuation, 37 'null': atom, 'fn:false()': atom, 'fn:true()': atom 38 }; 39 40 // a list of 'basic' keywords. For each add a property to kwObj with the value of 41 // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"} 42 var basic = ['after','ancestor','ancestor-or-self','and','as','ascending','assert','attribute','before', 43 'by','case','cast','child','comment','declare','default','define','descendant','descendant-or-self', 44 'descending','document','document-node','element','else','eq','every','except','external','following', 45 'following-sibling','follows','for','function','if','import','in','instance','intersect','item', 46 'let','module','namespace','node','node','of','only','or','order','parent','precedes','preceding', 47 'preceding-sibling','processing-instruction','ref','return','returns','satisfies','schema','schema-element', 48 'self','some','sortby','stable','text','then','to','treat','typeswitch','union','variable','version','where', 49 'xquery', 'empty-sequence']; 50 for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);}; 51 52 // a list of types. For each add a property to kwObj with the value of 53 // {type: "atom", style: "atom"} 54 var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime', 55 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary', 56 'xs:base64Binary', 'xs:anyURI', 'xs:QName', 'xs:byte','xs:boolean','xs:anyURI','xf:yearMonthDuration']; 57 for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;}; 58 59 // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"} 60 var operators = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', ':=', '=', '>', '>=', '<', '<=', '.', '|', '?', 'and', 'or', 'div', 'idiv', 'mod', '*', '/', '+', '-']; 61 for(var i=0, l=operators.length; i < l; i++) { kwObj[operators[i]] = operator;}; 62 63 // each axis_specifiers will add a property to kwObj with value of {type: "axis_specifier", style: "qualifier"} 64 var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", 65 "ancestor::", "ancestor-or-self::", "following::", "preceding::", "following-sibling::", "preceding-sibling::"]; 66 for(var i=0, l=axis_specifiers.length; i < l; i++) { kwObj[axis_specifiers[i]] = qualifier; }; 67 68 return kwObj; 69 }(); 70 71 function chain(stream, state, f) { 72 state.tokenize = f; 73 return f(stream, state); 74 } 75 76 // the primary mode tokenizer 77 function tokenBase(stream, state) { 78 var ch = stream.next(), 79 mightBeFunction = false, 80 isEQName = isEQNameAhead(stream); 81 82 // an XML tag (if not in some sub, chained tokenizer) 83 if (ch == "<") { 84 if(stream.match("!--", true)) 85 return chain(stream, state, tokenXMLComment); 86 87 if(stream.match("![CDATA", false)) { 88 state.tokenize = tokenCDATA; 89 return "tag"; 90 } 91 92 if(stream.match("?", false)) { 93 return chain(stream, state, tokenPreProcessing); 94 } 95 96 var isclose = stream.eat("/"); 97 stream.eatSpace(); 98 var tagName = "", c; 99 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; 100 101 return chain(stream, state, tokenTag(tagName, isclose)); 102 } 103 // start code block 104 else if(ch == "{") { 105 pushStateStack(state,{ type: "codeblock"}); 106 return null; 107 } 108 // end code block 109 else if(ch == "}") { 110 popStateStack(state); 111 return null; 112 } 113 // if we're in an XML block 114 else if(isInXmlBlock(state)) { 115 if(ch == ">") 116 return "tag"; 117 else if(ch == "/" && stream.eat(">")) { 118 popStateStack(state); 119 return "tag"; 120 } 121 else 122 return "variable"; 123 } 124 // if a number 125 else if (/\d/.test(ch)) { 126 stream.match(/^\d*(?:\.\d*)?(?:E[+\-]?\d+)?/); 127 return "atom"; 128 } 129 // comment start 130 else if (ch === "(" && stream.eat(":")) { 131 pushStateStack(state, { type: "comment"}); 132 return chain(stream, state, tokenComment); 133 } 134 // quoted string 135 else if ( !isEQName && (ch === '"' || ch === "'")) 136 return chain(stream, state, tokenString(ch)); 137 // variable 138 else if(ch === "$") { 139 return chain(stream, state, tokenVariable); 140 } 141 // assignment 142 else if(ch ===":" && stream.eat("=")) { 143 return "keyword"; 144 } 145 // open paren 146 else if(ch === "(") { 147 pushStateStack(state, { type: "paren"}); 148 return null; 149 } 150 // close paren 151 else if(ch === ")") { 152 popStateStack(state); 153 return null; 154 } 155 // open paren 156 else if(ch === "[") { 157 pushStateStack(state, { type: "bracket"}); 158 return null; 159 } 160 // close paren 161 else if(ch === "]") { 162 popStateStack(state); 163 return null; 164 } 165 else { 166 var known = keywords.propertyIsEnumerable(ch) && keywords[ch]; 167 168 // if there's a EQName ahead, consume the rest of the string portion, it's likely a function 169 if(isEQName && ch === '\"') while(stream.next() !== '"'){} 170 if(isEQName && ch === '\'') while(stream.next() !== '\''){} 171 172 // gobble up a word if the character is not known 173 if(!known) stream.eatWhile(/[\w\$_-]/); 174 175 // gobble a colon in the case that is a lib func type call fn:doc 176 var foundColon = stream.eat(":"); 177 178 // if there's not a second colon, gobble another word. Otherwise, it's probably an axis specifier 179 // which should get matched as a keyword 180 if(!stream.eat(":") && foundColon) { 181 stream.eatWhile(/[\w\$_-]/); 182 } 183 // if the next non whitespace character is an open paren, this is probably a function (if not a keyword of other sort) 184 if(stream.match(/^[ \t]*\(/, false)) { 185 mightBeFunction = true; 186 } 187 // is the word a keyword? 188 var word = stream.current(); 189 known = keywords.propertyIsEnumerable(word) && keywords[word]; 190 191 // if we think it's a function call but not yet known, 192 // set style to variable for now for lack of something better 193 if(mightBeFunction && !known) known = {type: "function_call", style: "variable def"}; 194 195 // if the previous word was element, attribute, axis specifier, this word should be the name of that 196 if(isInXmlConstructor(state)) { 197 popStateStack(state); 198 return "variable"; 199 } 200 // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and 201 // push the stack so we know to look for it on the next word 202 if(word == "element" || word == "attribute" || known.type == "axis_specifier") pushStateStack(state, {type: "xmlconstructor"}); 203 204 // if the word is known, return the details of that else just call this a generic 'word' 205 return known ? known.style : "variable"; 206 } 207 } 208 209 // handle comments, including nested 210 function tokenComment(stream, state) { 211 var maybeEnd = false, maybeNested = false, nestedCount = 0, ch; 212 while (ch = stream.next()) { 213 if (ch == ")" && maybeEnd) { 214 if(nestedCount > 0) 215 nestedCount--; 216 else { 217 popStateStack(state); 218 break; 219 } 220 } 221 else if(ch == ":" && maybeNested) { 222 nestedCount++; 223 } 224 maybeEnd = (ch == ":"); 225 maybeNested = (ch == "("); 226 } 227 228 return "comment"; 229 } 230 231 // tokenizer for string literals 232 // optionally pass a tokenizer function to set state.tokenize back to when finished 233 function tokenString(quote, f) { 234 return function(stream, state) { 235 var ch; 236 237 if(isInString(state) && stream.current() == quote) { 238 popStateStack(state); 239 if(f) state.tokenize = f; 240 return "string"; 241 } 242 243 pushStateStack(state, { type: "string", name: quote, tokenize: tokenString(quote, f) }); 244 245 // if we're in a string and in an XML block, allow an embedded code block 246 if(stream.match("{", false) && isInXmlAttributeBlock(state)) { 247 state.tokenize = tokenBase; 248 return "string"; 249 } 250 251 252 while (ch = stream.next()) { 253 if (ch == quote) { 254 popStateStack(state); 255 if(f) state.tokenize = f; 256 break; 257 } 258 else { 259 // if we're in a string and in an XML block, allow an embedded code block in an attribute 260 if(stream.match("{", false) && isInXmlAttributeBlock(state)) { 261 state.tokenize = tokenBase; 262 return "string"; 263 } 264 265 } 266 } 267 268 return "string"; 269 }; 270 } 271 272 // tokenizer for variables 273 function tokenVariable(stream, state) { 274 var isVariableChar = /[\w\$_-]/; 275 276 // a variable may start with a quoted EQName so if the next character is quote, consume to the next quote 277 if(stream.eat("\"")) { 278 while(stream.next() !== '\"'){}; 279 stream.eat(":"); 280 } else { 281 stream.eatWhile(isVariableChar); 282 if(!stream.match(":=", false)) stream.eat(":"); 283 } 284 stream.eatWhile(isVariableChar); 285 state.tokenize = tokenBase; 286 return "variable"; 287 } 288 289 // tokenizer for XML tags 290 function tokenTag(name, isclose) { 291 return function(stream, state) { 292 stream.eatSpace(); 293 if(isclose && stream.eat(">")) { 294 popStateStack(state); 295 state.tokenize = tokenBase; 296 return "tag"; 297 } 298 // self closing tag without attributes? 299 if(!stream.eat("/")) 300 pushStateStack(state, { type: "tag", name: name, tokenize: tokenBase}); 301 if(!stream.eat(">")) { 302 state.tokenize = tokenAttribute; 303 return "tag"; 304 } 305 else { 306 state.tokenize = tokenBase; 307 } 308 return "tag"; 309 }; 310 } 311 312 // tokenizer for XML attributes 313 function tokenAttribute(stream, state) { 314 var ch = stream.next(); 315 316 if(ch == "/" && stream.eat(">")) { 317 if(isInXmlAttributeBlock(state)) popStateStack(state); 318 if(isInXmlBlock(state)) popStateStack(state); 319 return "tag"; 320 } 321 if(ch == ">") { 322 if(isInXmlAttributeBlock(state)) popStateStack(state); 323 return "tag"; 324 } 325 if(ch == "=") 326 return null; 327 // quoted string 328 if (ch == '"' || ch == "'") 329 return chain(stream, state, tokenString(ch, tokenAttribute)); 330 331 if(!isInXmlAttributeBlock(state)) 332 pushStateStack(state, { type: "attribute", tokenize: tokenAttribute}); 333 334 stream.eat(/[a-zA-Z_:]/); 335 stream.eatWhile(/[-a-zA-Z0-9_:.]/); 336 stream.eatSpace(); 337 338 // the case where the attribute has not value and the tag was closed 339 if(stream.match(">", false) || stream.match("/", false)) { 340 popStateStack(state); 341 state.tokenize = tokenBase; 342 } 343 344 return "attribute"; 345 } 346 347 // handle comments, including nested 348 function tokenXMLComment(stream, state) { 349 var ch; 350 while (ch = stream.next()) { 351 if (ch == "-" && stream.match("->", true)) { 352 state.tokenize = tokenBase; 353 return "comment"; 354 } 355 } 356 } 357 358 359 // handle CDATA 360 function tokenCDATA(stream, state) { 361 var ch; 362 while (ch = stream.next()) { 363 if (ch == "]" && stream.match("]", true)) { 364 state.tokenize = tokenBase; 365 return "comment"; 366 } 367 } 368 } 369 370 // handle preprocessing instructions 371 function tokenPreProcessing(stream, state) { 372 var ch; 373 while (ch = stream.next()) { 374 if (ch == "?" && stream.match(">", true)) { 375 state.tokenize = tokenBase; 376 return "comment meta"; 377 } 378 } 379 } 380 381 382 // functions to test the current context of the state 383 function isInXmlBlock(state) { return isIn(state, "tag"); } 384 function isInXmlAttributeBlock(state) { return isIn(state, "attribute"); } 385 function isInXmlConstructor(state) { return isIn(state, "xmlconstructor"); } 386 function isInString(state) { return isIn(state, "string"); } 387 388 function isEQNameAhead(stream) { 389 // assume we've already eaten a quote (") 390 if(stream.current() === '"') 391 return stream.match(/^[^\"]+\"\:/, false); 392 else if(stream.current() === '\'') 393 return stream.match(/^[^\"]+\'\:/, false); 394 else 395 return false; 396 } 397 398 function isIn(state, type) { 399 return (state.stack.length && state.stack[state.stack.length - 1].type == type); 400 } 401 402 function pushStateStack(state, newState) { 403 state.stack.push(newState); 404 } 405 406 function popStateStack(state) { 407 state.stack.pop(); 408 var reinstateTokenize = state.stack.length && state.stack[state.stack.length-1].tokenize; 409 state.tokenize = reinstateTokenize || tokenBase; 410 } 411 412 // the interface for the mode API 413 return { 414 startState: function() { 415 return { 416 tokenize: tokenBase, 417 cc: [], 418 stack: [] 419 }; 420 }, 421 422 token: function(stream, state) { 423 if (stream.eatSpace()) return null; 424 var style = state.tokenize(stream, state); 425 return style; 426 }, 427 428 blockCommentStart: "(:", 429 blockCommentEnd: ":)" 430 431 }; 432 433 }); 434 435 CodeMirror.defineMIME("application/xquery", "xquery"); 436 437 });