mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
parent
dc137ce28f
commit
adfa9ce977
|
@ -505,6 +505,16 @@
|
|||
"default": "prompt",
|
||||
"description": "%typescript.updateImportsOnFileMove.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.autoClosingTags": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%typescript.autoClosingTags%"
|
||||
},
|
||||
"javascript.autoClosingTags": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%typescript.autoClosingTags%"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -55,5 +55,6 @@
|
|||
"typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: 'single' quotes, 'double' quotes, or 'auto' infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.",
|
||||
"typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports:\n- \"relative\" to the file location.\n- \"non-relative\" based on the 'baseUrl' configured in your 'jsconfig.json' / 'tsconfig.json'.\n- \"auto\" infer the shortest path type.\nRequires using TypeScript 2.9 or newer in the workspace.",
|
||||
"typescript.showUnused": "Enable/disable highlighting of unused variables in code. Requires using TypeScript 2.9 or newer in the workspace.",
|
||||
"typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace."
|
||||
"typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace.",
|
||||
"typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags. Requires using TypeScript 3.0 or newer in the workspace."
|
||||
}
|
||||
|
|
|
@ -7,44 +7,104 @@ import * as vscode from 'vscode';
|
|||
import * as Proto from '../protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import API from '../utils/api';
|
||||
import { VersionDependentRegistration } from '../utils/dependentRegistration';
|
||||
import { VersionDependentRegistration, ConfigurationDependentRegistration, ConditionalRegistration } from '../utils/dependentRegistration';
|
||||
import { disposeAll } from '../utils/dispose';
|
||||
import * as typeConverters from '../utils/typeConverters';
|
||||
|
||||
class TypeScriptTagCompletion implements vscode.CompletionItemProvider {
|
||||
class TagClosing {
|
||||
|
||||
private _disposed = false;
|
||||
private timeout: NodeJS.Timer | undefined = undefined;
|
||||
|
||||
private readonly disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly client: ITypeScriptServiceClient
|
||||
) { }
|
||||
) {
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
event => this.onDidChangeTextDocument(event.document, event.contentChanges),
|
||||
null, this.disposables);
|
||||
|
||||
async provideCompletionItems(
|
||||
document: vscode.TextDocument,
|
||||
position: vscode.Position,
|
||||
token: vscode.CancellationToken,
|
||||
_context: vscode.CompletionContext
|
||||
): Promise<vscode.CompletionItem[] | undefined> {
|
||||
const filepath = this.client.toPath(document.uri);
|
||||
if (!filepath) {
|
||||
return undefined;
|
||||
}
|
||||
vscode.window.onDidChangeActiveTextEditor(
|
||||
() => this.updateEnabledState(),
|
||||
null, this.disposables);
|
||||
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position);
|
||||
let body: Proto.TextInsertion | undefined = undefined;
|
||||
try {
|
||||
const response = await this.client.execute('jsxClosingTag', args, token);
|
||||
body = response && response.body;
|
||||
if (!body) {
|
||||
return undefined;
|
||||
}
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [this.getCompletion(body)];
|
||||
this.updateEnabledState();
|
||||
}
|
||||
|
||||
private getCompletion(body: Proto.TextInsertion) {
|
||||
const completion = new vscode.CompletionItem(body.newText);
|
||||
completion.insertText = this.getTagSnippet(body);
|
||||
return completion;
|
||||
public dispose() {
|
||||
disposeAll(this.disposables);
|
||||
this._disposed = true;
|
||||
this.timeout = undefined;
|
||||
}
|
||||
|
||||
private updateEnabledState() {
|
||||
|
||||
}
|
||||
|
||||
private onDidChangeTextDocument(
|
||||
document: vscode.TextDocument,
|
||||
changes: vscode.TextDocumentContentChangeEvent[]
|
||||
) {
|
||||
const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document;
|
||||
if (document !== activeDocument || changes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filepath = this.client.toPath(document.uri);
|
||||
if (!filepath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.timeout !== 'undefined') {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
const lastChange = changes[changes.length - 1];
|
||||
const lastCharacter = lastChange.text[lastChange.text.length - 1];
|
||||
if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') {
|
||||
return;
|
||||
}
|
||||
|
||||
const rangeStart = lastChange.range.start;
|
||||
const version = document.version;
|
||||
this.timeout = setTimeout(async () => {
|
||||
if (this._disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let position = new vscode.Position(rangeStart.line, rangeStart.character + lastChange.text.length);
|
||||
let body: Proto.TextInsertion | undefined = undefined;
|
||||
const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position);
|
||||
|
||||
try {
|
||||
const response = await this.client.execute('jsxClosingTag', args, null as any);
|
||||
body = response && response.body;
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._disposed) {
|
||||
const activeEditor = vscode.window.activeTextEditor;
|
||||
if (activeEditor) {
|
||||
const activeDocument = activeEditor.document;
|
||||
if (document === activeDocument && activeDocument.version === version) {
|
||||
const selections = activeEditor.selections;
|
||||
const snippet = this.getTagSnippet(body);
|
||||
if (selections.length && selections.some(s => s.active.isEqual(position))) {
|
||||
activeEditor.insertSnippet(snippet, selections.map(s => s.active));
|
||||
} else {
|
||||
activeEditor.insertSnippet(snippet, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.timeout = void 0;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString {
|
||||
|
@ -55,12 +115,42 @@ class TypeScriptTagCompletion implements vscode.CompletionItemProvider {
|
|||
}
|
||||
}
|
||||
|
||||
export class ActiveDocumentDependentRegistration {
|
||||
private readonly _registration: ConditionalRegistration;
|
||||
private readonly _disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly selector: vscode.DocumentSelector,
|
||||
register: () => vscode.Disposable,
|
||||
) {
|
||||
this._registration = new ConditionalRegistration(register);
|
||||
|
||||
this.update();
|
||||
|
||||
vscode.window.onDidChangeActiveTextEditor(() => {
|
||||
this.update();
|
||||
}, null, this._disposables);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
disposeAll(this._disposables);
|
||||
this._registration.dispose();
|
||||
}
|
||||
|
||||
private update() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const enabled = !!(editor && vscode.languages.match(this.selector, editor.document));
|
||||
this._registration.update(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
export function register(
|
||||
selector: vscode.DocumentSelector,
|
||||
modeId: string,
|
||||
client: ITypeScriptServiceClient,
|
||||
) {
|
||||
return new VersionDependentRegistration(client, API.v300, () =>
|
||||
vscode.languages.registerCompletionItemProvider(selector,
|
||||
new TypeScriptTagCompletion(client),
|
||||
'>'));
|
||||
new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () =>
|
||||
new ActiveDocumentDependentRegistration(selector, () =>
|
||||
new TagClosing(client))));
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ export default class LanguageProvider {
|
|||
this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse));
|
||||
this.disposables.push((await import('./features/rename')).register(selector, this.client));
|
||||
this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client));
|
||||
this.disposables.push((await import('./features/tagCompletion')).register(selector, this.client));
|
||||
this.disposables.push((await import('./features/tagCompletion')).register(selector, this.description.id, this.client));
|
||||
this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client));
|
||||
this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds));
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ITypeScriptServiceClient } from '../typescriptService';
|
|||
import API from './api';
|
||||
import { disposeAll } from './dispose';
|
||||
|
||||
class ConditionalRegistration {
|
||||
export class ConditionalRegistration {
|
||||
private registration: vscode.Disposable | undefined = undefined;
|
||||
|
||||
public constructor(
|
||||
|
|
Loading…
Reference in a new issue