From 70a7911661293a6095d4b518566f4277b0c625b1 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Fri, 9 Oct 2026 14:16:01 -0600 Subject: [PATCH] Keep a remembered R following your default R after interpreter discovery --- .../runtimeStartup/common/runtimeStartup.ts | 11 +++++- .../test/common/runtimeStartup.vitest.ts | 37 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts b/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts index 69be4aee20de..d8a96ae42d98 100644 --- a/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts +++ b/src/vs/workbench/services/runtimeStartup/common/runtimeStartup.ts @@ -1616,8 +1616,15 @@ export class RuntimeStartupService extends Disposable implements IRuntimeStartup if (metadata.runtimeId === affiliatedRuntimeId) { // Heal any stale fields in the stored affiliation (e.g. a runtimePath // that was ~-shortened before the runtimePath/runtimeDisplayPath split) - // by overwriting with the freshly-discovered metadata. - this.saveAffiliatedRuntime({ ...affiliated, metadata }); + // by overwriting with the freshly-discovered metadata. Keep the stored + // extraRuntimeData: the extension reads it when validating the + // affiliation, and it records facts from when the runtime was chosen + // (e.g. R's `current` flag, which makes the affiliation follow the + // current R instead of this binary). + this.saveAffiliatedRuntime({ + ...affiliated, + metadata: { ...metadata, extraRuntimeData: affiliated.metadata.extraRuntimeData }, + }); try { diff --git a/src/vs/workbench/services/runtimeStartup/test/common/runtimeStartup.vitest.ts b/src/vs/workbench/services/runtimeStartup/test/common/runtimeStartup.vitest.ts index a4b58c26d29d..bd6536234afa 100644 --- a/src/vs/workbench/services/runtimeStartup/test/common/runtimeStartup.vitest.ts +++ b/src/vs/workbench/services/runtimeStartup/test/common/runtimeStartup.vitest.ts @@ -1238,6 +1238,43 @@ describe('RuntimeStartupService - affiliation healing', () => { // getAffiliatedRuntimes() returns the live registered metadata (absolute path). expect(svc.getAffiliatedRuntimes()[0].runtimePath).toBe(freshRuntimePath); }); + + it('keeps a stored affiliation that follows the current R when an older R re-registers', async () => { + // R runtime IDs hash only the binary path and version, so the same + // runtime ID comes back after `rig default` points at a newer R. The + // R extension's validateMetadata only swaps in the current R when the + // stored affiliation says `current: true`. + const runtimeId = 'r-4.5-current-flag-test'; + const languageId = 'r'; + const runtimePath = '/Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/bin/R'; + const storageKey = `positron.affiliatedRuntimeMetadata.v2.${languageId}`; + const scope = StorageScope.WORKSPACE; + + // Affiliated while R 4.5 was the current R. + const affiliated = { + metadata: { + ...metadata({ languageId, runtimePath, runtimeId, extensionId: 'ms.r' }), + extraRuntimeData: { binpath: runtimePath, current: true }, + }, + lastUsed: 0, + lastStarted: 0, + }; + ctx.get(IStorageService).store(storageKey, JSON.stringify(affiliated), scope, StorageTarget.MACHINE); + + const svc = ctx.disposables.add( + ctx.instantiationService.createInstance(RuntimeStartupService)) as RuntimeStartupService; + (svc as unknown as { _startupPhase: RuntimeStartupPhase })._startupPhase = RuntimeStartupPhase.LoadingCache; + + // Re-discovered after a newer R became current. + await ctx.get(ILanguageRuntimeService).registerRuntime({ + ...metadata({ languageId, runtimePath, runtimeId, extensionId: 'ms.r' }), + startupBehavior: LanguageRuntimeStartupBehavior.Manual, + extraRuntimeData: { binpath: runtimePath, current: false }, + }); + + const stored = JSON.parse(ctx.get(IStorageService).get(storageKey, scope)!); + expect(stored.metadata.extraRuntimeData.current).toBe(true); + }); }); describe('RuntimeStartupService - restored sessions', () => {