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
Prev Previous commit
Next Next commit
refactor out LanguageParticipants
  • Loading branch information
aeschli committed Jul 7, 2022
commit 00e2fdce6ec3ddaaf6b78f27fc1c67ee6f0b3a23
5 changes: 3 additions & 2 deletions extensions/html-language-features/client/src/autoInsertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

import { window, workspace, Disposable, TextDocument, Position, SnippetString, TextDocumentChangeEvent, TextDocumentChangeReason, TextDocumentContentChangeEvent } from 'vscode';
import { Runtime } from './htmlClient';
import { LanguageParticipants } from './languageParticipants';

export function activateAutoInsertion(provider: (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position) => Thenable<string>, supportedLanguages: { [id: string]: boolean }, runtime: Runtime): Disposable {
export function activateAutoInsertion(provider: (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position) => Thenable<string>, languageParticipants: LanguageParticipants, runtime: Runtime): Disposable {
const disposables: Disposable[] = [];
workspace.onDidChangeTextDocument(onDidChangeTextDocument, null, disposables);

Expand All @@ -33,7 +34,7 @@ export function activateAutoInsertion(provider: (kind: 'autoQuote' | 'autoClose'
return;
}
const document = editor.document;
if (!supportedLanguages[document.languageId]) {
if (!languageParticipants.useAutoInsert(document.languageId)) {
return;
}
const configurations = workspace.getConfiguration(undefined, document.uri);
Expand Down
2 changes: 1 addition & 1 deletion extensions/html-language-features/client/src/customData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function collectInWorkspaces(workspaceUris: Set<string>): Set<string> {
}

function collectInExtensions(localExtensionUris: Set<string>, externalUris: Set<string>): void {
for (const extension of extensions.all) {
for (const extension of extensions.allAcrossExtensionHosts) {
const customData = extension.packageJSON?.contributes?.html?.customData;
if (Array.isArray(customData)) {
for (const uriOrPath of customData) {
Expand Down
95 changes: 49 additions & 46 deletions extensions/html-language-features/client/src/htmlClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { FileSystemProvider, serveFileSystemRequests } from './requests';
import { getCustomDataSource } from './customData';
import { activateAutoInsertion } from './autoInsertion';
import { getHtmlLanguageContributions, getSupportedLanguagesAutoInsert, getDocumentSelector, HtmlLanguageContribution } from './htmlLanguageContribution';
import { getLanguageParticipants } from './languageParticipants';

namespace CustomDataChangedNotification {
export const type: NotificationType<string[]> = new NotificationType('html/customDataChanged');
Expand Down Expand Up @@ -86,10 +86,12 @@ export interface Runtime {

export async function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime): Promise<BaseLanguageClient> {

const toDispose = context.subscriptions;
const toDispose: Disposable[] = context.subscriptions;

const htmlContributions: HtmlLanguageContribution[] = getHtmlLanguageContributions(toDispose);
const documentSelector = getDocumentSelector(htmlContributions);
const languageParticipants = getLanguageParticipants();
toDispose.push(languageParticipants);

const documentSelector = languageParticipants.documentSelector;
const embeddedLanguages = { css: true, javascript: true };

let rangeFormatting: Disposable | undefined = undefined;
Expand Down Expand Up @@ -140,13 +142,13 @@ export async function startClient(context: ExtensionContext, newLanguageClient:

toDispose.push(serveFileSystemRequests(client, runtime));

const customDataSource = getCustomDataSource(runtime, context.subscriptions);
const customDataSource = getCustomDataSource(runtime, toDispose);

client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
customDataSource.onDidChange(() => {
client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris);
});
client.onRequest(CustomDataContent.type, customDataSource.getContent);
}, undefined, toDispose);
toDispose.push(client.onRequest(CustomDataContent.type, customDataSource.getContent));


const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise<string> => {
Expand All @@ -155,46 +157,46 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
position: client.code2ProtocolConverter.asPosition(position)
};
const supportedLanguages = getSupportedLanguagesAutoInsert(htmlContributions);
const disposable = activateAutoInsertion(insertRequestor, supportedLanguages, runtime);
toDispose.push(disposable);

disposable = client.onTelemetry(e => {
runtime.telemetry?.sendTelemetryEvent(e.key, e.data);
});
toDispose.push(disposable);

// manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652.
updateFormatterRegistration();
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration()));

client.sendRequest(SemanticTokenLegendRequest.type).then(legend => {
if (legend) {
const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = {
provideDocumentSemanticTokens(doc) {
const params: SemanticTokenParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
};
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
return data && new SemanticTokens(new Uint32Array(data));
});
},
provideDocumentRangeSemanticTokens(doc, range) {
const params: SemanticTokenParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
ranges: [client.code2ProtocolConverter.asRange(range)]
};
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
return data && new SemanticTokens(new Uint32Array(data));
});
}
};
toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers)));
}
});
});
return client.sendRequest(AutoInsertRequest.type, param);
};

