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  });