prism-xquery.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. (function (Prism) {
  2. Prism.languages.xquery = Prism.languages.extend('markup', {
  3. 'xquery-comment': {
  4. pattern: /\(:[\s\S]*?:\)/,
  5. greedy: true,
  6. alias: 'comment'
  7. },
  8. 'string': {
  9. pattern: /(["'])(?:\1\1|(?!\1)[\s\S])*\1/,
  10. greedy: true
  11. },
  12. 'extension': {
  13. pattern: /\(#.+?#\)/,
  14. alias: 'symbol'
  15. },
  16. 'variable': /\$[-\w:]+/,
  17. 'axis': {
  18. pattern: /(^|[^-])(?:ancestor(?:-or-self)?|attribute|child|descendant(?:-or-self)?|following(?:-sibling)?|parent|preceding(?:-sibling)?|self)(?=::)/,
  19. lookbehind: true,
  20. alias: 'operator'
  21. },
  22. 'keyword-operator': {
  23. pattern: /(^|[^:-])\b(?:and|castable as|div|eq|except|ge|gt|idiv|instance of|intersect|is|le|lt|mod|ne|or|union)\b(?=$|[^:-])/,
  24. lookbehind: true,
  25. alias: 'operator'
  26. },
  27. 'keyword': {
  28. pattern: /(^|[^:-])\b(?:as|ascending|at|base-uri|boundary-space|case|cast as|collation|construction|copy-namespaces|declare|default|descending|else|empty (?:greatest|least)|encoding|every|external|for|function|if|import|in|inherit|lax|let|map|module|namespace|no-inherit|no-preserve|option|order(?: by|ed|ing)?|preserve|return|satisfies|schema|some|stable|strict|strip|then|to|treat as|typeswitch|unordered|validate|variable|version|where|xquery)\b(?=$|[^:-])/,
  29. lookbehind: true
  30. },
  31. 'function': /[\w-]+(?::[\w-]+)*(?=\s*\()/,
  32. 'xquery-element': {
  33. pattern: /(element\s+)[\w-]+(?::[\w-]+)*/,
  34. lookbehind: true,
  35. alias: 'tag'
  36. },
  37. 'xquery-attribute': {
  38. pattern: /(attribute\s+)[\w-]+(?::[\w-]+)*/,
  39. lookbehind: true,
  40. alias: 'attr-name'
  41. },
  42. 'builtin': {
  43. pattern: /(^|[^:-])\b(?:attribute|comment|document|element|processing-instruction|text|xs:(?:ENTITIES|ENTITY|ID|IDREFS?|NCName|NMTOKENS?|NOTATION|Name|QName|anyAtomicType|anyType|anyURI|base64Binary|boolean|byte|date|dateTime|dayTimeDuration|decimal|double|duration|float|gDay|gMonth|gMonthDay|gYear|gYearMonth|hexBinary|int|integer|language|long|negativeInteger|nonNegativeInteger|nonPositiveInteger|normalizedString|positiveInteger|short|string|time|token|unsigned(?:Byte|Int|Long|Short)|untyped(?:Atomic)?|yearMonthDuration))\b(?=$|[^:-])/,
  44. lookbehind: true
  45. },
  46. 'number': /\b\d+(?:\.\d+)?(?:E[+-]?\d+)?/,
  47. 'operator': [
  48. /[+*=?|@]|\.\.?|:=|!=|<[=<]?|>[=>]?/,
  49. {
  50. pattern: /(\s)-(?=\s)/,
  51. lookbehind: true
  52. }
  53. ],
  54. 'punctuation': /[[\](){},;:/]/
  55. });
  56. Prism.languages.xquery.tag.pattern = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/;
  57. Prism.languages.xquery['tag'].inside['attr-value'].pattern = /=(?:("|')(?:\\[\s\S]|\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}|(?!\1)[^\\])*\1|[^\s'">=]+)/;
  58. Prism.languages.xquery['tag'].inside['attr-value'].inside['punctuation'] = /^="|"$/;
  59. Prism.languages.xquery['tag'].inside['attr-value'].inside['expression'] = {
  60. // Allow for two levels of nesting
  61. pattern: /\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}/,
  62. inside: Prism.languages.xquery,
  63. alias: 'language-xquery'
  64. };
  65. // The following will handle plain text inside tags
  66. var stringifyToken = function (token) {
  67. if (typeof token === 'string') {
  68. return token;
  69. }
  70. if (typeof token.content === 'string') {
  71. return token.content;
  72. }
  73. return token.content.map(stringifyToken).join('');
  74. };
  75. var walkTokens = function (tokens) {
  76. var openedTags = [];
  77. for (var i = 0; i < tokens.length; i++) {
  78. var token = tokens[i];
  79. var notTagNorBrace = false;
  80. if (typeof token !== 'string') {
  81. if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
  82. // We found a tag, now find its kind
  83. if (token.content[0].content[0].content === '</') {
  84. // Closing tag
  85. if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
  86. // Pop matching opening tag
  87. openedTags.pop();
  88. }
  89. } else {
  90. if (token.content[token.content.length - 1].content === '/>') {
  91. // Autoclosed tag, ignore
  92. } else {
  93. // Opening tag
  94. openedTags.push({
  95. tagName: stringifyToken(token.content[0].content[1]),
  96. openedBraces: 0
  97. });
  98. }
  99. }
  100. } else if (
  101. openedTags.length > 0 && token.type === 'punctuation' && token.content === '{' &&
  102. // Ignore `{{`
  103. (!tokens[i + 1] || tokens[i + 1].type !== 'punctuation' || tokens[i + 1].content !== '{') &&
  104. (!tokens[i - 1] || tokens[i - 1].type !== 'plain-text' || tokens[i - 1].content !== '{')
  105. ) {
  106. // Here we might have entered an XQuery expression inside a tag
  107. openedTags[openedTags.length - 1].openedBraces++;
  108. } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
  109. // Here we might have left an XQuery expression inside a tag
  110. openedTags[openedTags.length - 1].openedBraces--;
  111. } else if (token.type !== 'comment') {
  112. notTagNorBrace = true;
  113. }
  114. }
  115. if (notTagNorBrace || typeof token === 'string') {
  116. if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
  117. // Here we are inside a tag, and not inside an XQuery expression.
  118. // That's plain text: drop any tokens matched.
  119. var plainText = stringifyToken(token);
  120. // And merge text with adjacent text
  121. if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
  122. plainText += stringifyToken(tokens[i + 1]);
  123. tokens.splice(i + 1, 1);
  124. }
  125. if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
  126. plainText = stringifyToken(tokens[i - 1]) + plainText;
  127. tokens.splice(i - 1, 1);
  128. i--;
  129. }
  130. if (/^\s+$/.test(plainText)) {
  131. tokens[i] = plainText;
  132. } else {
  133. tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
  134. }
  135. }
  136. }
  137. if (token.content && typeof token.content !== 'string') {
  138. walkTokens(token.content);
  139. }
  140. }
  141. };
  142. Prism.hooks.add('after-tokenize', function (env) {
  143. if (env.language !== 'xquery') {
  144. return;
  145. }
  146. walkTokens(env.tokens);
  147. });
  148. }(Prism));