Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Read our guide about submitting issues: --<!-- Search existing issues to avoid creating duplicates. --<!-- Test using the latest Insiders build to see if your issue has already been fixed: --<!-- Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. --<!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes/No Yes <!-- If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. --<!-- Issues caused by an extension need to be reported directly to the extension publisher. The 'Help Report Issue' dialog can assist with this. --- VS Code Version: 1.71.2 (user setup) OS: Windows_NT x64 10.0. CPU: i5- Run command and pick a invalid uri (e.g. path doesn't exist) when having "large" (like a few hundreds) amount of recent history entries, vscode will freeze for a few seconds !
By the way, having "large" amount of recent history entries will also slow down the start up time (both vscode start up and open file time) for a few seconds as well, just tested that with out any history with a few hundreds
thanks for testing that, do you recall how you got into this state of having many entries? Just by opening folders and files that contribute to the history? If you could do me a favor: open the command palette run the command "Log Storage Database Contents" run command "Toggle Developer Tools" click on "Console" expand the first "Storage..." output that is printed there find the entry (see picture below) how many entries do you see? <img width="1005" alt="image" src="https://user-"
Total of 671 entries, most of them are temp files opened from things like MobaXterm and !
Got it, we do have a limit of 500 entries when presented, but I still have to understand the performance issue. You could actually help out by doing a performance trace yourself when this happens by starting performance tracing before invoking the time consuming action and stopping right after. The steps are outlined here:
<!-- AUTHORVERIFICATIONREQUEST --This bug has been fixed in the latest release of ! you can help us out by commenting if things are now working as expected. If things still don't seem right, please ensure you're on version of Insiders (today's or later - you can use in the command palette to check), and leave a comment letting us know what isn't working as expected. Happy Coding!
/verified
Thanks!

Discussion:
export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened { const result: IRecentlyOpened = { workspaces: [], files: [] }; if (data) { <del> const restoreGracefully = function <T>(entries: T[], func: (entry: T, index: number) => void) { </del> <ins> const restoreGracefully = function <T>(entries: T[], onEntry: (entry: T, index: number) => void) { </ins> for (let i = 0; i < entries.length; i++) { try { <del> func(entries[i], i); </del> <ins> onEntry(entries[i], i); </ins> } catch (e) { logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`); }

const storedRecents = data as ISerializedRecentlyOpened; if (Array.isArray(storedRecents.entries)) { <del> restoreGracefully(storedRecents.entries, (entry) => { </del> <ins> restoreGracefully(storedRecents.entries, entry => { </ins> const label = entry.label; const remoteAuthority = entry.remoteAuthority;

import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFile, isRecentFolder, isRecentWorkspace, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; <ins> import { ResourceMap } from 'vs/base/common/map'; </ins> export const IWorkspacesHistoryMainService = createDecorator<IWorkspacesHistoryMainService>('workspacesHistoryMainService');

//#region Workspaces History async addRecentlyOpened(recentToAdd: IRecent[]): Promise<void> { <del> const workspaces: Array<IRecentFolder | IRecentWorkspace> = []; const files: IRecentFile[] = []; </del> <ins> let workspaces: Array<IRecentFolder | IRecentWorkspace> = []; let files: IRecentFile[] = []; </ins> for (const recent of recentToAdd) { // Workspace if (isRecentWorkspace(recent)) { <del> if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && this.indexOfWorkspace(workspaces, recent.workspace) === -1) { </del> <ins> if (!this.workspacesManagementMainService.isUntitledWorkspace(recent.workspace) && !this.containsWorkspace(workspaces, recent.workspace)) { </ins> workspaces.push(recent); } } // Folder else if (isRecentFolder(recent)) { <del> if (this.indexOfFolder(workspaces, recent.folderUri) === -1) { </del> <ins> if (!this.containsFolder(workspaces, recent.folderUri)) { </ins> workspaces.push(recent); } } // File else { <del> const alreadyExistsInHistory = this.indexOfFile(files, recent.fileUri) >= 0; </del> <ins> const alreadyExistsInHistory = this.containsFile(files, recent.fileUri); </ins> const shouldBeFiltered = recent.fileUri.scheme === Schemas.file && WorkspacesHistoryMainService.COMMON_FILES_FILTER.indexOf(basename(recent.fileUri)) >= 0; if (!alreadyExistsInHistory && !shouldBeFiltered) {

} } <del> await this.addEntriesFromStorage(workspaces, files); </del> <ins> const mergedEntries = await this.mergeEntriesFromStorage({ workspaces, files }); workspaces = mergedEntries.workspaces; files = mergedEntries.files; </ins> if (workspaces.length > WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES) { workspaces.length = WorkspacesHistoryMainService.MAX_TOTAL_RECENT_ENTRIES;

} async getRecentlyOpened(): Promise<IRecentlyOpened> { <del> const workspaces: Array<IRecentFolder | IRecentWorkspace> = []; const files: IRecentFile[] = []; </del> <ins> return this.mergeEntriesFromStorage(); } </ins> <del> await this.addEntriesFromStorage(workspaces, files); </del> <ins> private async mergeEntriesFromStorage(existingEntries?: IRecentlyOpened): Promise<IRecentlyOpened> { </ins> <del> return { workspaces, files }; } </del> <ins> // Build maps for more efficient lookup of existing entries that // are passed in by storing based on workspace/file identifier const mapWorkspaceIdToWorkspace = new ResourceMap<IRecentFolder | IRecentWorkspace>(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri)); if (existingEntries?.workspaces) { for (const workspace of existingEntries.workspaces) { mapWorkspaceIdToWorkspace.set(this.location(workspace), workspace); } } </ins> <del> private async addEntriesFromStorage(workspaces: Array<IRecentFolder | IRecentWorkspace>, files: IRecentFile[]): Promise<void> { </del> <ins> const mapFileIdToFile = new ResourceMap<IRecentFile>(uri => extUriBiasedIgnorePathCase.getComparisonKey(uri)); if (existingEntries?.files) { for (const file of existingEntries.files) { mapFileIdToFile.set(this.location(file), file); } } // Merge in entries from storage, preserving existing known entries </ins> <del> // Get from storage const recents = await this.getRecentlyOpenedFromStorage(); for (const recent of recents.workspaces) { const index = isRecentFolder(recent) ? this.indexOfFolder(workspaces, recent.folderUri) : this.indexOfWorkspace(workspaces, recent.workspace); if (index >= 0) { workspaces[index].label = workspaces[index].label || recent.label; </del> <ins> const recentFromStorage = await this.getRecentlyOpenedFromStorage(); for (const recentWorkspaceFromStorage of recentFromStorage.workspaces) { const existingRecentWorkspace = mapWorkspaceIdToWorkspace.get(this.location(recentWorkspaceFromStorage)); if (existingRecentWorkspace) { existingRecentWorkspace.label = existingRecentWorkspace.label ?? recentWorkspaceFromStorage.label; </ins> } else { <del> workspaces.push(recent); </del> <ins> mapWorkspaceIdToWorkspace.set(this.location(recentWorkspaceFromStorage), recentWorkspaceFromStorage); </ins> } } <del> for (const recent of recents.files) { const index = this.indexOfFile(files, recent.fileUri); if (index >= 0) { files[index].label = files[index].label || recent.label; </del> <ins> for (const recentFileFromStorage of recentFromStorage.files) { const existingRecentFile = mapFileIdToFile.get(this.location(recentFileFromStorage)); if (existingRecentFile) { existingRecentFile.label = existingRecentFile.label ?? recentFileFromStorage.label; </ins> } else { <del> files.push(recent); </del> <ins> mapFileIdToFile.set(this.location(recentFileFromStorage), recentFileFromStorage); </ins> } } <ins> return { workspaces: [...mapWorkspaceIdToWorkspace.values()], files: [...mapFileIdToFile.values()] }; </ins> } private async getRecentlyOpenedFromStorage(): Promise<IRecentlyOpened> {

return recent.workspace.configPath; } <del> private indexOfWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): number { return recents.findIndex(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id); </del> <ins> private containsWorkspace(recents: IRecent[], candidate: IWorkspaceIdentifier): boolean { return !!recents.find(recent => isRecentWorkspace(recent) && recent.workspace.id === candidate.id); </ins> } <del> private indexOfFolder(recents: IRecent[], candidate: URI): number { return recents.findIndex(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate)); </del> <ins> private containsFolder(recents: IRecent[], candidate: URI): boolean { return !!recents.find(recent => isRecentFolder(recent) && extUriBiasedIgnorePathCase.isEqual(recent.folderUri, candidate)); </ins> } <del> private indexOfFile(recents: IRecentFile[], candidate: URI): number { return recents.findIndex(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate)); </del> <ins> private containsFile(recents: IRecentFile[], candidate: URI): boolean { return !!recents.find(recent => extUriBiasedIgnorePathCase.isEqual(recent.fileUri, candidate)); </ins> } //#endregion