Browse Source

Merge pull request #229 from Razoric480/fix-malformed-msg

Fix no header, non-TCP compliant msg for natives
Geequlim 4 years ago
parent
commit
c13f48c1f4
1 changed files with 239 additions and 134 deletions
  1. 239 134
      src/lsp/NativeDocumentManager.ts

+ 239 - 134
src/lsp/NativeDocumentManager.ts

@@ -1,81 +1,113 @@
-import * as vscode from 'vscode';
+import * as vscode from "vscode";
 import { EventEmitter } from "events";
 import { MessageIO } from "./MessageIO";
 import { NotificationMessage } from "vscode-jsonrpc";
 import * as Prism from "../deps/prism/prism";
 import * as marked from "marked";
-import { Methods, NativeSymbolInspectParams, GodotNativeSymbol, GodotNativeClassInfo, GodotCapabilities } from './gdscript.capabilities';
+import { get_configuration } from "../utils";
+import {
+	Methods,
+	NativeSymbolInspectParams,
+	GodotNativeSymbol,
+	GodotNativeClassInfo,
+	GodotCapabilities,
+} from "./gdscript.capabilities";
 marked.setOptions({
 	highlight: function (code, lang) {
 		return Prism.highlight(code, GDScriptGrammar, lang);
-	}
+	},
 });
 
 const enum WebViewMessageType {
-	INSPECT_NATIVE_SYMBOL = 'INSPECT_NATIVE_SYMBOL',
+	INSPECT_NATIVE_SYMBOL = "INSPECT_NATIVE_SYMBOL",
 }
