Prechádzať zdrojové kódy

Feat: first work for rsx vscode ext

Jonathan Kelley 4 rokov pred
rodič
commit
2e626aea51

+ 2 - 0
Cargo.toml

@@ -10,6 +10,8 @@ members = [
     "packages/router",
     "packages/ssr",
     "packages/webview",
+    # Not a rust package, :(. One day, maybe
+    "packages/vscode-ext",
     # "packages/macro",
     # TODO @Jon, share the validation code
     # "packages/web",

+ 0 - 2
examples/common.rs

@@ -46,7 +46,5 @@ pub mod logger {
             .chain(std::io::stdout())
             .apply()
             .unwrap();
-
-        debug!("finished setting up logging! yay!");
     }
 }

+ 0 - 1
examples/doc_generator.rs

@@ -17,7 +17,6 @@ fn main() {
 
             <head>
             </head>
-
             <body>
             </body>
 

+ 6 - 9
examples/hello_web.rs

@@ -1,9 +1,6 @@
-fn main() {
-    println!("Hello world!");
-    println!("Hello world!");
-    println!("Hello world!");
-    println!("Hello world!");
-    println!("Hello world!");
-    println!("Hello world!");
-    println!("Hello world!");
-}
+//! Example: Hello-web
+//! -----------------
+//!
+//! This example showcases Dioxus-websys to build interactive single-page-applications, much like React.
+
+fn main() {}

+ 7 - 5
examples/router.rs

@@ -1,9 +1,11 @@
 //! Dioxus Router
+//! -------------
+//!
+//! This exmaple showcases the Dioxus Router hook. This hook makes it possible to using the browser's navigation API to
+//! display different content based on the Page's URL. The hook provides a configuration object that calls various
+//! specified callbacks whenever the page URL changes. Using this hook should feel like building a "URL directory", similar
+//! to how Tide handles paths.
 
 use dioxus::prelude::*;
 
-fn main() {
-    // let mut app = App::new();
-
-    // app.at(c)
-}
+fn main() {}

+ 21 - 14
examples/tide_ssr.rs

@@ -1,3 +1,6 @@
+//! Example: Tide Server-Side-Rendering
+//! -----------------------------------
+//!
 //! This demo shows how to use the to_string utility on VNodes to convert them into valid HTML.
 //! You can use the html! macro to craft webpages generated by the server on-the-fly.
 //!
@@ -5,6 +8,7 @@
 //! thousands of simultaneous clients on minimal hardware.
 
 use dioxus::prelude::*;
+use rand::Rng;
 use tide::{Request, Response};
 
 #[async_std::main]
@@ -18,35 +22,38 @@ async fn main() -> Result<(), std::io::Error> {
     // Launch the server
     let addr = "127.0.0.1:8080";
     log::info!("App is ready at {}", addr);
-    log::info!("Navigate to an fibonacci number, like so http://localhost:8080/fib/21");
+    log::info!("Navigate to a fibonacci number: http://{}/fib/21", addr);
     app.listen(addr).await?;
 
     Ok(())
 }
 
+fn fib(n: usize) -> usize {
+    if n == 0 || n == 1 {
+        n
+    } else {
+        fib(n - 1) + fib(n - 2)
+    }
+}
+
 /// Calculate the fibonacci number for a given request input
 async fn fibsum(req: Request<()>) -> tide::Result<tide::Response> {
-    use std::time::Instant;
     let n: usize = req.param("n")?.parse().unwrap_or(0);
 
-    fn fib(n: usize) -> usize {
-        if n == 0 || n == 1 {
-            n
-        } else {
-            fib(n - 1) + fib(n - 2)
-        }
-    }
-
     // Start a stopwatch
     // Compute the nth number in the fibonacci sequence
     // Stop the stopwatch
-    let start = Instant::now();
+    let start = std::time::Instant::now();
     let fib_n = fib(n);
     let duration = start.elapsed().as_nanos();
 
-    let mut rng = rand::thread_rng();
-    use rand::Rng;
-    let other_fib_to_try = rng.gen_range(1..42);
+    // Generate another random number to try
+    let other_fib_to_try = rand::thread_rng().gen_range(1..42);
+
+    let g = html! {
+        <div>
+        </div>
+    };
 
     let nodes = html! {
         <html>

+ 1 - 0
examples/webview.rs

@@ -1,4 +1,5 @@
 //! Example: Webview Renderer
+//! -------------------------
 //!
 //! This example shows how to use the dioxus_webview crate to build a basic desktop application.
 //!

+ 4 - 0
packages/vscode-ext/.gitignore

@@ -0,0 +1,4 @@
+out
+node_modules
+client/server
+.vscode-test

+ 44 - 0
packages/vscode-ext/.vscode/launch.json

@@ -0,0 +1,44 @@
+// A launch configuration that compiles the extension and then opens it inside a new window
+{
+	"version": "0.2.0",
+	"configurations": [
+		{
+			"type": "extensionHost",
+			"request": "launch",
+			"name": "Launch Client",
+			"runtimeExecutable": "${execPath}",
+			"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
+			"outFiles": ["${workspaceRoot}/client/out/**/*.js"],
+			"preLaunchTask": {
+				"type": "npm",
+				"script": "watch"
+			}
+		},
+		{
+			"type": "node",
+			"request": "attach",
+			"name": "Attach to Server",
+			"port": 6009,
+			"restart": true,
+			"outFiles": ["${workspaceRoot}/server/out/**/*.js"]
+		},
+		{
+			"name": "Language Server E2E Test",
+			"type": "extensionHost",
+			"request": "launch",
+			"runtimeExecutable": "${execPath}",
+			"args": [
+				"--extensionDevelopmentPath=${workspaceRoot}",
+				"--extensionTestsPath=${workspaceRoot}/client/out/test",
+				"${workspaceRoot}/client/testFixture"
+			],
+			"outFiles": ["${workspaceRoot}/client/out/test/**/*.js"]
+		}
+	],
+	"compounds": [
+		{
+			"name": "Client + Server",
+			"configurations": ["Launch Client", "Attach to Server"]
+		}
+	]
+}

+ 33 - 0
packages/vscode-ext/.vscode/tasks.json

@@ -0,0 +1,33 @@
+{
+	"version": "2.0.0",
+	"tasks": [
+		{
+			"type": "npm",
+			"script": "compile",
+			"group": "build",
+			"presentation": {
+				"panel": "dedicated",
+				"reveal": "never"
+			},
+			"problemMatcher": [
+				"$tsc"
+			]
+		},
+		{
+			"type": "npm",
+			"script": "watch",
+			"isBackground": true,
+			"group": {
+				"kind": "build",
+				"isDefault": true
+			},
+			"presentation": {
+				"panel": "dedicated",
+				"reveal": "never"
+			},
+			"problemMatcher": [
+				"$tsc-watch"
+			]
+		}
+	]
+}

+ 14 - 0
packages/vscode-ext/.vscodeignore

@@ -0,0 +1,14 @@
+.vscode/**
+**/*.ts
+**/*.map
+.gitignore
+**/tsconfig.json
+**/tsconfig.base.json
+contributing.md
+.travis.yml
+client/node_modules/**
+!client/node_modules/vscode-jsonrpc/**
+!client/node_modules/vscode-languageclient/**
+!client/node_modules/vscode-languageserver-protocol/**
+!client/node_modules/vscode-languageserver-types/**
+!client/node_modules/semver/**

+ 14 - 0
packages/vscode-ext/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "syntax-gen"
+version = "0.1.0"
+authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+
+
+[[bin]]
+path = "./rust/main.rs"
+name = "main"

+ 17 - 0
packages/vscode-ext/README.md

@@ -0,0 +1,17 @@
+# VSCode support for Dioxus html macro
+
+This macro provides syntax highlighting for the html! macro used in Dioxus projects. Users should feel at home writing html and css alongside the custom attributes used by Dioxus.
+
+
+## How it works
+This extension works by:
+- Creating a custom HTML ruleset for dioxus html! nodes
+- Request forwarding content
+
+
+## Resources
+Request forwarding is performed intelligently by the extension. 
+Requests within the html! tag are forwarded to the html language service. It's simple and doesn't 
+
+https://code.visualstudio.com/api/language-extensions/embedded-languages#language-services
+

+ 76 - 0
packages/vscode-ext/client/package-lock.json

@@ -0,0 +1,76 @@
+{
+	"name": "lsp-sample-client",
+	"version": "0.0.1",
+	"lockfileVersion": 1,
+	"requires": true,
+	"dependencies": {
+		"@types/vscode": {
+			"version": "1.43.0",
+			"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.43.0.tgz",
+			"integrity": "sha512-kIaR9qzd80rJOxePKpCB/mdy00mz8Apt2QA5Y6rdrKFn13QNFNeP3Hzmsf37Bwh/3cS7QjtAeGSK7wSqAU0sYQ==",
+			"dev": true
+		},
+		"vscode-html-languageservice": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-3.0.3.tgz",
+			"integrity": "sha512-U+upM3gHp3HaF3wXAnUduA6IDKcz6frWS/dTAju3cZVIyZwOLBBFElQVlLH0ycHyMzqUFrjvdv+kEyPAEWfQ/g==",
+			"requires": {
+				"vscode-languageserver-types": "^3.15.0-next.2",
+				"vscode-nls": "^4.1.1",
+				"vscode-uri": "^2.0.3"
+			},
+			"dependencies": {
+				"vscode-languageserver-types": {
+					"version": "3.15.1",
+					"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+					"integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+				}
+			}
+		},
+		"vscode-jsonrpc": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
+			"integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A=="
+		},
+		"vscode-languageclient": {
+			"version": "6.1.3",
+			"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz",
+			"integrity": "sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==",
+			"requires": {
+				"semver": "^6.3.0",
+				"vscode-languageserver-protocol": "^3.15.3"
+			},
+			"dependencies": {
+				"semver": {
+					"version": "6.3.0",
+					"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+					"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+				}
+			}
+		},
+		"vscode-languageserver-protocol": {
+			"version": "3.15.3",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz",
+			"integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==",
+			"requires": {
+				"vscode-jsonrpc": "^5.0.1",
+				"vscode-languageserver-types": "3.15.1"
+			}
+		},
+		"vscode-languageserver-types": {
+			"version": "3.15.1",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+			"integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+		},
+		"vscode-nls": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz",
+			"integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw=="
+		},
+		"vscode-uri": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.1.tgz",
+			"integrity": "sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A=="
+		}
+	}
+}

+ 22 - 0
packages/vscode-ext/client/package.json

@@ -0,0 +1,22 @@
+{
+	"name": "lsp-sample-client",
+	"description": "VSCode part of a language server",
+	"author": "Microsoft Corporation",
+	"license": "MIT",
+	"version": "0.0.1",
+	"publisher": "vscode",
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/Microsoft/vscode-extension-samples"
+	},
+	"engines": {
+		"vscode": "^1.43.0"
+	},
+	"dependencies": {
+		"vscode-html-languageservice": "^3.0.3",
+		"vscode-languageclient": "^6.1.3"
+	},
+	"devDependencies": {
+		"@types/vscode": "^1.43.0"
+	}
+}

+ 415 - 0
packages/vscode-ext/client/src/embeddedSupport.ts

@@ -0,0 +1,415 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { TextDocument, Position, Range } from 'vscode-languageclient';
+import { LanguageService, TokenType } from 'vscode-html-languageservice';
+
+export interface LanguageRange extends Range {
+	languageId: string | undefined;
+	attributeValue?: boolean;
+}
+
+export interface HTMLDocumentRegions {
+	getEmbeddedDocument(languageId: string, ignoreAttributeValues?: boolean): TextDocument;
+	getLanguageRanges(range: Range): LanguageRange[];
+	getLanguageAtPosition(position: Position): string | undefined;
+	getLanguagesInDocument(): string[];
+	getImportedScripts(): string[];
+}
+
+export const CSS_STYLE_RULE = '__';
+
+interface EmbeddedRegion {
+	languageId: string | undefined;
+	start: number;
+	end: number;
+	attributeValue?: boolean;
+}
+
+export function isInsideStyleRegion(
+	languageService: LanguageService,
+	documentText: string,
+	offset: number
+) {
+	let scanner = languageService.createScanner(documentText);
+
+	let token = scanner.scan();
+	while (token !== TokenType.EOS) {
+		switch (token) {
+			case TokenType.Styles:
+				if (offset >= scanner.getTokenOffset() && offset <= scanner.getTokenEnd()) {
+					return true;
+				}
+		}
+		token = scanner.scan();
+	}
+	
+	return false;
+}
+
+export function getCSSVirtualContent(
+	languageService: LanguageService,
+	documentText: string
+): string {
+	let regions: EmbeddedRegion[] = [];
+	let scanner = languageService.createScanner(documentText);
+	let lastTagName: string = '';
+	let lastAttributeName: string | null = null;
+	let languageIdFromType: string | undefined = undefined;
+	let importedScripts: string[] = [];
+
+	let token = scanner.scan();
+	while (token !== TokenType.EOS) {
+		switch (token) {
+			case TokenType.StartTag:
+				lastTagName = scanner.getTokenText();
+				lastAttributeName = null;
+				languageIdFromType = 'javascript';
+				break;
+			case TokenType.Styles:
+				regions.push({
+					languageId: 'css',
+					start: scanner.getTokenOffset(),
+					end: scanner.getTokenEnd()
+				});
+				break;
+			case TokenType.Script:
+				regions.push({
+					languageId: languageIdFromType,
+					start: scanner.getTokenOffset(),
+					end: scanner.getTokenEnd()
+				});
+				break;
+			case TokenType.AttributeName:
+				lastAttributeName = scanner.getTokenText();
+				break;
+			case TokenType.AttributeValue:
+				if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') {
+					let value = scanner.getTokenText();
+					if (value[0] === "'" || value[0] === '"') {
+						value = value.substr(1, value.length - 1);
+					}
+					importedScripts.push(value);
+				} else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') {
+					if (
+						/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
+							scanner.getTokenText()
+						)
+					) {
+						languageIdFromType = 'javascript';
+					} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
+						languageIdFromType = 'typescript';
+					} else {
+						languageIdFromType = undefined;
+					}
+				} else {
+					let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
+					if (attributeLanguageId) {
+						let start = scanner.getTokenOffset();
+						let end = scanner.getTokenEnd();
+						let firstChar = documentText[start];
+						if (firstChar === "'" || firstChar === '"') {
+							start++;
+							end--;
+						}
+						regions.push({
+							languageId: attributeLanguageId,
+							start,
+							end,
+							attributeValue: true
+						});
+					}
+				}
+				lastAttributeName = null;
+				break;
+		}
+		token = scanner.scan();
+	}
+
+	let content = documentText
+		.split('\n')
+		.map(line => {
+			return ' '.repeat(line.length);
+		}).join('\n');
+
+	regions.forEach(r => {
+		if (r.languageId === 'css') {
+			content = content.slice(0, r.start) + documentText.slice(r.start, r.end) + content.slice(r.end);
+		}
+	});
+
+	return content;
+}
+
+
+export function getDocumentRegions(
+	languageService: LanguageService,
+	document: TextDocument
+): HTMLDocumentRegions {
+	let regions: EmbeddedRegion[] = [];
+	let scanner = languageService.createScanner(document.getText());
+	let lastTagName: string = '';
+	let lastAttributeName: string | null = null;
+	let languageIdFromType: string | undefined = undefined;
+	let importedScripts: string[] = [];
+
+	let token = scanner.scan();
+	while (token !== TokenType.EOS) {
+		switch (token) {
+			case TokenType.StartTag:
+				lastTagName = scanner.getTokenText();
+				lastAttributeName = null;
+				languageIdFromType = 'javascript';
+				break;
+			case TokenType.Styles:
+				regions.push({
+					languageId: 'css',
+					start: scanner.getTokenOffset(),
+					end: scanner.getTokenEnd()
+				});
+				break;
+			case TokenType.Script:
+				regions.push({
+					languageId: languageIdFromType,
+					start: scanner.getTokenOffset(),
+					end: scanner.getTokenEnd()
+				});
+				break;
+			case TokenType.AttributeName:
+				lastAttributeName = scanner.getTokenText();
+				break;
+			case TokenType.AttributeValue:
+				if (lastAttributeName === 'src' && lastTagName.toLowerCase() === 'script') {
+					let value = scanner.getTokenText();
+					if (value[0] === "'" || value[0] === '"') {
+						value = value.substr(1, value.length - 1);
+					}
+					importedScripts.push(value);
+				} else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') {
+					if (
+						/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(
+							scanner.getTokenText()
+						)
+					) {
+						languageIdFromType = 'javascript';
+					} else if (/["']text\/typescript["']/.test(scanner.getTokenText())) {
+						languageIdFromType = 'typescript';
+					} else {
+						languageIdFromType = undefined;
+					}
+				} else {
+					let attributeLanguageId = getAttributeLanguage(lastAttributeName!);
+					if (attributeLanguageId) {
+						let start = scanner.getTokenOffset();
+						let end = scanner.getTokenEnd();
+						let firstChar = document.getText()[start];
+						if (firstChar === "'" || firstChar === '"') {
+							start++;
+							end--;
+						}
+						regions.push({
+							languageId: attributeLanguageId,
+							start,
+							end,
+							attributeValue: true
+						});
+					}
+				}
+				lastAttributeName = null;
+				break;
+		}
+		token = scanner.scan();
+	}
+	return {
+		getLanguageRanges: (range: Range) => getLanguageRanges(document, regions, range),
+		getEmbeddedDocument: (languageId: string, ignoreAttributeValues: boolean) =>
+			getEmbeddedDocument(document, regions, languageId, ignoreAttributeValues),
+		getLanguageAtPosition: (position: Position) =>
+			getLanguageAtPosition(document, regions, position),
+		getLanguagesInDocument: () => getLanguagesInDocument(document, regions),
+		getImportedScripts: () => importedScripts
+	};
+}
+
+function getLanguageRanges(
+	document: TextDocument,
+	regions: EmbeddedRegion[],
+	range: Range
+): LanguageRange[] {
+	let result: LanguageRange[] = [];
+	let currentPos = range ? range.start : Position.create(0, 0);
+	let currentOffset = range ? document.offsetAt(range.start) : 0;
+	let endOffset = range ? document.offsetAt(range.end) : document.getText().length;
+	for (let region of regions) {
+		if (region.end > currentOffset && region.start < endOffset) {
+			let start = Math.max(region.start, currentOffset);
+			let startPos = document.positionAt(start);
+			if (currentOffset < region.start) {
+				result.push({
+					start: currentPos,
+					end: startPos,
+					languageId: 'html'
+				});
+			}
+			let end = Math.min(region.end, endOffset);
+			let endPos = document.positionAt(end);
+			if (end > region.start) {
+				result.push({
+					start: startPos,
+					end: endPos,
+					languageId: region.languageId,
+					attributeValue: region.attributeValue
+				});
+			}
+			currentOffset = end;
+			currentPos = endPos;
+		}
+	}
+	if (currentOffset < endOffset) {
+		let endPos = range ? range.end : document.positionAt(endOffset);
+		result.push({
+			start: currentPos,
+			end: endPos,
+			languageId: 'html'
+		});
+	}
+	return result;
+}
+
+function getLanguagesInDocument(
+	_document: TextDocument,
+	regions: EmbeddedRegion[]
+): string[] {
+	let result = [];
+	for (let region of regions) {
+		if (region.languageId && result.indexOf(region.languageId) === -1) {
+			result.push(region.languageId);
+			if (result.length === 3) {
+				return result;
+			}
+		}
+	}
+	result.push('html');
+	return result;
+}
+
+function getLanguageAtPosition(
+	document: TextDocument,
+	regions: EmbeddedRegion[],
+	position: Position
+): string | undefined {
+	let offset = document.offsetAt(position);
+	for (let region of regions) {
+		if (region.start <= offset) {
+			if (offset <= region.end) {
+				return region.languageId;
+			}
+		} else {
+			break;
+		}
+	}
+	return 'html';
+}
+
+function getEmbeddedDocument(
+	document: TextDocument,
+	contents: EmbeddedRegion[],
+	languageId: string,
+	ignoreAttributeValues: boolean
+): TextDocument {
+	let currentPos = 0;
+	let oldContent = document.getText();
+	let result = '';
+	let lastSuffix = '';
+	for (let c of contents) {
+		if (c.languageId === languageId && (!ignoreAttributeValues || !c.attributeValue)) {
+			result = substituteWithWhitespace(
+				result,
+				currentPos,
+				c.start,
+				oldContent,
+				lastSuffix,
+				getPrefix(c)
+			);
+			result += oldContent.substring(c.start, c.end);
+			currentPos = c.end;
+			lastSuffix = getSuffix(c);
+		}
+	}
+	result = substituteWithWhitespace(
+		result,
+		currentPos,
+		oldContent.length,
+		oldContent,
+		lastSuffix,
+		''
+	);
+	return TextDocument.create(document.uri, languageId, document.version, result);
+}
+
+function getPrefix(c: EmbeddedRegion) {
+	if (c.attributeValue) {
+		switch (c.languageId) {
+			case 'css':
+				return CSS_STYLE_RULE + '{';
+		}
+	}
+	return '';
+}
+function getSuffix(c: EmbeddedRegion) {
+	if (c.attributeValue) {
+		switch (c.languageId) {
+			case 'css':
+				return '}';
+			case 'javascript':
+				return ';';
+		}
+	}
+	return '';
+}
+
+function substituteWithWhitespace(
+	result: string,
+	start: number,
+	end: number,
+	oldContent: string,
+	before: string,
+	after: string
+) {
+	let accumulatedWS = 0;
+	result += before;
+	for (let i = start + before.length; i < end; i++) {
+		let ch = oldContent[i];
+		if (ch === '\n' || ch === '\r') {
+			// only write new lines, skip the whitespace
+			accumulatedWS = 0;
+			result += ch;
+		} else {
+			accumulatedWS++;
+		}
+	}
+	result = append(result, ' ', accumulatedWS - after.length);
+	result += after;
+	return result;
+}
+
+function append(result: string, str: string, n: number): string {
+	while (n > 0) {
+		if (n & 1) {
+			result += str;
+		}
+		n >>= 1;
+		str += str;
+	}
+	return result;
+}
+
+function getAttributeLanguage(attributeName: string): string | null {
+	let match = attributeName.match(/^(style)$|^(on\w+)$/i);
+	if (!match) {
+		return null;
+	}
+	return match[1] ? 'css' : 'javascript';
+}

+ 87 - 0
packages/vscode-ext/client/src/extension.ts

@@ -0,0 +1,87 @@
+/* --------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ * ------------------------------------------------------------------------------------------ */
+
+import * as path from 'path';
+import { commands, CompletionList, ExtensionContext, Uri, workspace } from 'vscode';
+import { getLanguageService } from 'vscode-html-languageservice';
+import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient';
+import { getCSSVirtualContent, isInsideStyleRegion } from './embeddedSupport';
+
+let client: LanguageClient;
+
+const htmlLanguageService = getLanguageService();
+
+export function activate(context: ExtensionContext) {
+	// The server is implemented in node
+	let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
+	// The debug options for the server
+	// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
+	let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };
+
+	// If the extension is launched in debug mode then the debug server options are used
+	// Otherwise the run options are used
+	let serverOptions: ServerOptions = {
+		run: { module: serverModule, transport: TransportKind.ipc },
+		debug: {
+			module: serverModule,
+			transport: TransportKind.ipc,
+			options: debugOptions
+		}
+	};
+
+	const virtualDocumentContents = new Map<string, string>();
+
+	workspace.registerTextDocumentContentProvider('embedded-content', {
+		provideTextDocumentContent: uri => {
+			const originalUri = uri.path.slice(1).slice(0, -4);
+			const decodedUri = decodeURIComponent(originalUri);
+			return virtualDocumentContents.get(decodedUri);
+		}
+	});
+
+	let clientOptions: LanguageClientOptions = {
+		documentSelector: [{ scheme: 'file', language: 'html1' }],
+		middleware: {
+			provideCompletionItem: async (document, position, context, token, next) => {
+				// If not in `<style>`, do not perform request forwarding
+				if (!isInsideStyleRegion(htmlLanguageService, document.getText(), document.offsetAt(position))) {
+					return await next(document, position, context, token);
+				}
+
+				const originalUri = document.uri.toString();
+				virtualDocumentContents.set(originalUri, getCSSVirtualContent(htmlLanguageService, document.getText()));
+
+				const vdocUriString = `embedded-content://css/${encodeURIComponent(
+					originalUri
+				)}.css`;
+				const vdocUri = Uri.parse(vdocUriString);
+				return await commands.executeCommand<CompletionList>(
+					'vscode.executeCompletionItemProvider',
+					vdocUri,
+					position,
+					context.triggerCharacter
+				);
+			}
+		}
+	};
+
+	// Create the language client and start the client.
+	client = new LanguageClient(
+		'languageServerExample',
+		'Language Server Example',
+		serverOptions,
+		clientOptions
+	);
+
+	// Start the client. This will also launch the server
+	client.start();
+}
+
+export function deactivate(): Thenable<void> | undefined {
+	if (!client) {
+		return undefined;
+	}
+	return client.stop();
+}

+ 12 - 0
packages/vscode-ext/client/tsconfig.json

@@ -0,0 +1,12 @@
+{
+	"compilerOptions": {
+		"module": "commonjs",
+		"target": "es2019",
+		"lib": ["ES2019"],
+		"outDir": "out",
+		"rootDir": "src",
+		"sourceMap": true
+	},
+	"include": ["src"],
+	"exclude": ["node_modules", ".vscode-test"]
+}

+ 1050 - 0
packages/vscode-ext/package-lock.json

@@ -0,0 +1,1050 @@
+{
+  "name": "dioxus-rsx-plugin",
+  "version": "0.1.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+      "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.10.4"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.12.11",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
+      "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+      "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.4",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        }
+      }
+    },
+    "@eslint/eslintrc": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
+      "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.1.1",
+        "espree": "^7.3.0",
+        "globals": "^12.1.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^3.13.1",
+        "lodash": "^4.17.20",
+        "minimatch": "^3.0.4",
+        "strip-json-comments": "^3.1.1"
+      }
+    },
+    "@types/eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
+      "dev": true
+    },
+    "@types/json-schema": {
+      "version": "7.0.7",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
+      "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==",
+      "dev": true
+    },
+    "@types/mocha": {
+      "version": "5.2.7",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
+      "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "12.19.15",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.15.tgz",
+      "integrity": "sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw==",
+      "dev": true
+    },
+    "@typescript-eslint/eslint-plugin": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz",
+      "integrity": "sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/experimental-utils": "3.10.1",
+        "debug": "^4.1.1",
+        "functional-red-black-tree": "^1.0.1",
+        "regexpp": "^3.0.0",
+        "semver": "^7.3.2",
+        "tsutils": "^3.17.1"
+      }
+    },
+    "@typescript-eslint/experimental-utils": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
+      "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
+      "dev": true,
+      "requires": {
+        "@types/json-schema": "^7.0.3",
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/typescript-estree": "3.10.1",
+        "eslint-scope": "^5.0.0",
+        "eslint-utils": "^2.0.0"
+      }
+    },
+    "@typescript-eslint/parser": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz",
+      "integrity": "sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==",
+      "dev": true,
+      "requires": {
+        "@types/eslint-visitor-keys": "^1.0.0",
+        "@typescript-eslint/experimental-utils": "3.10.1",
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/typescript-estree": "3.10.1",
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "@typescript-eslint/types": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
+      "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==",
+      "dev": true
+    },
+    "@typescript-eslint/typescript-estree": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
+      "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "3.10.1",
+        "@typescript-eslint/visitor-keys": "3.10.1",
+        "debug": "^4.1.1",
+        "glob": "^7.1.6",
+        "is-glob": "^4.0.1",
+        "lodash": "^4.17.15",
+        "semver": "^7.3.2",
+        "tsutils": "^3.17.1"
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
+      "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+      "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+      "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+      "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      }
+    },
+    "debug": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+      "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "enquirer": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+      "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^4.1.1"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "eslint": {
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz",
+      "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@eslint/eslintrc": "^0.3.0",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.0.1",
+        "doctrine": "^3.0.0",
+        "enquirer": "^2.3.5",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^2.0.0",
+        "espree": "^7.3.1",
+        "esquery": "^1.2.0",
+        "esutils": "^2.0.2",
+        "file-entry-cache": "^6.0.0",
+        "functional-red-black-tree": "^1.0.1",
+        "glob-parent": "^5.0.0",
+        "globals": "^12.1.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "js-yaml": "^3.13.1",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash": "^4.17.20",
+        "minimatch": "^3.0.4",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "progress": "^2.0.0",
+        "regexpp": "^3.1.0",
+        "semver": "^7.2.1",
+        "strip-ansi": "^6.0.0",
+        "strip-json-comments": "^3.1.0",
+        "table": "^6.0.4",
+        "text-table": "^0.2.0",
+        "v8-compile-cache": "^2.0.3"
+      },
+      "dependencies": {
+        "eslint-visitor-keys": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz",
+          "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "7.3.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.4.0",
+        "acorn-jsx": "^5.3.1",
+        "eslint-visitor-keys": "^1.3.0"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "esquery": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+      "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.1.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+          "dev": true
+        }
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+          "dev": true
+        }
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "file-entry-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz",
+      "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "flat-cache": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz",
+      "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
+      "dev": true
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "globals": {
+      "version": "12.4.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+      "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.8.1"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.20",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+      "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+      "dev": true
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "optionator": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+      "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+      "dev": true,
+      "requires": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      }
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true
+    },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "regexpp": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+      "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
+      "dev": true
+    },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "semver": {
+      "version": "7.3.4",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+      "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
+    },
+    "shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^3.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true
+    },
+    "slice-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        }
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "string-width": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+      "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+      "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.0"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "table": {
+      "version": "6.0.7",
+      "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz",
+      "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==",
+      "dev": true,
+      "requires": {
+        "ajv": "^7.0.2",
+        "lodash": "^4.17.20",
+        "slice-ansi": "^4.0.0",
+        "string-width": "^4.2.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz",
+          "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+          "dev": true
+        }
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "dev": true
+    },
+    "tsutils": {
+      "version": "3.20.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz",
+      "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      }
+    },
+    "type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1"
+      }
+    },
+    "type-fest": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+      "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+      "dev": true
+    },
+    "typescript": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
+      "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "v8-compile-cache": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
+      "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
+      "dev": true
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "dev": true
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    }
+  }
+}

