提交 af42aa5b 编写于 作者: J Johannes Rieken

show diaglog when extensions participate in file operations, have "don't show...

show diaglog when extensions participate in file operations, have "don't show again" option and command to reset choice, add logging
上级 3c4de451
......@@ -16,10 +16,18 @@ import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { raceCancellation } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
@extHostCustomer
export class MainThreadFileSystemEventService {
static readonly MementoKeyAdditionalEdits = `file.particpants.additionalEdits`;
private readonly _listener = new DisposableStore();
constructor(
......@@ -27,7 +35,10 @@ export class MainThreadFileSystemEventService {
@IFileService fileService: IFileService,
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
@IBulkEditService bulkEditService: IBulkEditService,
@IProgressService progressService: IProgressService
@IProgressService progressService: IProgressService,
@IDialogService dialogService: IDialogService,
@IStorageService storageService: IStorageService,
@ILogService logService: ILogService
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemEventService);
......@@ -92,9 +103,55 @@ export class MainThreadFileSystemEventService {
return;
}
const edit = reviveWorkspaceEditDto2(data);
await bulkEditService.apply(edit, { undoRedoGroupId });
const needsConfirmation = data.edit.edits.some(edit => edit.metadata?.needsConfirmation);
let showPreview = storageService.getBoolean(MainThreadFileSystemEventService.MementoKeyAdditionalEdits, StorageScope.GLOBAL);
if (showPreview === undefined) {
// show a user facing message
let message: string;
if (data.extensionNames.length === 1) {
message = localize('ask.1', "Extension '{0}' wants to make additional edits for this file operation.", data.extensionNames[0]);
} else {
message = localize('ask.N', "{0} extensions want to make additional edits for this file operation.", data.extensionNames.length);
}
if (needsConfirmation) {
// edit which needs confirmation -> always show dialog
const answer = await dialogService.show(Severity.Info, message, [localize('preview', "Show Preview"), localize('cancel', "Skip additional edits")], { cancelId: 1 });
showPreview = true;
if (answer.choice === 1) {
// no additional edits wanted
return;
}
} else {
// choice
const answer = await dialogService.show(Severity.Info, message,
[localize('ok', "OK"), localize('preview', "Show Preview"), localize('cancel', "Skip additional edits")],
{
cancelId: 2,
checkbox: { label: localize('again', "Don't ask again") }
}
);
if (answer.choice === 2) {
// no additional edits wanted, don't persist cancel option
return;
}
showPreview = answer.choice === 1;
if (answer.checkboxChecked /* && answer.choice !== 2 */) {
storageService.store(MainThreadFileSystemEventService.MementoKeyAdditionalEdits, showPreview, StorageScope.GLOBAL, StorageTarget.USER);
}
}
}
logService.info('[onWill-handler] applying additional workspace edit from extensions', data.extensionNames);
await bulkEditService.apply(
reviveWorkspaceEditDto2(data.edit),
{ undoRedoGroupId, showPreview }
);
}
private _progressLabel(operation: FileOperation): string {
switch (operation) {
case FileOperation.CREATE:
......@@ -119,10 +176,21 @@ export class MainThreadFileSystemEventService {
dispose(): void {
this._listener.dispose();
}
}
registerAction2(class ResetMemento extends Action2 {
constructor() {
super({
id: 'files.participants.resetChoice',
title: localize('label', "Reset choice for 'File operation needs preview'"),
f1: true
});
}
run(accessor: ServicesAccessor) {
accessor.get(IStorageService).remove(MainThreadFileSystemEventService.MementoKeyAdditionalEdits, StorageScope.GLOBAL);
}
});
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
id: 'files',
......
......@@ -1157,9 +1157,14 @@ export interface SourceTargetPair {
target: UriComponents;
}
export interface IWillRunFileOperationParticipation {
edit: IWorkspaceEditDto;
extensionNames: string[]
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
$onWillRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined>;
$onDidRunFileOperation(operation: files.FileOperation, files: SourceTargetPair[]): void;
}
......
......@@ -8,7 +8,7 @@ import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import type * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, SourceTargetPair, IWorkspaceEditDto } from './extHost.protocol';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, SourceTargetPair, IWorkspaceEditDto, IWillRunFileOperationParticipation } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
......@@ -177,7 +177,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
};
}
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWorkspaceEditDto | undefined> {
async $onWillRunFileOperation(operation: FileOperation, files: SourceTargetPair[], timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined> {
switch (operation) {
case FileOperation.MOVE:
return await this._fireWillEvent(this._onWillRenameFile, { files: files.map(f => ({ oldUri: URI.revive(f.source!), newUri: URI.revive(f.target) })) }, timeout, token);
......@@ -189,8 +189,9 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
return undefined;
}
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<IWorkspaceEditDto | undefined> {
private async _fireWillEvent<E extends IWaitUntil>(emitter: AsyncEmitter<E>, data: Omit<E, 'waitUntil'>, timeout: number, token: CancellationToken): Promise<IWillRunFileOperationParticipation | undefined> {
const extensionNames = new Set<string>();
const edits: WorkspaceEdit[] = [];
await emitter.fireAsync(data, token, async (thenable, listener) => {
......@@ -199,10 +200,11 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
const result = await Promise.resolve(thenable);
if (result instanceof WorkspaceEdit) {
edits.push(result);
extensionNames.add((<IExtensionListener<E>>listener).extension.displayName ?? (<IExtensionListener<E>>listener).extension.identifier.value);
}
if (Date.now() - now > timeout) {
this._logService.warn('SLOW file-participant', (<IExtensionListener<E>>listener).extension?.identifier);
this._logService.warn('SLOW file-participant', (<IExtensionListener<E>>listener).extension.identifier);
}
});
......@@ -220,6 +222,6 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
dto.edits = dto.edits.concat(edits);
}
return dto;
return { edit: dto, extensionNames: Array.from(extensionNames) };
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册