Skip to content

Commit 4caf63f

Browse files
authored
Lag/Freeze when running workbench.action.openRecent on not existing path (fix microsoft#161664) (microsoft#161742)
* Lag/Freeze when running `workbench.action.openRecent` on not existing path (fix microsoft#161664) * use resource map
1 parent eab9812 commit 4caf63f

File tree

2 files changed

+54
-33
lines changed

2 files changed

+54
-33
lines changed

src/vs/platform/workspaces/common/workspaces.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,10 +331,10 @@ function isSerializedRecentFile(data: any): data is ISerializedRecentFile {
331331
export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened {
332332
const result: IRecentlyOpened = { workspaces: [], files: [] };
333333
if (data) {
334-
const restoreGracefully = function <T>(entries: T[], func: (entry: T, index: number) => void) {
334+
const restoreGracefully = function <T>(entries: T[], onEntry: (entry: T, index: number) => void) {
335335
for (let i = 0; i < entries.length; i++) {
336336
try {
337-
func(entries[i], i);
337+
onEntry(entries[i], i);
338338
} catch (e) {
339339
logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`);
340340
}
@@ -343,7 +343,7 @@ export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefine
343343

344344
const storedRecents = data as ISerializedRecentlyOpened;
345345
if (Array.isArray(storedRecents.entries)) {
346-
restoreGracefully(storedRecents.entries, (entry) => {
346+
restoreGracefully(storedRecents.entries, entry => {
347347
const label = entry.label;
348348
const remoteAuthority = entry.remoteAuthority;
349349

src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { IApplicationStorageMainService } from 'vs/platform/storage/electron-mai
2323
import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFile, isRecentFolder, isRecentWorkspace, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces';
2424
import { IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace';
2525
import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService';
26+
import { ResourceMap } from 'vs/base/common/map';
2627

2728
export const IWorkspacesHistoryMainService = createDecorator<IWorkspacesHistoryMainService>('workspacesHistoryMainService');
2829

@@ -73,28 +74,28 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa
7374
//#region Workspaces History
7475

7576
async addRecentlyOpened(recentToAdd: IRecent[]): Promise<void> {
76-
const workspaces: Array<IRecentFolder | IRecentWorkspace> = [];
77-
const files: IRecentFile[] = [];
77+
let workspaces: Array<IRecentFolder | IRecentWorkspace> = [];
78+
let files: IRecentFile[] = [];
7879

7980
for (const recent of recentToAdd) {
8081

8182
// Workspace
8283
if (isRecentWorkspace(recent)) {
83-
if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && this.indexOfWorkspace(workspaces, recent.workspace) === -1) {
84+
if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && !this.containsWorkspace(workspaces, recent.workspace)) {
8485
workspaces.push(recent);
8586
}
8687
}
8788

8889
// Folder
8990
else if (isRecentFolder(recent)) {
90-
if (this.indexOfFolder(workspaces, recent.folderUri) === -1) {
91+
if (!this.containsFolder(workspaces, recent.folderUri)) {
9192
workspaces.push(recent);
9293
}
9394
}
9495

9596
// File
9697
else {
97-
const alreadyExistsInHistory = this.indexOfFile(files, recent.fileUri) >= 0;
98+
const alreadyExistsInHistory = this.containsFile(files, recent.fileUri);
9899
const shouldBeFiltered = recent.fileUri.scheme === Schemas.file && WorkspacesHistoryMainService.COMMON_FILES_FILTER.indexOf(basename(recent.fileUri)) >= 0;
99100

100101
if (!alreadyExistsInHistory && !shouldBeFiltered) {
@@ -108,7 +109,9 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa
108109
}
109110
}
110111

111-
await this.addEntriesFromStorage(workspaces, files);
112+
const mergedEntries = await this.mergeEntriesFromStorage({ workspaces, files });
113+
workspaces = mergedEntries.workspaces;
114+
files = mergedEntries.files;
112115

113116
if (workspaces.length > WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES) {
114117
workspaces.length = WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES;
@@ -163,35 +166,53 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa
163166
}
164167

165168
async getRecentlyOpened(): Promise<IRecentlyOpened> {
166-
const workspaces: Array<IRecentFolder | IRecentWorkspace> = [];
167-
const files: IRecentFile[] = [];
169+
return this.mergeEntriesFromStorage();
170+
}
168171

169-
await this.addEntriesFromStorage(workspaces, files);
172+
private async mergeEntriesFromStorage(existingEntries?: IRecentlyOpened): Promise<IRecentlyOpened> {
170173

171-
return { workspaces, files };
172-
}
174+
// Build maps for more efficient lookup of existing entries that
175+
// are passed in by storing based on workspace/file identifier
176+
177+
const mapWorkspaceIdToWorkspace = new ResourceMap<IRecentFolder | IRecentWorkspace>(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri));
178+
if (existingEntries?.workspaces) {
179+
for (const workspace of existingEntries.workspaces) {
180+
mapWorkspaceIdToWorkspace.set(this.location(workspace), workspace);
181+
}
182+
}
173183

174-
private async addEntriesFromStorage(workspaces: Array<IRecentFolder | IRecentWorkspace>, files: IRecentFile[]): Promise<void> {
184+
const mapFileIdToFile = new ResourceMap<IRecentFile>(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri));
185+
if (existingEntries?.files) {
186+
for (const file of existingEntries.files) {
187+
mapFileIdToFile.set(this.location(file), file);
188+
}
189+
}
190+
191+
// Merge in entries from storage, preserving existing known entries
175192

176-
// Get from storage
177-
const recents = await this.getRecentlyOpenedFromStorage();
178-
for (const recent of recents.workspaces) {
179-
const index = isRecentFolder(recent) ? this.indexOfFolder(workspaces, recent.folderUri) : this.indexOfWorkspace(workspaces, recent.workspace);
180-
if (index >= 0) {
181-
workspaces[index].label = workspaces[index].label || recent.label;
193+
const recentFromStorage = await this.getRecentlyOpenedFromStorage();
194+
for (const recentWorkspaceFromStorage of recentFromStorage.workspaces) {
195+
const existingRecentWorkspace = mapWorkspaceIdToWorkspace.get(this.location(recentWorkspaceFromStorage));
196+
if (existingRecentWorkspace) {
197+
existingRecentWorkspace.label = existingRecentWorkspace.label ?? recentWorkspaceFromStorage.label;
182198
} else {
183-
workspaces.push(recent);
199+
mapWorkspaceIdToWorkspace.set(this.location(recentWorkspaceFromStorage), recentWorkspaceFromStorage);
184200
}
185201
}
186202

187-
for (const recent of recents.files) {
188-
const index = this.indexOfFile(files, recent.fileUri);
189-
if (index >= 0) {
190-
files[index].label = files[index].label || recent.label;
203+
for (const recentFileFromStorage of recentFromStorage.files) {
204+
const existingRecentFile = mapFileIdToFile.get(this.location(recentFileFromStorage));
205+
if (existingRecentFile) {
206+
existingRecentFile.label = existingRecentFile.label ?? recentFileFromStorage.label;
191207
} else {
192-
files.push(recent);
208+
mapFileIdToFile.set(this.location(recentFileFromStorage), recentFileFromStorage);
193209
}
194210
}
211+
212+
return {
213+
workspaces: [...mapWorkspaceIdToWorkspace.values()],
214+
files: [...mapFileIdToFile.values()]
215+
};
195216
}
196217

197218
private async getRecentlyOpenedFromStorage(): Promise<IRecentlyOpened> {
@@ -235,16 +256,16 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa
235256
return recent.workspace.configPath;
236257
}
237258

238-
private indexOfWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): number {
239-
return recents.findIndex(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id);
259+
private containsWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): boolean {
260+
return !!recents.find(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id);
240261
}
241262

242-
private indexOfFolder(recents: IRecent[], candidate: URI): number {
243-
return recents.findIndex(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate));
263+
private containsFolder(recents: IRecent[], candidate: URI): boolean {
264+
return !!recents.find(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate));
244265
}
245266

246-
private indexOfFile(recents: IRecentFile[], candidate: URI): number {
247-
return recents.findIndex(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate));
267+
private containsFile(recents: IRecentFile[], candidate: URI): boolean {
268+
return !!recents.find(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate));
248269
}
249270

250271
//#endregion

0 commit comments

Comments
 (0)