Skip to content

Commit 7d5cd0d

Browse files
committed
Case-sensitivity and file system providers
fixes #48258
1 parent 7a374c3 commit 7d5cd0d

File tree

7 files changed

+92
-62
lines changed

7 files changed

+92
-62
lines changed

src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
77
import { URI } from 'vs/base/common/uri';
8-
import * as resources from 'vs/base/common/resources';
98
import { IEditorViewState } from 'vs/editor/common/editorCommon';
109
import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor';
1110
import { ITextFileService, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
@@ -25,6 +24,8 @@ import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor
2524
import { timeout } from 'vs/base/common/async';
2625
import { withNullAsUndefined } from 'vs/base/common/types';
2726
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
27+
import { isEqualOrParent, joinPath } from 'vs/base/common/resources';
28+
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
2829

2930
export class FileEditorTracker extends Disposable implements IWorkbenchContribution {
3031

@@ -42,7 +43,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
4243
@IConfigurationService private readonly configurationService: IConfigurationService,
4344
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
4445
@IHostService private readonly hostService: IHostService,
45-
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
46+
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
47+
@IExplorerService private readonly explorerService: IExplorerService
4648
) {
4749
super();
4850

@@ -101,13 +103,13 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
101103

102104
// Update Editor if file (or any parent of the input) got renamed or moved
103105
const resource = editor.getResource();
104-
if (resources.isEqualOrParent(resource, oldResource)) {
106+
if (isEqualOrParent(resource, oldResource)) {
105107
let reopenFileResource: URI;
106108
if (oldResource.toString() === resource.toString()) {
107109
reopenFileResource = newResource; // file got moved
108110
} else {
109-
const index = this.getIndexOfPath(resource.path, oldResource.path, resources.hasToIgnoreCase(resource));
110-
reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved
111+
const index = this.getIndexOfPath(resource.path, oldResource.path, this.explorerService.shouldIgnoreCase(resource));
112+
reopenFileResource = joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved
111113
}
112114

113115
let encoding: string | undefined = undefined;
@@ -195,15 +197,15 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
195197
// Do NOT close any opened editor that matches the resource path (either equal or being parent) of the
196198
// resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same
197199
// path but different casing.
198-
if (movedTo && resources.isEqualOrParent(resource, movedTo)) {
200+
if (movedTo && isEqualOrParent(resource, movedTo)) {
199201
return;
200202
}
201203

202204
let matches = false;
203205
if (arg1 instanceof FileChangesEvent) {
204206
matches = arg1.contains(resource, FileChangeType.DELETED);
205207
} else {
206-
matches = resources.isEqualOrParent(resource, arg1);
208+
matches = isEqualOrParent(resource, arg1);
207209
}
208210

209211
if (!matches) {

src/vs/workbench/contrib/files/browser/fileActions.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,12 @@ function containsBothDirectoryAndFile(distinctElements: ExplorerItem[]): boolean
328328
}
329329

330330

331-
export function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean, allowOverwrite: boolean }, incrementalNaming: 'simple' | 'smart'): URI {
331+
export function findValidPasteFileTarget(explorerService: IExplorerService, targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean, allowOverwrite: boolean }, incrementalNaming: 'simple' | 'smart'): URI {
332332
let name = resources.basenameOrAuthority(fileToPaste.resource);
333333

334334
let candidate = resources.joinPath(targetFolder.resource, name);
335335
while (true && !fileToPaste.allowOverwrite) {
336-
if (!targetFolder.root.find(candidate)) {
336+
if (!explorerService.findClosest(candidate)) {
337337
break;
338338
}
339339

@@ -870,7 +870,7 @@ async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boole
870870
throw new Error('Parent folder is readonly.');
871871
}
872872

873-
const newStat = new NewExplorerItem(folder, isFolder);
873+
const newStat = new NewExplorerItem(explorerService, folder, isFolder);
874874
await folder.fetchChildren(fileService, explorerService);
875875

876876
folder.addChild(newStat);
@@ -1049,7 +1049,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => {
10491049
}
10501050

10511051
const incrementalNaming = configurationService.getValue<IFilesConfiguration>().explorer.incrementalNaming;
1052-
const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwrite: pasteShouldMove }, incrementalNaming);
1052+
const targetFile = findValidPasteFileTarget(explorerService, target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwrite: pasteShouldMove }, incrementalNaming);
10531053

10541054
// Move/Copy File
10551055
if (pasteShouldMove) {

src/vs/workbench/contrib/files/browser/views/explorerViewer.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
2020
import { IThemeService } from 'vs/platform/theme/common/themeService';
2121
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
2222
import { IFilesConfiguration, IExplorerService } from 'vs/workbench/contrib/files/common/files';
23-
import { dirname, joinPath, isEqualOrParent, basename, hasToIgnoreCase, distinctParents } from 'vs/base/common/resources';
23+
import { dirname, joinPath, isEqualOrParent, basename, distinctParents } from 'vs/base/common/resources';
2424
import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
2525
import { localize } from 'vs/nls';
2626
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
@@ -95,7 +95,7 @@ export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | Explo
9595
if (element instanceof ExplorerItem && element.isRoot) {
9696
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
9797
// Single folder create a dummy explorer item to show error
98-
const placeholder = new ExplorerItem(element.resource, undefined, false);
98+
const placeholder = new ExplorerItem(element.resource, this.explorerService, undefined, false);
9999
placeholder.isError = true;
100100
return [placeholder];
101101
} else {
@@ -920,7 +920,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
920920
// Check for name collisions
921921
const targetNames = new Set<string>();
922922
if (targetStat.children) {
923-
const ignoreCase = hasToIgnoreCase(target.resource);
923+
const ignoreCase = this.explorerService.shouldIgnoreCase(target.resource);
924924
targetStat.children.forEach(child => {
925925
targetNames.add(ignoreCase ? child.name.toLowerCase() : child.name);
926926
});
@@ -929,7 +929,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
929929
// Run add in sequence
930930
const addPromisesFactory: ITask<Promise<void>>[] = [];
931931
await Promise.all(resources.map(async resource => {
932-
if (targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase())) {
932+
if (targetNames.has(this.explorerService.shouldIgnoreCase(resource) ? basename(resource).toLowerCase() : basename(resource))) {
933933
const confirmationResult = await this.dialogService.confirm(getFileOverwriteConfirm(basename(resource)));
934934
if (!confirmationResult.confirmed) {
935935
return;
@@ -1031,7 +1031,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
10311031
// Reuse duplicate action if user copies
10321032
if (isCopy) {
10331033
const incrementalNaming = this.configurationService.getValue<IFilesConfiguration>().explorer.incrementalNaming;
1034-
const stat = await this.textFileService.copy(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwrite: false }, incrementalNaming));
1034+
const stat = await this.textFileService.copy(source.resource, findValidPasteFileTarget(this.explorerService, target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwrite: false }, incrementalNaming));
10351035
if (!stat.isDirectory) {
10361036
await this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } });
10371037
}

src/vs/workbench/contrib/files/common/explorerModel.ts

+23-18
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import { URI } from 'vs/base/common/uri';
77
import { isEqual } from 'vs/base/common/extpath';
88
import { posix } from 'vs/base/common/path';
9-
import * as resources from 'vs/base/common/resources';
109
import { ResourceMap } from 'vs/base/common/map';
1110
import { IFileStat, IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
1211
import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings';
@@ -16,16 +15,20 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1615
import { memoize } from 'vs/base/common/decorators';
1716
import { Emitter, Event } from 'vs/base/common/event';
1817
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
18+
import { joinPath, isEqualOrParent, basenameOrAuthority } from 'vs/base/common/resources';
1919

2020
export class ExplorerModel implements IDisposable {
2121

2222
private _roots!: ExplorerItem[];
2323
private _listener: IDisposable;
2424
private readonly _onDidChangeRoots = new Emitter<void>();
2525

26-
constructor(private readonly contextService: IWorkspaceContextService) {
26+
constructor(
27+
private readonly contextService: IWorkspaceContextService,
28+
explorerService: IExplorerService
29+
) {
2730
const setRoots = () => this._roots = this.contextService.getWorkspace().folders
28-
.map(folder => new ExplorerItem(folder.uri, undefined, true, false, false, folder.name));
31+
.map(folder => new ExplorerItem(folder.uri, explorerService, undefined, true, false, false, folder.name));
2932
setRoots();
3033

3134
this._listener = this.contextService.onDidChangeWorkspaceFolders(() => {
@@ -80,11 +83,12 @@ export class ExplorerItem {
8083

8184
constructor(
8285
public resource: URI,
86+
private readonly explorerService: IExplorerService,
8387
private _parent: ExplorerItem | undefined,
8488
private _isDirectory?: boolean,
8589
private _isSymbolicLink?: boolean,
8690
private _isReadonly?: boolean,
87-
private _name: string = resources.basenameOrAuthority(resource),
91+
private _name: string = basenameOrAuthority(resource),
8892
private _mtime?: number,
8993
) {
9094
this._isDirectoryResolved = false;
@@ -154,8 +158,8 @@ export class ExplorerItem {
154158
return this === this.root;
155159
}
156160

157-
static create(service: IFileService, raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem {
158-
const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, service.hasCapability(raw.resource, FileSystemProviderCapabilities.Readonly), raw.name, raw.mtime);
161+
static create(explorerService: IExplorerService, fileService: IFileService, raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: readonly URI[]): ExplorerItem {
162+
const stat = new ExplorerItem(raw.resource, explorerService, parent, raw.isDirectory, raw.isSymbolicLink, fileService.hasCapability(raw.resource, FileSystemProviderCapabilities.Readonly), raw.name, raw.mtime);
159163

160164
// Recursively add children if present
161165
if (stat.isDirectory) {
@@ -164,13 +168,13 @@ export class ExplorerItem {
164168
// the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo
165169
// array of resource path to resolve.
166170
stat._isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => {
167-
return resources.isEqualOrParent(r, stat.resource);
171+
return isEqualOrParent(r, stat.resource);
168172
}));
169173

170174
// Recurse into children
171175
if (raw.children) {
172176
for (let i = 0, len = raw.children.length; i < len; i++) {
173-
const child = ExplorerItem.create(service, raw.children[i], stat, resolveTo);
177+
const child = ExplorerItem.create(explorerService, fileService, raw.children[i], stat, resolveTo);
174178
stat.addChild(child);
175179
}
176180
}
@@ -262,7 +266,7 @@ export class ExplorerItem {
262266
const resolveMetadata = explorerService.sortOrder === 'modified';
263267
try {
264268
const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata });
265-
const resolved = ExplorerItem.create(fileService, stat, this);
269+
const resolved = ExplorerItem.create(explorerService, fileService, stat, this);
266270
ExplorerItem.mergeLocalWithDisk(resolved, this);
267271
} catch (e) {
268272
this.isError = true;
@@ -302,7 +306,7 @@ export class ExplorerItem {
302306
}
303307

304308
private getPlatformAwareName(name: string): string {
305-
return (!name || !resources.hasToIgnoreCase(this.resource)) ? name : name.toLowerCase();
309+
return this.explorerService.shouldIgnoreCase(this.resource) ? name.toLowerCase() : name;
306310
}
307311

308312
/**
@@ -319,7 +323,7 @@ export class ExplorerItem {
319323

320324
private updateResource(recursive: boolean): void {
321325
if (this._parent) {
322-
this.resource = resources.joinPath(this._parent.resource, this.name);
326+
this.resource = joinPath(this._parent.resource, this.name);
323327
}
324328

325329
if (recursive) {
@@ -352,16 +356,17 @@ export class ExplorerItem {
352356
find(resource: URI): ExplorerItem | null {
353357
// Return if path found
354358
// For performance reasons try to do the comparison as fast as possible
359+
const ignoreCase = this.explorerService.shouldIgnoreCase(resource);
355360
if (resource && this.resource.scheme === resource.scheme && equalsIgnoreCase(this.resource.authority, resource.authority) &&
356-
(resources.hasToIgnoreCase(resource) ? startsWithIgnoreCase(resource.path, this.resource.path) : startsWith(resource.path, this.resource.path))) {
357-
return this.findByPath(rtrim(resource.path, posix.sep), this.resource.path.length);
361+
(ignoreCase ? startsWithIgnoreCase(resource.path, this.resource.path) : startsWith(resource.path, this.resource.path))) {
362+
return this.findByPath(rtrim(resource.path, posix.sep), this.resource.path.length, ignoreCase);
358363
}
359364

360365
return null; //Unable to find
361366
}
362367

363-
private findByPath(path: string, index: number): ExplorerItem | null {
364-
if (isEqual(rtrim(this.resource.path, posix.sep), path, resources.hasToIgnoreCase(this.resource))) {
368+
private findByPath(path: string, index: number, ignoreCase: boolean): ExplorerItem | null {
369+
if (isEqual(rtrim(this.resource.path, posix.sep), path, ignoreCase)) {
365370
return this;
366371
}
367372

@@ -383,7 +388,7 @@ export class ExplorerItem {
383388

384389
if (child) {
385390
// We found a child with the given name, search inside it
386-
return child.findByPath(path, indexOfNextSep);
391+
return child.findByPath(path, indexOfNextSep, ignoreCase);
387392
}
388393
}
389394

@@ -392,7 +397,7 @@ export class ExplorerItem {
392397
}
393398

394399
export class NewExplorerItem extends ExplorerItem {
395-
constructor(parent: ExplorerItem, isDirectory: boolean) {
396-
super(URI.file(''), parent, isDirectory);
400+
constructor(explorerService: IExplorerService, parent: ExplorerItem, isDirectory: boolean) {
401+
super(URI.file(''), explorerService, parent, isDirectory);
397402
}
398403
}

0 commit comments

Comments
 (0)