in src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts [181:366]
private async pickResource(isSave: boolean = false): Promise<URI | undefined> {
this.allowFolderSelection = !!this.options.canSelectFolders;
this.allowFileSelection = !!this.options.canSelectFiles;
this.separator = this.labelService.getSeparator(this.scheme, this.remoteAuthority);
this.hidden = false;
let homedir: URI = this.options.defaultUri ? this.options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri;
let stat: IFileStat | undefined;
let ext: string = resources.extname(homedir);
if (this.options.defaultUri) {
try {
stat = await this.fileService.resolve(this.options.defaultUri);
} catch (e) {
// The file or folder doesn't exist
}
if (!stat || !stat.isDirectory) {
homedir = resources.dirname(this.options.defaultUri);
this.trailing = resources.basename(this.options.defaultUri);
}
// append extension
if (isSave && !ext && this.options.filters) {
for (let i = 0; i < this.options.filters.length; i++) {
if (this.options.filters[i].extensions[0] !== '*') {
ext = '.' + this.options.filters[i].extensions[0];
this.trailing = this.trailing ? this.trailing + ext : ext;
break;
}
}
}
}
return new Promise<URI | undefined>(async (resolve) => {
this.filePickBox = this.quickInputService.createQuickPick<FileQuickPickItem>();
this.busy = true;
this.filePickBox.matchOnLabel = false;
this.filePickBox.autoFocusOnList = false;
this.filePickBox.ignoreFocusOut = true;
this.filePickBox.ok = true;
if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1) && (this.options.availableFileSystems.indexOf(Schemas.file) > -1)) {
this.filePickBox.customButton = true;
this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local');
let action;
if (isSave) {
action = SaveLocalFileCommand;
} else {
action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderCommand : OpenLocalFileCommand) : OpenLocalFolderCommand;
}
const keybinding = this.keybindingService.lookupKeybinding(action.ID);
if (keybinding) {
const label = keybinding.getLabel();
if (label) {
this.filePickBox.customHover = format('{0} ({1})', action.LABEL, label);
}
}
}
let isResolving: number = 0;
let isAcceptHandled = false;
this.currentFolder = resources.dirname(homedir);
this.userEnteredPathSegment = '';
this.autoCompletePathSegment = '';
this.filePickBox.title = this.options.title;
this.filePickBox.value = this.pathFromUri(this.currentFolder, true);
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.filePickBox.items = [];
function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) {
if (uri) {
uri = resources.removeTrailingPathSeparator(uri);
}
resolve(uri);
dialog.contextKey.set(false);
dialog.filePickBox.dispose();
dispose(dialog.disposables);
}
this.filePickBox.onDidCustom(() => {
if (isAcceptHandled || this.busy) {
return;
}
isAcceptHandled = true;
isResolving++;
if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
this.options.availableFileSystems.shift();
}
this.filePickBox.hide();
if (isSave) {
return this.fileDialogService.showSaveDialog(this.options).then(result => {
doResolve(this, result);
});
} else {
return this.fileDialogService.showOpenDialog(this.options).then(result => {
doResolve(this, result ? result[0] : undefined);
});
}
});
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++;
dialog.onDidAccept().then(resolveValue => {
if (resolveValue) {
dialog.filePickBox.hide();
doResolve(dialog, resolveValue);
} else if (dialog.hidden) {
doResolve(dialog, undefined);
} else {
isResolving--;
isAcceptHandled = false;
}
});
}
this.filePickBox.onDidAccept(_ => {
handleAccept(this);
});
this.filePickBox.onDidChangeActive(i => {
isAcceptHandled = false;
// update input box to match the first selected item
if ((i.length === 1) && this.isSelectionChangeFromUser()) {
this.filePickBox.validationMessage = undefined;
const userPath = this.constructFullUserPath();
if (!equalsIgnoreCase(this.filePickBox.value.substring(0, userPath.length), userPath)) {
this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
this.insertText(userPath, userPath);
}
this.setAutoComplete(userPath, this.userEnteredPathSegment, i[0], true);
}
});
this.filePickBox.onDidChangeValue(async value => {
try {
// onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything
if (this.isValueChangeFromUser()) {
// If the user has just entered more bad path, don't change anything
if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) {
this.filePickBox.validationMessage = undefined;
const filePickBoxUri = this.filePickBoxValue();
let updated: UpdateResult = UpdateResult.NotUpdated;
if (!resources.isEqual(this.currentFolder, filePickBoxUri, true)) {
updated = await this.tryUpdateItems(value, filePickBoxUri);
}
if (updated === UpdateResult.NotUpdated) {
this.setActiveItems(value);
}
} else {
this.filePickBox.activeItems = [];
this.userEnteredPathSegment = '';
}
}
} catch {
// Since any text can be entered in the input box, there is potential for error causing input. If this happens, do nothing.
}
});
this.filePickBox.onDidHide(() => {
this.hidden = true;
if (isResolving === 0) {
doResolve(this, undefined);
}
});
this.filePickBox.show();
this.contextKey.set(true);
await this.updateItems(homedir, true, this.trailing);
if (this.trailing) {
this.filePickBox.valueSelection = [this.filePickBox.value.length - this.trailing.length, this.filePickBox.value.length - ext.length];
} else {
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
}
this.busy = false;
});
}