Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Contribute to html language server with a custom language.
Fixes #146730

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Apr 5, 2022
commit d647fb1e93001fe487bc9132c4a489d3be2ba8fb
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."
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API prefers that undefined equals false, so maybe noAutoInsert is better because it conveys the default value?

@aeschli Martin Aeschlimann (aeschli) Jul 21, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want this to become a list of all features: 'hover', 'completions', .. and it looks much nicer if they are not negated. By default

}
}
}
}
}
}
}
}