mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
debt - more async/await adoption in workbench
This commit is contained in:
parent
4929acf926
commit
f41d863849
|
@ -31,46 +31,47 @@ export class BackupRestorer implements IWorkbenchContribution {
|
|||
this.lifecycleService.when(LifecyclePhase.Restored).then(() => this.doRestoreBackups());
|
||||
}
|
||||
|
||||
private doRestoreBackups(): Promise<URI[] | undefined> {
|
||||
private async doRestoreBackups(): Promise<URI[] | undefined> {
|
||||
|
||||
// Find all files and untitled with backups
|
||||
return this.backupFileService.getWorkspaceFileBackups().then(backups => {
|
||||
const backups = await this.backupFileService.getWorkspaceFileBackups();
|
||||
const unresolvedBackups = await this.doResolveOpenedBackups(backups);
|
||||
|
||||
// Resolve backups that are opened
|
||||
return this.doResolveOpenedBackups(backups).then((unresolved): Promise<URI[] | undefined> | undefined => {
|
||||
// Some failed to restore or were not opened at all so we open and resolve them manually
|
||||
if (unresolvedBackups.length > 0) {
|
||||
await this.doOpenEditors(unresolvedBackups);
|
||||
|
||||
// Some failed to restore or were not opened at all so we open and resolve them manually
|
||||
if (unresolved.length > 0) {
|
||||
return this.doOpenEditors(unresolved).then(() => this.doResolveOpenedBackups(unresolved));
|
||||
}
|
||||
return this.doResolveOpenedBackups(unresolvedBackups);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private doResolveOpenedBackups(backups: URI[]): Promise<URI[]> {
|
||||
const restorePromises: Promise<unknown>[] = [];
|
||||
const unresolved: URI[] = [];
|
||||
private async doResolveOpenedBackups(backups: URI[]): Promise<URI[]> {
|
||||
const unresolvedBackups: URI[] = [];
|
||||
|
||||
backups.forEach(backup => {
|
||||
await Promise.all(backups.map(async backup => {
|
||||
const openedEditor = this.editorService.getOpened({ resource: backup });
|
||||
if (openedEditor) {
|
||||
restorePromises.push(openedEditor.resolve().then(undefined, () => unresolved.push(backup)));
|
||||
try {
|
||||
await openedEditor.resolve(); // trigger load
|
||||
} catch (error) {
|
||||
unresolvedBackups.push(backup); // ignore error and remember as unresolved
|
||||
}
|
||||
} else {
|
||||
unresolved.push(backup);
|
||||
unresolvedBackups.push(backup);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
return Promise.all(restorePromises).then(() => unresolved, () => unresolved);
|
||||
return unresolvedBackups;
|
||||
}
|
||||
|
||||
private doOpenEditors(resources: URI[]): Promise<void> {
|
||||
private async doOpenEditors(resources: URI[]): Promise<void> {
|
||||
const hasOpenedEditors = this.editorService.visibleEditors.length > 0;
|
||||
const inputs = resources.map((resource, index) => this.resolveInput(resource, index, hasOpenedEditors));
|
||||
|
||||
// Open all remaining backups as editors and resolve them to load their backups
|
||||
return this.editorService.openEditors(inputs).then(() => undefined);
|
||||
await this.editorService.openEditors(inputs);
|
||||
}
|
||||
|
||||
private resolveInput(resource: URI, index: number, hasOpenedEditors: boolean): IResourceInput | IUntitledResourceInput {
|
||||
|
|
|
@ -47,24 +47,19 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
|||
);
|
||||
}
|
||||
|
||||
private openInternal(input: EditorInput, options: EditorOptions): Promise<void> {
|
||||
private async openInternal(input: EditorInput, options: EditorOptions): Promise<void> {
|
||||
if (input instanceof FileEditorInput) {
|
||||
input.setForceOpenAsText();
|
||||
|
||||
return this.editorService.openEditor(input, options, this.group).then(() => undefined);
|
||||
await this.editorService.openEditor(input, options, this.group);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private openExternal(resource: URI): void {
|
||||
this.windowsService.openExternal(resource.toString()).then(didOpen => {
|
||||
if (!didOpen) {
|
||||
return this.windowsService.showItemInFolder(resource);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
private async openExternal(resource: URI): Promise<void> {
|
||||
const didOpen = await this.windowsService.openExternal(resource.toString());
|
||||
if (!didOpen) {
|
||||
return this.windowsService.showItemInFolder(resource);
|
||||
}
|
||||
}
|
||||
|
||||
getTitle(): string | null {
|
||||
|
|
|
@ -132,7 +132,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
|
||||
private handleDeletes(arg1: URI | FileChangesEvent, isExternal: boolean, movedTo?: URI): void {
|
||||
const nonDirtyFileEditors = this.getOpenedFileEditors(false /* non-dirty only */);
|
||||
nonDirtyFileEditors.forEach(editor => {
|
||||
nonDirtyFileEditors.forEach(async editor => {
|
||||
const resource = editor.getResource();
|
||||
|
||||
// Handle deletes in opened editors depending on:
|
||||
|
@ -165,20 +165,17 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
|
|||
// file is really gone and not just a faulty file event.
|
||||
// This only applies to external file events, so we need to check for the isExternal
|
||||
// flag.
|
||||
let checkExists: Promise<boolean>;
|
||||
let exists = false;
|
||||
if (isExternal) {
|
||||
checkExists = timeout(100).then(() => this.fileService.exists(resource));
|
||||
} else {
|
||||
checkExists = Promise.resolve(false);
|
||||
await timeout(100);
|
||||
exists = await this.fileService.exists(resource);
|
||||
}
|
||||
|
||||
checkExists.then(exists => {
|
||||
if (!exists && !editor.isDisposed()) {
|
||||
editor.dispose();
|
||||
} else if (this.environmentService.verbose) {
|
||||
console.warn(`File exists even though we received a delete event: ${resource.toString()}`);
|
||||
}
|
||||
});
|
||||
if (!exists && !editor.isDisposed()) {
|
||||
editor.dispose();
|
||||
} else if (this.environmentService.verbose) {
|
||||
console.warn(`File exists even though we received a delete event: ${resource.toString()}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -125,101 +125,103 @@ export class TextFileEditor extends BaseTextEditor {
|
|||
}
|
||||
}
|
||||
|
||||
setInput(input: FileEditorInput, options: EditorOptions, token: CancellationToken): Promise<void> {
|
||||
async setInput(input: FileEditorInput, options: EditorOptions, token: CancellationToken): Promise<void> {
|
||||
|
||||
// Update/clear view settings if input changes
|
||||
this.doSaveOrClearTextEditorViewState(this.input);
|
||||
|
||||
// Set input and resolve
|
||||
return super.setInput(input, options, token).then(() => {
|
||||
return input.resolve().then(resolvedModel => {
|
||||
await super.setInput(input, options, token);
|
||||
try {
|
||||
const resolvedModel = await input.resolve();
|
||||
|
||||
// Check for cancellation
|
||||
if (token.isCancellationRequested) {
|
||||
return undefined;
|
||||
}
|
||||
// Check for cancellation
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a special case where the text editor has to handle binary file editor input: if a binary file
|
||||
// has been resolved and cached before, it maybe an actual instance of BinaryEditorModel. In this case our text
|
||||
// editor has to open this model using the binary editor. We return early in this case.
|
||||
if (resolvedModel instanceof BinaryEditorModel) {
|
||||
return this.openAsBinary(input, options);
|
||||
}
|
||||
// There is a special case where the text editor has to handle binary file editor input: if a binary file
|
||||
// has been resolved and cached before, it maybe an actual instance of BinaryEditorModel. In this case our text
|
||||
// editor has to open this model using the binary editor. We return early in this case.
|
||||
if (resolvedModel instanceof BinaryEditorModel) {
|
||||
return this.openAsBinary(input, options);
|
||||
}
|
||||
|
||||
const textFileModel = <ITextFileEditorModel>resolvedModel;
|
||||
const textFileModel = <ITextFileEditorModel>resolvedModel;
|
||||
|
||||
// Editor
|
||||
const textEditor = this.getControl();
|
||||
textEditor.setModel(textFileModel.textEditorModel);
|
||||
// Editor
|
||||
const textEditor = this.getControl();
|
||||
textEditor.setModel(textFileModel.textEditorModel);
|
||||
|
||||
// Always restore View State if any associated
|
||||
const editorViewState = this.loadTextEditorViewState(this.input.getResource());
|
||||
if (editorViewState) {
|
||||
textEditor.restoreViewState(editorViewState);
|
||||
}
|
||||
// Always restore View State if any associated
|
||||
const editorViewState = this.loadTextEditorViewState(this.input.getResource());
|
||||
if (editorViewState) {
|
||||
textEditor.restoreViewState(editorViewState);
|
||||
}
|
||||
|
||||
// TextOptions (avoiding instanceof here for a reason, do not change!)
|
||||
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
|
||||
(<TextEditorOptions>options).apply(textEditor, ScrollType.Immediate);
|
||||
}
|
||||
// TextOptions (avoiding instanceof here for a reason, do not change!)
|
||||
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
|
||||
(<TextEditorOptions>options).apply(textEditor, ScrollType.Immediate);
|
||||
}
|
||||
|
||||
// Readonly flag
|
||||
textEditor.updateOptions({ readOnly: textFileModel.isReadonly() });
|
||||
}, error => {
|
||||
// Readonly flag
|
||||
textEditor.updateOptions({ readOnly: textFileModel.isReadonly() });
|
||||
} catch (error) {
|
||||
|
||||
// In case we tried to open a file inside the text editor and the response
|
||||
// indicates that this is not a text file, reopen the file through the binary
|
||||
// editor.
|
||||
if ((<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY) {
|
||||
return this.openAsBinary(input, options);
|
||||
}
|
||||
// In case we tried to open a file inside the text editor and the response
|
||||
// indicates that this is not a text file, reopen the file through the binary
|
||||
// editor.
|
||||
if ((<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY) {
|
||||
return this.openAsBinary(input, options);
|
||||
}
|
||||
|
||||
// Similar, handle case where we were asked to open a folder in the text editor.
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_DIRECTORY) {
|
||||
this.openAsFolder(input);
|
||||
// Similar, handle case where we were asked to open a folder in the text editor.
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_DIRECTORY) {
|
||||
this.openAsFolder(input);
|
||||
|
||||
return Promise.reject(new Error(nls.localize('openFolderError', "File is a directory")));
|
||||
}
|
||||
throw new Error(nls.localize('openFolderError', "File is a directory"));
|
||||
}
|
||||
|
||||
// Offer to create a file from the error if we have a file not found and the name is valid
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && isValidBasename(basename(input.getResource()))) {
|
||||
return Promise.reject(createErrorWithActions(toErrorMessage(error), {
|
||||
actions: [
|
||||
new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), undefined, true, () => {
|
||||
return this.textFileService.create(input.getResource()).then(() => this.editorService.openEditor({
|
||||
resource: input.getResource(),
|
||||
options: {
|
||||
pinned: true // new file gets pinned by default
|
||||
}
|
||||
}));
|
||||
})
|
||||
]
|
||||
}));
|
||||
}
|
||||
// Offer to create a file from the error if we have a file not found and the name is valid
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND && isValidBasename(basename(input.getResource()))) {
|
||||
throw createErrorWithActions(toErrorMessage(error), {
|
||||
actions: [
|
||||
new Action('workbench.files.action.createMissingFile', nls.localize('createFile', "Create File"), undefined, true, async () => {
|
||||
await this.textFileService.create(input.getResource());
|
||||
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_EXCEED_MEMORY_LIMIT) {
|
||||
const memoryLimit = Math.max(MIN_MAX_MEMORY_SIZE_MB, +this.configurationService.getValue<number>(undefined, 'files.maxMemoryForLargeFilesMB') || FALLBACK_MAX_MEMORY_SIZE_MB);
|
||||
return this.editorService.openEditor({
|
||||
resource: input.getResource(),
|
||||
options: {
|
||||
pinned: true // new file gets pinned by default
|
||||
}
|
||||
});
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(createErrorWithActions(toErrorMessage(error), {
|
||||
actions: [
|
||||
new Action('workbench.window.action.relaunchWithIncreasedMemoryLimit', nls.localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), undefined, true, () => {
|
||||
return this.windowsService.relaunch({
|
||||
addArgs: [
|
||||
`--max-memory=${memoryLimit}`
|
||||
]
|
||||
});
|
||||
}),
|
||||
new Action('workbench.window.action.configureMemoryLimit', nls.localize('configureMemoryLimit', 'Configure Memory Limit'), undefined, true, () => {
|
||||
return this.preferencesService.openGlobalSettings(undefined, { query: 'files.maxMemoryForLargeFilesMB' });
|
||||
})
|
||||
]
|
||||
}));
|
||||
}
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_EXCEED_MEMORY_LIMIT) {
|
||||
const memoryLimit = Math.max(MIN_MAX_MEMORY_SIZE_MB, +this.configurationService.getValue<number>(undefined, 'files.maxMemoryForLargeFilesMB') || FALLBACK_MAX_MEMORY_SIZE_MB);
|
||||
|
||||
// Otherwise make sure the error bubbles up
|
||||
return Promise.reject(error);
|
||||
});
|
||||
});
|
||||
throw createErrorWithActions(toErrorMessage(error), {
|
||||
actions: [
|
||||
new Action('workbench.window.action.relaunchWithIncreasedMemoryLimit', nls.localize('relaunchWithIncreasedMemoryLimit', "Restart with {0} MB", memoryLimit), undefined, true, () => {
|
||||
return this.windowsService.relaunch({
|
||||
addArgs: [
|
||||
`--max-memory=${memoryLimit}`
|
||||
]
|
||||
});
|
||||
}),
|
||||
new Action('workbench.window.action.configureMemoryLimit', nls.localize('configureMemoryLimit', 'Configure Memory Limit'), undefined, true, () => {
|
||||
return this.preferencesService.openGlobalSettings(undefined, { query: 'files.maxMemoryForLargeFilesMB' });
|
||||
})
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise make sure the error bubbles up
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private openAsBinary(input: FileEditorInput, options: EditorOptions): void {
|
||||
|
@ -227,21 +229,20 @@ export class TextFileEditor extends BaseTextEditor {
|
|||
this.editorService.openEditor(input, options, this.group);
|
||||
}
|
||||
|
||||
private openAsFolder(input: FileEditorInput): void {
|
||||
private async openAsFolder(input: FileEditorInput): Promise<void> {
|
||||
if (!this.group) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Since we cannot open a folder, we have to restore the previous input if any and close the editor
|
||||
this.group.closeEditor(this.input).then(() => {
|
||||
await this.group.closeEditor(this.input);
|
||||
|
||||
// Best we can do is to reveal the folder in the explorer
|
||||
if (this.contextService.isInsideWorkspace(input.getResource())) {
|
||||
this.viewletService.openViewlet(VIEWLET_ID).then(() => {
|
||||
this.explorerService.select(input.getResource(), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
// Best we can do is to reveal the folder in the explorer
|
||||
if (this.contextService.isInsideWorkspace(input.getResource())) {
|
||||
await this.viewletService.openViewlet(VIEWLET_ID);
|
||||
|
||||
this.explorerService.select(input.getResource(), true);
|
||||
}
|
||||
}
|
||||
|
||||
protected getAriaLabel(): string {
|
||||
|
|
|
@ -240,26 +240,26 @@ class ResolveSaveConflictAction extends Action {
|
|||
super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare"));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
async run(): Promise<any> {
|
||||
if (!this.model.isDisposed()) {
|
||||
const resource = this.model.getResource();
|
||||
const name = basename(resource);
|
||||
const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (in file) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong);
|
||||
|
||||
return TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true }).then(() => {
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) {
|
||||
return; // return if this message is ignored
|
||||
}
|
||||
await TextFileContentProvider.open(resource, CONFLICT_RESOLUTION_SCHEME, editorLabel, this.editorService, { pinned: true });
|
||||
|
||||
// Show additional help how to resolve the save conflict
|
||||
const actions: INotificationActions = { primary: [], secondary: [] };
|
||||
actions.primary!.push(this.instantiationService.createInstance(ResolveConflictLearnMoreAction));
|
||||
actions.secondary!.push(this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction));
|
||||
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, StorageScope.GLOBAL)) {
|
||||
return; // return if this message is ignored
|
||||
}
|
||||
|
||||
const handle = this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp, actions });
|
||||
Event.once(handle.onDidClose)(() => dispose(...actions.primary!, ...actions.secondary!));
|
||||
pendingResolveSaveConflictMessages.push(handle);
|
||||
});
|
||||
// Show additional help how to resolve the save conflict
|
||||
const actions: INotificationActions = { primary: [], secondary: [] };
|
||||
actions.primary!.push(this.instantiationService.createInstance(ResolveConflictLearnMoreAction));
|
||||
actions.secondary!.push(this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction));
|
||||
|
||||
const handle = this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp, actions });
|
||||
Event.once(handle.onDidClose)(() => dispose(...actions.primary!, ...actions.secondary!));
|
||||
pendingResolveSaveConflictMessages.push(handle);
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
|
@ -316,31 +316,28 @@ export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource:
|
|||
const editor = control.input;
|
||||
const group = control.group;
|
||||
|
||||
resolverService.createModelReference(resource).then(reference => {
|
||||
resolverService.createModelReference(resource).then(async reference => {
|
||||
const model = reference.object as IResolvedTextFileEditorModel;
|
||||
const localModelSnapshot = model.createSnapshot();
|
||||
|
||||
clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions
|
||||
|
||||
// Revert to be able to save
|
||||
return model.revert().then(() => {
|
||||
await model.revert();
|
||||
|
||||
// Restore user value (without loosing undo stack)
|
||||
modelService.updateModel(model.textEditorModel, createTextBufferFactoryFromSnapshot(localModelSnapshot));
|
||||
// Restore user value (without loosing undo stack)
|
||||
modelService.updateModel(model.textEditorModel, createTextBufferFactoryFromSnapshot(localModelSnapshot));
|
||||
|
||||
// Trigger save
|
||||
return model.save().then(() => {
|
||||
// Trigger save
|
||||
await model.save();
|
||||
|
||||
// Reopen file input
|
||||
return editorService.openEditor({ resource: model.getResource() }, group).then(() => {
|
||||
// Reopen file input
|
||||
await editorService.openEditor({ resource: model.getResource() }, group);
|
||||
|
||||
// Clean up
|
||||
group.closeEditor(editor);
|
||||
editor.dispose();
|
||||
reference.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
// Clean up
|
||||
group.closeEditor(editor);
|
||||
editor.dispose();
|
||||
reference.dispose();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -355,22 +352,20 @@ export const revertLocalChangesCommand = (accessor: ServicesAccessor, resource:
|
|||
const editor = control.input;
|
||||
const group = control.group;
|
||||
|
||||
resolverService.createModelReference(resource).then(reference => {
|
||||
resolverService.createModelReference(resource).then(async reference => {
|
||||
const model = reference.object as ITextFileEditorModel;
|
||||
|
||||
clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions
|
||||
|
||||
// Revert on model
|
||||
return model.revert().then(() => {
|
||||
await model.revert();
|
||||
|
||||
// Reopen file input
|
||||
return editorService.openEditor({ resource: model.getResource() }, group).then(() => {
|
||||
// Reopen file input
|
||||
await editorService.openEditor({ resource: model.getResource() }, group);
|
||||
|
||||
// Clean up
|
||||
group.closeEditor(editor);
|
||||
editor.dispose();
|
||||
reference.dispose();
|
||||
});
|
||||
});
|
||||
// Clean up
|
||||
group.closeEditor(editor);
|
||||
editor.dispose();
|
||||
reference.dispose();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -273,16 +273,17 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
return this.doResolveAsText();
|
||||
}
|
||||
|
||||
private doResolveAsText(): Promise<TextFileEditorModel | BinaryEditorModel> {
|
||||
private async doResolveAsText(): Promise<TextFileEditorModel | BinaryEditorModel> {
|
||||
|
||||
// Resolve as text
|
||||
return this.textFileService.models.loadOrCreate(this.resource, {
|
||||
mode: this.preferredMode,
|
||||
encoding: this.preferredEncoding,
|
||||
reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model
|
||||
allowBinary: this.forceOpenAsText,
|
||||
reason: LoadReason.EDITOR
|
||||
}).then(model => {
|
||||
try {
|
||||
await this.textFileService.models.loadOrCreate(this.resource, {
|
||||
mode: this.preferredMode,
|
||||
encoding: this.preferredEncoding,
|
||||
reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model
|
||||
allowBinary: this.forceOpenAsText,
|
||||
reason: LoadReason.EDITOR
|
||||
});
|
||||
|
||||
// This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary
|
||||
// or very large files do not resolve to a text file model but should be opened as binary files without text. First calling into
|
||||
|
@ -292,8 +293,10 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
this.textModelReference = this.textModelResolverService.createModelReference(this.resource);
|
||||
}
|
||||
|
||||
return this.textModelReference.then(ref => ref.object as TextFileEditorModel);
|
||||
}, error => {
|
||||
const ref = await this.textModelReference;
|
||||
|
||||
return ref.object as TextFileEditorModel;
|
||||
} catch (error) {
|
||||
|
||||
// In case of an error that indicates that the file is binary or too large, just return with the binary editor model
|
||||
if (
|
||||
|
@ -304,12 +307,12 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
}
|
||||
|
||||
// Bubble any other error up
|
||||
return Promise.reject(error);
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private doResolveAsBinary(): Promise<BinaryEditorModel> {
|
||||
return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load().then(m => m as BinaryEditorModel);
|
||||
private async doResolveAsBinary(): Promise<BinaryEditorModel> {
|
||||
return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load();
|
||||
}
|
||||
|
||||
isResolved(): boolean {
|
||||
|
|
|
@ -143,15 +143,13 @@ export class TextFileContentProvider implements ITextModelContentProvider {
|
|||
@IModelService private readonly modelService: IModelService
|
||||
) { }
|
||||
|
||||
static open(resource: URI, scheme: string, label: string, editorService: IEditorService, options?: ITextEditorOptions): Promise<void> {
|
||||
return editorService.openEditor(
|
||||
{
|
||||
leftResource: TextFileContentProvider.resourceToTextFile(scheme, resource),
|
||||
rightResource: resource,
|
||||
label,
|
||||
options
|
||||
}
|
||||
).then();
|
||||
static async open(resource: URI, scheme: string, label: string, editorService: IEditorService, options?: ITextEditorOptions): Promise<void> {
|
||||
await editorService.openEditor({
|
||||
leftResource: TextFileContentProvider.resourceToTextFile(scheme, resource),
|
||||
rightResource: resource,
|
||||
label,
|
||||
options
|
||||
});
|
||||
}
|
||||
|
||||
private static resourceToTextFile(scheme: string, resource: URI): URI {
|
||||
|
@ -162,56 +160,55 @@ export class TextFileContentProvider implements ITextModelContentProvider {
|
|||
return resource.with({ scheme: JSON.parse(resource.query)['scheme'], query: null });
|
||||
}
|
||||
|
||||
provideTextContent(resource: URI): Promise<ITextModel> {
|
||||
async provideTextContent(resource: URI): Promise<ITextModel> {
|
||||
const savedFileResource = TextFileContentProvider.textFileToResource(resource);
|
||||
|
||||
// Make sure our text file is resolved up to date
|
||||
return this.resolveEditorModel(resource).then(codeEditorModel => {
|
||||
const codeEditorModel = await this.resolveEditorModel(resource);
|
||||
|
||||
// Make sure to keep contents up to date when it changes
|
||||
if (!this.fileWatcherDisposable) {
|
||||
this.fileWatcherDisposable = this.fileService.onFileChanges(changes => {
|
||||
if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
|
||||
this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
|
||||
}
|
||||
});
|
||||
|
||||
if (codeEditorModel) {
|
||||
once(codeEditorModel.onWillDispose)(() => {
|
||||
dispose(this.fileWatcherDisposable);
|
||||
this.fileWatcherDisposable = undefined;
|
||||
});
|
||||
// Make sure to keep contents up to date when it changes
|
||||
if (!this.fileWatcherDisposable) {
|
||||
this.fileWatcherDisposable = this.fileService.onFileChanges(changes => {
|
||||
if (changes.contains(savedFileResource, FileChangeType.UPDATED)) {
|
||||
this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return codeEditorModel;
|
||||
});
|
||||
if (codeEditorModel) {
|
||||
once(codeEditorModel.onWillDispose)(() => {
|
||||
dispose(this.fileWatcherDisposable);
|
||||
this.fileWatcherDisposable = undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return codeEditorModel;
|
||||
}
|
||||
|
||||
private resolveEditorModel(resource: URI, createAsNeeded?: true): Promise<ITextModel>;
|
||||
private resolveEditorModel(resource: URI, createAsNeeded?: boolean): Promise<ITextModel | null>;
|
||||
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
|
||||
private async resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
|
||||
const savedFileResource = TextFileContentProvider.textFileToResource(resource);
|
||||
|
||||
return this.textFileService.readStream(savedFileResource).then(content => {
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
if (codeEditorModel) {
|
||||
this.modelService.updateModel(codeEditorModel, content.value);
|
||||
} else if (createAsNeeded) {
|
||||
const textFileModel = this.modelService.getModel(savedFileResource);
|
||||
const content = await this.textFileService.readStream(savedFileResource);
|
||||
|
||||
let languageSelector: ILanguageSelection;
|
||||
if (textFileModel) {
|
||||
languageSelector = this.modeService.create(textFileModel.getModeId());
|
||||
} else {
|
||||
languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.path);
|
||||
}
|
||||
let codeEditorModel = this.modelService.getModel(resource);
|
||||
if (codeEditorModel) {
|
||||
this.modelService.updateModel(codeEditorModel, content.value);
|
||||
} else if (createAsNeeded) {
|
||||
const textFileModel = this.modelService.getModel(savedFileResource);
|
||||
|
||||
codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
|
||||
let languageSelector: ILanguageSelection;
|
||||
if (textFileModel) {
|
||||
languageSelector = this.modeService.create(textFileModel.getModeId());
|
||||
} else {
|
||||
languageSelector = this.modeService.createByFilepathOrFirstLine(savedFileResource.path);
|
||||
}
|
||||
|
||||
return codeEditorModel;
|
||||
});
|
||||
codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource);
|
||||
}
|
||||
|
||||
return codeEditorModel;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -34,26 +34,25 @@ suite('Files - FileEditorTracker', () => {
|
|||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
test('file change event updates model', function () {
|
||||
test('file change event updates model', async function () {
|
||||
const tracker = instantiationService.createInstance(FileEditorTracker);
|
||||
|
||||
const resource = toResource.call(this, '/path/index.txt');
|
||||
|
||||
return accessor.textFileService.models.loadOrCreate(resource).then((model: IResolvedTextFileEditorModel) => {
|
||||
model.textEditorModel.setValue('Super Good');
|
||||
assert.equal(snapshotToString(model.createSnapshot()!), 'Super Good');
|
||||
const model = await accessor.textFileService.models.loadOrCreate(resource) as IResolvedTextFileEditorModel;
|
||||
|
||||
return model.save().then(() => {
|
||||
model.textEditorModel.setValue('Super Good');
|
||||
assert.equal(snapshotToString(model.createSnapshot()!), 'Super Good');
|
||||
|
||||
// change event (watcher)
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }]));
|
||||
await model.save();
|
||||
|
||||
return timeout(0).then(() => { // due to event updating model async
|
||||
assert.equal(snapshotToString(model.createSnapshot()!), 'Hello Html');
|
||||
// change event (watcher)
|
||||
accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource, type: FileChangeType.UPDATED }]));
|
||||
|
||||
tracker.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
await timeout(0); // due to event updating model async
|
||||
|
||||
assert.equal(snapshotToString(model.createSnapshot()!), 'Hello Html');
|
||||
|
||||
tracker.dispose();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -390,7 +390,7 @@ export class GotoSymbolHandler extends QuickOpenHandler {
|
|||
this.rangeHighlightDecorationId = undefined;
|
||||
}
|
||||
|
||||
getResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel | null> {
|
||||
async getResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel | null> {
|
||||
searchValue = searchValue.trim();
|
||||
|
||||
// Support to cancel pending outline requests
|
||||
|
@ -407,20 +407,19 @@ export class GotoSymbolHandler extends QuickOpenHandler {
|
|||
}
|
||||
|
||||
// Resolve Outline Model
|
||||
return this.getOutline().then(outline => {
|
||||
if (!outline) {
|
||||
return outline;
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return outline;
|
||||
}
|
||||
|
||||
// Filter by search
|
||||
outline.applyFilter(searchValue);
|
||||
|
||||
const outline = await this.getOutline();
|
||||
if (!outline) {
|
||||
return outline;
|
||||
});
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return outline;
|
||||
}
|
||||
|
||||
// Filter by search
|
||||
outline.applyFilter(searchValue);
|
||||
|
||||
return outline;
|
||||
}
|
||||
|
||||
getEmptyLabel(searchString: string): string {
|
||||
|
|
|
@ -147,43 +147,45 @@ export class OpenFileHandler extends QuickOpenHandler {
|
|||
return this.doFindResults(query, token, this.cacheState.cacheKey, maxSortedResults);
|
||||
}
|
||||
|
||||
private doFindResults(query: IPreparedQuery, token: CancellationToken, cacheKey?: string, maxSortedResults?: number): Promise<FileQuickOpenModel> {
|
||||
private async doFindResults(query: IPreparedQuery, token: CancellationToken, cacheKey?: string, maxSortedResults?: number): Promise<FileQuickOpenModel> {
|
||||
const queryOptions = this.doResolveQueryOptions(query, cacheKey, maxSortedResults);
|
||||
|
||||
let iconClass: string;
|
||||
let iconClass: string | undefined = undefined;
|
||||
if (this.options && this.options.forceUseIcons && !this.themeService.getFileIconTheme()) {
|
||||
iconClass = 'file'; // only use a generic file icon if we are forced to use an icon and have no icon theme set otherwise
|
||||
}
|
||||
|
||||
return this.getAbsolutePathResult(query).then(result => {
|
||||
if (token.isCancellationRequested) {
|
||||
return Promise.resolve(<ISearchComplete>{ results: [] });
|
||||
let complete: ISearchComplete | undefined = undefined;
|
||||
|
||||
const result = await this.getAbsolutePathResult(query);
|
||||
if (token.isCancellationRequested) {
|
||||
complete = <ISearchComplete>{ results: [] };
|
||||
}
|
||||
|
||||
// If the original search value is an existing file on disk, return it immediately and bypass the search service
|
||||
else if (result) {
|
||||
complete = <ISearchComplete>{ results: [{ resource: result }] };
|
||||
}
|
||||
|
||||
else {
|
||||
complete = await this.searchService.fileSearch(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token);
|
||||
}
|
||||
|
||||
const results: QuickOpenEntry[] = [];
|
||||
|
||||
if (!token.isCancellationRequested) {
|
||||
for (const fileMatch of complete.results) {
|
||||
const label = basename(fileMatch.resource);
|
||||
const description = this.labelService.getUriLabel(dirname(fileMatch.resource), { relative: true });
|
||||
|
||||
results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass));
|
||||
}
|
||||
}
|
||||
|
||||
// If the original search value is an existing file on disk, return it immediately and bypass the search service
|
||||
if (result) {
|
||||
return Promise.resolve(<ISearchComplete>{ results: [{ resource: result }] });
|
||||
}
|
||||
|
||||
return this.searchService.fileSearch(this.queryBuilder.file(this.contextService.getWorkspace().folders.map(folder => folder.uri), queryOptions), token);
|
||||
}).then(complete => {
|
||||
const results: QuickOpenEntry[] = [];
|
||||
|
||||
if (!token.isCancellationRequested) {
|
||||
for (const fileMatch of complete.results) {
|
||||
|
||||
const label = basename(fileMatch.resource);
|
||||
const description = this.labelService.getUriLabel(dirname(fileMatch.resource), { relative: true });
|
||||
|
||||
results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass));
|
||||
}
|
||||
}
|
||||
|
||||
return new FileQuickOpenModel(results, <IFileSearchStats>complete.stats);
|
||||
});
|
||||
return new FileQuickOpenModel(results, <IFileSearchStats>complete.stats);
|
||||
}
|
||||
|
||||
private getAbsolutePathResult(query: IPreparedQuery): Promise<URI | undefined> {
|
||||
private async getAbsolutePathResult(query: IPreparedQuery): Promise<URI | undefined> {
|
||||
const detildifiedQuery = untildify(query.original, this.environmentService.userHome);
|
||||
if (isAbsolute(detildifiedQuery)) {
|
||||
const workspaceFolders = this.contextService.getWorkspace().folders;
|
||||
|
@ -191,12 +193,16 @@ export class OpenFileHandler extends QuickOpenHandler {
|
|||
workspaceFolders[0].uri.with({ path: detildifiedQuery }) :
|
||||
URI.file(detildifiedQuery);
|
||||
|
||||
return this.fileService.resolve(resource).then(
|
||||
stat => stat.isDirectory ? undefined : resource,
|
||||
error => undefined);
|
||||
try {
|
||||
const stat = await this.fileService.resolve(resource);
|
||||
|
||||
return stat.isDirectory ? undefined : resource;
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IFileQueryBuilderOptions {
|
||||
|
|
|
@ -172,7 +172,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
|||
}
|
||||
}
|
||||
|
||||
private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): void {
|
||||
private async runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): Promise<void> {
|
||||
/* __GDPR__
|
||||
"workbenchActionExecuted" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
|
@ -182,9 +182,15 @@ class NativeContextMenuService extends Disposable implements IContextMenuService
|
|||
this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' });
|
||||
|
||||
const context = delegate.getActionsContext ? delegate.getActionsContext(event) : event;
|
||||
const res = actionRunner.run(actionToRun, context) || Promise.resolve(null);
|
||||
|
||||
res.then(undefined, e => this.notificationService.error(e));
|
||||
const runnable = actionRunner.run(actionToRun, context);
|
||||
if (runnable) {
|
||||
try {
|
||||
await runnable;
|
||||
} catch (error) {
|
||||
this.notificationService.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,17 +78,16 @@ class NativeDialogService implements IDialogService {
|
|||
sharedProcessService.registerChannel('dialog', new DialogChannel(this));
|
||||
}
|
||||
|
||||
confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
|
||||
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
|
||||
this.logService.trace('DialogService#confirm', confirmation.message);
|
||||
|
||||
const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation));
|
||||
|
||||
return this.windowService.showMessageBox(options).then(result => {
|
||||
return {
|
||||
confirmed: buttonIndexMap[result.button] === 0 ? true : false,
|
||||
checkboxChecked: result.checkboxChecked
|
||||
};
|
||||
});
|
||||
const result = await this.windowService.showMessageBox(options);
|
||||
return {
|
||||
confirmed: buttonIndexMap[result.button] === 0 ? true : false,
|
||||
checkboxChecked: result.checkboxChecked
|
||||
};
|
||||
}
|
||||
|
||||
private getConfirmOptions(confirmation: IConfirmation): Electron.MessageBoxOptions {
|
||||
|
@ -128,7 +127,7 @@ class NativeDialogService implements IDialogService {
|
|||
return opts;
|
||||
}
|
||||
|
||||
show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise<number> {
|
||||
async show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise<number> {
|
||||
this.logService.trace('DialogService#show', message);
|
||||
|
||||
const { options, buttonIndexMap } = this.massageMessageBoxOptions({
|
||||
|
@ -139,7 +138,8 @@ class NativeDialogService implements IDialogService {
|
|||
detail: dialogOptions ? dialogOptions.detail : undefined
|
||||
});
|
||||
|
||||
return this.windowService.showMessageBox(options).then(result => buttonIndexMap[result.button]);
|
||||
const result = await this.windowService.showMessageBox(options);
|
||||
return buttonIndexMap[result.button];
|
||||
}
|
||||
|
||||
private massageMessageBoxOptions(options: Electron.MessageBoxOptions): IMassagedMessageBoxOptions {
|
||||
|
|
|
@ -62,17 +62,16 @@ export class CodeEditorService extends CodeEditorServiceImpl {
|
|||
return this.doOpenCodeEditor(input, source, sideBySide);
|
||||
}
|
||||
|
||||
private doOpenCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
|
||||
return this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(control => {
|
||||
if (control) {
|
||||
const widget = control.getControl();
|
||||
if (isCodeEditor(widget)) {
|
||||
return widget;
|
||||
}
|
||||
private async doOpenCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
|
||||
const control = await this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
if (control) {
|
||||
const widget = control.getControl();
|
||||
if (isCodeEditor(widget)) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -328,7 +328,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
|
||||
openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]>;
|
||||
openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]>;
|
||||
openEditors(editors: Array<IEditorInputWithOptions | IResourceEditor>, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]> {
|
||||
async openEditors(editors: Array<IEditorInputWithOptions | IResourceEditor>, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise<IEditor[]> {
|
||||
|
||||
// Convert to typed editors and options
|
||||
const typedEditors: IEditorInputWithOptions[] = [];
|
||||
|
@ -364,7 +364,9 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
result.push(group.openEditors(editorsWithOptions));
|
||||
});
|
||||
|
||||
return Promise.all(result).then(editors => coalesce(editors));
|
||||
const openedEditors = await Promise.all(result);
|
||||
|
||||
return coalesce(openedEditors);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -71,9 +71,9 @@ export class IntegrityServiceImpl implements IIntegrityService {
|
|||
|
||||
this.isPure().then(r => {
|
||||
if (r.isPure) {
|
||||
// all is good
|
||||
return;
|
||||
return; // all is good
|
||||
}
|
||||
|
||||
this._prompt();
|
||||
});
|
||||
}
|
||||
|
@ -106,29 +106,25 @@ export class IntegrityServiceImpl implements IIntegrityService {
|
|||
return this._isPurePromise;
|
||||
}
|
||||
|
||||
private _isPure(): Promise<IntegrityTestResult> {
|
||||
private async _isPure(): Promise<IntegrityTestResult> {
|
||||
const expectedChecksums = product.checksums || {};
|
||||
|
||||
return this.lifecycleService.when(LifecyclePhase.Eventually).then(() => {
|
||||
let asyncResults: Promise<ChecksumPair>[] = Object.keys(expectedChecksums).map((filename) => {
|
||||
return this._resolve(filename, expectedChecksums[filename]);
|
||||
});
|
||||
await this.lifecycleService.when(LifecyclePhase.Eventually);
|
||||
|
||||
return Promise.all(asyncResults).then<IntegrityTestResult>((allResults) => {
|
||||
let isPure = true;
|
||||
for (let i = 0, len = allResults.length; i < len; i++) {
|
||||
if (!allResults[i].isPure) {
|
||||
isPure = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const allResults = await Promise.all(Object.keys(expectedChecksums).map(filename => this._resolve(filename, expectedChecksums[filename])));
|
||||
|
||||
return {
|
||||
isPure: isPure,
|
||||
proof: allResults
|
||||
};
|
||||
});
|
||||
});
|
||||
let isPure = true;
|
||||
for (let i = 0, len = allResults.length; i < len; i++) {
|
||||
if (!allResults[i].isPure) {
|
||||
isPure = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isPure: isPure,
|
||||
proof: allResults
|
||||
};
|
||||
}
|
||||
|
||||
private _resolve(filename: string, expected: string): Promise<ChecksumPair> {
|
||||
|
|
|
@ -208,7 +208,8 @@ export class ScopedProgressService extends ScopedService implements IProgressSer
|
|||
};
|
||||
}
|
||||
|
||||
showWhile(promise: Promise<any>, delay?: number): Promise<void> {
|
||||
async showWhile(promise: Promise<any>, delay?: number): Promise<void> {
|
||||
|
||||
// Join with existing running promise to ensure progress is accurate
|
||||
if (this.progressState.type === ProgressState.Type.While) {
|
||||
promise = Promise.all([promise, this.progressState.whilePromise]);
|
||||
|
@ -217,24 +218,25 @@ export class ScopedProgressService extends ScopedService implements IProgressSer
|
|||
// Keep Promise in State
|
||||
this.progressState = new ProgressState.While(promise, delay || 0, Date.now());
|
||||
|
||||
let stop = () => {
|
||||
try {
|
||||
this.doShowWhile(delay);
|
||||
|
||||
// If this is not the last promise in the list of joined promises, return early
|
||||
if (this.progressState.type === ProgressState.Type.While && this.progressState.whilePromise !== promise) {
|
||||
return;
|
||||
await promise;
|
||||
} catch (error) {
|
||||
// ignore
|
||||
} finally {
|
||||
|
||||
// If this is not the last promise in the list of joined promises, skip this
|
||||
if (this.progressState.type !== ProgressState.Type.While || this.progressState.whilePromise === promise) {
|
||||
|
||||
// The while promise is either null or equal the promise we last hooked on
|
||||
this.progressState = ProgressState.None;
|
||||
|
||||
if (this.isActive) {
|
||||
this.progressbar.stop().hide();
|
||||
}
|
||||
}
|
||||
|
||||
// The while promise is either null or equal the promise we last hooked on
|
||||
this.progressState = ProgressState.None;
|
||||
|
||||
if (this.isActive) {
|
||||
this.progressbar.stop().hide();
|
||||
}
|
||||
};
|
||||
|
||||
this.doShowWhile(delay);
|
||||
|
||||
return promise.then(stop, stop);
|
||||
}
|
||||
}
|
||||
|
||||
private doShowWhile(delay?: number): void {
|
||||
|
@ -280,13 +282,15 @@ export class ProgressService implements IProgressService {
|
|||
};
|
||||
}
|
||||
|
||||
showWhile(promise: Promise<any>, delay?: number): Promise<void> {
|
||||
const stop = () => {
|
||||
async showWhile(promise: Promise<any>, delay?: number): Promise<void> {
|
||||
try {
|
||||
this.progressbar.infinite().show(delay);
|
||||
|
||||
await promise;
|
||||
} catch (error) {
|
||||
// ignore
|
||||
} finally {
|
||||
this.progressbar.stop().hide();
|
||||
};
|
||||
|
||||
this.progressbar.infinite().show(delay);
|
||||
|
||||
return promise.then(stop, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,28 +34,28 @@ export class RemoteExtensionEnvironmentChannelClient {
|
|||
|
||||
constructor(private channel: IChannel) { }
|
||||
|
||||
getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI[]): Promise<IRemoteAgentEnvironment> {
|
||||
async getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI[]): Promise<IRemoteAgentEnvironment> {
|
||||
const args: IGetEnvironmentDataArguments = {
|
||||
language: platform.language,
|
||||
remoteAuthority,
|
||||
extensionDevelopmentPath
|
||||
};
|
||||
return this.channel.call<IRemoteAgentEnvironmentDTO>('getEnvironmentData', args)
|
||||
.then((data: IRemoteAgentEnvironmentDTO): IRemoteAgentEnvironment => {
|
||||
return {
|
||||
pid: data.pid,
|
||||
appRoot: URI.revive(data.appRoot),
|
||||
appSettingsHome: URI.revive(data.appSettingsHome),
|
||||
settingsPath: URI.revive(data.settingsPath),
|
||||
logsPath: URI.revive(data.logsPath),
|
||||
extensionsPath: URI.revive(data.extensionsPath),
|
||||
extensionHostLogsPath: URI.revive(data.extensionHostLogsPath),
|
||||
globalStorageHome: URI.revive(data.globalStorageHome),
|
||||
userHome: URI.revive(data.userHome),
|
||||
extensions: data.extensions.map(ext => { (<any>ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }),
|
||||
os: data.os
|
||||
};
|
||||
});
|
||||
|
||||
const data = await this.channel.call<IRemoteAgentEnvironmentDTO>('getEnvironmentData', args);
|
||||
|
||||
return {
|
||||
pid: data.pid,
|
||||
appRoot: URI.revive(data.appRoot),
|
||||
appSettingsHome: URI.revive(data.appSettingsHome),
|
||||
settingsPath: URI.revive(data.settingsPath),
|
||||
logsPath: URI.revive(data.logsPath),
|
||||
extensionsPath: URI.revive(data.extensionsPath),
|
||||
extensionHostLogsPath: URI.revive(data.extensionHostLogsPath),
|
||||
globalStorageHome: URI.revive(data.globalStorageHome),
|
||||
userHome: URI.revive(data.userHome),
|
||||
extensions: data.extensions.map(ext => { (<any>ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }),
|
||||
os: data.os
|
||||
};
|
||||
}
|
||||
|
||||
getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise<IDiagnosticInfo> {
|
||||
|
|
|
@ -31,7 +31,7 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod
|
|||
super();
|
||||
}
|
||||
|
||||
createReferencedObject(key: string, skipActivateProvider?: boolean): Promise<ITextEditorModel> {
|
||||
async createReferencedObject(key: string, skipActivateProvider?: boolean): Promise<ITextEditorModel> {
|
||||
this.modelsToDispose.delete(key);
|
||||
|
||||
const resource = URI.parse(key);
|
||||
|
@ -43,15 +43,19 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod
|
|||
|
||||
// Virtual documents
|
||||
if (this.providers[resource.scheme]) {
|
||||
return this.resolveTextModelContent(key).then(() => this.instantiationService.createInstance(ResourceEditorModel, resource));
|
||||
await this.resolveTextModelContent(key);
|
||||
|
||||
return this.instantiationService.createInstance(ResourceEditorModel, resource);
|
||||
}
|
||||
|
||||
// Either unknown schema, or not yet registered, try to activate
|
||||
if (!skipActivateProvider) {
|
||||
return this.fileService.activateProvider(resource.scheme).then(() => this.createReferencedObject(key, true));
|
||||
await this.fileService.activateProvider(resource.scheme);
|
||||
|
||||
return this.createReferencedObject(key, true);
|
||||
}
|
||||
|
||||
return Promise.reject(new Error('resource is not available'));
|
||||
throw new Error('resource is not available');
|
||||
}
|
||||
|
||||
destroyReferencedObject(key: string, modelPromise: Promise<ITextEditorModel>): void {
|
||||
|
@ -101,18 +105,17 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod
|
|||
return this.providers[scheme] !== undefined;
|
||||
}
|
||||
|
||||
private resolveTextModelContent(key: string): Promise<ITextModel> {
|
||||
private async resolveTextModelContent(key: string): Promise<ITextModel> {
|
||||
const resource = URI.parse(key);
|
||||
const providers = this.providers[resource.scheme] || [];
|
||||
const factories = providers.map(p => () => Promise.resolve(p.provideTextContent(resource)));
|
||||
|
||||
return first(factories).then(model => {
|
||||
if (!model) {
|
||||
return Promise.reject(new Error('resource is not available'));
|
||||
}
|
||||
const model = await first(factories);
|
||||
if (!model) {
|
||||
throw new Error('resource is not available');
|
||||
}
|
||||
|
||||
return model;
|
||||
});
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,14 +134,16 @@ export class TextModelResolverService implements ITextModelService {
|
|||
}
|
||||
|
||||
createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
return this._createModelReference(resource);
|
||||
return this.doCreateModelReference(resource);
|
||||
}
|
||||
|
||||
private _createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
private async doCreateModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
|
||||
// Untitled Schema: go through cached input
|
||||
if (resource.scheme === network.Schemas.untitled) {
|
||||
return this.untitledEditorService.loadOrCreate({ resource }).then(model => new ImmortalReference(model as IResolvedTextEditorModel));
|
||||
const model = await this.untitledEditorService.loadOrCreate({ resource });
|
||||
|
||||
return new ImmortalReference(model as IResolvedTextEditorModel);
|
||||
}
|
||||
|
||||
// InMemory Schema: go through model service cache
|
||||
|
@ -154,14 +159,15 @@ export class TextModelResolverService implements ITextModelService {
|
|||
|
||||
const ref = this.resourceModelCollection.acquire(resource.toString());
|
||||
|
||||
return ref.object.then(
|
||||
model => ({ object: model, dispose: () => ref.dispose() }),
|
||||
err => {
|
||||
ref.dispose();
|
||||
try {
|
||||
const model = await ref.object;
|
||||
|
||||
return Promise.reject(err);
|
||||
}
|
||||
);
|
||||
return { object: model as IResolvedTextEditorModel, dispose: () => ref.dispose() };
|
||||
} catch (error) {
|
||||
ref.dispose();
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable {
|
||||
|
|
|
@ -71,6 +71,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||
if (reason !== ShutdownReason.LOAD && reason !== ShutdownReason.CLOSE) {
|
||||
return undefined; // only interested when window is closing or loading
|
||||
}
|
||||
|
||||
const workspaceIdentifier = this.getCurrentWorkspaceIdentifier();
|
||||
if (!workspaceIdentifier || !isEqualOrParent(workspaceIdentifier.configPath, this.environmentService.untitledWorkspacesHome)) {
|
||||
return undefined; // only care about untitled workspaces to ask for saving
|
||||
|
@ -190,16 +191,23 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||
}
|
||||
}
|
||||
|
||||
private doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToDelete: URI[], index?: number, donotNotifyError: boolean = false): Promise<void> {
|
||||
return this.contextService.updateFolders(foldersToAdd, foldersToDelete, index)
|
||||
.then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error));
|
||||
private async doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToDelete: URI[], index?: number, donotNotifyError: boolean = false): Promise<void> {
|
||||
try {
|
||||
await this.contextService.updateFolders(foldersToAdd, foldersToDelete, index);
|
||||
} catch (error) {
|
||||
if (donotNotifyError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.handleWorkspaceConfigurationEditingError(error);
|
||||
}
|
||||
}
|
||||
|
||||
addFolders(foldersToAdd: IWorkspaceFolderCreationData[], donotNotifyError: boolean = false): Promise<void> {
|
||||
return this.doAddFolders(foldersToAdd, undefined, donotNotifyError);
|
||||
}
|
||||
|
||||
private doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise<void> {
|
||||
private async doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise<void> {
|
||||
const state = this.contextService.getWorkbenchState();
|
||||
|
||||
// If we are in no-workspace or single-folder workspace, adding folders has to
|
||||
|
@ -217,11 +225,18 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||
}
|
||||
|
||||
// Delegate addition of folders to workspace service otherwise
|
||||
return this.contextService.addFolders(foldersToAdd, index)
|
||||
.then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error));
|
||||
try {
|
||||
await this.contextService.addFolders(foldersToAdd, index);
|
||||
} catch (error) {
|
||||
if (donotNotifyError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.handleWorkspaceConfigurationEditingError(error);
|
||||
}
|
||||
}
|
||||
|
||||
removeFolders(foldersToRemove: URI[], donotNotifyError: boolean = false): Promise<void> {
|
||||
async removeFolders(foldersToRemove: URI[], donotNotifyError: boolean = false): Promise<void> {
|
||||
|
||||
// If we are in single-folder state and the opened folder is to be removed,
|
||||
// we create an empty workspace and enter it.
|
||||
|
@ -230,8 +245,15 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||
}
|
||||
|
||||
// Delegate removal of folders to workspace service otherwise
|
||||
return this.contextService.removeFolders(foldersToRemove)
|
||||
.then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error));
|
||||
try {
|
||||
await this.contextService.removeFolders(foldersToRemove);
|
||||
} catch (error) {
|
||||
if (donotNotifyError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.handleWorkspaceConfigurationEditingError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private includesSingleFolderWorkspace(folders: URI[]): boolean {
|
||||
|
@ -283,10 +305,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
|||
detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
|
||||
noLink: true
|
||||
};
|
||||
return this.windowService.showMessageBox(options).then(() => false);
|
||||
await this.windowService.showMessageBox(options);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return Promise.resolve(true); // OK
|
||||
return true; // OK
|
||||
}
|
||||
|
||||
private async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise<any> {
|
||||
|
|
Loading…
Reference in a new issue