mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-25 11:08:51 +01:00
[html] VSCode doesn't automatically close HTML tags Fixes #2246.
This commit is contained in:
@@ -6,10 +6,11 @@
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import { languages, workspace, ExtensionContext, IndentAction } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Range, RequestType } from 'vscode-languageclient';
|
||||
import { languages, workspace, ExtensionContext, IndentAction, Position, TextDocument } from 'vscode';
|
||||
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Range as LSRange, RequestType, TextDocumentPositionParams } from 'vscode-languageclient';
|
||||
import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared';
|
||||
import { activateColorDecorations } from './colorDecorators';
|
||||
import { activateTagClosing } from './tagClosing';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
|
||||
import { ConfigurationFeature } from 'vscode-languageclient/lib/proposed';
|
||||
@@ -18,7 +19,11 @@ import * as nls from 'vscode-nls';
|
||||
let localize = nls.loadMessageBundle();
|
||||
|
||||
namespace ColorSymbolRequest {
|
||||
export const type: RequestType<string, Range[], any, any> = new RequestType('css/colorSymbols');
|
||||
export const type: RequestType<string, LSRange[], any, any> = new RequestType('html/colorSymbols');
|
||||
}
|
||||
|
||||
namespace TagCloseRequest {
|
||||
export const type: RequestType<TextDocumentPositionParams, string, any, any> = new RequestType('html/tag');
|
||||
}
|
||||
|
||||
interface IPackageInfo {
|
||||
@@ -28,10 +33,13 @@ interface IPackageInfo {
|
||||
}
|
||||
|
||||
export function activate(context: ExtensionContext) {
|
||||
let toDispose = context.subscriptions;
|
||||
|
||||
let packageInfo = getPackageInfo(context);
|
||||
let telemetryReporter: TelemetryReporter = packageInfo && new TelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
context.subscriptions.push(telemetryReporter);
|
||||
if (telemetryReporter) {
|
||||
toDispose.push(telemetryReporter);
|
||||
}
|
||||
|
||||
// The server is implemented in node
|
||||
let serverModule = context.asAbsolutePath(path.join('server', 'out', 'htmlServerMain.js'));
|
||||
@@ -64,7 +72,7 @@ export function activate(context: ExtensionContext) {
|
||||
client.registerFeature(new ConfigurationFeature(client));
|
||||
|
||||
let disposable = client.start();
|
||||
context.subscriptions.push(disposable);
|
||||
toDispose.push(disposable);
|
||||
client.onReady().then(() => {
|
||||
let colorRequestor = (uri: string) => {
|
||||
return client.sendRequest(ColorSymbolRequest.type, uri).then(ranges => ranges.map(client.protocol2CodeConverter.asRange));
|
||||
@@ -73,12 +81,21 @@ export function activate(context: ExtensionContext) {
|
||||
return workspace.getConfiguration().get<boolean>('css.colorDecorators.enable');
|
||||
};
|
||||
let disposable = activateColorDecorations(colorRequestor, { html: true, handlebars: true, razor: true }, isDecoratorEnabled);
|
||||
context.subscriptions.push(disposable);
|
||||
client.onTelemetry(e => {
|
||||
toDispose.push(disposable);
|
||||
|
||||
let tagRequestor = (document: TextDocument, position: Position) => {
|
||||
let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position);
|
||||
return client.sendRequest(TagCloseRequest.type, param);
|
||||
};
|
||||
disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true, razor: true }, 'html.autoClosingTags.enable');
|
||||
toDispose.push(disposable);
|
||||
|
||||
disposable = client.onTelemetry(e => {
|
||||
if (telemetryReporter) {
|
||||
telemetryReporter.sendTelemetryEvent(e.key, e.data);
|
||||
}
|
||||
});
|
||||
toDispose.push(disposable);
|
||||
});
|
||||
|
||||
languages.setLanguageConfiguration('html', {
|
||||
|
||||
67
extensions/html/client/src/tagClosing.ts
Normal file
67
extensions/html/client/src/tagClosing.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { window, workspace, Disposable, TextDocumentContentChangeEvent, TextDocument, Position, SnippetString } from 'vscode';
|
||||
|
||||
export function activateTagClosing(tagProvider: (document: TextDocument, position: Position) => Thenable<string>, supportedLanguages: { [id: string]: boolean }, configName: string): Disposable {
|
||||
|
||||
let disposables: Disposable[] = [];
|
||||
workspace.onDidChangeTextDocument(event => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);
|
||||
|
||||
let isEnabled = false;
|
||||
updateEnabledState();
|
||||
window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables);
|
||||
|
||||
let timeout: NodeJS.Timer = void 0;
|
||||
|
||||
function updateEnabledState() {
|
||||
isEnabled = false;
|
||||
let editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
let document = editor.document;
|
||||
if (!supportedLanguages[document.languageId]) {
|
||||
return;
|
||||
}
|
||||
if (!workspace.getConfiguration(void 0, document.uri).get<boolean>(configName)) {
|
||||
return;
|
||||
}
|
||||
isEnabled = true;
|
||||
}
|
||||
|
||||
function onDidChangeTextDocument(document: TextDocument, changes: TextDocumentContentChangeEvent[]) {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
let activeDocument = window.activeTextEditor && window.activeTextEditor.document;
|
||||
if (document !== activeDocument || changes.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (typeof timeout !== 'undefined') {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
let lastChange = changes[changes.length - 1];
|
||||
let lastCharacter = lastChange.text[lastChange.text.length - 1];
|
||||
if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') {
|
||||
return;
|
||||
}
|
||||
let rangeStart = lastChange.range.start;
|
||||
let version = document.version;
|
||||
timeout = setTimeout(() => {
|
||||
let position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
|
||||
tagProvider(document, position).then(text => {
|
||||
if (text && isEnabled) {
|
||||
let activeDocument = window.activeTextEditor && window.activeTextEditor.document;
|
||||
if (document === activeDocument && activeDocument.version === version) {
|
||||
window.activeTextEditor.insertSnippet(new SnippetString(text), position);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
return Disposable.from(...disposables);
|
||||
}
|
||||
Reference in New Issue
Block a user