diff --git a/.gitignore b/.gitignore index d3abde90..fac72179 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ npm-debug.log* dist/doc-temp dist/test dist +package-lock.json diff --git a/syntaxes/html.json b/syntaxes/html.json index 120ebbcf..e9b54e36 100644 --- a/syntaxes/html.json +++ b/syntaxes/html.json @@ -1,536 +1,519 @@ -{ - "fileTypes": [], - "name": "au.html", - "patterns": [ - { - "include": "#compose.html.au" - }, - { - "include": "#router-view.html.au" - }, - { - "include": "#meta.tag.block.any.html" - }, - { - "include": "#meta.tag.inline.any.html" - }, - { - "include": "#meta.tag.other.html" - }, - { - "include": "#string.interpolation.html.au" - }, - { - "include": "text.html.basic" - } - ], - "repository": { - "string.interpolation.html.au": { - "begin": "\\$\\{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.interpolation.start" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.interpolation.end" - } - }, - "name": "meta.string.interpolation" - }, - "meta.tag.block.any.html": { - "begin": "(<\/?)((?i:address|blockquote|dd|div|section|article|aside|header|footer|nav|menu|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|pre)(?!-)\\b)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "entity.name.tag.block.any.html" - } - }, - "end": "(>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.block.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - "meta.tag.inline.any.html": { - "begin": "(<\/?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)(?!-)\\b)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "entity.name.tag.inline.any.html" - } - }, - "end": "((?: ?)?>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.inline.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - "compose.html.au": { - "begin": "(<\/?)((?i:compose)\\b)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "compose.element.html.au" - } - }, - "end": "(>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.inline.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - "router-view.html.au": { - "begin": "(<\/?)((?i:router\\-view)\\b)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "router-view.element.html.au" - } - }, - "end": "(>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.inline.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - "meta.tag.other.html": { - "begin": "(<\/?)([a-zA-Z0-9:\\-]+)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "entity.name.tag.other.html" - } - }, - "end": "(>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.block.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - "tag-stuff": { - "patterns": [ - { - "include": "#tag-aurelia-repeat-for-attribute" - }, - { - "include": "#tag-aurelia-view-model-attribute" - }, - { - "include": "#tag-aurelia-model-attribute" - }, - { - "include" : "#tag-aurelia-view-attribute" - }, - { - "include" : "#tag-aurelia-controller-attribute" - }, - { - "include" : "#tag-aurelia-matcher-attribute" - }, - { - "include" : "#tag-aurelia-if-attribute" - }, - { - "include" : "#tag-aurelia-show-attribute" - }, - { - "include" : "#tag-aurelia-route-href-attribute" - }, - { - "include": "#tag-aurelia-containerless-attribute" - }, - { - "include": "#tag-aurelia-compile-spy-attribute" - }, - { - "include": "#tag-aurelia-view-spy-attribute" - }, - { - "include": "#tag-aurelia-replace-part-attribute" - }, - { - "include": "#tag-aurelia-bindable-attribute" - }, - { - "include": "#tag-aurelia-attribute-with-databinding-attribute" - }, - { - "include": "#tag-aurelia-attribute-with-ref-attribute" - }, - { - "include": "#tag-aurelia-attribute-with-invoke-attribute" - }, - { - "include": "#tag-aurelia-ref-attribute" - }, - { - "include": "#tag-aurelia-replaceable-attribute" - }, - { - "include": "#tag-aurelia-as-element-attribute" - }, - { - "include": "#tag-generic-attribute" - }, - { - "include": "#string-double-quoted" - }, - { - "include": "#string-single-quoted" - } - ] - }, - "tag-aurelia-attribute-with-databinding-attribute": { - "match": "(?)", - "captures": { - "1": { - "name": "containerless.attribute.html.au" - } - } - }, - "tag-aurelia-compile-spy-attribute" : { - "match": "(compile-spy)(?= |>)", - "captures": { - "1": { - "name": "compile-spy.attribute.html.au" - } - } - }, - "tag-aurelia-view-spy-attribute" : { - "match": "(view-spy)(?= |>)", - "captures": { - "1": { - "name": "view-spy.attribute.html.au" - } - } - }, - "tag-aurelia-if-attribute" : { - "match": "(?<=[^\"-])(if|naive-if)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", - "captures": { - "1": { - "name": "if.attribute.html.au" - }, - "2": { - "name": "punctuation.definition.tag.begin.html" - }, - "3": { - "name": "databinding.attribute.html.au" - } - } - }, - "tag-aurelia-route-href-attribute" : { - "match": "(?<=[^\"-])(route-href)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", - "captures": { - "1": { - "name": "route-href.attribute.html.au" - }, - "2": { - "name": "punctuation.definition.tag.begin.html" - }, - "3": { - "name": "databinding.attribute.html.au" - } - } - }, - "tag-aurelia-show-attribute" : { - "match": "(?<=[^\"-])(show)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", - "captures": { - "1": { - "name": "show.attribute.html.au" - }, - "2": { - "name": "punctuation.definition.tag.begin.html" - }, - "3": { - "name": "databinding.attribute.html.au" - } - } - }, - "tag-generic-attribute": { - "match": "(?<=[^=])\\b([a-zA-Z0-9:-]+)", - "name": "entity.other.attribute-name.html" - }, - "tag-aurelia-ref-attribute": { - "match": "\\bref\\b", - "name": "ref.attribute.html.au" - }, - "tag-aurelia-as-element-attribute": { - "match": "(?)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.block.any.html", + "patterns": [{ + "include": "#tag-stuff" + }] + }, + "meta.tag.inline.any.html": { + "begin": "(<\/?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)(?!-)\\b)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.inline.any.html" + } + }, + "end": "((?: ?)?>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.any.html", + "patterns": [{ + "include": "#tag-stuff" + }] + }, + "compose.html.au": { + "begin": "(<\/?)((?i:compose)\\b)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "compose.element.html.au" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.any.html", + "patterns": [{ + "include": "#tag-stuff" + }] + }, + "router-view.html.au": { + "begin": "(<\/?)((?i:router\\-view)\\b)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "router-view.element.html.au" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.any.html", + "patterns": [{ + "include": "#tag-stuff" + }] + }, + "meta.tag.other.html": { + "begin": "(<\/?)([a-zA-Z0-9:\\-]+)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.other.html" + } + }, + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.block.any.html", + "patterns": [{ + "include": "#tag-stuff" + }] + }, + "tag-stuff": { + "patterns": [{ + "include": "#tag-aurelia-repeat-for-attribute" + }, + { + "include": "#tag-aurelia-view-model-attribute" + }, + { + "include": "#tag-aurelia-model-attribute" + }, + { + "include": "#tag-aurelia-view-attribute" + }, + { + "include": "#tag-aurelia-controller-attribute" + }, + { + "include": "#tag-aurelia-matcher-attribute" + }, + { + "include": "#tag-aurelia-if-attribute" + }, + { + "include": "#tag-aurelia-show-attribute" + }, + { + "include": "#tag-aurelia-route-href-attribute" + }, + { + "include": "#tag-aurelia-containerless-attribute" + }, + { + "include": "#tag-aurelia-compile-spy-attribute" + }, + { + "include": "#tag-aurelia-view-spy-attribute" + }, + { + "include": "#tag-aurelia-replace-part-attribute" + }, + { + "include": "#tag-aurelia-bindable-attribute" + }, + { + "include": "#tag-aurelia-attribute-with-databinding-attribute" + }, + { + "include": "#tag-aurelia-attribute-with-ref-attribute" + }, + { + "include": "#tag-aurelia-attribute-with-invoke-attribute" + }, + { + "include": "#tag-aurelia-ref-attribute" + }, + { + "include": "#tag-aurelia-replaceable-attribute" + }, + { + "include": "#tag-aurelia-as-element-attribute" + }, + { + "include": "#tag-generic-attribute" + }, + { + "include": "#string-double-quoted" + }, + { + "include": "#string-single-quoted" + } + ] + }, + "tag-aurelia-attribute-with-databinding-attribute": { + "match": "(?)", + "captures": { + "1": { + "name": "containerless.attribute.html.au" + } + } + }, + "tag-aurelia-compile-spy-attribute": { + "match": "(compile-spy)(?= |>)", + "captures": { + "1": { + "name": "compile-spy.attribute.html.au" + } + } + }, + "tag-aurelia-view-spy-attribute": { + "match": "(view-spy)(?= |>)", + "captures": { + "1": { + "name": "view-spy.attribute.html.au" + } + } + }, + "tag-aurelia-if-attribute": { + "match": "(?<=[^\"-])(if|naive-if)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", + "captures": { + "1": { + "name": "if.attribute.html.au" + }, + "2": { + "name": "punctuation.definition.tag.begin.html" + }, + "3": { + "name": "databinding.attribute.html.au" + } + } + }, + "tag-aurelia-route-href-attribute": { + "match": "(?<=[^\"-])(route-href)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", + "captures": { + "1": { + "name": "route-href.attribute.html.au" + }, + "2": { + "name": "punctuation.definition.tag.begin.html" + }, + "3": { + "name": "databinding.attribute.html.au" + } + } + }, + "tag-aurelia-show-attribute": { + "match": "(?<=[^\"-])(show)(?![-])(\\.)?(bind|one-way|two-way|one-time)?", + "captures": { + "1": { + "name": "show.attribute.html.au" + }, + "2": { + "name": "punctuation.definition.tag.begin.html" + }, + "3": { + "name": "databinding.attribute.html.au" + } + } + }, + "tag-generic-attribute": { + "match": "(?<=[^=])\\b([a-zA-Z0-9:-]+)", + "name": "entity.other.attribute-name.html" + }, + "tag-aurelia-ref-attribute": { + "match": "\\bref\\b", + "name": "ref.attribute.html.au" + }, + "tag-aurelia-as-element-attribute": { + "match": "(? { - - it('must tokenize string interpolation in element with scope "meta.string.interpolation"', () => { - - // arrange - let scope = 'meta.string.interpolation'; - - // act - let lineToken = tokenizeLine('
${foo}
'); - - // assert - let startToken = getTokenOnCharRange(lineToken, 5, 7); - let middleToken = getTokenOnCharRange(lineToken, 7, 10); - let endToken = getTokenOnCharRange(lineToken, 10, 11); - - assert.isOk(hasScope(startToken.scopes, scope)); - assert.isOk(hasScope(middleToken.scopes, scope)); - assert.isOk(hasScope(endToken.scopes, scope)); - - }); - - it('must tokenize string interpolation in element with scope "punctuation.definition.string.interpolation.start"', () => { - - // arrange - let scope = 'punctuation.definition.string.interpolation.start'; - - // act - let lineToken = tokenizeLine('
${foo}
'); - - // assert - let token = getTokenOnCharRange(lineToken, 5, 7); - assert.isOk(hasScope(token.scopes, scope)); - - }); - - it('must tokenize string interpolation in element with scope "punctuation.definition.string.interpolation.end"', () => { - - // arrange - let scope = 'punctuation.definition.string.interpolation.end'; - - // act - let lineToken = tokenizeLine('
${foo}
'); - - // assert - let token = getTokenOnCharRange(lineToken, 10, 11); - assert.isOk(hasScope(token.scopes, scope)); - - }); - - it('must tokenize string interpolation in attribute with scope "meta.string.interpolation"', () => { - - // arrange - let scope = 'meta.string.interpolation'; - - // act - let lineToken = tokenizeLine('
'); - - // assert - let startToken = getTokenOnCharRange(lineToken, 12, 14); - let middleToken = getTokenOnCharRange(lineToken, 14, 17); - let endToken = getTokenOnCharRange(lineToken, 17, 18); - - assert.isOk(hasScope(startToken.scopes, scope)); - assert.isOk(hasScope(middleToken.scopes, scope)); - assert.isOk(hasScope(endToken.scopes, scope)); - - }); - - it('must tokenize string interpolation in attribute with scope "punctuation.definition.string.interpolation.start"', () => { - - // arrange - let scope = 'punctuation.definition.string.interpolation.start'; - - // act - let lineToken = tokenizeLine('
'); - - // assert - let token = getTokenOnCharRange(lineToken, 12, 14); - assert.isOk(hasScope(token.scopes, scope)); - - }); - - it('must tokenize string interpolation in attribute with scope "punctuation.definition.string.interpolation.end"', () => { - - // arrange - let scope = 'punctuation.definition.string.interpolation.end'; - - // act - let lineToken = tokenizeLine('
'); - - // assert - let token = getTokenOnCharRange(lineToken, 17, 18); - assert.isOk(hasScope(token.scopes, scope)); - - }); - - it('must tokenize string interpolation with condition with scope "meta.string.interpolation"', () => { - - // arrange - let scope = 'meta.string.interpolation'; - - // act - let lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); - - // assert - let startToken = getTokenOnCharRange(lineToken, 0, 2); - let middleToken = getTokenOnCharRange(lineToken, 2, 26); - let endToken = getTokenOnCharRange(lineToken, 26, 27); - - assert.isOk(hasScope(startToken.scopes, scope)); - assert.isOk(hasScope(middleToken.scopes, scope)); - assert.isOk(hasScope(endToken.scopes, scope)); - - }); - - it('must tokenize string interpolation with condition with scope "punctuation.definition.string.interpolation.start"', () => { - - // arrange - let scope = 'punctuation.definition.string.interpolation.start'; - - // act - let lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); - - // assert - let token = getTokenOnCharRange(lineToken, 0, 2); - assert.isOk(hasScope(token.scopes, scope)); - - }); - - it('must tokenize string interpolation with condition with scope "punctuation.definition.string.interpolation.end"', () => { - - // arrange - let scope = 'meta.string.interpolation'; - - // act - let lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); - - // assert - let token = getTokenOnCharRange(lineToken, 26, 27); - assert.isOk(hasScope(token.scopes, scope)); - - }); - -}); +import { assert } from 'chai'; +import { getTokenOnCharRange, hasScope, tokenizeLine, writeOut } from './test.utils'; + +describe('The Aurelia HTML syntax string interpolation', () => { + + it('must tokenize string interpolation in element with scope correct scopes', () => { + + // act + const lineToken = tokenizeLine('
${foo}
'); + + // assert + const startToken = getTokenOnCharRange(lineToken, 5, 7); + const middleToken = getTokenOnCharRange(lineToken, 7, 10); + const endToken = getTokenOnCharRange(lineToken, 10, 11); + + assert.isOk(hasScope(startToken.scopes, 'punctuation.definition.string.interpolation.start')); + assert.isOk(hasScope(middleToken.scopes, 'meta.string.interpolation')); + assert.isOk(hasScope(endToken.scopes, 'punctuation.definition.string.interpolation.end')); + + }); + + it('must tokenize string interpolation in element with scope "punctuation.definition.string.interpolation.start"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.start'; + + // act + const lineToken = tokenizeLine('
${foo}
'); + + // assert + const token = getTokenOnCharRange(lineToken, 5, 7); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize string interpolation in element with scope "punctuation.definition.string.interpolation.end"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.end'; + + // act + const lineToken = tokenizeLine('
${foo}
'); + + // assert + const token = getTokenOnCharRange(lineToken, 10, 11); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize string interpolation in attribute with correct scopes', () => { + + // act + const lineToken = tokenizeLine('
'); + + // assert + const startToken = getTokenOnCharRange(lineToken, 12, 14); + const middleToken = getTokenOnCharRange(lineToken, 14, 17); + const endToken = getTokenOnCharRange(lineToken, 17, 18); + + assert.isOk(hasScope(startToken.scopes, 'punctuation.definition.string.interpolation.start')); + assert.isOk(hasScope(middleToken.scopes, 'meta.string.interpolation')); + assert.isOk(hasScope(endToken.scopes, 'punctuation.definition.string.interpolation.end')); + + }); + + it('must tokenize string interpolation in attribute with scope "punctuation.definition.string.interpolation.start"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.start'; + + // act + const lineToken = tokenizeLine('
'); + + // assert + const token = getTokenOnCharRange(lineToken, 12, 14); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize string interpolation in attribute with scope "punctuation.definition.string.interpolation.end"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.end'; + + // act + const lineToken = tokenizeLine('
'); + + // assert + const token = getTokenOnCharRange(lineToken, 17, 18); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize string interpolation with condition with correct scopes', () => { + + // act + const lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); + + // assert + const startToken = getTokenOnCharRange(lineToken, 0, 2); + const middleToken = getTokenOnCharRange(lineToken, 2, 26); + const endToken = getTokenOnCharRange(lineToken, 26, 27); + + assert.isOk(hasScope(startToken.scopes, 'punctuation.definition.string.interpolation.start')); + assert.isOk(hasScope(middleToken.scopes, 'meta.string.interpolation')); + assert.isOk(hasScope(endToken.scopes, 'punctuation.definition.string.interpolation.end')); + + }); + + it('must tokenize string interpolation with condition with scope "punctuation.definition.string.interpolation.start"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.start'; + + // act + const lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); + + // assert + const token = getTokenOnCharRange(lineToken, 0, 2); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize string interpolation with condition with scope "punctuation.definition.string.interpolation.end"', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.end'; + + // act + const lineToken = tokenizeLine('${foo === true ? "a" : "b"}'); + + // assert + const token = getTokenOnCharRange(lineToken, 26, 27); + assert.isOk(hasScope(token.scopes, scope)); + + }); + + it('must tokenize only the last } with the scope "punctuation.definition.string.interpolation.end", resolves issue #48', () => { + + // arrange + const scope = 'punctuation.definition.string.interpolation.end'; + + // act + const lineToken = tokenizeLine(''); + + // assert + const closeToken = getTokenOnCharRange(lineToken, 41, 42); + assert.isOk(hasScope(closeToken.scopes, scope)); + + const tokenInText = getTokenOnCharRange(lineToken, 19, 41); + assert.isNotOk(hasScope(tokenInText.scopes, scope)); + }); + +});