+ 54 - 0
packages/vscode-ext/package.json

@@ -0,0 +1,54 @@
+{
+  "name": "dioxus-rsx-plugin",
+  "description": "Get HTML completion inside the html! macro for dioxus projects",
+  "author": "Jonathan Kelley",
+  "license": "MIT",
+  "version": "0.1.0",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jkelleyrtp/dioxus"
+  },
+  "publisher": "jkelleyrtp",
+  "categories": [],
+  "keywords": [],
+  "engines": {
+    "vscode": "^1.43.0",
+    "node": "*"
+  },
+  "activationEvents": [
+    "onLanguage:html1"
+  ],
+  "main": "./client/out/extension",
+  "contributes": {
+    "languages": [
+      {
+        "id": "rsx",
+        "extensions": [
+          ".rs"
+        ]
+      }
+    ],
+    "grammars": [
+      {
+        "language": "rsx",
+        "scopeName": "text.rsx.basic",
+        "path": "./syntaxes/rsx.tmLanguage.json"
+      }
+    ]
+  },
+  "scripts": {
+    "vscode:prepublish": "cd client && npm install && cd .. && npm run compile",
+    "compile": "tsc -b",
+    "watch": "tsc -b -w",
+    "// postinstall": "cd client && npm install && cd ../server && npm install && cd ..",
+    "test": "sh ./scripts/e2e.sh"
+  },
+  "devDependencies": {
+    "@types/mocha": "^5.2.7",
+    "@types/node": "^12.12.0",
+    "@typescript-eslint/eslint-plugin": "^3.0.2",
+    "@typescript-eslint/parser": "^3.0.2",
+    "eslint": "^7.1.0",
+    "typescript": "^4.0.2"
+  }
+}

