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

pull/2748/head
Martin Aeschlimann 4 years ago
parent e3f8dd8f34
commit c8b5d5b6d5
No known key found for this signature in database
GPG Key ID: 2609A01E695523E3

63
monaco.d.ts vendored

@ -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';
}

14
package-lock.json generated

@ -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": {

@ -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"
},

@ -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();

@ -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

@ -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,

@ -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';
Loading…
Cancel
Save