Merge from vscode 81d7885dc2e9dc617e1522697a2966bc4025a45d (#5949)

* Merge from vscode 81d7885dc2e9dc617e1522697a2966bc4025a45d

* Fix vs unit tests and hygiene issue

* Fix strict null check issue
This commit is contained in:
Chris LaFreniere
2019-06-10 18:27:09 -07:00
committed by GitHub
parent ff38bc8143
commit d15a3fcc98
926 changed files with 19529 additions and 11383 deletions

View File

@@ -86,6 +86,7 @@ export class FileDialogService implements IFileDialogService {
private shouldUseSimplified(schema: string): boolean {
const setting = this.configurationService.getValue('files.simpleDialog.enable');
return (schema !== Schemas.file) || (setting === true);
}
@@ -93,7 +94,7 @@ export class FileDialogService implements IFileDialogService {
return schema !== Schemas.file ? [schema, Schemas.file] : [schema];
}
pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
@@ -103,21 +104,23 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return (this.fileService.resolve(uri)).then(stat => {
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
});
}
return undefined;
});
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
const stat = await this.fileService.resolve(uri);
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
}
return;
}
return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));
}
pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
@@ -127,18 +130,19 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFile.title', 'Open File');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return;
}
return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));
}
pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
@@ -148,18 +152,19 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFolder.title', 'Open Folder');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
const uri = await this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return;
}
return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));
}
pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
@@ -170,12 +175,13 @@ export class FileDialogService implements IFileDialogService {
const title = nls.localize('openWorkspace.title', 'Open Workspace');
const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
if (uri) {
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
}
return;
}
return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
@@ -190,33 +196,34 @@ export class FileDialogService implements IFileDialogService {
};
}
showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
const schema = this.getFileSystemSchema(options);
if (this.shouldUseSimplified(schema)) {
if (!options.availableFileSystems) {
options.availableFileSystems = [schema]; // by default only allow saving in the own file system
}
return this.saveRemoteResource(options);
}
return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => {
if (result) {
return URI.file(result);
}
const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options));
if (result) {
return URI.file(result);
}
return undefined;
});
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
}
showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
const schema = this.getFileSystemSchema(options);
if (this.shouldUseSimplified(schema)) {
if (!options.availableFileSystems) {
options.availableFileSystems = [schema]; // by default only allow loading in the own file system
}
return this.pickRemoteResource(options).then(uri => {
return uri ? [uri] : undefined;
});
const uri = await this.pickRemoteResource(options);
return uri ? [uri] : undefined;
}
const defaultUri = options.defaultUri;
@@ -243,16 +250,20 @@ export class FileDialogService implements IFileDialogService {
newOptions.properties!.push('multiSelections');
}
return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined);
const result = await this.windowService.showOpenDialog(newOptions);
return result ? result.map(URI.file) : undefined;
}
private pickRemoteResource(options: IOpenDialogOptions): Promise<URI | undefined> {
const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog);
return remoteFileDialog.showOpenDialog(options);
}
private saveRemoteResource(options: ISaveDialogOptions): Promise<URI | undefined> {
const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog);
return remoteFileDialog.showSaveDialog(options);
}
@@ -263,7 +274,6 @@ export class FileDialogService implements IFileDialogService {
private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string {
return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow();
}
}
function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean {

View File

@@ -28,6 +28,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
import { isValidBasename } from 'vs/base/common/extpath';
import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys';
import { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
interface FileQuickPickItem extends IQuickPickItem {
uri: URI;
@@ -60,6 +62,11 @@ export class RemoteFileDialog {
private badPath: string | undefined;
private remoteAgentEnvironment: IRemoteAgentEnvironment | null;
private separator: string;
private onBusyChangeEmitter = new Emitter<boolean>();
protected disposables: IDisposable[] = [
this.onBusyChangeEmitter
];
constructor(
@IFileService private readonly fileService: IFileService,
@@ -79,6 +86,17 @@ export class RemoteFileDialog {
this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService);
}
set busy(busy: boolean) {
if (this.filePickBox.busy !== busy) {
this.filePickBox.busy = busy;
this.onBusyChangeEmitter.fire(busy);
}
}
get busy(): boolean {
return this.filePickBox.busy;
}
public async showOpenDialog(options: IOpenDialogOptions = {}): Promise<URI | undefined> {
this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems);
this.userHome = await this.getUserHome();
@@ -185,7 +203,7 @@ export class RemoteFileDialog {
return new Promise<URI | undefined>(async (resolve) => {
this.filePickBox = this.quickInputService.createQuickPick<FileQuickPickItem>();
this.filePickBox.busy = true;
this.busy = true;
this.filePickBox.matchOnLabel = false;
this.filePickBox.autoFocusOnList = false;
this.filePickBox.ignoreFocusOut = true;
@@ -203,7 +221,7 @@ export class RemoteFileDialog {
}
}
let isResolving = false;
let isResolving: number = 0;
let isAcceptHandled = false;
this.currentFolder = homedir;
this.userEnteredPathSegment = '';
@@ -218,15 +236,16 @@ export class RemoteFileDialog {
resolve(uri);
dialog.contextKey.set(false);
dialog.filePickBox.dispose();
dispose(dialog.disposables);
}
this.filePickBox.onDidCustom(() => {
if (isAcceptHandled || this.filePickBox.busy) {
if (isAcceptHandled || this.busy) {
return undefined; // {{SQL CARBON EDIT}} @todo anthonydresser return to return; when we do strict null checks
}
isAcceptHandled = true;
isResolving = true;
isResolving++;
if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
this.options.availableFileSystems.shift();
}
@@ -243,25 +262,38 @@ export class RemoteFileDialog {
}
});
this.filePickBox.onDidAccept(_ => {
if (isAcceptHandled || this.filePickBox.busy) {
function handleAccept(dialog: RemoteFileDialog) {
if (dialog.busy) {
// Save the accept until the file picker is not busy.
dialog.onBusyChangeEmitter.event((busy: boolean) => {
if (!busy) {
handleAccept(dialog);
}
});
return;
} else if (isAcceptHandled) {
return;
}
isAcceptHandled = true;
isResolving = true;
this.onDidAccept().then(resolveValue => {
isResolving++;
dialog.onDidAccept().then(resolveValue => {
if (resolveValue) {
this.filePickBox.hide();
doResolve(this, resolveValue);
} else if (this.hidden) {
doResolve(this, undefined);
dialog.filePickBox.hide();
doResolve(dialog, resolveValue);
} else if (dialog.hidden) {
doResolve(dialog, undefined);
} else {
isResolving = false;
isResolving--;
isAcceptHandled = false;
}
});
}
this.filePickBox.onDidAccept(_ => {
handleAccept(this);
});
this.filePickBox.onDidChangeActive(i => {
isAcceptHandled = false;
// update input box to match the first selected item
@@ -296,7 +328,7 @@ export class RemoteFileDialog {
});
this.filePickBox.onDidHide(() => {
this.hidden = true;
if (!isResolving) {
if (isResolving === 0) {
doResolve(this, undefined);
}
});
@@ -309,7 +341,7 @@ export class RemoteFileDialog {
} else {
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
}
this.filePickBox.busy = false;
this.busy = false;
});
}
@@ -340,14 +372,15 @@ export class RemoteFileDialog {
const relativePath = resources.relativePath(currentDisplayUri, directUri);
const isSameRoot = (this.filePickBox.value.length > 1 && currentPath.length > 1) ? equalsIgnoreCase(this.filePickBox.value.substr(0, 2), currentPath.substr(0, 2)) : false;
if (relativePath && isSameRoot) {
return resources.joinPath(this.currentFolder, relativePath);
const path = resources.joinPath(this.currentFolder, relativePath);
return resources.hasTrailingPathSeparator(directUri) ? resources.addTrailingPathSeparator(path) : path;
} else {
return directUri;
}
}
private async onDidAccept(): Promise<URI | undefined> {
this.filePickBox.busy = true;
this.busy = true;
if (this.filePickBox.activeItems.length === 1) {
const item = this.filePickBox.selectedItems[0];
if (item.isFolder) {
@@ -357,10 +390,9 @@ export class RemoteFileDialog {
// When possible, cause the update to happen by modifying the input box.
// This allows all input box updates to happen first, and uses the same code path as the user typing.
const newPath = this.pathFromUri(item.uri);
if (startsWithIgnoreCase(newPath, this.filePickBox.value)) {
const insertValue = newPath.substring(this.filePickBox.value.length, newPath.length);
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.insertText(newPath, insertValue);
if (startsWithIgnoreCase(newPath, this.filePickBox.value) && (equalsIgnoreCase(item.label, resources.basename(item.uri)))) {
this.filePickBox.valueSelection = [this.pathFromUri(this.currentFolder).length, this.filePickBox.value.length];
this.insertText(newPath, item.label);
} else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) {
this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length];
this.insertText(newPath, '');
@@ -368,11 +400,13 @@ export class RemoteFileDialog {
await this.updateItems(item.uri, true);
}
}
this.filePickBox.busy = false;
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
}
} else {
// If the items have updated, don't try to resolve
if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) {
this.filePickBox.busy = false;
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
}
}
@@ -388,10 +422,10 @@ export class RemoteFileDialog {
resolveValue = this.addPostfix(resolveValue);
}
if (await this.validate(resolveValue)) {
this.filePickBox.busy = false;
this.busy = false;
return resolveValue;
}
this.filePickBox.busy = false;
this.busy = false;
return undefined;
}
@@ -470,7 +504,7 @@ export class RemoteFileDialog {
}
private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem, force: boolean = false): boolean {
if (this.filePickBox.busy) {
if (this.busy) {
// We're in the middle of something else. Doing an auto complete now can result jumbled or incorrect autocompletes.
this.userEnteredPathSegment = startingBasename;
this.autoCompletePathSegment = '';
@@ -494,9 +528,6 @@ export class RemoteFileDialog {
// Changing the active items will trigger the onDidActiveItemsChanged. Clear the autocomplete first, then set it after.
this.autoCompletePathSegment = '';
this.filePickBox.activeItems = [quickPickItem];
this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename.substr(startingBasename.length));
this.insertText(startingValue + this.autoCompletePathSegment, this.autoCompletePathSegment);
this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length];
return true;
} else if (force && (!equalsIgnoreCase(quickPickItem.label, (this.userEnteredPathSegment + this.autoCompletePathSegment)))) {
this.userEnteredPathSegment = '';
@@ -641,7 +672,7 @@ export class RemoteFileDialog {
}
private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) {
this.filePickBox.busy = true;
this.busy = true;
this.userEnteredPathSegment = trailing ? trailing : '';
this.autoCompletePathSegment = '';
const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true);
@@ -663,7 +694,7 @@ export class RemoteFileDialog {
// If there is trailing, we don't move the cursor. If there is no trailing, cursor goes at the end.
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
}
this.filePickBox.busy = false;
this.busy = false;
});
}