const disposable = activateAutoInsertion(insertRequestor, languageParticipants, runtime);
toDispose.push(disposable);

const disposable2 = client.onTelemetry(e => {
runtime.telemetry?.sendTelemetryEvent(e.key, e.data);
});
toDispose.push(disposable2);

// manually register / deregister format provider based on the `html.format.enable` setting avoiding issues with late registration. See #71652.
updateFormatterRegistration();
toDispose.push({ dispose: () => rangeFormatting && rangeFormatting.dispose() });
toDispose.push(workspace.onDidChangeConfiguration(e => e.affectsConfiguration(SettingIds.formatEnable) && updateFormatterRegistration()));

client.sendRequest(SemanticTokenLegendRequest.type).then(legend => {
if (legend) {
const provider: DocumentSemanticTokensProvider & DocumentRangeSemanticTokensProvider = {
provideDocumentSemanticTokens(doc) {
const params: SemanticTokenParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
};
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
return data && new SemanticTokens(new Uint32Array(data));
});
},
provideDocumentRangeSemanticTokens(doc, range) {
const params: SemanticTokenParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(doc),
ranges: [client.code2ProtocolConverter.asRange(range)]
};
return client.sendRequest(SemanticTokenRequest.type, params).then(data => {
return data && new SemanticTokens(new Uint32Array(data));
});
}
};
toDispose.push(languages.registerDocumentSemanticTokensProvider(documentSelector, provider, new SemanticTokensLegend(legend.types, legend.modifiers)));
}
});

function updateFormatterRegistration() {
const formatEnabled = workspace.getConfiguration().get(SettingIds.formatEnable);
Expand Down Expand Up @@ -300,4 +302,5 @@ export async function startClient(context: ExtensionContext, newLanguageClient:
}

return client;

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* 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 { Event, EventEmitter, extensions } from 'vscode';

/**
* HTML language participant contribution.
*/
interface LanguageParticipantContribution {
/**
* The id of the language which participates with the HTML language server.
*/
languageId: string;
/**
* true if the language activates the auto insertion and false otherwise.
*/
autoInsert?: boolean;
}

export interface LanguageParticipants {
readonly onDidChange: Event<void>;
readonly documentSelector: DocumentSelector;
useAutoInsert(languageId: string): boolean;
dispose(): void;
}

export function getLanguageParticipants(): LanguageParticipants {
const onDidChangeEmmiter = new EventEmitter<void>();
let languages = new Set<string>();
let autoInsert = new Set<string>();

function update() {
const oldLanguages = languages, oldAutoInsert = autoInsert;

languages = new Set();
languages.add('html');
autoInsert = new Set();
autoInsert.add('html');

for (const extension of extensions.allAcrossExtensionHosts) {
const htmlLanguages = extension.packageJSON?.contributes?.htmlLanguages as LanguageParticipantContribution[];
if (Array.isArray(htmlLanguages)) {
for (const htmlLanguage of htmlLanguages) {
const languageId = htmlLanguage.languageId;
if (typeof languageId === 'string') {
languages.add(languageId);
if (htmlLanguage.autoInsert !== false) {
autoInsert.add(languageId);
}
}
}
}
}
return !isEqualSet(languages, oldLanguages) || !isEqualSet(oldLanguages, oldAutoInsert);
}
update();

const changeListener = extensions.onDidChange(_ => {
if (update()) {
onDidChangeEmmiter.fire();
}
});

return {
onDidChange: onDidChangeEmmiter.event,
get documentSelector() { return Array.from(languages); },
useAutoInsert(languageId: string) { return autoInsert.has(languageId); },
dispose: () => changeListener.dispose()
};
}

function isEqualSet<T>(s1: Set<T>, s2: Set<T>) {
if (s1.size !== s2.size) {
return false;
}
for (const e of s1) {
if (!s2.has(e)) {
return false;
}
}
return true;
}
Loading