Skip to content

Commit

Permalink
Contribute to html language server with a custom language.
Browse files Browse the repository at this point in the history
Fixes microsoft#146730

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Apr 4, 2022
1 parent 145e593 commit a02d6ff
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 13 deletions.
14 changes: 12 additions & 2 deletions extensions/handlebars/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,20 @@
"scopeName": "text.html.handlebars",
"path": "./syntaxes/Handlebars.tmLanguage.json"
}
]
],
"html": {
"languages": [
{
"documentSelector": [
"handlebars"
],
"activateAutoInsertion": true
}
]
}
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
}
}
}
9 changes: 5 additions & 4 deletions extensions/html-language-features/client/src/htmlClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { FileSystemProvider, serveFileSystemRequests } from './requests';
import { getCustomDataSource } from './customData';
import { activateAutoInsertion } from './autoInsertion';
import { collectHtmlLanguageContributions, getActivateAutoInsertionSupportedLanguages, getDocumentSelector, HtmlLanguageContribution } from './htmlExtension';

namespace CustomDataChangedNotification {
export const type: NotificationType<string[]> = new NotificationType('html/customDataChanged');
Expand Down Expand Up @@ -87,8 +88,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua

let toDispose = context.subscriptions;


let documentSelector = ['html', 'handlebars'];
const htmlContributions: HtmlLanguageContribution[] = collectHtmlLanguageContributions(extensions.all);
let documentSelector = getDocumentSelector(htmlContributions);
let embeddedLanguages = { css: true, javascript: true };

let rangeFormatting: Disposable | undefined = undefined;
Expand Down Expand Up @@ -158,7 +159,8 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
};
return client.sendRequest(AutoInsertRequest.type, param);
};
let disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime);
const supportedLanguages = getActivateAutoInsertionSupportedLanguages(htmlContributions);
let disposable = activateAutoInsertion(insertRequestor, supportedLanguages, runtime);
toDispose.push(disposable);

disposable = client.onTelemetry(e => {
Expand Down Expand Up @@ -299,5 +301,4 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua
toDispose.push(activeEditorListener);
}
}

}
153 changes: 153 additions & 0 deletions extensions/html-language-features/client/src/htmlExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { DocumentFilter, DocumentSelector } from 'vscode-languageclient';
import { isDeepStrictEqual } from 'util';

const RELOAD_WINDOW = 'workbench.action.reloadWindow';

let existingExtensions: HtmlLanguageContribution[];

/**
* Html language contribution
*/
export interface HtmlLanguageContribution {
documentSelector: DocumentSelector;
activateAutoInsertion: false;
}

/**
* Returns all Html language contributions from package.json
*
* @param extensions array of extensions to search contributions from
*/
export function collectHtmlLanguageContributions(extensions: readonly vscode.Extension<any>[]): HtmlLanguageContribution[] {
const result: HtmlLanguageContribution[] = [];
if (extensions && extensions.length) {
for (const extension of extensions) {
const htmlLanguages = extension.packageJSON?.contributes?.html?.languages;
if (Array.isArray(htmlLanguages)) {
htmlLanguages.forEach(htmlExtensionSection => {
const htmlExtension = createHtmlExtension(htmlExtensionSection);
if (htmlExtension) {
result.push(htmlExtension);
}
});
}
}
}
// Make a copy of extensions:
existingExtensions = result.slice();
return result;
}

export function handleExtensionChange(extensions: readonly vscode.Extension<any>[]): void {
if (!existingExtensions) {
return;
}
const oldExtensions = new Set(existingExtensions.slice());
const newExtensions = collectHtmlLanguageContributions(extensions);
let hasChanged = (oldExtensions.size !== newExtensions.length);
if (!hasChanged) {
for (const newExtension of newExtensions) {
let found = false;
for (const oldExtension of oldExtensions) {
if (isDeepStrictEqual(oldExtension, newExtension)) {
found = true;
break;
}
}
if (found) {
continue;
} else {
hasChanged = true;
break;
}
}
}

if (hasChanged) {
const msg = `Extensions to the Html Language Server changed, reloading ${vscode.env.appName} is required for the changes to take effect.`;
const action = 'Reload';
vscode.window.showWarningMessage(msg, action).then((selection) => {
if (action === selection) {
vscode.commands.executeCommand(RELOAD_WINDOW);
}
});
}
}