-const LIST_NATIVE_CLASS_COMMAND = 'godot-tool.list_native_classes';
+const LIST_NATIVE_CLASS_COMMAND = "godot-tool.list_native_classes";
 
 export default class NativeDocumentManager extends EventEmitter {
-
 	private io: MessageIO = null;
-	private native_classes: {[key: string]: GodotNativeClassInfo } = {};
+	private native_classes: { [key: string]: GodotNativeClassInfo } = {};
 
 	constructor(io: MessageIO) {
 		super();
 		this.io = io;
-		io.on("message", (message: NotificationMessage)=>{
+		io.on("message", (message: NotificationMessage) => {
 			if (message.method == Methods.SHOW_NATIVE_SYMBOL) {
 				this.show_native_symbol(message.params);
 			} else if (message.method == Methods.GDSCRIPT_CAPABILITIES) {
-				for (const gdclass of (message.params as GodotCapabilities).native_classes) {
+				for (const gdclass of (message.params as GodotCapabilities)
+					.native_classes) {
 					this.native_classes[gdclass.name] = gdclass;
 				}
-				for (const gdclass of (message.params as GodotCapabilities).native_classes) {
+				for (const gdclass of (message.params as GodotCapabilities)
+					.native_classes) {
 					if (gdclass.inherits) {
-						const extended_classes = this.native_classes[gdclass.inherits].extended_classes || [];
+						const extended_classes =
+							this.native_classes[gdclass.inherits].extended_classes || [];
 						extended_classes.push(gdclass.name);
-						this.native_classes[gdclass.inherits].extended_classes = extended_classes;
+						this.native_classes[
+							gdclass.inherits
+						].extended_classes = extended_classes;
 					}
 				}
 			}
 		});
 
-		vscode.commands.registerCommand(LIST_NATIVE_CLASS_COMMAND, this.list_native_classes.bind(this));
+		vscode.commands.registerCommand(
+			LIST_NATIVE_CLASS_COMMAND,
+			this.list_native_classes.bind(this)
+		);
 	}
 
 	private async list_native_classes() {
 		let classname = await vscode.window.showQuickPick(
 			Object.keys(this.native_classes).sort(),
 			{
-				placeHolder: 'Type godot class name here',
-				canPickMany: false
+				placeHolder: "Type godot class name here",
+				canPickMany: false,
 			}
 		);
 		if (classname) {
-			this.inspect_native_symbol({native_class: classname, symbol_name: classname});
+			this.inspect_native_symbol({
+				native_class: classname,
+				symbol_name: classname,
+			});
 		}
 	}
 
 	private inspect_native_symbol(params: NativeSymbolInspectParams) {
-		this.io.send_message(JSON.stringify({
-			id: -1,
-			jsonrpc: "2.0",
-			method: Methods.INSPECT_NATIVE_SYMBOL,
-			params
-		}));
+		let json_data = "";
+		if (get_configuration("gdscript_lsp_server_protocol", "tcp") == "ws") {
+			json_data = JSON.stringify({
+				id: -1,
+				jsonrpc: "2.0",
+				method: Methods.INSPECT_NATIVE_SYMBOL,
+				params,
+			});
+		} else {
+			json_data = JSON.stringify({
+				jsonrpc: "2.0",
+				method: Methods.INSPECT_NATIVE_SYMBOL,
+				params: params,
+			});
+			this.send_header(json_data.length);
+		}
+		this.io.send_message(json_data);
 	}
 
+	private send_header(data_length: number) {
+		this.io.send_message(`Content-Length: ${data_length}\r\n\r\n`);
+	}
 
 	private show_native_symbol(symbol: GodotNativeSymbol) {
 		// 创建webview
 		const panel = vscode.window.createWebviewPanel(
-			'doc',
+			"doc",
 			symbol.name,
 			vscode.ViewColumn.Nine,
 			{
 				enableScripts: true, // 启用JS,默认禁用
 				retainContextWhenHidden: false, // webview被隐藏时保持状态,避免被重置
+				enableFindWidget: true,
 			}
 		);
 		panel.title = symbol.name;
@@ -131,7 +163,6 @@ export default class NativeDocumentManager extends EventEmitter {
 		</html>`;
 	}
 
-
 	private make_symbol_document(symbol: GodotNativeSymbol): string {
 		const classlink = make_link(symbol.native_class, undefined);
 		const classinfo = this.native_classes[symbol.native_class];
@@ -142,87 +173,135 @@ export default class NativeDocumentManager extends EventEmitter {
 				return "";
 			}
 			const ret_type = make_link(parts[2] || "void", undefined);
-			let args = (parts[1] || "").replace(/\:\s([A-z0-9_]+)(\,\s*)?/g, `: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`);
+			let args = (parts[1] || "").replace(
+				/\:\s([A-z0-9_]+)(\,\s*)?/g,
+				`: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`
+			);
 			args = args.replace(/\s=\s(.*?)[\,\)]/g, "");
-			return `${ret_type} ${with_class?`${classlink}.`:''}${element("a", s.name, {href: `#${s.name}`})}( ${args} )`;
+			return `${ret_type} ${with_class ? `${classlink}.` : ""}${element(
+				"a",
+				s.name,
+				{ href: `#${s.name}` }
+			)}( ${args} )`;
 		}
 
-		function make_symbol_elements(s: GodotNativeSymbol, with_class = false): {index?: string, body: string} {
+		function make_symbol_elements(
+			s: GodotNativeSymbol,
+			with_class = false
+		): { index?: string; body: string } {
 			switch (s.kind) {
 				case vscode.SymbolKind.Property:
-				case vscode.SymbolKind.Variable: {
-					// var Control.anchor_left: float
-					const parts = /\.([A-z_0-9]+)\:\s(.*)$/.exec(s.detail);
-					if (!parts) {
-						return;
+				case vscode.SymbolKind.Variable:
+					{
+						// var Control.anchor_left: float
+						const parts = /\.([A-z_0-9]+)\:\s(.*)$/.exec(s.detail);
+						if (!parts) {
+							return;
+						}
+						let type = make_link(parts[2], undefined);
+						let name = element("a", s.name, { href: `#${s.name}` });
+						const title = element(
+							"h4",
+							`${type} ${with_class ? `${classlink}.` : ""}${s.name}`
+						);
+						const doc = element(
+							"p",
+							format_documentation(s.documentation, symbol.native_class)
+						);
+						const div = element("div", title + doc);
+						return {
+							index: type + " " + name,
+							body: div,
+						};
 					}
-					let type = make_link(parts[2], undefined);
-					let name = element("a", s.name, {href: `#${s.name}`});
-					const title = element('h4', `${type} ${with_class?`${classlink}.`:''}${s.name}`);
-					const doc = element("p", format_documentation(s.documentation, symbol.native_class));
-					const div = element("div", title + doc);
-					return {
-						index: type + " " + name,
-						body: div,
-					};
-				} break;
-				case vscode.SymbolKind.Constant: {
-					// const Control.FOCUS_ALL: FocusMode = 2
-					// const Control.NOTIFICATION_RESIZED = 40
-					const parts = /\.([A-Za-z_0-9]+)(\:\s*)?([A-z0-9_\.]+)?\s*=\s*(.*)$/.exec(s.detail);
-					if (!parts) {
-						return;
+					break;
+				case vscode.SymbolKind.Constant:
+					{
+						// const Control.FOCUS_ALL: FocusMode = 2
+						// const Control.NOTIFICATION_RESIZED = 40
+						const parts = /\.([A-Za-z_0-9]+)(\:\s*)?([A-z0-9_\.]+)?\s*=\s*(.*)$/.exec(
+							s.detail
+						);
+						if (!parts) {
+							return;
+						}
+						let type = make_link(parts[3] || "int", undefined);
+						let name = parts[1];
+						let value = element("code", parts[4]);
+
+						const title = element(
+							"p",
+							`${type} ${with_class ? `${classlink}.` : ""}${name} = ${value}`
+						);
+						const doc = element(
+							"p",
+							format_documentation(s.documentation, symbol.native_class)
+						);
+						const div = element("div", title + doc);
+						return {
+							body: div,
+						};
 					}
-					let type = make_link(parts[3] || 'int', undefined);
-					let name = parts[1];
-					let value = element('code', parts[4]);
-
-					const title = element('p', `${type} ${with_class?`${classlink}.`:''}${name} = ${value}`);
-					const doc = element("p", format_documentation(s.documentation, symbol.native_class));
-					const div = element("div", title + doc);
-					return {
-						body: div
-					};
-				} break;
-				case vscode.SymbolKind.Event: {
-					const parts = /\.([A-z0-9]+)\((.*)?\)/.exec(s.detail);
-					if (!parts) {
-						return;
+					break;
+				case vscode.SymbolKind.Event:
+					{
+						const parts = /\.([A-z0-9]+)\((.*)?\)/.exec(s.detail);
+						if (!parts) {
+							return;
+						}
+						const args = (parts[2] || "").replace(
+							/\:\s([A-z0-9_]+)(\,\s*)?/g,
+							`: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`
+						);
+						const title = element(
+							"p",
+							`${
+								with_class ? `signal ${with_class ? `${classlink}.` : ""}` : ""
+							}${s.name}( ${args} )`
+						);
+						const doc = element(
+							"p",
+							format_documentation(s.documentation, symbol.native_class)
+						);
+						const div = element("div", title + doc);
+						return {
+							body: div,
+						};
 					}
-					const args = (parts[2] || "").replace(/\:\s([A-z0-9_]+)(\,\s*)?/g, `: <a href="" onclick="inspect('$1', '$1')">$1</a>$2`);
-					const title = element('p', `${with_class?`signal ${with_class?`${classlink}.`:''}`:''}${s.name}( ${args} )`);
-					const doc = element("p", format_documentation(s.documentation, symbol.native_class));
-					const div = element("div", title + doc);
-					return {
-						body: div
-					};
-				} break;
+					break;
 				case vscode.SymbolKind.Method:
-				case vscode.SymbolKind.Function: {
-					const signature = make_function_signature(s, with_class);
-					const title = element("h4", signature);
-					const doc = element("p", format_documentation(s.documentation, symbol.native_class));
-					const div = element("div", title + doc);
-					return {
-						index: signature,
-						body: div
-					};
-				} break;
+				case vscode.SymbolKind.Function:
+					{
+						const signature = make_function_signature(s, with_class);
+						const title = element("h4", signature);
+						const doc = element(
+							"p",
+							format_documentation(s.documentation, symbol.native_class)
+						);
+						const div = element("div", title + doc);
+						return {
+							index: signature,
+							body: div,
+						};
+					}
+					break;
 				default:
 					break;
 			}
 		}
 
 		if (symbol.kind == vscode.SymbolKind.Class) {
-
 			let doc = element("h2", `Native class ${symbol.name}`);
 			const parts = /extends\s+([A-z0-9]+)/.exec(symbol.detail);
-			let inherits = parts && parts.length > 1 ? parts[1] : '';
+			let inherits = parts && parts.length > 1 ? parts[1] : "";
 			if (inherits) {
-				let inherits_chian = '';
+				let inherits_chian = "";
 				let base_class = this.native_classes[inherits];
-				while(base_class) {
-					inherits_chian += `${inherits_chian?' >':''} ${make_link(base_class.name, undefined)}`;
+				while (base_class) {
+					inherits_chian += `${inherits_chian ? " >" : ""} ${make_link(
+						base_class.name,
+						undefined
+					)}`;
 					base_class = this.native_classes[base_class.inherits];
 				}
 				inherits = `Inherits: ${inherits_chian}`;
@@ -231,7 +310,7 @@ export default class NativeDocumentManager extends EventEmitter {
 			if (classinfo && classinfo.extended_classes) {
 				let inherited = "";
 				for (const c of classinfo.extended_classes) {
-					inherited += (inherited ? ', ' : ' ') + make_link(c, c);
+					inherited += (inherited ? ", " : " ") + make_link(c, c);
 				}
 				doc += element("p", `Inherited by:${inherited}`);
 			}
@@ -250,33 +329,36 @@ export default class NativeDocumentManager extends EventEmitter {
 					case vscode.SymbolKind.Property:
 					case vscode.SymbolKind.Variable:
 						properties_index += element("li", elements.index);
-						propertyies += element("li", elements.body, {id: s.name});
+						propertyies += element("li", elements.body, { id: s.name });
 						break;
 					case vscode.SymbolKind.Constant:
-						constants += element("li", elements.body, {id: s.name});
+						constants += element("li", elements.body, { id: s.name });
 						break;
 					case vscode.SymbolKind.Event:
-						signals += element("li", elements.body, {id: s.name});
+						signals += element("li", elements.body, { id: s.name });
 						break;
 					case vscode.SymbolKind.Method:
 					case vscode.SymbolKind.Function:
 						methods_index += element("li", elements.index);
-						methods += element("li", elements.body, {id: s.name});
+						methods += element("li", elements.body, { id: s.name });
 						break;
 					default:
-						others += element("li", elements.body, {id: s.name});
+						others += element("li", elements.body, { id: s.name });
 						break;
 				}
 			}
 
 			function add_group(title: string, block: string) {
 				if (block) {
-					doc += element('h3', title);
-					doc += element('ul', block);
+					doc += element("h3", title);
+					doc += element("ul", block);
 				}
 			}
 
-			doc += element("p", format_documentation(symbol.documentation, symbol.native_class));
+			doc += element(
+				"p",
+				format_documentation(symbol.documentation, symbol.native_class)
+			);
 			add_group("Properties", properties_index);
 			add_group("Constants", constants);
 			add_group("Signals", signals);
@@ -291,7 +373,11 @@ export default class NativeDocumentManager extends EventEmitter {
 			let doc = "";
 			const elements = make_symbol_elements(symbol, true);
 			if (elements.index) {
-				if ([vscode.SymbolKind.Function, vscode.SymbolKind.Method].indexOf(symbol.kind) == -1) {
+				if (
+					[vscode.SymbolKind.Function, vscode.SymbolKind.Method].indexOf(
+						symbol.kind
+					) == -1
+				) {
 					doc += element("h2", elements.index);
 				}
 			}
@@ -301,25 +387,39 @@ export default class NativeDocumentManager extends EventEmitter {
 	}
 }
 
-function element<K extends keyof HTMLElementTagNameMap>(tag: K, content: string, props = {}, new_line?: boolean, indent?:string) {
+function element<K extends keyof HTMLElementTagNameMap>(
+	tag: K,
+	content: string,
+	props = {},
+	new_line?: boolean,
+	indent?: string
+) {
 	let props_str = "";
 	for (const key in props) {
 		if (props.hasOwnProperty(key)) {
 			props_str += ` ${key}="${props[key]}"`;
 		}
 	}
-	return `${indent || ''}<${tag} ${props_str}>${content}</${tag}>${new_line ? '\n' : ''}`;
+	return `${indent || ""}<${tag} ${props_str}>${content}</${tag}>${
+		new_line ? "\n" : ""
+	}`;
 }
 function make_link(classname: string, symbol: string) {
 	if (!symbol || symbol == classname) {
-		return element('a', classname, {onclick: `inspect('${classname}', '${classname}')`, href: ''});
+		return element("a", classname, {
+			onclick: `inspect('${classname}', '${classname}')`,
+			href: "",
+		});
 	} else {
-		return element('a', `${classname}.${symbol}`, {onclick: `inspect('${classname}', '${symbol}')`, href: ''});
+		return element("a", `${classname}.${symbol}`, {
+			onclick: `inspect('${classname}', '${symbol}')`,
+			href: "",
+		});
 	}
 }
 
 function make_codeblock(code: string) {
-	const md = marked('```gdscript\n' + code + '\n```');
+	const md = marked("```gdscript\n" + code + "\n```");
 	return `<div class="codeblock">${md}</div>`;
 }
 
@@ -354,9 +454,15 @@ function format_documentation(p_bbcode: string, classname: string) {
 			// [i] [/u] [code] --> <i> </u> <code>
 			line = line.replace(/(\[(\/?)([a-z]+)\])/g, `<$2$3>`);
 			// [Reference] --> <a>Reference</a>
-			line = line.replace(/(\[([A-Z]+[A-Z_a-z0-9]*)\])/g, `<a href="" onclick="inspect('$2', '$2')">$2</a>`);
+			line = line.replace(
+				/(\[([A-Z]+[A-Z_a-z0-9]*)\])/g,
+				`<a href="" onclick="inspect('$2', '$2')">$2</a>`
+			);
 			// [method _set] --> <a>_set</a>
-			line = line.replace(/(\[([a-z]+)\s+([A-Z_a-z][A-Z_a-z0-9]*)\])/g, `<a href="" onclick="inspect('${classname}', '$3')">$3</a>`);
+			line = line.replace(
+				/(\[([a-z]+)\s+([A-Z_a-z][A-Z_a-z0-9]*)\])/g,
+				`<a href="" onclick="inspect('${classname}', '$3')">$3</a>`
+			);
 			line += "<br/>";
 			html += line;
 		} else {
@@ -369,66 +475,65 @@ function format_documentation(p_bbcode: string, classname: string) {
 	return html;
 }
 
-
 const GDScriptGrammar = {
-	'comment': {
+	comment: {
 		pattern: /(^|[^\\])#.*/,
-		lookbehind: true
+		lookbehind: true,
 	},
-	'string-interpolation': {
+	"string-interpolation": {
 		pattern: /(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,
 		greedy: true,
 		inside: {
-			'interpolation': {
+			interpolation: {
 				// "{" <expression> <optional "!s", "!r", or "!a"> <optional ":" format specifier> "}"
 				pattern: /((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,
 				lookbehind: true,
 				inside: {
-					'format-spec': {
+					"format-spec": {
 						pattern: /(:)[^:(){}]+(?=}$)/,
-						lookbehind: true
+						lookbehind: true,
 					},
-					'conversion-option': {
+					"conversion-option": {
 						pattern: /![sra](?=[:}]$)/,
-						alias: 'punctuation'
+						alias: "punctuation",
 					},
-					rest: null
-				}
+					rest: null,
+				},
 			},
-			'string': /[\s\S]+/
-		}
+			string: /[\s\S]+/,
+		},
 	},
-	'triple-quoted-string': {
+	"triple-quoted-string": {
 		pattern: /(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,
 		greedy: true,
-		alias: 'string'
+		alias: "string",
 	},
-	'string': {
+	string: {
 		pattern: /(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,
-		greedy: true
+		greedy: true,
 	},
-	'function': {
+	function: {
 		pattern: /((?:^|\s)func[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,
-		lookbehind: true
+		lookbehind: true,
 	},
-	'class-name': {
+	"class-name": {
 		pattern: /(\bclass\s+)\w+/i,
-		lookbehind: true
+		lookbehind: true,
 	},
-	'decorator': {
+	decorator: {
 		pattern: /(^\s*)@\w+(?:\.\w+)*/im,
 		lookbehind: true,
-		alias: ['annotation', 'punctuation'],
+		alias: ["annotation", "punctuation"],
 		inside: {
-			'punctuation': /\./
-		}
+			punctuation: /\./,
+		},
 	},
-	'keyword': /\b(?:if|elif|else|for|while|break|continue|pass|return|match|func|class|class_name|extends|is|onready|tool|static|export|setget|const|var|as|void|enum|preload|assert|yield|signal|breakpoint|rpc|sync|master|puppet|slave|remotesync|mastersync|puppetsync)\b/,
-	'builtin': /\b(?:PI|TAU|NAN|INF|_|sin|cos|tan|sinh|cosh|tanh|asin|acos|atan|atan2|sqrt|fmod|fposmod|floor|ceil|round|abs|sign|pow|log|exp|is_nan|is_inf|ease|decimals|stepify|lerp|dectime|randomize|randi|randf|rand_range|seed|rand_seed|deg2rad|rad2deg|linear2db|db2linear|max|min|clamp|nearest_po2|weakref|funcref|convert|typeof|type_exists|char|str|print|printt|prints|printerr|printraw|var2str|str2var|var2bytes|bytes2var|range|load|inst2dict|dict2inst|hash|Color8|print_stack|instance_from_id|preload|yield|assert|Vector2|Vector3|Color|Rect2|Array|Basis|Dictionary|Plane|Quat|RID|Rect3|Transform|Transform2D|AABB|String|Color|NodePath|RID|Object|Dictionary|Array|PoolByteArray|PoolIntArray|PoolRealArray|PoolStringArray|PoolVector2Array|PoolVector3Array|PoolColorArray)\b/,
-	'boolean': /\b(?:true|false)\b/,
-	'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,
-	'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,
-	'punctuation': /[{}[\];(),.:]/
+	keyword: /\b(?:if|elif|else|for|while|break|continue|pass|return|match|func|class|class_name|extends|is|onready|tool|static|export|setget|const|var|as|void|enum|preload|assert|yield|signal|breakpoint|rpc|sync|master|puppet|slave|remotesync|mastersync|puppetsync)\b/,
+	builtin: /\b(?:PI|TAU|NAN|INF|_|sin|cos|tan|sinh|cosh|tanh|asin|acos|atan|atan2|sqrt|fmod|fposmod|floor|ceil|round|abs|sign|pow|log|exp|is_nan|is_inf|ease|decimals|stepify|lerp|dectime|randomize|randi|randf|rand_range|seed|rand_seed|deg2rad|rad2deg|linear2db|db2linear|max|min|clamp|nearest_po2|weakref|funcref|convert|typeof|type_exists|char|str|print|printt|prints|printerr|printraw|var2str|str2var|var2bytes|bytes2var|range|load|inst2dict|dict2inst|hash|Color8|print_stack|instance_from_id|preload|yield|assert|Vector2|Vector3|Color|Rect2|Array|Basis|Dictionary|Plane|Quat|RID|Rect3|Transform|Transform2D|AABB|String|Color|NodePath|RID|Object|Dictionary|Array|PoolByteArray|PoolIntArray|PoolRealArray|PoolStringArray|PoolVector2Array|PoolVector3Array|PoolColorArray)\b/,
+	boolean: /\b(?:true|false)\b/,
+	number: /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,
+	operator: /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,
+	punctuation: /[{}[\];(),.:]/,
 };
 
 const PrismStyleSheet = `