+ 3 - 0
packages/vscode-ext/rust/main.rs

@@ -0,0 +1,3 @@
+//! Generate the syntax for the RSX language
+
+fn main() {}

+ 67 - 0
packages/vscode-ext/server/package-lock.json

@@ -0,0 +1,67 @@
+{
+	"name": "lsp-sample-server",
+	"version": "1.0.0",
+	"lockfileVersion": 1,
+	"requires": true,
+	"dependencies": {
+		"vscode-html-languageservice": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-3.0.3.tgz",
+			"integrity": "sha512-U+upM3gHp3HaF3wXAnUduA6IDKcz6frWS/dTAju3cZVIyZwOLBBFElQVlLH0ycHyMzqUFrjvdv+kEyPAEWfQ/g==",
+			"requires": {
+				"vscode-languageserver-types": "^3.15.0-next.2",
+				"vscode-nls": "^4.1.1",
+				"vscode-uri": "^2.0.3"
+			},
+			"dependencies": {
+				"vscode-languageserver-types": {
+					"version": "3.15.1",
+					"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+					"integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+				},
+				"vscode-uri": {
+					"version": "2.1.1",
+					"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.1.tgz",
+					"integrity": "sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A=="
+				}
+			}
+		},
+		"vscode-jsonrpc": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
+			"integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A=="
+		},
+		"vscode-languageserver": {
+			"version": "6.1.1",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz",
+			"integrity": "sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ==",
+			"requires": {
+				"vscode-languageserver-protocol": "^3.15.3"
+			}
+		},
+		"vscode-languageserver-protocol": {
+			"version": "3.15.3",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz",
+			"integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==",
+			"requires": {
+				"vscode-jsonrpc": "^5.0.1",
+				"vscode-languageserver-types": "3.15.1"
+			}
+		},
+		"vscode-languageserver-textdocument": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
+			"integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
+		},
+		"vscode-languageserver-types": {
+			"version": "3.15.1",
+			"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+			"integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+		},
+		"vscode-nls": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz",
+			"integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw=="
+		}
+	}
+}

