Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to the "pixi-code" extension will be documented in this file
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.1]

- Keep valid Python environments visible when a Pixi environment targets an unsupported platform
- Handle package refresh failures and use Pixi environment names for package queries
- Support relative paths and `${workspaceFolder}` in `pixi-code.pixiExecutable`
- Use the first workspace folder as the default working directory for Pixi commands

## [0.2.0]

- Use published @vscode/python-environments API package
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The extension will automatically discover Pixi environments and register them wi

## Extension Settings

- `pixi-code.pixiExecutable`: Path to the Pixi executable. Leave empty to use auto-discovery (default).
- `pixi-code.pixiExecutable`: Path to the Pixi executable. Supports `${workspaceFolder}` and paths relative to a workspace folder. Leave empty to use auto-discovery (default).

## Limitations

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Pixi support for the Python Environments extension",
"icon": "assets/icon.png",
"license": "MIT",
"version": "0.2.0",
"version": "0.2.1",
"homepage": "https://github.com/kaiserls/pixi-code",
"repository": {
"type": "git",
Expand Down Expand Up @@ -51,7 +51,7 @@
"pixi-code.pixiExecutable": {
"type": "string",
"default": "",
"description": "Path to the Pixi executable. Leave empty to use auto-discovery.",
"description": "Path to the Pixi executable. Supports ${workspaceFolder} and paths relative to a workspace folder. Leave empty to use auto-discovery.",
"scope": "machine-overridable"
}
}
Expand Down
26 changes: 20 additions & 6 deletions src/pixi/projectManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
window,
} from 'vscode';

import { traceVerbose } from '../common/logging';
import { traceInfo, traceVerbose } from '../common/logging';
import { PixiEnvironment } from './types';
import { listPixiPackages, pixiPkgsToPackages } from './utils';

Expand Down Expand Up @@ -72,13 +72,27 @@ export class PixiPackageManager implements PackageManager, Disposable {
}

const projectPath = path.dirname(environment.pixiInfo.project_info.manifest_path);
const pixiPackages = await listPixiPackages(environment.name, projectPath);
const pixiEnvironment = environment.pixiInfo.environments_info.find(
(info) => info.prefix === environment.envId.id,
);
if (!pixiEnvironment) {
traceInfo(
`Failed to refresh packages: Pixi environment '${environment.name}' is missing from project info`,
);
return;
}

const before = environment.packages;
const after = pixiPkgsToPackages(pixiPackages, environment.envId.id);
try {
const pixiPackages = await listPixiPackages(pixiEnvironment.name, projectPath);

environment.packages = after;
this.triggerOnDidChangePackages(environment, before, after);
const before = environment.packages;
const after = pixiPkgsToPackages(pixiPackages, environment.envId.id);

environment.packages = after;
this.triggerOnDidChangePackages(environment, before, after);
} catch (error) {
traceInfo(`Failed to refresh packages for environment '${environment.name}': ${error}`);
}
},
);
}
Expand Down
38 changes: 33 additions & 5 deletions src/pixi/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Package } from '@vscode/python-environments';
import * as ch from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { CancellationError, CancellationToken, ThemeIcon, Uri, window, workspace } from 'vscode';
import which from 'which';

import { createDeferred } from '../common/deferred';
import { quoteArgs } from '../common/execUtils';
import { quoteArgs, quoteStringIfNecessary } from '../common/execUtils';
import { findPythonExecutable } from '../common/findPython';
import { traceError, traceInfo, traceVerbose } from '../common/logging';
import { getWorkspacePersistentState } from '../common/persistentState';
Expand All @@ -27,7 +29,21 @@ export async function getPixi(): Promise<string> {
const value = config.get<string>('pixiExecutable');

if (value) {
return untildify(value);
let resolved = untildify(value);
if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) {
const firstFolder = workspace.workspaceFolders[0].uri.fsPath;
resolved = path.normalize(resolved.replace(/\$\{workspaceFolder\}/g, firstFolder));
if (!path.isAbsolute(resolved)) {
for (const folder of workspace.workspaceFolders) {
const candidate = path.resolve(folder.uri.fsPath, resolved);
if (fs.existsSync(candidate)) {
resolved = candidate;
break;
}
}
}
}
return resolved;
}

const pixiPath = await findPixi();
Expand All @@ -48,7 +64,7 @@ async function _runPixi(
): Promise<string> {
const deferred = createDeferred<string>();
args = quoteArgs(args);
const proc = ch.spawn(pixi, args, { shell: true, ...options });
const proc = ch.spawn(quoteStringIfNecessary(pixi), args, { shell: true, ...options });

const cancelDisposable = token?.onCancellationRequested(() => {
proc.kill();
Expand Down Expand Up @@ -87,7 +103,12 @@ async function _runPixi(

export async function runPixi(args: string[], options?: ch.SpawnOptions, token?: CancellationToken): Promise<string> {
const pixi = await getPixi();
return _runPixi(pixi, args, options, token);
const defaultCwd = workspace.workspaceFolders?.[0]?.uri.fsPath;
const spawnOptions: ch.SpawnOptions = {
...(defaultCwd ? { cwd: defaultCwd } : {}),
...options,
};
return _runPixi(pixi, args, spawnOptions, token);
}

export async function listPixiPackages(envName: string, projectPath: string): Promise<PixiPackage[]> {
Expand All @@ -113,7 +134,14 @@ export async function refreshPixi(projectPath: string): Promise<PixiEnvironment[

const results = await Promise.all(
pixiInfo.environments_info.map(async (pixiEnv) => {
const pixiPackages = await listPixiPackages(pixiEnv.name, projectPath);
let pixiPackages: PixiPackage[];
try {
pixiPackages = await listPixiPackages(pixiEnv.name, projectPath);
} catch (error) {
traceInfo(`Skipping Pixi environment '${pixiEnv.name}': ${error}`);
return null;
}

const pythonPackage = pixiPackages.find((pkg) => pkg.name === 'python');

if (!pythonPackage) {
Expand Down
Loading
Loading