Skip to content

Commit 55f7331

Browse files
authored
Improve startup performance & status (#6210)
This change improves the startup experience in the Positron Console and also contains performance improvements that significantly decrease startup time (especially in dev builds) Before the change, the console would say "Starting..." / "Starting up..." for several seconds before a console opened. https://github.com/user-attachments/assets/64c998c9-c855-4b9c-a58e-70823353aaa2 After it, in most Positron sessions no noticeable time will be spent in either state; instead, Positron will show you the interpreter it's working on starting. https://github.com/user-attachments/assets/e68f12e1-4df0-41b4-bb66-15ca24c76e50 Still more we could do to make this better, such as improving the styling and coordinating better with console startups, but want to minimize overlap with multi-console work. Addresses #3566. ### Release Notes #### New Features - Speed up console startup and show which interpreter will start (#3566) #### Bug Fixes - N/A ### QA Notes You will see the new experience when Positron knows what runtime to start, but mostly the old one when Positron has to discover all interpreters (though even discovery will be a little faster now since it starts earlier). Specifically, you should see it: - when a runtime is affiliated with the workspace - when R or Python recommend a runtime for the workspace (currently NYI but will be implemented in those language packs in e.g. #6208. - when reloading a window Test tags: `@:console`
1 parent 3a0e452 commit 55f7331

File tree

9 files changed

+221
-73
lines changed

9 files changed

+221
-73
lines changed

extensions/positron-r/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@
671671
"which": "^3.0.0",
672672
"xdg-portable": "^10.6.0"
673673
},
674+
"extensionDependencies": [
675+
"positron.positron-supervisor"
676+
],
674677
"peerDependencies": {
675678
"@vscode/windows-registry": "^1.0.0"
676679
},

