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 ff2e8cb
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 6 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);
}
}

}
154 changes: 154 additions & 0 deletions extensions/html-language-features/client/src/htmlExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* 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;
}

0 comments on commit ff2e8cb

Please sign in to comment.