ソースを参照

Support html custom data. For microsoft/monaco-editor#2284

Martin Aeschlimann 4 年 前
コミット
c8b5d5b6d5
7 ファイル変更165 行追加161 行削除
  1. 60 3
      monaco.d.ts
  2. 11 3
      package-lock.json
  3. 2 2
      package.json
  4. 0 4
      src/htmlMode.ts
  5. 12 5
      src/htmlWorker.ts
  6. 13 140
      src/languageFeatures.ts
  7. 67 4
      src/monaco.contribution.ts

+ 60 - 3
monaco.d.ts

@@ -21,7 +21,7 @@ declare namespace monaco.languages.html {
 		readonly wrapAttributes: 'auto' | 'force' | 'force-aligned' | 'force-expand-multiline';
 	}
 	export interface CompletionConfiguration {
-		[provider: string]: boolean;
+		[providerId: string]: boolean;
 	}
 	export interface Options {
 		/**
@@ -32,6 +32,10 @@ declare namespace monaco.languages.html {
 		 * A list of known schemas and/or associations of schemas to file names.
 		 */
 		readonly suggest?: CompletionConfiguration;
+		/**
+		 * Configures the HTML data types known by the HTML langauge service.
+		 */
+		readonly data?: HTMLDataConfiguration;
 	}
 	export interface ModeConfiguration {
 		/**
@@ -102,9 +106,9 @@ declare namespace monaco.languages.html {
 	}
 	/**
 	 * Registers a new HTML language service for the languageId.
-	 * Note: 'html', 'handlebar' and 'razor' registered by default.
+	 * Note: 'html', 'handlebar' and 'razor' are registered by default.
 	 *
-	 * Use this method only to register additional language ids with a HTML service.
+	 * Use this method to register additional language ids with a HTML service.
 	 * The language server has to be registered before an editor model is opened.
 	 */
 	export function registerHTMLLanguageService(
@@ -112,4 +116,57 @@ declare namespace monaco.languages.html {
 		options: Options,
 		modeConfiguration: ModeConfiguration
 	): LanguageServiceRegistration;
+	export interface HTMLDataConfiguration {
+		/**
+		 * Defines whether the standard HTML tags and attributes are shown
+		 */
+		useDefaultDataProvider?: boolean;
+		/**
+		 * Provides a set of custom data providers.
+		 */
+		dataProviders?: {
+			[providerId: string]: HTMLDataV1;
+		};
+	}
+	/**
+	 * Custom HTML tags attributes and attribute values
+	 * https://github.com/microsoft/vscode-html-languageservice/blob/main/docs/customData.md
+	 */
+	export interface HTMLDataV1 {
+		version: 1 | 1.1;
+		tags?: ITagData[];
+		globalAttributes?: IAttributeData[];
+		valueSets?: IValueSet[];
+	}
+	export interface IReference {
+		name: string;
+		url: string;
+	}
+	export interface ITagData {
+		name: string;
+		description?: string | MarkupContent;
+		attributes: IAttributeData[];
+		references?: IReference[];
+	}
+	export interface IAttributeData {
+		name: string;
+		description?: string | MarkupContent;
+		valueSet?: string;
+		values?: IValueData[];
+		references?: IReference[];
+	}
+	export interface IValueData {
+		name: string;
+		description?: string | MarkupContent;
+		references?: IReference[];
+	}
+	export interface IValueSet {
+		name: string;
+		values: IValueData[];
+	}
+	export interface MarkupContent {
+		kind: MarkupKind;
+		value: string;
+	}
+	export type MarkupKind = 'plaintext' | 'markdown';
 }

+ 11 - 3
package-lock.json

@@ -443,15 +443,23 @@
 			"dev": true
 		},
 		"vscode-html-languageservice": {
-			"version": "4.0.1",
-			"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-4.0.1.tgz",
-			"integrity": "sha512-CZtnuQoDwZdmPLKLMC6RqFlRTw0jvZK71l53u5ZIM3hSoVKAqW33gahBVNFpC3TPFxZSx0jqEhBTLf37RUMkWg==",
+			"version": "4.0.5",
+			"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-4.0.5.tgz",
+			"integrity": "sha512-9ZKp7nfR6ObUA+K65GfgDPdOmXaPH8MOWxE2RwWF3tVnVMq2w+COKjDNHMvv+uNxtmaRT7/skls7CD/HzrW99w==",
 			"dev": true,
 			"requires": {
 				"vscode-languageserver-textdocument": "^1.0.1",
 				"vscode-languageserver-types": "^3.16.0",
 				"vscode-nls": "^5.0.0",
 				"vscode-uri": "^3.0.2"
+			},
+			"dependencies": {
+				"vscode-languageserver-types": {
+					"version": "3.16.0",
+					"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
+					"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==",
+					"dev": true
+				}
 			}
 		},
 		"vscode-languageserver-textdocument": {

+ 2 - 2
package.json

@@ -29,9 +29,9 @@
 		"prettier": "^2.2.1",
 		"pretty-quick": "^3.1.0",
 		"requirejs": "^2.3.6",
-		"typescript": "^4.2.3",
 		"terser": "^5.6.0",
-		"vscode-html-languageservice": "4.0.1",
+		"typescript": "^4.2.3",
+		"vscode-html-languageservice": "^4.0.5",
 		"vscode-languageserver-types": "3.16.0",
 		"vscode-languageserver-textdocument": "^1.0.1"
 	},