extensions/python/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
"scripts": {
5858
"update-grammar": "node ../node_modules/vscode-grammar-updater/bin MagicStack/MagicPython grammars/MagicPython.tmLanguage ./syntaxes/MagicPython.tmLanguage.json grammars/MagicRegExp.tmLanguage ./syntaxes/MagicRegExp.tmLanguage.json"
5959
},
60+
"extensionDependencies": [
61+
"positron.positron-supervisor"
62+
],
6063
"repository": {
6164
"type": "git",
6265
"url": "https://github.com/microsoft/vscode.git"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
.runtime-starting {
7+
display: flex;
8+
flex-direction: column;
9+
}
10+
11+
.runtime-starting .action,
12+
.runtime-starting .runtime-name {
13+
margin: 2px;
14+
text-transform: uppercase;
15+
text-align: center;
16+
}
17+
18+
.runtime-starting .action {
19+
font-size: 11px;
20+
}
21+
22+
.runtime-starting .runtime-name {
23+
font-weight: bold;
24+
}
25+
26+
.runtime-starting-icon {
27+
height: 50px;
28+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
// CSS.
7+
import './runtimeStartupProgress.css';
8+
9+
// React.
10+
import React from 'react';
11+
12+
// Other dependencies.
13+
import { localize } from '../../../../../nls.js';
14+
import { IRuntimeAutoStartEvent } from '../../../../services/runtimeStartup/common/runtimeStartupService.js';
15+
16+
// RuntimeStartupProgressProps interface.
17+
export interface RuntimeStartupProgressProps {
18+
evt: IRuntimeAutoStartEvent;
19+
}
20+
21+
const preparing = localize('positron.runtimeStartup.newSession', "Preparing");
22+
const reconnecting = localize('positron.runtimeStartup.existingSession', "Reconnecting");
23+
24+
/**
25+
* RuntimeStartupProgress component.
26+
*
27+
* This component renders the status for a runtime that is about to start up.
28+
* It's only rendered before any runtime actually starts in new Positron
29+
* windows.
30+
*
31+
* @param props A RuntimeStartupProgressProps that contains the component
32+
* properties.
33+
* @returns The rendered component.
34+
*/
35+
export const RuntimeStartupProgress = (props: RuntimeStartupProgressProps) => {
36+
// Render.
37+
return (
38+
<div className='runtime-starting'>
39+
<img className='runtime-starting-icon' src={`data:image/svg+xml;base64,${props.evt.runtime.base64EncodedIconSvg}`} />
40+
<div className='runtime-name'>{props.evt.runtime.runtimeName}</div>
41+
<div className='action'>{props.evt.newSession ? preparing : reconnecting}</div>
42+
</div>
43+
);
44+
};
45+

src/vs/workbench/contrib/positronConsole/browser/components/startupStatus.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { usePositronConsoleContext } from '../positronConsoleContext.js';
1515
import { ProgressBar } from '../../../../../base/browser/ui/progressbar/progressbar.js';
1616
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
1717
import { RuntimeStartupPhase } from '../../../../services/languageRuntime/common/languageRuntimeService.js';
18+
import { IRuntimeAutoStartEvent } from '../../../../services/runtimeStartup/common/runtimeStartupService.js';
19+
import { RuntimeStartupProgress } from './runtimeStartupProgress.js';
1820

1921
// Load localized copy for control.
2022
const initalizing = localize('positron.console.initializing', "Starting up");
@@ -42,6 +44,8 @@ export const StartupStatus = () => {
4244
useState(positronConsoleContext.languageRuntimeService.registeredRuntimes.length);
4345
const [startupPhase, setStartupPhase] =
4446
useState(positronConsoleContext.languageRuntimeService.startupPhase);
47+
const [runtimeStartupEvent, setRuntimeStartupEvent] =
48+
useState<IRuntimeAutoStartEvent | undefined>(undefined);
4549

4650
useEffect(() => {
4751
const disposableStore = new DisposableStore();
@@ -70,6 +74,15 @@ export const StartupStatus = () => {
7074
setStartupPhase(phase);
7175
}));
7276

77+
// When we're notified that a runtime may auto-start in the workspace,
78+
// show it. Note that this event is not reliable as a signal that a
79+
// runtime will actually start; see notes in the RuntimeStartupService.
80+
disposableStore.add(
81+
positronConsoleContext.runtimeStartupService.onWillAutoStartRuntime(
82+
evt => {
83+
setRuntimeStartupEvent(evt);
84+
}));
85+
7386
// Return the cleanup function that will dispose of the disposables.
7487
return () => {
7588
bar?.done();
@@ -81,19 +94,22 @@ export const StartupStatus = () => {
8194
return (
8295
<div className='startup-status'>
8396
<div className='progress' ref={progressRef}></div>
97+
{runtimeStartupEvent &&
98+
<RuntimeStartupProgress evt={runtimeStartupEvent} />
99+
}
84100
{startupPhase === RuntimeStartupPhase.Initializing &&
85101
<div className='initializing'>{initalizing}...</div>
86102
}
87-
{startupPhase === RuntimeStartupPhase.Reconnecting &&
88-
<div className='initializing'>{reconnecting}...</div>
103+
{startupPhase === RuntimeStartupPhase.Reconnecting && !runtimeStartupEvent &&
104+
<div className='reconnecting'>{reconnecting}...</div>
89105
}
90106
{startupPhase === RuntimeStartupPhase.AwaitingTrust &&
91107
<div className='awaiting'>{awaitingTrust}...</div>
92108
}
93-
{startupPhase === RuntimeStartupPhase.Starting &&
109+
{startupPhase === RuntimeStartupPhase.Starting && !runtimeStartupEvent &&
94110
<div className='starting'>{starting}...</div>
95111
}
96-
{startupPhase === RuntimeStartupPhase.Discovering &&
112+
{startupPhase === RuntimeStartupPhase.Discovering && !runtimeStartupEvent &&
97113
<div className='discovery'>{discoveringIntrepreters}
98114
{discovered > 0 && <span> ({discovered})</span>}...</div>
99115
}

src/vs/workbench/services/extensions/common/abstractExtensionService.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,18 +1015,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
10151015
return this._installedExtensionsReady.wait();
10161016
}
10171017

1018-
// --- Start Positron ---
1019-
/**
1020-
* Wait for all extension hosts to start, and for all eagerly activated
1021-
* extensions to activate.
1022-
*
1023-
* @returns A promise that resolves when all extension hosts have started.
1024-
*/
1025-
public whenAllExtensionHostsStarted(): Promise<boolean> {
1026-
return this._allExtensionHostsStarted.wait();
1027-
}
1028-
// --- End Positron ---
1029-
10301018
get extensions(): IExtensionDescription[] {
10311019
return this._registry.getAllExtensionDescriptions();
10321020
}

src/vs/workbench/services/extensions/common/extensions.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -480,14 +480,6 @@ export interface IExtensionService {
480480
*/
481481
whenInstalledExtensionsRegistered(): Promise<boolean>;
482482

483-
// --- Start Positron ---
484-
/**
485-
* An promise that resolves when all the extension hosts have started and
486-
* eager extensions have been activated.
487-
*/
488-
whenAllExtensionHostsStarted(): Promise<boolean>;
489-
// --- End Positron ---
490-
491483
/**
492484
* Return a specific extension
493485
* @param id An extension id
@@ -603,9 +595,6 @@ export class NullExtensionService implements IExtensionService {
603595
activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(undefined); }
604596
activationEventIsDone(_activationEvent: string): boolean { return false; }
605597
whenInstalledExtensionsRegistered(): Promise<boolean> { return Promise.resolve(true); }
606-
// --- Start Positron ---
607-
whenAllExtensionHostsStarted(): Promise<boolean> { return Promise.resolve(true); }
608-
// --- End Positron ---
609598
getExtension() { return Promise.resolve(undefined); }
610599
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
611600
getExtensionsStatus(): { [id: string]: IExtensionsStatus } { return Object.create(null); }

0 commit comments

Comments
 (0)