+ 20 - 0
packages/vscode-ext/server/package.json

@@ -0,0 +1,20 @@
+{
+	"name": "lsp-sample-server",
+	"description": "Example implementation of a language server in node.",
+	"version": "1.0.0",
+	"author": "Microsoft Corporation",
+	"license": "MIT",
+	"engines": {
+		"node": "*"
+	},
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/Microsoft/vscode-extension-samples"
+	},
+	"dependencies": {
+		"vscode-html-languageservice": "^3.0.3",
+		"vscode-languageserver": "^6.1.1",
+		"vscode-languageserver-textdocument": "^1.0.1"
+	},
+	"scripts": {}
+}

+ 48 - 0
packages/vscode-ext/server/src/server.ts

@@ -0,0 +1,48 @@
+/* --------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ * ------------------------------------------------------------------------------------------ */
+
+import { getLanguageService } from 'vscode-html-languageservice';
+import { createConnection, InitializeParams, ProposedFeatures, TextDocuments, TextDocumentSyncKind } from 'vscode-languageserver';
+import { TextDocument } from 'vscode-languageserver-textdocument';
+
+// Create a connection for the server. The connection uses Node's IPC as a transport.
+// Also include all preview / proposed LSP features.
+let connection = createConnection(ProposedFeatures.all);
+
+// Create a simple text document manager. The text document manager
+// supports full document sync only
+let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
+
+const htmlLanguageService = getLanguageService();
+
+connection.onInitialize((_params: InitializeParams) => {
+	return {
+		capabilities: {
+			textDocumentSync: TextDocumentSyncKind.Full,
+			// Tell the client that the server supports code completion
+			completionProvider: {
+				resolveProvider: false
+			}
+		}
+	};
+});
+
+connection.onInitialized(() => { });
+
+connection.onCompletion(async (textDocumentPosition, token) => {
+	const document = documents.get(textDocumentPosition.textDocument.uri);
+	if (!document) {
+		return null;
+	}
+
+	return htmlLanguageService.doComplete(
+		document,
+		textDocumentPosition.position,
+		htmlLanguageService.parseHTMLDocument(document)
+	);
+});
+
+documents.listen(connection);
+connection.listen();

+ 14 - 0
packages/vscode-ext/server/tsconfig.json

@@ -0,0 +1,14 @@
+{
+	"compilerOptions": {
+		"target": "es2019",
+		"lib": ["ES2019"],
+		"module": "commonjs",
+		"moduleResolution": "node",
+		"sourceMap": true,
+		"strict": true,
+		"outDir": "out",
+		"rootDir": "src"
+	},
+	"include": ["src"],
+	"exclude": ["node_modules", ".vscode-test"]
+}

+ 2643 - 0
packages/vscode-ext/syntaxes/rsx.tmLanguage.json