+ 0 - 4
src/htmlMode.ts

@@ -54,7 +54,6 @@ export function setupMode1(defaults: LanguageServiceDefaults): void {
 			languageId,
 			new languageFeatures.DocumentRangeFormattingEditProvider(worker)
 		);
-		new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults);
 	}
 }
 
@@ -145,9 +144,6 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable {
 				)
 			);
 		}
-		if (modeConfiguration.diagnostics) {
-			providers.push(new languageFeatures.DiagnosticsAdapter(languageId, worker, defaults));
-		}
 	}
 
 	registerProviders();

+ 12 - 5
src/htmlWorker.ts

@@ -6,6 +6,7 @@
 import { worker } from './fillers/monaco-editor-core';
 import * as htmlService from 'vscode-html-languageservice';
 import type { Options } from './monaco.contribution';
+import { IHTMLDataProvider } from 'vscode-html-languageservice';
 
 export class HTMLWorker {
 	private _ctx: worker.IWorkerContext;
@@ -17,13 +18,19 @@ export class HTMLWorker {
 		this._ctx = ctx;
 		this._languageSettings = createData.languageSettings;
 		this._languageId = createData.languageId;
-		this._languageService = htmlService.getLanguageService();
-	}
 
-	async doValidation(uri: string): Promise<htmlService.Diagnostic[]> {
-		// not yet suported
-		return Promise.resolve([]);
+		const data = this._languageSettings.data;
+
+		const useDefaultDataProvider = data?.useDefaultDataProvider;
+		const customDataProviders: IHTMLDataProvider[] = [];
+		if (data?.dataProviders) {
+			for (const id in data.dataProviders) {
+				customDataProviders.push(htmlService.newHTMLDataProvider(id, data.dataProviders[id]));
+			}
+		}
+		this._languageService = htmlService.getLanguageService({ useDefaultDataProvider, customDataProviders });
 	}
+
 	async doComplete(
 		uri: string,
 		position: htmlService.Position

+ 13 - 140
src/languageFeatures.ts

@@ -3,7 +3,6 @@
  *  Licensed under the MIT License. See License.txt in the project root for license information.
  *--------------------------------------------------------------------------------------------*/
 
-import { LanguageServiceDefaults } from './monaco.contribution';
 import type { HTMLWorker } from './htmlWorker';
 import * as htmlService from 'vscode-html-languageservice';
 import {
@@ -13,139 +12,13 @@ import {
 	Position,
 	Range,
 	CancellationToken,
-	IDisposable,
-	MarkerSeverity,
 	IMarkdownString
 } from './fillers/monaco-editor-core';
-import { InsertReplaceEdit, TextEdit } from 'vscode-html-languageservice';
 
 export interface WorkerAccessor {
 	(...more: Uri[]): Promise<HTMLWorker>;
 }
 
-// --- diagnostics --- ---
-
-export class DiagnosticsAdapter {
-	private _disposables: IDisposable[] = [];
-	private _listener: { [uri: string]: IDisposable } = Object.create(null);
-
-	constructor(
-		private _languageId: string,
-		private _worker: WorkerAccessor,
-		defaults: LanguageServiceDefaults
-	) {
-		const onModelAdd = (model: editor.IModel): void => {
-			const modeId = model.getModeId();
-			if (modeId !== this._languageId) {
-				return;
-			}
-
-			let handle: number;
-			this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
-				clearTimeout(handle);
-				handle = setTimeout(() => this._doValidate(model.uri, modeId), 500);
-			});
-
-			this._doValidate(model.uri, modeId);
-		};
-
-		const onModelRemoved = (model: editor.IModel): void => {
-			editor.setModelMarkers(model, this._languageId, []);
-			const uriStr = model.uri.toString();
-			const listener = this._listener[uriStr];
-			if (listener) {
-				listener.dispose();
-				delete this._listener[uriStr];
-			}
-		};
-
-		this._disposables.push(editor.onDidCreateModel(onModelAdd));
-		this._disposables.push(
-			editor.onWillDisposeModel((model) => {
-				onModelRemoved(model);
-			})
-		);
-		this._disposables.push(
-			editor.onDidChangeModelLanguage((event) => {
-				onModelRemoved(event.model);
-				onModelAdd(event.model);
-			})
-		);
-
-		this._disposables.push(
-			defaults.onDidChange((_) => {
-				editor.getModels().forEach((model) => {
-					if (model.getModeId() === this._languageId) {
-						onModelRemoved(model);
-						onModelAdd(model);
-					}
-				});
-			})
-		);
-
-		this._disposables.push({
-			dispose: () => {
-				for (const key in this._listener) {
-					this._listener[key].dispose();
-				}
-			}
-		});
-
-		editor.getModels().forEach(onModelAdd);
-	}
-
-	public dispose(): void {
-		this._disposables.forEach((d) => d && d.dispose());
-		this._disposables = [];
-	}
-
-	private _doValidate(resource: Uri, languageId: string): void {
-		this._worker(resource)
-			.then((worker) => {
-				return worker.doValidation(resource.toString()).then((diagnostics) => {
-					const markers = diagnostics.map((d) => toDiagnostics(resource, d));
-					const model = editor.getModel(resource);
-					if (model && model.getModeId() === languageId) {
-						editor.setModelMarkers(model, languageId, markers);
-					}
-				});
-			})
-			.then(undefined, (err) => {
-				console.error(err);
-			});
-	}
-}
-
-function toSeverity(lsSeverity: number): MarkerSeverity {
-	switch (lsSeverity) {
-		case htmlService.DiagnosticSeverity.Error:
-			return MarkerSeverity.Error;
-		case htmlService.DiagnosticSeverity.Warning:
-			return MarkerSeverity.Warning;
-		case htmlService.DiagnosticSeverity.Information:
-			return MarkerSeverity.Info;
-		case htmlService.DiagnosticSeverity.Hint:
-			return MarkerSeverity.Hint;
-		default:
-			return MarkerSeverity.Info;
-	}
-}
-
-function toDiagnostics(resource: Uri, diag: htmlService.Diagnostic): editor.IMarkerData {
-	const code = typeof diag.code === 'number' ? String(diag.code) : <string>diag.code;
-
-	return {
-		severity: toSeverity(diag.severity),
-		startLineNumber: diag.range.start.line + 1,
-		startColumn: diag.range.start.character + 1,
-		endLineNumber: diag.range.end.line + 1,
-		endColumn: diag.range.end.character + 1,
-		message: diag.message,
-		code: code,
-		source: diag.source
-	};
-}
-
 // --- completion ------
 
 function fromPosition(position: Position): htmlService.Position {
@@ -177,10 +50,10 @@ function toRange(range: htmlService.Range): Range {
 	);
 }
 
-function isInsertReplaceEdit(edit: TextEdit | InsertReplaceEdit): edit is InsertReplaceEdit {
+function isInsertReplaceEdit(edit: htmlService.TextEdit | htmlService.InsertReplaceEdit): edit is htmlService.InsertReplaceEdit {
 	return (
-		typeof (<InsertReplaceEdit>edit).insert !== 'undefined' &&
-		typeof (<InsertReplaceEdit>edit).replace !== 'undefined'
+		typeof (<htmlService.InsertReplaceEdit>edit).insert !== 'undefined' &&
+		typeof (<htmlService.InsertReplaceEdit>edit).replace !== 'undefined'
 	);
 }
 
@@ -285,7 +158,7 @@ function toTextEdit(textEdit: htmlService.TextEdit): editor.ISingleEditOperation
 }
 
 export class CompletionAdapter implements languages.CompletionItemProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public get triggerCharacters(): string[] {
 		return ['.', ':', '<', '"', '=', '/'];
@@ -399,7 +272,7 @@ function toMarkedStringArray(
 }
 
 export class HoverAdapter implements languages.HoverProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	provideHover(
 		model: editor.IReadOnlyModel,
@@ -441,7 +314,7 @@ function toHighlighKind(kind: htmlService.DocumentHighlightKind): languages.Docu
 }
 
 export class DocumentHighlightAdapter implements languages.DocumentHighlightProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideDocumentHighlights(
 		model: editor.IReadOnlyModel,
@@ -511,7 +384,7 @@ function toSymbolKind(kind: htmlService.SymbolKind): languages.SymbolKind {
 }
 
 export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideDocumentSymbols(
 		model: editor.IReadOnlyModel,
@@ -539,7 +412,7 @@ export class DocumentSymbolAdapter implements languages.DocumentSymbolProvider {
 }
 
 export class DocumentLinkAdapter implements languages.LinkProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideLinks(
 		model: editor.IReadOnlyModel,
@@ -573,7 +446,7 @@ function fromFormattingOptions(
 }
 
 export class DocumentFormattingEditProvider implements languages.DocumentFormattingEditProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideDocumentFormattingEdits(
 		model: editor.IReadOnlyModel,
@@ -597,7 +470,7 @@ export class DocumentFormattingEditProvider implements languages.DocumentFormatt
 
 export class DocumentRangeFormattingEditProvider
 	implements languages.DocumentRangeFormattingEditProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideDocumentRangeFormattingEdits(
 		model: editor.IReadOnlyModel,
@@ -621,7 +494,7 @@ export class DocumentRangeFormattingEditProvider
 }
 
 export class RenameAdapter implements languages.RenameProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	provideRenameEdits(
 		model: editor.IReadOnlyModel,
@@ -664,7 +537,7 @@ function toWorkspaceEdit(edit: htmlService.WorkspaceEdit): languages.WorkspaceEd
 }
 
 export class FoldingRangeAdapter implements languages.FoldingRangeProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideFoldingRanges(
 		model: editor.IReadOnlyModel,
@@ -705,7 +578,7 @@ function toFoldingRangeKind(kind: htmlService.FoldingRangeKind): languages.Foldi
 }
 
 export class SelectionRangeAdapter implements languages.SelectionRangeProvider {
-	constructor(private _worker: WorkerAccessor) {}
+	constructor(private _worker: WorkerAccessor) { }
 
 	public provideSelectionRanges(
 		model: editor.IReadOnlyModel,

+ 67 - 4
src/monaco.contribution.ts

@@ -22,7 +22,7 @@ export interface HTMLFormatConfiguration {
 }
 
 export interface CompletionConfiguration {
-	[provider: string]: boolean;
+	[providerId: string]: boolean;
 }
 
 export interface Options {
@@ -34,6 +34,10 @@ export interface Options {
 	 * A list of known schemas and/or associations of schemas to file names.
 	 */
 	readonly suggest?: CompletionConfiguration;
+	/**
+	 * Configures the HTML data types known by the HTML langauge service.
+	 */
+	readonly data?: HTMLDataConfiguration;
 }
 
 export interface ModeConfiguration {
@@ -165,17 +169,20 @@ const formatDefaults: Required<HTMLFormatConfiguration> = {
 
 const htmlOptionsDefault: Required<Options> = {
 	format: formatDefaults,
-	suggest: { html5: true, angular1: true, ionic: true }
+	suggest: { html5: true, angular1: true, ionic: true },
+	data: { useDefaultDataProvider: true }
 };
 
 const handlebarOptionsDefault: Required<Options> = {
 	format: formatDefaults,
-	suggest: { html5: true }
+	suggest: { html5: true },
+	data: { useDefaultDataProvider: true }
 };
 
 const razorOptionsDefault: Required<Options> = {
 	format: formatDefaults,
-	suggest: { html5: true, razor: true }
+	suggest: { html5: true, razor: true },
+	data: { useDefaultDataProvider: true }
 };
 
 function getConfigurationDefault(languageId: string): Required<ModeConfiguration> {
@@ -261,3 +268,59 @@ export function registerHTMLLanguageService(
 		}
 	};
 }
+
+
+
+export interface HTMLDataConfiguration {
+	/**
+	 * Defines whether the standard HTML tags and attributes are shown
+	 */
+	useDefaultDataProvider?: boolean;
+	/**
+	 * Provides a set of custom data providers.
+	 */
+	dataProviders?: { [providerId: string]: HTMLDataV1 };
+}
+
+/**
+ * Custom HTML tags attributes and attribute values
+ * https://github.com/microsoft/vscode-html-languageservice/blob/main/docs/customData.md
+ */
+export interface HTMLDataV1 {
+    version: 1 | 1.1;
+    tags?: ITagData[];
+    globalAttributes?: IAttributeData[];
+    valueSets?: IValueSet[];
+}
+
+export interface IReference {
+    name: string;
+    url: string;
+}
+export interface ITagData {
+    name: string;
+    description?: string | MarkupContent;
+    attributes: IAttributeData[];
+    references?: IReference[];
+}
+export interface IAttributeData {
+    name: string;
+    description?: string | MarkupContent;
+    valueSet?: string;
+    values?: IValueData[];
+    references?: IReference[];
+}
+export interface IValueData {
+    name: string;
+    description?: string | MarkupContent;
+    references?: IReference[];
+}
+export interface IValueSet {
+    name: string;
+    values: IValueData[];
+}
+export interface MarkupContent {
+    kind: MarkupKind;
+    value: string;
+}
+export declare type MarkupKind = 'plaintext' | 'markdown';