Skip to content

Commit ac0c93c

Browse files
committed
feat: add Pixi extension recommendation and support for Pixi environments
1 parent 8e3904b commit ac0c93c

File tree

4 files changed

+77
-23
lines changed

4 files changed

+77
-23
lines changed

src/common/localize.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ export namespace SysManagerStrings {
136136
export const packageRefreshError = l10n.t('Error refreshing packages');
137137
}
138138

139+
export namespace PixiStrings {
140+
export const pixiExtensionRecommendation = l10n.t(
141+
'Pixi environments were detected. Install the Pixi extension for full support including activation and environment management.',
142+
);
143+
export const install = l10n.t('Install Pixi Extension');
144+
export const dontAskAgain = l10n.t("Don't Ask Again");
145+
}
146+
139147
export namespace CondaStrings {
140148
export const condaManager = l10n.t('Manages Conda environments');
141149
export const condaDiscovering = l10n.t('Discovering Conda environments');

src/common/utils/pythonPath.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,19 @@ const priorityOrder = [
1616
`${PYTHON_EXTENSION_ID}:system`,
1717
];
1818
function sortManagersByPriority(managers: InternalEnvironmentManager[]): InternalEnvironmentManager[] {
19+
const systemId = priorityOrder[priorityOrder.length - 1];
1920
return managers.sort((a, b) => {
2021
const aIndex = priorityOrder.indexOf(a.id);
2122
const bIndex = priorityOrder.indexOf(b.id);
2223
if (aIndex === -1 && bIndex === -1) {
2324
return 0;
2425
}
2526
if (aIndex === -1) {
26-
return 1;
27+
// Unknown managers should come before system (last resort) but after other known managers
28+
return b.id === systemId ? -1 : 1;
2729
}
2830
if (bIndex === -1) {
29-
return -1;
31+
return a.id === systemId ? 1 : -1;
3032
}
3133
return aIndex - bIndex;
3234
});

src/managers/builtin/utils.ts

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ import {
99
PythonEnvironmentInfo,
1010
} from '../../api';
1111
import { showErrorMessageWithLogs } from '../../common/errors/utils';
12-
import { SysManagerStrings } from '../../common/localize';
13-
import { traceVerbose } from '../../common/logging';
14-
import { withProgress } from '../../common/window.apis';
12+
import { getExtension } from '../../common/extension.apis';
13+
import { PixiStrings, SysManagerStrings } from '../../common/localize';
14+
import { traceInfo, traceVerbose } from '../../common/logging';
15+
import { getGlobalPersistentState } from '../../common/persistentState';
16+
import { showInformationMessage, withProgress } from '../../common/window.apis';
17+
import { installExtension } from '../../common/workbenchCommands';
1518
import {
1619
isNativeEnvInfo,
1720
NativeEnvInfo,
@@ -22,6 +25,10 @@ import { shortVersion, sortEnvironments } from '../common/utils';
2225
import { runPython, runUV, shouldUseUv } from './helpers';
2326
import { parsePipList, PipPackage } from './pipListUtils';
2427

28+
const PIXI_EXTENSION_ID = 'renan-r-santos.pixi-code';
29+
const PIXI_RECOMMEND_DONT_ASK_KEY = 'pixi-extension-recommend-dont-ask';
30+
let pixiRecommendationShown = false;
31+
2532
function asPackageQuickPickItem(name: string, version?: string): QuickPickItem {
2633
return {
2734
label: name,
@@ -99,6 +106,38 @@ function getPythonInfo(env: NativeEnvInfo): PythonEnvironmentInfo {
99106
}
100107
}
101108

109+
async function recommendPixiExtension(): Promise<void> {
110+
if (pixiRecommendationShown) {
111+
return;
112+
}
113+
pixiRecommendationShown = true;
114+
115+
if (getExtension(PIXI_EXTENSION_ID)) {
116+
return;
117+
}
118+
119+
const state = await getGlobalPersistentState();
120+
const dontAsk = await state.get<boolean>(PIXI_RECOMMEND_DONT_ASK_KEY);
121+
if (dontAsk) {
122+
traceInfo('Skipping Pixi extension recommendation: user selected "Don\'t ask again"');
123+
return;
124+
}
125+
126+
const result = await showInformationMessage(
127+
PixiStrings.pixiExtensionRecommendation,
128+
PixiStrings.install,
129+
PixiStrings.dontAskAgain,
130+
);
131+
132+
if (result === PixiStrings.install) {
133+
traceInfo(`Installing extension: ${PIXI_EXTENSION_ID}`);
134+
await installExtension(PIXI_EXTENSION_ID);
135+
} else if (result === PixiStrings.dontAskAgain) {
136+
await state.set(PIXI_RECOMMEND_DONT_ASK_KEY, true);
137+
traceInfo('User selected "Don\'t ask again" for Pixi extension recommendation');
138+
}
139+
}
140+
102141
export async function refreshPythons(
103142
hardRefresh: boolean,
104143
nativeFinder: NativePythonFinder,
@@ -109,24 +148,28 @@ export async function refreshPythons(
109148
): Promise<PythonEnvironment[]> {
110149
const collection: PythonEnvironment[] = [];
111150
const data = await nativeFinder.refresh(hardRefresh, uris);
112-
const envs = data
113-
.filter((e) => isNativeEnvInfo(e))
114-
.map((e) => e as NativeEnvInfo)
115-
.filter(
116-
(e) =>
117-
e.kind === undefined ||
118-
(e.kind &&
119-
[
120-
NativePythonEnvironmentKind.globalPaths,
121-
NativePythonEnvironmentKind.homebrew,
122-
NativePythonEnvironmentKind.linuxGlobal,
123-
NativePythonEnvironmentKind.macCommandLineTools,
124-
NativePythonEnvironmentKind.macPythonOrg,
125-
NativePythonEnvironmentKind.macXCode,
126-
NativePythonEnvironmentKind.windowsRegistry,
127-
NativePythonEnvironmentKind.windowsStore,
128-
].includes(e.kind)),
129-
);
151+
const allNativeEnvs = data.filter((e) => isNativeEnvInfo(e)).map((e) => e as NativeEnvInfo);
152+
153+
const hasPixiEnvs = allNativeEnvs.some((e) => e.kind === NativePythonEnvironmentKind.pixi);
154+
if (hasPixiEnvs) {
155+
recommendPixiExtension().catch((e) => log.error('Error recommending Pixi extension', e));
156+
}
157+
158+
const envs = allNativeEnvs.filter(
159+
(e) =>
160+
e.kind === undefined ||
161+
(e.kind &&
162+
[
163+
NativePythonEnvironmentKind.globalPaths,
164+
NativePythonEnvironmentKind.homebrew,
165+
NativePythonEnvironmentKind.linuxGlobal,
166+
NativePythonEnvironmentKind.macCommandLineTools,
167+
NativePythonEnvironmentKind.macPythonOrg,
168+
NativePythonEnvironmentKind.macXCode,
169+
NativePythonEnvironmentKind.windowsRegistry,
170+
NativePythonEnvironmentKind.windowsStore,
171+
].includes(e.kind)),
172+
);
130173
envs.forEach((env) => {
131174
try {
132175
const envInfo = getPythonInfo(env);

src/managers/common/nativePythonFinder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function isNativeEnvInfo(info: NativeInfo): info is NativeEnvInfo {
124124
export enum NativePythonEnvironmentKind {
125125
conda = 'Conda',
126126
homebrew = 'Homebrew',
127+
pixi = 'Pixi',
127128
pyenv = 'Pyenv',
128129
globalPaths = 'GlobalPaths',
129130
pyenvVirtualEnv = 'PyenvVirtualEnv',

0 commit comments

Comments
 (0)