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 5, 2022
1 parent 3c3c149 commit d647fb1
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 6 deletions.
8 changes: 7 additions & 1 deletion extensions/handlebars/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,16 @@
"scopeName": "text.html.handlebars",
"path": "./syntaxes/Handlebars.tmLanguage.json"
}
],
"htmlLanguages": [
{
"languageId": "handlebars",
"autoInsert": 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 { getHtmlLanguageContributions, getSupportedLanguagesAutoInsert, getDocumentSelector, HtmlLanguageContribution } from './htmlLanguageContribution';

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[] = getHtmlLanguageContributions(toDispose);
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 = getSupportedLanguagesAutoInsert(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);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { DocumentSelector } from 'vscode-languageclient';
import { isDeepStrictEqual } from 'util';
import { commands, Disposable, env, extensions, window } from 'vscode';

const RELOAD_WINDOW = 'workbench.action.reloadWindow';

let existingExtensions: HtmlLanguageContribution[];

/**
* Html language contribution.
*/
export interface HtmlLanguageContribution {
/**
* The language Id which contributes to the Html language server.
*/
languageId: string;
/**
* true if the language activates the auto insertion and false otherwise.
*/
autoInsert: false;
}

/**
* Returns all Html language contributions declared with 'contributes/htmlLanguages' from package.json:
*
*"contributes": {
* "languages": [
* {
* "id": "handlebars",
* ...
* }
* ],
* "htmlLanguages": [
* {
* "languageId": "handlebars",
* "autoInsert": true
* }
* ]
*}
*
* @param toDispose
*
* @returns all Html language contributions declared with 'contributes/htmlLanguages' from package.json.
*/
export function getHtmlLanguageContributions(toDispose?: Disposable[]): HtmlLanguageContribution[] {
const result: HtmlLanguageContribution[] = [];
for (const extension of extensions.all) {
const htmlLanguages = extension.packageJSON?.contributes?.htmlLanguages;
if (Array.isArray(htmlLanguages)) {
htmlLanguages.forEach(htmlLanguage => {
const htmlLanguageContribution = createHtmlLanguageContribution(htmlLanguage);
if (htmlLanguageContribution) {
result.push(htmlLanguageContribution);
}
});
}
}
// Make a copy of extensions:
existingExtensions = result.slice();

if (toDispose) {
toDispose.push(extensions.onDidChange(_ => {
handleExtensionChange();
}));
}
return result;
}

function handleExtensionChange(): void {
if (!existingExtensions) {
return;
}
const oldExtensions = new Set(existingExtensions.slice());
const newExtensions = getHtmlLanguageContributions();
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 ${env.appName} is required for the changes to take effect.`;
const action = 'Reload';
window.showWarningMessage(msg, action).then((selection) => {
if (action === selection) {
commands.executeCommand(RELOAD_WINDOW);
}
});
}
}

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

/**
* Returns the auto insert support for each Html languages.
*
* @param htmlContributions all Html language contributions from other VS Code extensions.
*
* @returns the auto insert support for each Html languages.
*/
export function getSupportedLanguagesAutoInsert(htmlContributions: HtmlLanguageContribution[]): { [id: string]: boolean } {
const supportedLanguages: { [id: string]: boolean } = { html: true };
htmlContributions.forEach((contribution: HtmlLanguageContribution) => {
supportedLanguages[contribution.languageId] = contribution.autoInsert;
});
return supportedLanguages;
}

function createHtmlLanguageContribution(section: any): HtmlLanguageContribution | undefined {
const languageId: string | undefined = section.languageId;
if (!languageId) {
return;
}
const autoInsert = section.autoInsert === true ? true : false;
return { languageId, autoInsert } as HtmlLanguageContribution;
}
19 changes: 18 additions & 1 deletion extensions/html-language-features/schemas/package.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,25 @@
"type": "string",
"description": "Relative path to a HTML custom data file"
}
},
"htmlLanguages": {
"type": "array",
"description": "A list of custom languages which contributes to the Html language server.",
"items": {
"type": "object",
"properties": {
"languageId": {
"type": "object",
"description": "The language Id which contributes to the Html language server."
},
"autoInsert": {
"type": "boolean",
"description": "true if the language activates the auto insertion and false otherwise."
}
}
}
}
}
}
}
}
}

0 comments on commit d647fb1

Please sign in to comment.