Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for XQuery #1411

Merged
merged 2 commits into from
May 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,11 @@
"title": "Xojo (REALbasic)",
"owner": "Golmote"
},
"xquery": {
"title": "XQuery",
"require": "markup",
"owner": "Golmote"
},
"yaml": {
"title": "YAML",
"owner": "hason"
Expand Down
164 changes: 164 additions & 0 deletions components/prism-xquery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
(function (Prism) {

Prism.languages.xquery = Prism.languages.extend('markup', {
'xquery-comment': {
pattern: /\(:[\s\S]*?:\)/,
greedy: true,
alias: "comment"
},
'string': {
pattern: /(["'])(?:\1\1|(?!\1)[\s\S])*\1/,
greedy: true
},
'extension': {
pattern: /\(#.+?#\)/,
alias: 'symbol'
},
'variable': /\$[\w-:]+/,
'axis': {
pattern: /(^|[^-])(?:ancestor(?:-or-self)?|attribute|child|descendant(?:-or-self)?|following(?:-sibling)?|parent|preceding(?:-sibling)?|self)(?=::)/,
lookbehind: true,
alias: 'operator'
},
'keyword-operator': {
pattern: /(^|[^:-])\b(?:and|castable as|div|eq|except|ge|gt|idiv|instance of|intersect|is|le|lt|mod|ne|or|union)\b(?=$|[^:-])/,
lookbehind: true,
alias: 'operator'
},
'keyword': {
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(?=$|[^:-])/,
lookbehind: true
},
'function': /[\w-]+(?::[\w-]+)*(?=\s*\()/,
'xquery-element': {
pattern: /(element\s+)[\w-]+(?::[\w-]+)*/,
lookbehind: true,
alias: 'tag'
},
'xquery-attribute': {
pattern: /(attribute\s+)[\w-]+(?::[\w-]+)*/,
lookbehind: true,
alias: 'attr-name'
},
'builtin': {
pattern: /(^|[^:-])\b(?:attribute|comment|document|element|processing-instruction|text|xs:(?:anyAtomicType|anyType|anyURI|base64Binary|boolean|byte|date|dateTime|dayTimeDuration|decimal|double|duration|ENTITIES|ENTITY|float|gDay|gMonth|gMonthDay|gYear|gYearMonth|hexBinary|ID|IDREFS?|int|integer|language|long|Name|NCName|negativeInteger|NMTOKENS?|nonNegativeInteger|nonPositiveInteger|normalizedString|NOTATION|positiveInteger|QName|short|string|time|token|unsigned(?:Byte|Int|Long|Short)|untyped(?:Atomic)?|yearMonthDuration))\b(?=$|[^:-])/,
lookbehind: true
},
'number': /\b\d+(?:\.\d+)?(?:E[+-]?\d+)?/,
'operator': [
/[+*=?|@]|\.\.?|:=|!=|<[=<]?|>[=>]?/,
{
pattern: /(\s)-(?=\s)/,
lookbehind: true
}
],
'punctuation': /[[\](){},;:/]/
});

Prism.languages.xquery.tag.pattern = /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|{(?!{)(?:{(?:{[^}]*}|[^}])*}|[^}])+}|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i;
Prism.languages.xquery['tag'].inside['attr-value'].pattern = /=(?:("|')(?:\\[\s\S]|{(?!{)(?:{(?:{[^}]*}|[^}])*}|[^}])+}|(?!\1)[^\\])*\1|[^\s'">=]+)/i;
Prism.languages.xquery['tag'].inside['attr-value'].inside['punctuation'] = /^="|"$/;
Prism.languages.xquery['tag'].inside['attr-value'].inside['expression'] = {
// Allow for two levels of nesting
pattern: /{(?!{)(?:{(?:{[^}]*}|[^}])*}|[^}])+}/,
inside: {
rest: Prism.languages.xquery
},
'alias': 'language-xquery'
};

// The following will handle plain text inside tags
var stringifyToken = function (token) {
if (typeof token === 'string') {
return token;
}
if (typeof token.content === 'string') {
return token.content;
}
return token.content.map(stringifyToken).join('');
};

var walkTokens = function (tokens) {
var openedTags = [];
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
var notTagNorBrace = false;

if (typeof token !== 'string') {
if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
// We found a tag, now find its kind

if (token.content[0].content[0].content === '</') {
// Closing tag
if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
// Pop matching opening tag
openedTags.pop();
}
} else {
if (token.content[token.content.length - 1].content === '/>') {
// Autoclosed tag, ignore
} else {
// Opening tag
openedTags.push({
tagName: stringifyToken(token.content[0].content[1]),
openedBraces: 0
});
}
}
} else if (
openedTags.length > 0 && token.type === 'punctuation' && token.content === '{' &&
// Ignore `{{`
(!tokens[i + 1] || tokens[i + 1].type !== 'punctuation' || tokens[i + 1].content !== '{') &&
(!tokens[i - 1] || tokens[i - 1].type !== 'plain-text' || tokens[i - 1].content !== '{')
) {
// Here we might have entered an XQuery expression inside a tag
openedTags[openedTags.length - 1].openedBraces++;

} else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {

// Here we might have left an XQuery expression inside a tag
openedTags[openedTags.length - 1].openedBraces--;

} else if (token.type !== 'comment') {
notTagNorBrace = true
}
}
if (notTagNorBrace || typeof token === 'string') {
if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
// Here we are inside a tag, and not inside an XQuery expression.
// That's plain text: drop any tokens matched.
var plainText = stringifyToken(token);

// And merge text with adjacent text
if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
plainText += stringifyToken(tokens[i + 1]);
tokens.splice(i + 1, 1);
}
if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
plainText = stringifyToken(tokens[i - 1]) + plainText;
tokens.splice(i - 1, 1);
i--;
}

if (/^\s+$/.test(plainText)) {
tokens[i] = plainText;
} else {
tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
}
}
}

