Skip to content

Commit

Permalink
Improve startup performance & status (#6210)
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
jmcphers authored Feb 7, 2025
1 parent 3a0e452 commit 55f7331
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 73 deletions.
3 changes: 3 additions & 0 deletions extensions/positron-r/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,9 @@
"which": "^3.0.0",
"xdg-portable": "^10.6.0"
},
"extensionDependencies": [
"positron.positron-supervisor"
],
"peerDependencies": {
"@vscode/windows-registry": "^1.0.0"
},
Expand Down
3 changes: 3 additions & 0 deletions extensions/python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
"scripts": {
"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"
},
"extensionDependencies": [
"positron.positron-supervisor"
],
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

.runtime-starting {
display: flex;
flex-direction: column;
}

.runtime-starting .action,
.runtime-starting .runtime-name {
margin: 2px;
text-transform: uppercase;
text-align: center;
}

.runtime-starting .action {
font-size: 11px;
}

.runtime-starting .runtime-name {
font-weight: bold;
}

.runtime-starting-icon {
height: 50px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

// CSS.
import './runtimeStartupProgress.css';

// React.
import React from 'react';

// Other dependencies.
import { localize } from '../../../../../nls.js';
import { IRuntimeAutoStartEvent } from '../../../../services/runtimeStartup/common/runtimeStartupService.js';

// RuntimeStartupProgressProps interface.
export interface RuntimeStartupProgressProps {
evt: IRuntimeAutoStartEvent;
}

const preparing = localize('positron.runtimeStartup.newSession', "Preparing");
const reconnecting = localize('positron.runtimeStartup.existingSession', "Reconnecting");

/**
* RuntimeStartupProgress component.
*
* This component renders the status for a runtime that is about to start up.
* It's only rendered before any runtime actually starts in new Positron
* windows.
*
* @param props A RuntimeStartupProgressProps that contains the component
* properties.
* @returns The rendered component.
*/
export const RuntimeStartupProgress = (props: RuntimeStartupProgressProps) => {
// Render.
return (
<div className='runtime-starting'>
<img className='runtime-starting-icon' src={`data:image/svg+xml;base64,${props.evt.runtime.base64EncodedIconSvg}`} />
<div className='runtime-name'>{props.evt.runtime.runtimeName}</div>
<div className='action'>{props.evt.newSession ? preparing : reconnecting}</div>
</div>
);
};

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { usePositronConsoleContext } from '../positronConsoleContext.js';
import { ProgressBar } from '../../../../../base/browser/ui/progressbar/progressbar.js';
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
import { RuntimeStartupPhase } from '../../../../services/languageRuntime/common/languageRuntimeService.js';
import { IRuntimeAutoStartEvent } from '../../../../services/runtimeStartup/common/runtimeStartupService.js';
import { RuntimeStartupProgress } from './runtimeStartupProgress.js';

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

useEffect(() => {
const disposableStore = new DisposableStore();
Expand Down Expand Up @@ -70,6 +74,15 @@ export const StartupStatus = () => {
setStartupPhase(phase);
}));

// When we're notified that a runtime may auto-start in the workspace,
// show it. Note that this event is not reliable as a signal that a
// runtime will actually start; see notes in the RuntimeStartupService.
disposableStore.add(
positronConsoleContext.runtimeStartupService.onWillAutoStartRuntime(
evt => {
setRuntimeStartupEvent(evt);
}));

// Return the cleanup function that will dispose of the disposables.
return () => {
bar?.done();
Expand All @@ -81,19 +94,22 @@ export const StartupStatus = () => {
return (
<div className='startup-status'>
<div className='progress' ref={progressRef}></div>
{runtimeStartupEvent &&
<RuntimeStartupProgress evt={runtimeStartupEvent} />
}
{startupPhase === RuntimeStartupPhase.Initializing &&
<div className='initializing'>{initalizing}...</div>
}
{startupPhase === RuntimeStartupPhase.Reconnecting &&
<div className='initializing'>{reconnecting}...</div>
{startupPhase === RuntimeStartupPhase.Reconnecting && !runtimeStartupEvent &&
<div className='reconnecting'>{reconnecting}...</div>
}
{startupPhase === RuntimeStartupPhase.AwaitingTrust &&
<div className='awaiting'>{awaitingTrust}...</div>
}
{startupPhase === RuntimeStartupPhase.Starting &&
{startupPhase === RuntimeStartupPhase.Starting && !runtimeStartupEvent &&
<div className='starting'>{starting}...</div>
}
{startupPhase === RuntimeStartupPhase.Discovering &&
{startupPhase === RuntimeStartupPhase.Discovering && !runtimeStartupEvent &&
<div className='discovery'>{discoveringIntrepreters}
{discovered > 0 && <span> ({discovered})</span>}...</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1015,18 +1015,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
return this._installedExtensionsReady.wait();
}

// --- Start Positron ---
/**
* Wait for all extension hosts to start, and for all eagerly activated
* extensions to activate.
*
* @returns A promise that resolves when all extension hosts have started.
*/
public whenAllExtensionHostsStarted(): Promise<boolean> {
return this._allExtensionHostsStarted.wait();
}
// --- End Positron ---

get extensions(): IExtensionDescription[] {
return this._registry.getAllExtensionDescriptions();
}
Expand Down
11 changes: 0 additions & 11 deletions src/vs/workbench/services/extensions/common/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,14 +480,6 @@ export interface IExtensionService {
*/
whenInstalledExtensionsRegistered(): Promise<boolean>;

// --- Start Positron ---
/**
* An promise that resolves when all the extension hosts have started and
* eager extensions have been activated.
*/
whenAllExtensionHostsStarted(): Promise<boolean>;
// --- End Positron ---

/**
* Return a specific extension
* @param id An extension id
Expand Down Expand Up @@ -603,9 +595,6 @@ export class NullExtensionService implements IExtensionService {
activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(undefined); }
activationEventIsDone(_activationEvent: string): boolean { return false; }
whenInstalledExtensionsRegistered(): Promise<boolean> { return Promise.resolve(true); }
// --- Start Positron ---
whenAllExtensionHostsStarted(): Promise<boolean> { return Promise.resolve(true); }
// --- End Positron ---
getExtension() { return Promise.resolve(undefined); }
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
getExtensionsStatus(): { [id: string]: IExtensionsStatus } { return Object.create(null); }
Expand Down
Loading

0 comments on commit 55f7331

Please sign in to comment.