summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/ide/lib/languages/vue.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide/lib/languages/vue.js')
-rw-r--r--app/assets/javascripts/ide/lib/languages/vue.js306
1 files changed, 306 insertions, 0 deletions
diff --git a/app/assets/javascripts/ide/lib/languages/vue.js b/app/assets/javascripts/ide/lib/languages/vue.js
new file mode 100644
index 00000000000..b9ff5c5d776
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/languages/vue.js
@@ -0,0 +1,306 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See https://github.com/microsoft/monaco-languages/blob/master/LICENSE.md
+ *--------------------------------------------------------------------------------------------*/
+
+// Based on handlebars template in https://github.com/microsoft/monaco-languages/blob/master/src/handlebars/handlebars.ts
+// Look for "vuejs template attributes" in this file for Vue specific syntax.
+
+import { languages } from 'monaco-editor';
+
+/* eslint-disable no-useless-escape */
+/* eslint-disable @gitlab/require-i18n-strings */
+
+const EMPTY_ELEMENTS = [
+ 'area',
+ 'base',
+ 'br',
+ 'col',
+ 'embed',
+ 'hr',
+ 'img',
+ 'input',
+ 'keygen',
+ 'link',
+ 'menuitem',
+ 'meta',
+ 'param',
+ 'source',
+ 'track',
+ 'wbr',
+];
+
+const conf = {
+ wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
+
+ comments: {
+ blockComment: ['{{!--', '--}}'],
+ },
+
+ brackets: [['<!--', '-->'], ['<', '>'], ['{{', '}}'], ['{', '}'], ['(', ')']],
+
+ autoClosingPairs: [
+ { open: '{', close: '}' },
+ { open: '[', close: ']' },
+ { open: '(', close: ')' },
+ { open: '"', close: '"' },
+ { open: "'", close: "'" },
+ ],
+
+ surroundingPairs: [
+ { open: '<', close: '>' },
+ { open: '"', close: '"' },
+ { open: "'", close: "'" },
+ ],
+
+ onEnterRules: [
+ {
+ beforeText: new RegExp(
+ `<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,
+ 'i',
+ ),
+ afterText: /^<\/(\w[\w\d]*)\s*>$/i,
+ action: { indentAction: languages.IndentAction.IndentOutdent },
+ },
+ {
+ beforeText: new RegExp(
+ `<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,
+ 'i',
+ ),
+ action: { indentAction: languages.IndentAction.Indent },
+ },
+ ],
+};
+
+const language = {
+ defaultToken: '',
+ tokenPostfix: '',
+ // ignoreCase: true,
+
+ // The main tokenizer for our languages
+ tokenizer: {
+ root: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.root' }],
+ [/<!DOCTYPE/, 'metatag.html', '@doctype'],
+ [/<!--/, 'comment.html', '@comment'],
+ [/(<)([\w]+)(\/>)/, ['delimiter.html', 'tag.html', 'delimiter.html']],
+ [/(<)(script)/, ['delimiter.html', { token: 'tag.html', next: '@script' }]],
+ [/(<)(style)/, ['delimiter.html', { token: 'tag.html', next: '@style' }]],
+ [/(<)([:\w]+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]],
+ [/(<\/)([\w]+)/, ['delimiter.html', { token: 'tag.html', next: '@otherTag' }]],
+ [/</, 'delimiter.html'],
+ [/\{/, 'delimiter.html'],
+ [/[^<{]+/], // text
+ ],
+
+ doctype: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.comment' }],
+ [/[^>]+/, 'metatag.content.html'],
+ [/>/, 'metatag.html', '@pop'],
+ ],
+
+ comment: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.comment' }],
+ [/-->/, 'comment.html', '@pop'],
+ [/[^-]+/, 'comment.content.html'],
+ [/./, 'comment.content.html'],
+ ],
+
+ otherTag: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.otherTag' }],
+ [/\/?>/, 'delimiter.html', '@pop'],
+
+ // -- BEGIN vuejs template attributes
+ [/(v-|@|:)[\w\-\.\:\[\]]+="([^"]*)"/, 'variable'],
+ [/(v-|@|:)[\w\-\.\:\[\]]+='([^']*)'/, 'variable'],
+
+ [/"([^"]*)"/, 'attribute.value'],
+ [/'([^']*)'/, 'attribute.value'],
+
+ [/[\w\-\.\:\[\]]+/, 'attribute.name'],
+ // -- END vuejs template attributes
+
+ [/=/, 'delimiter'],
+ [/[ \t\r\n]+/], // whitespace
+ ],
+
+ // -- BEGIN <script> tags handling
+
+ // After <script
+ script: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.script' }],
+ [/type/, 'attribute.name', '@scriptAfterType'],
+ [/"([^"]*)"/, 'attribute.value'],
+ [/'([^']*)'/, 'attribute.value'],
+ [/[\w\-]+/, 'attribute.name'],
+ [/=/, 'delimiter'],
+ [
+ />/,
+ {
+ token: 'delimiter.html',
+ next: '@scriptEmbedded.text/javascript',
+ nextEmbedded: 'text/javascript',
+ },
+ ],
+ [/[ \t\r\n]+/], // whitespace
+ [
+ /(<\/)(script\s*)(>)/,
+ ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }],
+ ],
+ ],
+
+ // After <script ... type
+ scriptAfterType: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.scriptAfterType' }],
+ [/=/, 'delimiter', '@scriptAfterTypeEquals'],
+ [
+ />/,
+ {
+ token: 'delimiter.html',
+ next: '@scriptEmbedded.text/javascript',
+ nextEmbedded: 'text/javascript',
+ },
+ ], // cover invalid e.g. <script type>
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/script\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ // After <script ... type =
+ scriptAfterTypeEquals: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.scriptAfterTypeEquals' }],
+ [/"([^"]*)"/, { token: 'attribute.value', switchTo: '@scriptWithCustomType.$1' }],
+ [/'([^']*)'/, { token: 'attribute.value', switchTo: '@scriptWithCustomType.$1' }],
+ [
+ />/,
+ {
+ token: 'delimiter.html',
+ next: '@scriptEmbedded.text/javascript',
+ nextEmbedded: 'text/javascript',
+ },
+ ], // cover invalid e.g. <script type=>
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/script\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ // After <script ... type = $S2
+ scriptWithCustomType: [
+ [
+ /\{\{/,
+ { token: '@rematch', switchTo: '@handlebarsInSimpleState.scriptWithCustomType.$S2' },
+ ],
+ [/>/, { token: 'delimiter.html', next: '@scriptEmbedded.$S2', nextEmbedded: '$S2' }],
+ [/"([^"]*)"/, 'attribute.value'],
+ [/'([^']*)'/, 'attribute.value'],
+ [/[\w\-]+/, 'attribute.name'],
+ [/=/, 'delimiter'],
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/script\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ scriptEmbedded: [
+ [
+ /\{\{/,
+ {
+ token: '@rematch',
+ switchTo: '@handlebarsInEmbeddedState.scriptEmbedded.$S2',
+ nextEmbedded: '@pop',
+ },
+ ],
+ [/<\/script/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
+ ],
+
+ // -- END <script> tags handling
+
+ // -- BEGIN <style> tags handling
+
+ // After <style
+ style: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.style' }],
+ [/type/, 'attribute.name', '@styleAfterType'],
+ [/"([^"]*)"/, 'attribute.value'],
+ [/'([^']*)'/, 'attribute.value'],
+ [/[\w\-]+/, 'attribute.name'],
+ [/=/, 'delimiter'],
+ [/>/, { token: 'delimiter.html', next: '@styleEmbedded.text/css', nextEmbedded: 'text/css' }],
+ [/[ \t\r\n]+/], // whitespace
+ [
+ /(<\/)(style\s*)(>)/,
+ ['delimiter.html', 'tag.html', { token: 'delimiter.html', next: '@pop' }],
+ ],
+ ],
+
+ // After <style ... type
+ styleAfterType: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.styleAfterType' }],
+ [/=/, 'delimiter', '@styleAfterTypeEquals'],
+ [/>/, { token: 'delimiter.html', next: '@styleEmbedded.text/css', nextEmbedded: 'text/css' }], // cover invalid e.g. <style type>
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/style\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ // After <style ... type =
+ styleAfterTypeEquals: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.styleAfterTypeEquals' }],
+ [/"([^"]*)"/, { token: 'attribute.value', switchTo: '@styleWithCustomType.$1' }],
+ [/'([^']*)'/, { token: 'attribute.value', switchTo: '@styleWithCustomType.$1' }],
+ [/>/, { token: 'delimiter.html', next: '@styleEmbedded.text/css', nextEmbedded: 'text/css' }], // cover invalid e.g. <style type=>
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/style\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ // After <style ... type = $S2
+ styleWithCustomType: [
+ [/\{\{/, { token: '@rematch', switchTo: '@handlebarsInSimpleState.styleWithCustomType.$S2' }],
+ [/>/, { token: 'delimiter.html', next: '@styleEmbedded.$S2', nextEmbedded: '$S2' }],
+ [/"([^"]*)"/, 'attribute.value'],
+ [/'([^']*)'/, 'attribute.value'],
+ [/[\w\-]+/, 'attribute.name'],
+ [/=/, 'delimiter'],
+ [/[ \t\r\n]+/], // whitespace
+ [/<\/style\s*>/, { token: '@rematch', next: '@pop' }],
+ ],
+
+ styleEmbedded: [
+ [
+ /\{\{/,
+ {
+ token: '@rematch',
+ switchTo: '@handlebarsInEmbeddedState.styleEmbedded.$S2',
+ nextEmbedded: '@pop',
+ },
+ ],
+ [/<\/style/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
+ ],
+
+ // -- END <style> tags handling
+
+ handlebarsInSimpleState: [
+ [/\{\{\{?/, 'delimiter.handlebars'],
+ [/\}\}\}?/, { token: 'delimiter.handlebars', switchTo: '@$S2.$S3' }],
+ { include: 'handlebarsRoot' },
+ ],
+
+ handlebarsInEmbeddedState: [
+ [/\{\{\{?/, 'delimiter.handlebars'],
+ [/\}\}\}?/, { token: 'delimiter.handlebars', switchTo: '@$S2.$S3', nextEmbedded: '$S3' }],
+ { include: 'handlebarsRoot' },
+ ],
+
+ handlebarsRoot: [
+ [/"[^"]*"/, 'string.handlebars'],
+ [/[#/][^\s}]+/, 'keyword.helper.handlebars'],
+ [/else\b/, 'keyword.helper.handlebars'],
+ [/[\s]+/],
+ [/[^}]/, 'variable.parameter.handlebars'],
+ ],
+ },
+};
+
+export default {
+ id: 'vue',
+ extensions: ['.vue'],
+ aliases: ['Vue', 'vue'],
+ mimetypes: ['text/x-vue-template'],
+ conf,
+ language,
+};