Skip to content

Commit 0c9dd09

Browse files
committed
Fix file picker backtracking when holding delete key
Fixes microsoft/vscode-remote-release#4
1 parent a579bb3 commit 0c9dd09

1 file changed

Lines changed: 42 additions & 37 deletions

File tree

src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import { Schemas } from 'vs/base/common/network';
2222
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
2323
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
2424
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
25-
import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys';
26-
import { equalsIgnoreCase, format } from 'vs/base/common/strings';
25+
import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings';
2726
import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
2827
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
2928
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
29+
import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys';
3030

3131
interface FileQuickPickItem extends IQuickPickItem {
3232
uri: URI;
@@ -63,6 +63,7 @@ export class RemoteFileDialog {
6363
private userHome: URI;
6464
private badPath: string | undefined;
6565
private remoteAgentEnvironment: IRemoteAgentEnvironment | null;
66+
private separator: string;
6667

6768
constructor(
6869
@IFileService private readonly fileService: IFileService,
@@ -159,6 +160,7 @@ export class RemoteFileDialog {
159160
private async pickResource(isSave: boolean = false): Promise<URI | undefined> {
160161
this.allowFolderSelection = !!this.options.canSelectFolders;
161162
this.allowFileSelection = !!this.options.canSelectFiles;
163+
this.separator = this.labelService.getSeparator(this.scheme, this.remoteAuthority);
162164
this.hidden = false;
163165
let homedir: URI = this.options.defaultUri ? this.options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri;
164166
let stat: IFileStat | undefined;
@@ -306,7 +308,7 @@ export class RemoteFileDialog {
306308

307309
this.filePickBox.show();
308310
this.contextKey.set(true);
309-
await this.updateItems(homedir, false, this.trailing);
311+
await this.updateItems(homedir, true, this.trailing);
310312
if (this.trailing) {
311313
this.filePickBox.valueSelection = [this.filePickBox.value.length - this.trailing.length, this.filePickBox.value.length - ext.length];
312314
} else {
@@ -350,21 +352,36 @@ export class RemoteFileDialog {
350352
}
351353

352354
private async onDidAccept(): Promise<URI | undefined> {
353-
let updateString: string;
354-
let updateUri: URI;
355+
this.filePickBox.busy = true;
355356
if (this.filePickBox.activeItems.length === 1) {
356-
updateUri = this.filePickBox.selectedItems[0].uri;
357-
updateString = this.pathFromUri(updateUri);
357+
const item = this.filePickBox.selectedItems[0];
358+
if (item.isFolder) {
359+
if (this.trailing) {
360+
await this.updateItems(item.uri, true, this.trailing);
361+
} else {
362+
// When possible, cause the update to happen by modifying the input box.
363+
// This allows all input box updates to happen first, and uses the same code path as the user typing.
364+
const newPath = this.pathFromUri(item.uri);
365+
if (startsWithIgnoreCase(newPath, this.filePickBox.value)) {
366+
const insertValue = newPath.substring(this.filePickBox.value.length, newPath.length);
367+
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
368+
this.insertText(newPath, insertValue);
369+
} else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) {
370+
this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length];
371+
this.insertText(newPath, '');
372+
} else {
373+
await this.updateItems(item.uri, true);
374+
}
375+
}
376+
return;
377+
}
358378
} else {
359-
updateString = this.filePickBox.value;
360-
updateUri = this.filePickBoxValue();
361-
}
362-
// If the items have updated, don't try to resolve
363-
if ((await this.tryUpdateItems(updateString, updateUri)) !== UpdateResult.NotUpdated) {
364-
return;
379+
// If the items have updated, don't try to resolve
380+
if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) {
381+
return;
382+
}
365383
}
366384

367-
this.filePickBox.busy = true;
368385
let resolveValue: URI | undefined;
369386
// Find resolve value
370387
if (this.filePickBox.activeItems.length === 0) {
@@ -384,10 +401,7 @@ export class RemoteFileDialog {
384401
}
385402

386403
private async tryUpdateItems(value: string, valueUri: URI): Promise<UpdateResult> {
387-
if (this.filePickBox.busy) {
388-
this.badPath = undefined;
389-
return UpdateResult.Updating;
390-
} else if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) {
404+
if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) {
391405
let newDir = this.userHome;
392406
if ((value[0] === '~') && (value.length > 1)) {
393407
newDir = resources.joinPath(newDir, value.substring(1));
@@ -636,7 +650,7 @@ export class RemoteFileDialog {
636650
return uri;
637651
} else {
638652
const dir = resources.dirname(uri);
639-
const base = resources.basename(uri) + this.labelService.getSeparator(uri.scheme, uri.authority);
653+
const base = resources.basename(uri) + this.separator;
640654
return resources.joinPath(dir, base);
641655
}
642656
}
@@ -646,24 +660,16 @@ export class RemoteFileDialog {
646660
this.userEnteredPathSegment = trailing ? trailing : '';
647661
this.autoCompletePathSegment = '';
648662
const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true);
649-
const oldFolder = this.currentFolder;
650-
const newFolderPath = this.pathFromUri(newFolder, true);
651663
this.currentFolder = this.ensureTrailingSeparator(newFolder);
652664
return this.createItems(this.currentFolder).then(items => {
653665
this.filePickBox.items = items;
654666
if (this.allowFolderSelection) {
655667
this.filePickBox.activeItems = [];
656668
}
657-
if (!equalsIgnoreCase(this.filePickBox.value, newValue)) {
658-
// the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory.
659-
if (!equalsIgnoreCase(this.filePickBox.value.substring(0, newValue.length), newValue)) {
660-
this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
661-
this.insertText(newValue, newValue);
662-
} else if (force || equalsIgnoreCase(this.pathFromUri(resources.dirname(oldFolder), true), newFolderPath)) {
663-
// This is the case where the user went up one dir or is clicking on dirs. We need to make sure that we remove the final dir.
664-
this.filePickBox.valueSelection = [newFolderPath.length, this.filePickBox.value.length];
665-
this.insertText(newValue, '');
666-
}
669+
// the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory.
670+
if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) {
671+
this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
672+
this.insertText(newValue, newValue);
667673
}
668674
if (force && trailing) {
669675
// Keep the cursor position in front of the save as name.
@@ -676,23 +682,22 @@ export class RemoteFileDialog {
676682
}
677683

678684
private pathFromUri(uri: URI, endWithSeparator: boolean = false): string {
679-
const sep = this.labelService.getSeparator(uri.scheme, uri.authority);
680685
let result: string = uri.fsPath.replace(/\n/g, '');
681-
if (sep === '/') {
682-
result = result.replace(/\\/g, sep);
686+
if (this.separator === '/') {
687+
result = result.replace(/\\/g, this.separator);
683688
} else {
684-
result = result.replace(/\//g, sep);
689+
result = result.replace(/\//g, this.separator);
685690
}
686691
if (endWithSeparator && !this.endsWithSlash(result)) {
687-
result = result + sep;
692+
result = result + this.separator;
688693
}
689694
return result;
690695
}
691696

692697
private pathAppend(uri: URI, additional: string): string {
693698
if ((additional === '..') || (additional === '.')) {
694699
const basePath = this.pathFromUri(uri);
695-
return basePath + (this.endsWithSlash(basePath) ? '' : this.labelService.getSeparator(uri.scheme, uri.authority)) + additional;
700+
return basePath + (this.endsWithSlash(basePath) ? '' : this.separator) + additional;
696701
} else {
697702
return this.pathFromUri(resources.joinPath(uri, additional));
698703
}

0 commit comments

Comments
 (0)