提交 9101a4a8 编写于 作者: M Matt Bierner 提交者: GitHub

Add TS Code Action Provider (#16299)

* Initial work using ts2.1 and TS Code Actions

* Clean up implementation and fix a few issues

* Gate provider to ts2.1+

* Switch gate to use 2.1.3 instead

* Fix a few null checks

* Format after completion
上级 08b89952
/*---------------------------------------------------------------------------------------------
* 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 { CodeActionProvider, TextDocument, Range, CancellationToken, CodeActionContext, Command, commands, Uri, workspace, WorkspaceEdit, TextEdit } from 'vscode';
import * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService';
interface NumberSet {
[key: number]: boolean;
}
interface Source {
uri: Uri;
version: number;
range: Range;
}
export default class TypeScriptCodeActionProvider implements CodeActionProvider {
private client: ITypescriptServiceClient;
private commandId: string;
private supportedCodeActions: Promise<NumberSet>;
constructor(client: ITypescriptServiceClient, modeId: string) {
this.client = client;
this.commandId = `typescript.codeActions.${modeId}`;
this.supportedCodeActions = client.execute('getSupportedCodeFixes', null, undefined)
.then(response => response.body || [])
.then(codes => {
return codes.map(code => +code).filter(code => !isNaN(code));
})
.then(codes =>
codes.reduce((obj, code) => {
obj[code] = true;
return obj;
}, Object.create(null)));
commands.registerCommand(this.commandId, this.onCodeAction, this);
}
public provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken): Thenable<Command[]> {
const file = this.client.asAbsolutePath(document.uri);
if (!file) {
return Promise.resolve(null);
}
const source: Source = {
uri: document.uri,
version: document.version,
range: range
};
return this.getSupportedCodeActions(context)
.then(supportedActions => {
return this.client.execute('getCodeFixes', {
file: file,
startLine: range.start.line + 1,
endLine: range.end.line + 1,
startOffset: range.start.character + 1,
endOffset: range.end.character + 1,
errorCodes: supportedActions
}, token);
})
.then(response => response.body || [])
.then(codeActions => codeActions.map(action => this.actionToEdit(source, action)));
}
private getSupportedCodeActions(context: CodeActionContext): Thenable<number[]> {
return this.supportedCodeActions
.then(supportedActions => {
return context.diagnostics
.map(diagnostic => +diagnostic.code)
.filter(code => supportedActions[code]);
});
}
private actionToEdit(source: Source, action: Proto.CodeAction): Command {
const workspaceEdit = new WorkspaceEdit();
action.changes.forEach(change => {
change.textChanges.forEach(textChange => {
workspaceEdit.replace(this.client.asUrl(change.fileName),
new Range(
textChange.start.line - 1, textChange.start.offset - 1,
textChange.end.line - 1, textChange.end.offset - 1),
textChange.newText);
});
});
return {
title: action.description,
command: this.commandId,
arguments: [source, workspaceEdit]
};
}
private onCodeAction(source: Source, workspaceEdit: WorkspaceEdit) {
workspace.applyEdit(workspaceEdit).then(success => {
if (!success) {
return Promise.reject(null);
}
// TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/12249
// apply formatting to the source range until TS returns formatted results
return commands.executeCommand('vscode.executeFormatRangeProvider', source.uri, source.range, {}).then((edits: TextEdit[]) => {
if (!edits || !edits.length) {
return false;
}
const workspaceEdit = new WorkspaceEdit();
workspaceEdit.set(source.uri, edits);
return workspace.applyEdit(workspaceEdit);
});
});
}
}
\ No newline at end of file
......@@ -34,6 +34,7 @@ import FormattingProvider from './features/formattingProvider';
import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import * as VersionStatus from './utils/versionStatus';
import * as ProjectStatus from './utils/projectStatus';
......@@ -164,6 +165,9 @@ class LanguageProvider {
languages.registerRenameProvider(selector, renameProvider);
languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n');
languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(client, modeId));
if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
}
languages.setLanguageConfiguration(modeId, {
indentationRules: {
// ^(.*\*/)?\s*\}.*$
......@@ -427,6 +431,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
let range = new Range(start.line - 1, start.offset - 1, end.line - 1, end.offset - 1);
let converted = new Diagnostic(range, text);
converted.source = source;
converted.code = '' + diagnostic.code;
result.push(converted);
}
return result;
......
......@@ -97,6 +97,8 @@ export interface ITypescriptServiceClient {
execute(command: 'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>;
execute(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs, token?: CancellationToken): Promise<any>;
execute(command: 'navtree', args: Proto.FileRequestArgs, token?: CancellationToken): Promise<Proto.NavTreeResponse>;
execute(command: 'getCodeFixes', args: Proto.CodeFixRequestArgs, token?: CancellationToken): Promise<Proto.GetCodeFixesResponse>;
execute(command: 'getSupportedCodeFixes', args: null, token?: CancellationToken): Promise<Proto.GetSupportedCodeFixesResponse>;
// execute(command: 'compileOnSaveAffectedFileList', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<Proto.CompileOnSaveAffectedFileListResponse>;
// execute(command: 'compileOnSaveEmitFile', args: Proto.CompileOnSaveEmitFileRequestArgs, token?: CancellationToken): Promise<any>;
execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise<any>;
......
......@@ -670,6 +670,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
case 'typingsInstalled':
let typingsInstalledPayload: Proto.TypingsInstalledTelemetryEventPayload = (telemetryData.payload as Proto.TypingsInstalledTelemetryEventPayload);
properties['installedPackages'] = typingsInstalledPayload.installedPackages;
if (is.defined(typingsInstalledPayload.installSuccess)) {
properties['installSuccess'] = typingsInstalledPayload.installSuccess.toString();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册