if (token.content && typeof token.content !== 'string') {
walkTokens(token.content);
}
}
};

Prism.hooks.add('after-tokenize', function (env) {
if (env.language !== 'xquery') {
return;
}
walkTokens(env.tokens);
});

}(Prism));
1 change: 1 addition & 0 deletions components/prism-xquery.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions examples/prism-xquery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<h2>Comments</h2>
<pre><code>(::)
(: Comment :)
(: Multi-line
comment :)
(:~
: The &lt;b>functx:substring-after-last&lt;/b> function returns the part
: of &lt;b>$string&lt;/b> that appears after the last occurrence of
: &lt;b>$delim&lt;/b>. If &lt;b>$string&lt;/b> does not contain
: &lt;b>$delim&lt;/b>, the entire string is returned.
:
: @param $string the string to substring
: @param $delim the delimiter
: @return the substring
:)</code></pre>

<h2>Variables</h2>
<pre><code>$myProduct
$foo-bar
$strings:LetterA</code></pre>

<h2>Functions</h2>
<pre><code>document-node(schema-element(catalog))
strings:trim($arg as xs:string?)
false()</code></pre>

<h2>Keywords</h2>
<pre><code>xquery version "1.0";
declare default element namespace "http://datypic.com/cat";
declare boundary-space preserve;
declare default collation "http://datypic.com/collation/custom";</code></pre>

<h2>Types</h2>
<pre><code>xs:anyAtomicType
element
xs:double</code></pre>

<h2>Full example</h2>
<pre><code>&lt;report xmlns="http://datypic.com/report"
xmlns:cat="http://datypic.com/cat"
xmlns:prod="http://datypic.com/prod"> {
for $product in doc("prod_ns.xml")/prod:product
return &lt;lineItem>
{$product/prod:number}
{$product/prod:name}
&lt;/lineItem>
} &lt;/report></code></pre>
2 changes: 1 addition & 1 deletion plugins/autoloader/prism-autoloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
}

// The dependencies map is built automatically with gulp
var lang_dependencies = /*languages_placeholder[*/{"javascript":"clike","actionscript":"javascript","arduino":"cpp","aspnet":["markup","csharp"],"bison":"c","c":"clike","csharp":"clike","cpp":"c","coffeescript":"javascript","crystal":"ruby","css-extras":"css","d":"clike","dart":"clike","django":"markup","erb":["ruby","markup-templating"],"fsharp":"clike","flow":"javascript","glsl":"clike","go":"clike","groovy":"clike","haml":"ruby","handlebars":"markup-templating","haxe":"clike","java":"clike","jolie":"clike","kotlin":"clike","less":"css","markdown":"markup","markup-templating":"markup","n4js":"javascript","nginx":"clike","objectivec":"c","opencl":"cpp","parser":"markup","php":["clike","markup-templating"],"php-extras":"php","plsql":"sql","processing":"clike","protobuf":"clike","pug":"javascript","qore":"clike","jsx":["markup","javascript"],"tsx":["jsx","typescript"],"reason":"clike","ruby":"clike","sass":"css","scss":"css","scala":"java","smarty":"markup-templating","soy":"markup-templating","swift":"clike","textile":"markup","tt2":["clike","markup-templating"],"twig":"markup","typescript":"javascript","vbnet":"basic","velocity":"markup","wiki":"markup","xeora":"markup"}/*]*/;
var lang_dependencies = /*languages_placeholder[*/{"javascript":"clike","actionscript":"javascript","arduino":"cpp","aspnet":["markup","csharp"],"bison":"c","c":"clike","csharp":"clike","cpp":"c","coffeescript":"javascript","crystal":"ruby","css-extras":"css","d":"clike","dart":"clike","django":"markup","erb":["ruby","markup-templating"],"fsharp":"clike","flow":"javascript","glsl":"clike","go":"clike","groovy":"clike","haml":"ruby","handlebars":"markup-templating","haxe":"clike","java":"clike","jolie":"clike","kotlin":"clike","less":"css","markdown":"markup","markup-templating":"markup","n4js":"javascript","nginx":"clike","objectivec":"c","opencl":"cpp","parser":"markup","php":["clike","markup-templating"],"php-extras":"php","plsql":"sql","processing":"clike","protobuf":"clike","pug":"javascript","qore":"clike","jsx":["markup","javascript"],"tsx":["jsx","typescript"],"reason":"clike","ruby":"clike","sass":"css","scss":"css","scala":"java","smarty":"markup-templating","soy":"markup-templating","swift":"clike","textile":"markup","tt2":["clike","markup-templating"],"twig":"markup","typescript":"javascript","vbnet":"basic","velocity":"markup","wiki":"markup","xeora":"markup","xquery":"markup"}/*]*/;

var lang_data = {};

Expand Down
Loading