@@ -0,0 +1,2643 @@
+{
+  "information_for_contributors": [
+    "This file has been converted from https://github.com/textmate/html.tmbundle/blob/master/Syntaxes/HTML.plist",
+    "If you want to provide a fix or improvement, please create a pull request against the original repository.",
+    "Once accepted there, we are happy to receive an update request."
+  ],
+  "version": "https://github.com/textmate/html.tmbundle/commit/0c3d5ee54de3a993f747f54186b73a4d2d3c44a2",
+  "name": "rsx",
+  "scopeName": "text.rsx.basic",
+  "injections": {
+    "R:text.html - (comment.block, text.html meta.embedded, meta.tag.*.*.html, meta.tag.*.*.*.html, meta.tag.*.*.*.*.html)": {
+      "comment": "Uses R: to ensure this matches after any other injections.",
+      "patterns": [
+        {
+          "match": "<",
+          "name": "invalid.illegal.bad-angle-bracket.html"
+        }
+      ]
+    }
+  },
+  "patterns": [
+    {
+      "include": "#xml-processing"
+    },
+    {
+      "include": "#comment"
+    },
+    {
+      "include": "#doctype"
+    },
+    {
+      "include": "#cdata"
+    },
+    {
+      "include": "#tags-valid"
+    },
+    {
+      "include": "#tags-invalid"
+    },
+    {
+      "include": "#entities"
+    }
+  ],
+  "repository": {
+    "attribute": {
+      "patterns": [
+        {
+          "begin": "(s(hape|cope|t(ep|art)|ize(s)?|p(ellcheck|an)|elected|lot|andbox|rc(set|doc|lang)?)|h(ttp-equiv|i(dden|gh)|e(ight|aders)|ref(lang)?)|n(o(nce|validate|module)|ame)|c(h(ecked|arset)|ite|o(nt(ent(editable)?|rols)|ords|l(s(pan)?|or))|lass|rossorigin)|t(ype(mustmatch)?|itle|a(rget|bindex)|ranslate)|i(s(map)?|n(tegrity|putmode)|tem(scope|type|id|prop|ref)|d)|op(timum|en)|d(i(sabled|r(name)?)|ownload|e(coding|f(er|ault))|at(etime|a)|raggable)|usemap|p(ing|oster|la(ysinline|ceholder)|attern|reload)|enctype|value|kind|for(m(novalidate|target|enctype|action|method)?)?|w(idth|rap)|l(ist|o(op|w)|a(ng|bel))|a(s(ync)?|c(ce(sskey|pt(-charset)?)|tion)|uto(c(omplete|apitalize)|play|focus)|l(t|low(usermedia|paymentrequest|fullscreen))|bbr)|r(ows(pan)?|e(versed|quired|ferrerpolicy|l|adonly))|m(in(length)?|u(ted|ltiple)|e(thod|dia)|a(nifest|x(length)?)))(?![\\w:-])",
+          "beginCaptures": {
+            "0": {
+              "name": "entity.other.attribute-name.html"
+            }
+          },
+          "comment": "HTML5 attributes, not event handlers",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.$1.html",
+          "patterns": [
+            {
+              "include": "#attribute-interior"
+            }
+          ]
+        },
+        {
+          "begin": "style(?![\\w:-])",
+          "beginCaptures": {
+            "0": {
+              "name": "entity.other.attribute-name.html"
+            }
+          },
+          "comment": "HTML5 style attribute",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.style.html",
+          "patterns": [
+            {
+              "begin": "=",
+              "beginCaptures": {
+                "0": {
+                  "name": "punctuation.separator.key-value.html"
+                }
+              },
+              "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)",
+              "patterns": [
+                {
+                  "begin": "(?=[^\\s=<>`/]|/(?!>))",
+                  "end": "(?!\\G)",
+                  "name": "meta.embedded.line.css",
+                  "patterns": [
+                    {
+                      "captures": {
+                        "0": {
+                          "name": "source.css"
+                        }
+                      },
+                      "match": "([^\\s\"'=<>`/]|/(?!>))+",
+                      "name": "string.unquoted.html"
+                    },
+                    {
+                      "begin": "\"",
+                      "beginCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.begin.html"
+                        }
+                      },
+                      "contentName": "source.css",
+                      "end": "(\")",
+                      "endCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.end.html"
+                        },
+                        "1": {
+                          "name": "source.css-ignored-vscode"
+                        }
+                      },
+                      "name": "string.quoted.double.html",
+                      "patterns": [
+                        {
+                          "include": "#entities"
+                        }
+                      ]
+                    },
+                    {
+                      "begin": "'",
+                      "beginCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.begin.html"
+                        }
+                      },
+                      "contentName": "source.css",
+                      "end": "(')",
+                      "endCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.end.html"
+                        },
+                        "1": {
+                          "name": "source.css-ignored-vscode"
+                        }
+                      },
+                      "name": "string.quoted.single.html",
+                      "patterns": [
+                        {
+                          "include": "#entities"
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "match": "=",
+                  "name": "invalid.illegal.unexpected-equals-sign.html"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "begin": "on(s(croll|t(orage|alled)|u(spend|bmit)|e(curitypolicyviolation|ek(ing|ed)|lect))|hashchange|c(hange|o(ntextmenu|py)|u(t|echange)|l(ick|ose)|an(cel|play(through)?))|t(imeupdate|oggle)|in(put|valid)|o(nline|ffline)|d(urationchange|r(op|ag(start|over|e(n(ter|d)|xit)|leave)?)|blclick)|un(handledrejection|load)|p(opstate|lay(ing)?|a(ste|use|ge(show|hide))|rogress)|e(nded|rror|mptied)|volumechange|key(down|up|press)|focus|w(heel|aiting)|l(oad(start|e(nd|d(data|metadata)))?|anguagechange)|a(uxclick|fterprint|bort)|r(e(s(ize|et)|jectionhandled)|atechange)|m(ouse(o(ut|ver)|down|up|enter|leave|move)|essage(error)?)|b(efore(unload|print)|lur))(?![\\w:-])",
+          "beginCaptures": {
+            "0": {
+              "name": "entity.other.attribute-name.html"
+            }
+          },
+          "comment": "HTML5 attributes, event handlers",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.event-handler.$1.html",
+          "patterns": [
+            {
+              "begin": "=",
+              "beginCaptures": {
+                "0": {
+                  "name": "punctuation.separator.key-value.html"
+                }
+              },
+              "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)",
+              "patterns": [
+                {
+                  "begin": "(?=[^\\s=<>`/]|/(?!>))",
+                  "end": "(?!\\G)",
+                  "name": "meta.embedded.line.js",
+                  "patterns": [
+                    {
+                      "captures": {
+                        "0": {
+                          "name": "source.js"
+                        },
+                        "1": {
+                          "patterns": [
+                            {
+                              "include": "source.js"
+                            }
+                          ]
+                        }
+                      },
+                      "match": "(([^\\s\"'=<>`/]|/(?!>))+)",
+                      "name": "string.unquoted.html"
+                    },
+                    {
+                      "begin": "\"",
+                      "beginCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.begin.html"
+                        }
+                      },
+                      "contentName": "source.js",
+                      "end": "(\")",
+                      "endCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.end.html"
+                        },
+                        "1": {
+                          "name": "source.js-ignored-vscode"
+                        }
+                      },
+                      "name": "string.quoted.double.html",
+                      "patterns": [
+                        {
+                          "captures": {
+                            "0": {
+                              "patterns": [
+                                {
+                                  "include": "source.js"
+                                }
+                              ]
+                            }
+                          },
+                          "match": "([^\\n\"/]|/(?![/*]))+"
+                        },
+                        {
+                          "begin": "//",
+                          "beginCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.js"
+                            }
+                          },
+                          "end": "(?=\")|\\n",
+                          "name": "comment.line.double-slash.js"
+                        },
+                        {
+                          "begin": "/\\*",
+                          "beginCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.begin.js"
+                            }
+                          },
+                          "end": "(?=\")|\\*/",
+                          "endCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.end.js"
+                            }
+                          },
+                          "name": "comment.block.js"
+                        }
+                      ]
+                    },
+                    {
+                      "begin": "'",
+                      "beginCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.begin.html"
+                        }
+                      },
+                      "contentName": "source.js",
+                      "end": "(')",
+                      "endCaptures": {
+                        "0": {
+                          "name": "punctuation.definition.string.end.html"
+                        },
+                        "1": {
+                          "name": "source.js-ignored-vscode"
+                        }
+                      },
+                      "name": "string.quoted.single.html",
+                      "patterns": [
+                        {
+                          "captures": {
+                            "0": {
+                              "patterns": [
+                                {
+                                  "include": "source.js"
+                                }
+                              ]
+                            }
+                          },
+                          "match": "([^\\n'/]|/(?![/*]))+"
+                        },
+                        {
+                          "begin": "//",
+                          "beginCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.js"
+                            }
+                          },
+                          "end": "(?=')|\\n",
+                          "name": "comment.line.double-slash.js"
+                        },
+                        {
+                          "begin": "/\\*",
+                          "beginCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.begin.js"
+                            }
+                          },
+                          "end": "(?=')|\\*/",
+                          "endCaptures": {
+                            "0": {
+                              "name": "punctuation.definition.comment.end.js"
+                            }
+                          },
+                          "name": "comment.block.js"
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "match": "=",
+                  "name": "invalid.illegal.unexpected-equals-sign.html"
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "begin": "(data-[a-z\\-]+)(?![\\w:-])",
+          "beginCaptures": {
+            "0": {
+              "name": "entity.other.attribute-name.html"
+            }
+          },
+          "comment": "HTML5 attributes, data-*",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.data-x.$1.html",
+          "patterns": [
+            {
+              "include": "#attribute-interior"
+            }
+          ]
+        },
+        {
+          "begin": "(align|bgcolor|border)(?![\\w:-])",
+          "beginCaptures": {
+            "0": {
+              "name": "invalid.deprecated.entity.other.attribute-name.html"
+            }
+          },
+          "comment": "HTML attributes, deprecated",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.$1.html",
+          "patterns": [
+            {
+              "include": "#attribute-interior"
+            }
+          ]
+        },
+        {
+          "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)",
+          "beginCaptures": {
+            "0": {
+              "name": "entity.other.attribute-name.html"
+            }
+          },
+          "comment": "Anything else that is valid",
+          "end": "(?=\\s*+[^=\\s])",
+          "name": "meta.attribute.unrecognized.$1.html",
+          "patterns": [
+            {
+              "include": "#attribute-interior"
+            }
+          ]
+        },
+        {
+          "match": "[^\\s>]+",
+          "name": "invalid.illegal.character-not-allowed-here.html"
+        }
+      ]
+    },
+    "attribute-interior": {
+      "patterns": [
+        {
+          "begin": "=",
+          "beginCaptures": {
+            "0": {
+              "name": "punctuation.separator.key-value.html"
+            }
+          },
+          "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)",
+          "patterns": [
+            {
+              "match": "([^\\s\"'=<>`/]|/(?!>))+",
+              "name": "string.unquoted.html"
+            },
+            {
+              "begin": "\"",
+              "beginCaptures": {
+                "0": {
+                  "name": "punctuation.definition.string.begin.html"
+                }
+              },
+              "end": "\"",
+              "endCaptures": {
+                "0": {
+                  "name": "punctuation.definition.string.end.html"
+                }
+              },
+              "name": "string.quoted.double.html",
+              "patterns": [
+                {
+                  "include": "#entities"
+                }
+              ]
+            },
+            {
+              "begin": "'",
+              "beginCaptures": {
+                "0": {
+                  "name": "punctuation.definition.string.begin.html"
+                }
+              },
+              "end": "'",
+              "endCaptures": {
+                "0": {
+                  "name": "punctuation.definition.string.end.html"
+                }
+              },
+              "name": "string.quoted.single.html",
+              "patterns": [
+                {
+                  "include": "#entities"
+                }
+              ]
+            },
+            {
+              "match": "=",
+              "name": "invalid.illegal.unexpected-equals-sign.html"
+            }
+          ]
+        }
+      ]
+    },
+    "cdata": {
+      "begin": "<!\\[CDATA\\[",
+      "beginCaptures": {
+        "0": {
+          "name": "punctuation.definition.tag.begin.html"
+        }
+      },
+      "contentName": "string.other.inline-data.html",
+      "end": "]]>",
+      "endCaptures": {
+        "0": {
+          "name": "punctuation.definition.tag.end.html"
+        }
+      },
+      "name": "meta.tag.metadata.cdata.html"
+    },
+    "comment": {
+      "begin": "<!--",
+      "captures": {
+        "0": {
+          "name": "punctuation.definition.comment.html"
+        }
+      },
+      "end": "-->",
+      "name": "comment.block.html",
+      "patterns": [
+        {
+          "match": "\\G-?>",
+          "name": "invalid.illegal.characters-not-allowed-here.html"
+        },
+        {
+          "match": "<!--(?!>)|<!-(?=-->)",
+          "name": "invalid.illegal.characters-not-allowed-here.html"
+        },
+        {
+          "match": "--!>",
+          "name": "invalid.illegal.characters-not-allowed-here.html"
+        }
+      ]
+    },
+    "core-minus-invalid": {
+      "comment": "This should be the root pattern array includes minus #tags-invalid",
+      "patterns": [
+        {
+          "include": "#xml-processing"
+        },
+        {
+          "include": "#comment"
+        },
+        {
+          "include": "#doctype"
+        },
+        {
+          "include": "#cdata"
+        },
+        {
+          "include": "#tags-valid"
+        },
+        {
+          "include": "#entities"
+        }
+      ]
+    },
+    "doctype": {
+      "begin": "<!(?=(?i:DOCTYPE\\s))",
+      "beginCaptures": {
+        "0": {
+          "name": "punctuation.definition.tag.begin.html"
+        }
+      },
+      "end": ">",
+      "endCaptures": {
+        "0": {
+          "name": "punctuation.definition.tag.end.html"
+        }
+      },
+      "name": "meta.tag.metadata.doctype.html",
+      "patterns": [
+        {
+          "match": "\\G(?i:DOCTYPE)",
+          "name": "entity.name.tag.html"
+        },
+        {
+          "begin": "\"",
+          "end": "\"",
+          "name": "string.quoted.double.html"
+        },
+        {
+          "match": "[^\\s>]+",
+          "name": "entity.other.attribute-name.html"
+        }
+      ]
+    },
+    "entities": {
+      "patterns": [
+        {
+          "captures": {
+            "1": {
+              "name": "punctuation.definition.entity.html"
+            },
+            "912": {
+              "name": "punctuation.definition.entity.html"
+            }
+          },
+          "comment": "Yes this is a bit ridiculous, there are quite a lot of these",
+          "match": "(?x)\n\t\t\t\t\t\t(&)\t(?=[a-zA-Z])\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(a(s(ymp(eq)?|cr|t)|n(d(slope|d|v|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a(h|c|d|e|f|a|g|b))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|e|E|acir)?|elig|f(r)?|w(conint|int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve))\n\t\t\t\t\t\t  | (B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h(d|u|D|U)?|times|H(d|u|D|U)?|d(R|l|r|L)|u(R|l|r|L)|plus|D(R|l|r|L)|v(R|h|H|l|r|L)?|U(R|l|r|L)|V(R|h|H|l|r|L)?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1(2|4)|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?))\n\t\t\t\t\t\t  | (c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr(l|r)|p(s|c(up|ap)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w(conint|int)|lubs(uit)?|a(cute|p(s|c(up|ap)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly(DoubleQuote|Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross))\n\t\t\t\t\t\t  | (d(s(c(y|r)|trok|ol)|har(l|r)|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up(DownArrow|Arrow)|VerticalBar|L(ong(RightArrow|Left(RightArrow|Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger)))\n\t\t\t\t\t\t  | (e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t(h|a)|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(ot|Dot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1(3|4))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty(SmallSquare|VerySmallSquare)|acr)))\n\t\t\t\t\t\t  | (f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(ig|lig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1(2|8|3|4|5|6)|78|2(3|5)|3(8|4|5)|45|5(8|6)))))|F(scr|cy|illed(SmallSquare|VerySmallSquare)|o(uriertrf|pf|rAll)|fr))\n\t\t\t\t\t\t  | (G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im(e|l)?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(qless|less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l(j|E|a)?|a(cute|p|mma(d)?)|rave|g(g)?|breve))\n\t\t\t\t\t\t  | (h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok(leftarrow|rightarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks(earow|warow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy))\n\t\t\t\t\t\t  | (i(s(cr|in(s(v)?|dot|v|E)?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(nt|int)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f(f|r)|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?))\n\t\t\t\t\t\t  | (j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr))\n\t\t\t\t\t\t  | (k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa))\n\t\t\t\t\t\t  | (l(s(h|cr|trok|im(e|g)?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(il|dil)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i(e|f)?|Par))?|Har|o(ng(left(arrow|rightarrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r(dhar|ushar))|ur(dshar|uhar)|jcy|par(lt)?|e(s(s(sim|dot|eq(qgtr|gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left(arrow|rightarrow)|rightarrow|Left(RightArrow|Arrow))|pf|wer(RightArrow|LeftArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot))\n\t\t\t\t\t\t  | (M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u(ltimap|map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker)))\n\t\t\t\t\t\t  | (n(s(hort(parallel|mid)|c(cue|e|r)?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|e|E)?|b(set(eq(q)?)?|e|E)?)|par|qsu(pe|be)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v(c|a|b))?|in(dot|v(c|a|b)|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g(t|e))|fr|w(near|ar(hk|r(ow)?)|Arr)|V(dash|Dash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft(arrow|rightarrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr(c|w)?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft(arrow|rightarrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes(SlantEqual|Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi(nSpace|ckSpace)|VeryThinSpace))|Jcy|fr|acute))\n\t\t\t\t\t\t  | (o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|f|m)?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly(DoubleQuote|Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr)))\n\t\t\t\t\t\t  | (p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d(o|u)|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?))\n\t\t\t\t\t\t  | (q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr))\n\t\t\t\t\t\t  | (R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(il|dil)|aron)|Barr|t(hree|imes|ri(e|f|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng(d|e|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr)))\n\t\t\t\t\t\t  | (s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma(v|f)?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot(e|b)?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n(e|E)|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|n(e|E)|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar(e|f))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort(RightArrow|DownArrow|UpArrow|LeftArrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle))\n\t\t\t\t\t\t  | (t(s(hcy|c(y|r)|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead(leftarrow|rightarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i(nSpace|ckSpace)|e(ta|refore))|c(y|edil|aron)|S(cy|Hcy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a(u|b)|ripleDot))\n\t\t\t\t\t\t  | (u(scr|h(ar(l|r)|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per(RightArrow|LeftArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve)))\n\t\t\t\t\t\t  | (v(s(cr|u(pn(e|E)|bn(e|E)))|nsu(p|b)|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar))\n\t\t\t\t\t\t  | (w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr))\n\t\t\t\t\t\t  | (X(scr|i|opf|fr)|x(s(cr|qcup)|h(arr|Arr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l(arr|Arr)|r(arr|Arr)|map))\n\t\t\t\t\t\t  | (y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy))\n\t\t\t\t\t\t  | (z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(nj|j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t(;)\n\t\t\t\t\t",
+          "name": "constant.character.entity.named.$2.html"
+        },
+        {
+          "captures": {
+            "1": {
+              "name": "punctuation.definition.entity.html"
+            },
+            "3": {
+              "name": "punctuation.definition.entity.html"
+            }
+          },
+          "match": "(&)#[0-9]+(;)",
+          "name": "constant.character.entity.numeric.decimal.html"
+        },
+        {
+          "captures": {
+            "1": {
+              "name": "punctuation.definition.entity.html"
+            },
+            "3": {
+              "name": "punctuation.definition.entity.html"
+            }
+          },
+          "match": "(&)#[xX][0-9a-fA-F]+(;)",
+          "name": "constant.character.entity.numeric.hexadecimal.html"
+        },
+        {
+          "match": "&(?=[a-zA-Z0-9]+;)",
+          "name": "invalid.illegal.ambiguous-ampersand.html"
+        }
+      ]
+    },
+    "math": {
+      "patterns": [
+        {
+          "begin": "(?i)(<)(math)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+          "beginCaptures": {
+            "0": {
+              "name": "meta.tag.structure.$2.start.html"
+            },
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "patterns": [
+                {
+                  "include": "#attribute"
+                }
+              ]
+            },
+            "5": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "end": "(?i)(</)(\\2)\\s*(>)",
+          "endCaptures": {
+            "0": {
+              "name": "meta.tag.structure.$2.end.html"
+            },
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.element.structure.$2.html",
+          "patterns": [
+            {
+              "begin": "(?<!>)\\G",
+              "end": ">",
+              "endCaptures": {
+                "0": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.tag.structure.start.html",
+              "patterns": [
+                {
+                  "include": "#attribute"
+                }
+              ]
+            },
+            {
+              "include": "#tags"
+            }
+          ]
+        }
+      ],
+      "repository": {
+        "attribute": {
+          "patterns": [
+            {
+              "begin": "(s(hift|ymmetric|cript(sizemultiplier|level|minsize)|t(ackalign|retchy)|ide|u(pscriptshift|bscriptshift)|e(parator(s)?|lection)|rc)|h(eight|ref)|n(otation|umalign)|c(haralign|olumn(spa(n|cing)|width|lines|align)|lose|rossout)|i(n(dent(shift(first|last)?|target|align(first|last)?)|fixlinebreakstyle)|d)|o(pen|verflow)|d(i(splay(style)?|r)|e(nomalign|cimalpoint|pth))|position|e(dge|qual(columns|rows))|voffset|f(orm|ence|rame(spacing)?)|width|l(space|ine(thickness|leading|break(style|multchar)?)|o(ngdivstyle|cation)|ength|quote|argeop)|a(c(cent(under)?|tiontype)|l(t(text|img(-(height|valign|width))?)|ign(mentscope)?))|r(space|ow(spa(n|cing)|lines|align)|quote)|groupalign|x(link:href|mlns)|m(in(size|labelspacing)|ovablelimits|a(th(size|color|variant|background)|xsize))|bevelled)(?![\\w:-])",
+              "beginCaptures": {
+                "0": {
+                  "name": "entity.other.attribute-name.html"
+                }
+              },
+              "end": "(?=\\s*+[^=\\s])",
+              "name": "meta.attribute.$1.html",
+              "patterns": [
+                {
+                  "include": "#attribute-interior"
+                }
+              ]
+            },
+            {
+              "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)",
+              "beginCaptures": {
+                "0": {
+                  "name": "entity.other.attribute-name.html"
+                }
+              },
+              "comment": "Anything else that is valid",
+              "end": "(?=\\s*+[^=\\s])",
+              "name": "meta.attribute.unrecognized.$1.html",
+              "patterns": [
+                {
+                  "include": "#attribute-interior"
+                }
+              ]
+            },
+            {
+              "match": "[^\\s>]+",
+              "name": "invalid.illegal.character-not-allowed-here.html"
+            }
+          ]
+        },
+        "tags": {
+          "patterns": [
+            {
+              "include": "#comment"
+            },
+            {
+              "include": "#cdata"
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.structure.math.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.structure.math.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.structure.math.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.structure.math.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.structure.math.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.structure.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.inline.math.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(mi|mn|mo|ms|mspace|mtext|maligngroup|malignmark)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.inline.math.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(mi|mn|mo|ms|mspace|mtext|maligngroup|malignmark)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.inline.math.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.inline.math.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.inline.math.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.inline.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.object.math.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(mglyph)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.object.math.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(mglyph)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.object.math.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.object.math.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.object.math.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.object.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(([\\w:]+))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.other.invalid.html"
+            },
+            {
+              "begin": "(?i)(<)((\\w[^\\s>]*))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)((\\2))\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.other.invalid.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.other.invalid.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "include": "#tags-invalid"
+            }
+          ]
+        }
+      }
+    },
+    "svg": {
+      "patterns": [
+        {
+          "begin": "(?i)(<)(svg)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+          "beginCaptures": {
+            "0": {
+              "name": "meta.tag.structure.$2.start.html"
+            },
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "patterns": [
+                {
+                  "include": "#attribute"
+                }
+              ]
+            },
+            "5": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "end": "(?i)(</)(\\2)\\s*(>)",
+          "endCaptures": {
+            "0": {
+              "name": "meta.tag.structure.$2.end.html"
+            },
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.element.structure.$2.html",
+          "patterns": [
+            {
+              "begin": "(?<!>)\\G",
+              "end": ">",
+              "endCaptures": {
+                "0": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.tag.structure.start.html",
+              "patterns": [
+                {
+                  "include": "#attribute"
+                }
+              ]
+            },
+            {
+              "include": "#tags"
+            }
+          ]
+        }
+      ],
+      "repository": {
+        "attribute": {
+          "patterns": [
+            {
+              "begin": "(s(hape-rendering|ystemLanguage|cale|t(yle|itchTiles|op-(color|opacity)|dDeviation|em(h|v)|artOffset|r(i(ng|kethrough-(thickness|position))|oke(-(opacity|dash(offset|array)|width|line(cap|join)|miterlimit))?))|urfaceScale|p(e(cular(Constant|Exponent)|ed)|acing|readMethod)|eed|lope)|h(oriz-(origin-x|adv-x)|eight|anging|ref(lang)?)|y(1|2|ChannelSelector)?|n(umOctaves|ame)|c(y|o(ntentS(criptType|tyleType)|lor(-(interpolation(-filters)?|profile|rendering))?)|ursor|l(ip(-(path|rule)|PathUnits)?|ass)|a(p-height|lcMode)|x)|t(ype|o|ext(-(decoration|anchor|rendering)|Length)|a(rget(X|Y)?|b(index|leValues))|ransform)|i(n(tercept|2)?|d(eographic)?|mage-rendering)|z(oomAndPan)?|o(p(erator|acity)|ver(flow|line-(thickness|position))|ffset|r(i(ent(ation)?|gin)|der))|d(y|i(splay|visor|ffuseConstant|rection)|ominant-baseline|ur|e(scent|celerate)|x)?|u(1|n(i(code(-(range|bidi))?|ts-per-em)|derline-(thickness|position))|2)|p(ing|oint(s(At(X|Y|Z))?|er-events)|a(nose-1|t(h(Length)?|tern(ContentUnits|Transform|Units))|int-order)|r(imitiveUnits|eserveA(spectRatio|lpha)))|e(n(d|able-background)|dgeMode|levation|x(ternalResourcesRequired|ponent))|v(i(sibility|ew(Box|Target))|-(hanging|ideographic|alphabetic|mathematical)|e(ctor-effect|r(sion|t-(origin-(y|x)|adv-y)))|alues)|k(1|2|3|e(y(Splines|Times|Points)|rn(ing|el(Matrix|UnitLength)))|4)?|f(y|il(ter(Res|Units)?|l(-(opacity|rule))?)|o(nt-(s(t(yle|retch)|ize(-adjust)?)|variant|family|weight)|rmat)|lood-(color|opacity)|r(om)?|x)|w(idth(s)?|ord-spacing|riting-mode)|l(i(ghting-color|mitingConeAngle)|ocal|e(ngthAdjust|tter-spacing)|ang)|a(scent|cc(umulate|ent-height)|ttribute(Name|Type)|zimuth|dditive|utoReverse|l(ignment-baseline|phabetic|lowReorder)|rabic-form|mplitude)|r(y|otate|e(s(tart|ult)|ndering-intent|peat(Count|Dur)|quired(Extensions|Features)|f(X|Y|errerPolicy)|l)|adius|x)?|g(1|2|lyph(Ref|-(name|orientation-(horizontal|vertical)))|radient(Transform|Units))|x(1|2|ChannelSelector|-height|link:(show|href|t(ype|itle)|a(ctuate|rcrole)|role)|ml:(space|lang|base))?|m(in|ode|e(thod|dia)|a(sk(ContentUnits|Units)?|thematical|rker(Height|-(start|end|mid)|Units|Width)|x))|b(y|ias|egin|ase(Profile|line-shift|Frequency)|box))(?![\\w:-])",
+              "beginCaptures": {
+                "0": {
+                  "name": "entity.other.attribute-name.html"
+                }
+              },
+              "end": "(?=\\s*+[^=\\s])",
+              "name": "meta.attribute.$1.html",
+              "patterns": [
+                {
+                  "include": "#attribute-interior"
+                }
+              ]
+            },
+            {
+              "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)",
+              "beginCaptures": {
+                "0": {
+                  "name": "entity.other.attribute-name.html"
+                }
+              },
+              "comment": "Anything else that is valid",
+              "end": "(?=\\s*+[^=\\s])",
+              "name": "meta.attribute.unrecognized.$1.html",
+              "patterns": [
+                {
+                  "include": "#attribute-interior"
+                }
+              ]
+            },
+            {
+              "match": "[^\\s>]+",
+              "name": "invalid.illegal.character-not-allowed-here.html"
+            }
+          ]
+        },
+        "tags": {
+          "patterns": [
+            {
+              "include": "#comment"
+            },
+            {
+              "include": "#cdata"
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.metadata.svg.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.metadata.svg.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.svg.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.svg.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.metadata.svg.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.metadata.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.structure.svg.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.structure.svg.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.structure.svg.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.structure.svg.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.structure.svg.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.structure.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.inline.svg.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.inline.svg.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.inline.svg.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.inline.svg.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.inline.svg.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.inline.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.object.svg.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.object.svg.$2.html"
+            },
+            {
+              "begin": "(?i)(<)(a|circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.object.svg.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)(\\2)\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.object.svg.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.object.svg.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.object.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.other.svg.$2.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.deprecated.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.other.svg.$2.html"
+            },
+            {
+              "begin": "(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.other.svg.$2.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.deprecated.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)((\\2))\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.other.svg.$2.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.deprecated.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.other.svg.$2.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.other.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "captures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.void.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "match": "(?i)(<)(([\\w:]+))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))",
+              "name": "meta.element.other.invalid.html"
+            },
+            {
+              "begin": "(?i)(<)((\\w[^\\s>]*))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                "6": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "end": "(?i)(</)((\\2))\\s*(>)|(/>)|(?=</\\w+)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.other.invalid.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "invalid.illegal.unrecognized-tag.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                },
+                "5": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.element.other.invalid.html",
+              "patterns": [
+                {
+                  "begin": "(?<!>)\\G",
+                  "end": "(?=/>)|>",
+                  "endCaptures": {
+                    "0": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "name": "meta.tag.other.invalid.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "include": "#tags"
+                }
+              ]
+            },
+            {
+              "include": "#tags-invalid"
+            }
+          ]
+        }
+      }
+    },
+    "tags-invalid": {
+      "patterns": [
+        {
+          "begin": "(</?)((\\w[^\\s>]*))(?<!/)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.illegal.unrecognized-tag.html"
+            }
+          },
+          "end": "((?: ?/)?>)",
+          "endCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.other.$2.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        }
+      ]
+    },
+    "tags-valid": {
+      "patterns": [
+        {
+          "begin": "(^[ \\t]+)?(?=<(?i:style)\\b(?!-))",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.whitespace.embedded.leading.html"
+            }
+          },
+          "end": "(?!\\G)([ \\t]*$\\n?)?",
+          "endCaptures": {
+            "1": {
+              "name": "punctuation.whitespace.embedded.trailing.html"
+            }
+          },
+          "patterns": [
+            {
+              "begin": "(?i)(<)(style)(?=\\s|/?>)",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.style.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                }
+              },
+              "end": "(?i)((<)/)(style)\\s*(>)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.style.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "source.css-ignored-vscode"
+                },
+                "3": {
+                  "name": "entity.name.tag.html"
+                },
+                "4": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.embedded.block.html",
+              "patterns": [
+                {
+                  "begin": "\\G",
+                  "captures": {
+                    "1": {
+                      "name": "punctuation.definition.tag.end.html"
+                    }
+                  },
+                  "end": "(>)",
+                  "name": "meta.tag.metadata.style.start.html",
+                  "patterns": [
+                    {
+                      "include": "#attribute"
+                    }
+                  ]
+                },
+                {
+                  "begin": "(?!\\G)",
+                  "end": "(?=</(?i:style))",
+                  "name": "source.css",
+                  "patterns": [
+                    {
+                      "include": "source.css"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "begin": "(^[ \\t]+)?(?=<(?i:script)\\b(?!-))",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.whitespace.embedded.leading.html"
+            }
+          },
+          "end": "(?!\\G)([ \\t]*$\\n?)?",
+          "endCaptures": {
+            "1": {
+              "name": "punctuation.whitespace.embedded.trailing.html"
+            }
+          },
+          "patterns": [
+            {
+              "begin": "(<)((?i:script))\\b",
+              "beginCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.script.start.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                }
+              },
+              "end": "(/)((?i:script))(>)",
+              "endCaptures": {
+                "0": {
+                  "name": "meta.tag.metadata.script.end.html"
+                },
+                "1": {
+                  "name": "punctuation.definition.tag.begin.html"
+                },
+                "2": {
+                  "name": "entity.name.tag.html"
+                },
+                "3": {
+                  "name": "punctuation.definition.tag.end.html"
+                }
+              },
+              "name": "meta.embedded.block.html",
+              "patterns": [
+                {
+                  "begin": "\\G",
+                  "end": "(?=/)",
+                  "patterns": [
+                    {
+                      "begin": "(>)",
+                      "beginCaptures": {
+                        "0": {
+                          "name": "meta.tag.metadata.script.start.html"
+                        },
+                        "1": {
+                          "name": "punctuation.definition.tag.end.html"
+                        }
+                      },
+                      "end": "((<))(?=/(?i:script))",
+                      "endCaptures": {
+                        "0": {
+                          "name": "meta.tag.metadata.script.end.html"
+                        },
+                        "1": {
+                          "name": "punctuation.definition.tag.begin.html"
+                        },
+                        "2": {
+                          "name": "source.js-ignored-vscode"
+                        }
+                      },
+                      "patterns": [
+                        {
+                          "begin": "\\G",
+                          "end": "(?=</(?i:script))",
+                          "name": "source.js",
+                          "patterns": [
+                            {
+                              "begin": "(^[ \\t]+)?(?=//)",
+                              "beginCaptures": {
+                                "1": {
+                                  "name": "punctuation.whitespace.comment.leading.js"
+                                }
+                              },
+                              "end": "(?!\\G)",
+                              "patterns": [
+                                {
+                                  "begin": "//",
+                                  "beginCaptures": {
+                                    "0": {
+                                      "name": "punctuation.definition.comment.js"
+                                    }
+                                  },
+                                  "end": "(?=</script)|\\n",
+                                  "name": "comment.line.double-slash.js"
+                                }
+                              ]
+                            },
+                            {
+                              "begin": "/\\*",
+                              "captures": {
+                                "0": {
+                                  "name": "punctuation.definition.comment.js"
+                                }
+                              },
+                              "end": "\\*/|(?=</script)",
+                              "name": "comment.block.js"
+                            },
+                            {
+                              "include": "source.js"
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "begin": "\\G",
+                      "end": "(?ix:\n\t\t\t\t\t\t\t\t\t\t\t\t(?=>\t\t\t\t\t\t\t\t\t\t\t# Tag without type attribute\n\t\t\t\t\t\t\t\t\t\t\t\t  | type(?=[\\s=])\n\t\t\t\t\t\t\t\t\t\t\t\t  \t(?!\\s*=\\s*\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t''\t\t\t\t\t\t\t\t# Empty\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | \"\"\t\t\t\t\t\t\t\t\t#   Values\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | ('|\"|)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext/\t\t\t\t\t\t\t# Text mime-types\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjavascript(1\\.[0-5])?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | x-javascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | jscript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | livescript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | (x-)?ecmascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | babel\t\t\t\t\t\t# Javascript variant currently\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \t\t\t\t\t\t\t\t#   recognized as such\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | application/\t\t\t\t\t# Application mime-types\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(x-)?javascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | (x-)?ecmascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  | module\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\\s\"'>]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t)",
+                      "name": "meta.tag.metadata.script.start.html",
+                      "patterns": [
+                        {
+                          "include": "#attribute"
+                        }
+                      ]
+                    },
+                    {
+                      "begin": "(?ix:\n\t\t\t\t\t\t\t\t\t\t\t\t(?=\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype\\s*=\\s*\n\t\t\t\t\t\t\t\t\t\t\t\t\t('|\"|)\n\t\t\t\t\t\t\t\t\t\t\t\t\ttext/\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tx-handlebars\n\t\t\t\t\t\t\t\t\t\t\t\t\t  | (x-(handlebars-)?|ng-)?template\n\t\t\t\t\t\t\t\t\t\t\t\t\t  | html\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\\s\"'>]\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t)",
+                      "end": "((<))(?=/(?i:script))",
+                      "endCaptures": {
+                        "0": {
+                          "name": "meta.tag.metadata.script.end.html"
+                        },
+                        "1": {
+                          "name": "punctuation.definition.tag.begin.html"
+                        },
+                        "2": {
+                          "name": "text.html.basic"
+                        }
+                      },
+                      "patterns": [
+                        {
+                          "begin": "\\G",
+                          "end": "(>)",
+                          "endCaptures": {
+                            "1": {
+                              "name": "punctuation.definition.tag.end.html"
+                            }
+                          },
+                          "name": "meta.tag.metadata.script.start.html",
+                          "patterns": [
+                            {
+                              "include": "#attribute"
+                            }
+                          ]
+                        },
+                        {
+                          "begin": "(?!\\G)",
+                          "end": "(?=</(?i:script))",
+                          "name": "text.html.basic",
+                          "patterns": [
+                            {
+                              "include": "text.html.basic"
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "begin": "(?=(?i:type))",
+                      "end": "(<)(?=/(?i:script))",
+                      "endCaptures": {
+                        "0": {
+                          "name": "meta.tag.metadata.script.end.html"
+                        },
+                        "1": {
+                          "name": "punctuation.definition.tag.begin.html"
+                        }
+                      },
+                      "patterns": [
+                        {
+                          "begin": "\\G",
+                          "end": "(>)",
+                          "endCaptures": {
+                            "1": {
+                              "name": "punctuation.definition.tag.end.html"
+                            }
+                          },
+                          "name": "meta.tag.metadata.script.start.html",
+                          "patterns": [
+                            {
+                              "include": "#attribute"
+                            }
+                          ]
+                        },
+                        {
+                          "begin": "(?!\\G)",
+                          "end": "(?=</(?i:script))",
+                          "name": "source.unknown"
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(base|link|meta)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.metadata.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(noscript|title)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.metadata.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)(noscript|title)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.metadata.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(col|hr|input)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.structure.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(address|article|aside|blockquote|body|button|caption|colgroup|datalist|dd|details|dialog|div|dl|dt|fieldset|figcaption|figure|footer|form|head|header|hgroup|html|h[1-6]|label|legend|li|main|map|menu|meter|nav|ol|optgroup|option|output|p|pre|progress|section|select|slot|summary|table|tbody|td|template|textarea|tfoot|th|thead|tr|ul)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.structure.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)(address|article|aside|blockquote|body|button|caption|colgroup|datalist|dd|details|dialog|div|dl|dt|fieldset|figcaption|figure|footer|form|head|header|hgroup|html|h[1-6]|label|legend|li|main|map|menu|meter|nav|ol|optgroup|option|output|p|pre|progress|section|select|slot|summary|table|tbody|td|template|textarea|tfoot|th|thead|tr|ul)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.structure.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(area|br|wbr)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.inline.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(a|abbr|b|bdi|bdo|cite|code|data|del|dfn|em|i|ins|kbd|mark|q|rp|rt|ruby|s|samp|small|span|strong|sub|sup|time|u|var)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.inline.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)(a|abbr|b|bdi|bdo|cite|code|data|del|dfn|em|i|ins|kbd|mark|q|rp|rt|ruby|s|samp|small|span|strong|sub|sup|time|u|var)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.inline.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(embed|img|param|source|track)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)(audio|canvas|iframe|object|picture|video)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)(audio|canvas|iframe|object|picture|video)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((basefont|isindex))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.metadata.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((center|frameset|noembed|noframes))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.structure.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)((center|frameset|noembed|noframes))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.structure.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((acronym|big|blink|font|strike|tt|xmp))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.inline.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)((acronym|big|blink|font|strike|tt|xmp))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.inline.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((frame))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.void.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((applet))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)((applet))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.deprecated.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.object.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(<)((dir|keygen|listing|menuitem|plaintext|spacer))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.illegal.no-longer-supported.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.other.$2.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(?i)(</)((dir|keygen|listing|menuitem|plaintext|spacer))(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            },
+            "3": {
+              "name": "invalid.illegal.no-longer-supported.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.other.$2.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "include": "#math"
+        },
+        {
+          "include": "#svg"
+        },
+        {
+          "begin": "(<)([a-zA-Z][.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*-[\\-.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": "/?>",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.custom.start.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        },
+        {
+          "begin": "(</)([a-zA-Z][.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*-[\\-.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*)(?=\\s|/?>)",
+          "beginCaptures": {
+            "1": {
+              "name": "punctuation.definition.tag.begin.html"
+            },
+            "2": {
+              "name": "entity.name.tag.html"
+            }
+          },
+          "end": ">",
+          "endCaptures": {
+            "0": {
+              "name": "punctuation.definition.tag.end.html"
+            }
+          },
+          "name": "meta.tag.custom.end.html",
+          "patterns": [
+            {
+              "include": "#attribute"
+            }
+          ]
+        }
+      ]
+    },
+    "xml-processing": {
+      "begin": "(<\\?)(xml)",
+      "captures": {
+        "1": {
+          "name": "punctuation.definition.tag.html"
+        },
+        "2": {
+          "name": "entity.name.tag.html"
+        }
+      },
+      "end": "(\\?>)",
+      "name": "meta.tag.metadata.processing.xml.html",
+      "patterns": [
+        {
+          "include": "#attribute"
+        }
+      ]
+    }
+  }
+}

+ 27 - 0
packages/vscode-ext/tsconfig.json

@@ -0,0 +1,27 @@
+{
+    "compilerOptions": {
+        "module": "commonjs",
+        "target": "es2019",
+        "lib": [
+            "ES2019"
+        ],
+        "outDir": "out",
+        "rootDir": "src",
+        "sourceMap": true
+    },
+    "include": [
+        "src"
+    ],
+    "exclude": [
+        "node_modules",
+        ".vscode-test"
+    ],
+    "references": [
+        {
+            "path": "./client"
+        },
+        {
+            "path": "./server"
+        }
+    ]
+}