/**
* Returns the document selector.
*
* The returned document selector contains the html document selectors
* and all document selectors contained in `htmlContributions`.
*
* @param htmlContributions Html language server contributions from other VS Code extensions
*/
export function getDocumentSelector(htmlContributions: HtmlLanguageContribution[]): DocumentSelector {
let documentSelector: DocumentSelector = [
'html'
];
htmlContributions.forEach((contribution: HtmlLanguageContribution) => {
documentSelector = documentSelector.concat(contribution.documentSelector);
});
return documentSelector;
}

export function getActivateAutoInsertionSupportedLanguages(htmlContributions: HtmlLanguageContribution[]): { [id: string]: boolean } {
const supportedLanguages: { [id: string]: boolean } = { html: true };
htmlContributions.forEach((contribution: HtmlLanguageContribution) => {
contribution.documentSelector.forEach(selector => {
if (typeof selector === 'string') {
const language: string = selector;
supportedLanguages[language] = contribution.activateAutoInsertion;
} else {
const language = (<DocumentFilter>selector).language;
if (language) {
supportedLanguages[language] = contribution.activateAutoInsertion;
}
}
});
});
return supportedLanguages;
}

function createHtmlExtension(section: any): HtmlLanguageContribution | undefined {
const documentSelector = createSelector(section);
if (documentSelector.length > 0) {
const activateAutoInsertion = section.activateAutoInsertion === true ? true : false;
return { documentSelector, activateAutoInsertion } as HtmlLanguageContribution;
}
return;
}

function createSelector(section: any): DocumentSelector {
if (!Array.isArray(section.documentSelector)) {
return [];
}
const documentSelector: DocumentSelector = [];
section.documentSelector.forEach((selector: any) => {
if (typeof selector === 'string') {
documentSelector.push(selector);
} else if (selector) {
const documentFilter: { [key: string]: string } = {};
if (typeof selector.language === 'string') {
documentFilter.language = selector.language;
}
if (typeof selector.scheme === 'string') {
documentFilter.scheme = selector.scheme;
}
if (typeof selector.pattern === 'string') {
documentFilter.pattern = selector.pattern;
}
if (documentFilter.language || documentFilter.scheme || documentFilter.pattern) {
documentSelector.push(documentFilter as DocumentFilter);
}
}
});
return documentSelector;
}
36 changes: 29 additions & 7 deletions extensions/html-language-features/schemas/package.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,37 @@
"contributes": {
"type": "object",
"properties": {
"html.customData": {
"type": "array",
"markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-html-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its HTML support for the custom HTML tags, attributes and attribute values you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.",
"items": {
"type": "string",
"description": "Relative path to a HTML custom data file"
"html": {
"type": "object",
"properties": {
"customData": {
"type": "array",
"markdownDescription": "A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-html-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its HTML support for the custom HTML tags, attributes and attribute values you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.",
"items": {
"type": "string",
"description": "Relative path to a HTML custom data file"
}
},
"languages": {
"type": "array",
"description": "A list of custom languages which contributes to the Html language server.",
"items": {
"type": "object",
"properties": {
"documentSelector": {
"type": "object",
"description": "The Document selector (string or DocumentFilter) which defines the custom language."
},
"activateAutoInsertion": {
"type": "boolean",
"description": "true if the custom language activates the auto insertion and false otherwise."
}
}
}
}
}
}
}
}
}
}
}

0 comments on commit a02d6ff

Please sign in to comment.