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
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
"categories": [
"Other"
],
"capabilities": {
"untrustedWorkspaces": {
"supported": false,
"description": "This extension doesn't support untrusted workspaces."
},
"virtualWorkspaces": {
"supported": false,
"description": "This extension doesn't support virtual workspaces."
}
},
"activationEvents": [
"onLanguage:python"
],
Expand All @@ -21,9 +31,6 @@
"bugs": {
"url": "https://github.com/microsoft/vscode-python-environments/issues"
},
"extensionDependencies": [
"ms-python.python"
],
"main": "./dist/extension.js",
"icon": "icon.png",
"contributes": {
Expand Down
4 changes: 2 additions & 2 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export type DidChangeEnvironmentEventArgs = {
/**
* The URI of the environment that changed.
*/
readonly uri: Uri;
readonly uri: Uri | undefined;

/**
* The old Python environment before the change.
Expand Down Expand Up @@ -968,7 +968,7 @@ export interface PythonPackageManagementApi {
export interface PythonPackageManagerApi
extends PythonPackageManagerRegistrationApi,
PythonPackageGetterApi,
PythonEnvironmentManagerApi,
PythonPackageManagementApi,
PythonPackageItemApi {}

export interface PythonProjectCreationApi {
Expand Down
3 changes: 2 additions & 1 deletion src/common/pickers/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ export async function pickEnvironment(
return {
label: e.displayName ?? e.name,
description: e.description,
e: { selected: e, manager: manager },
result: e,
manager: manager,
iconPath: getIconPath(e.iconPath),
};
}),
Expand Down
37 changes: 25 additions & 12 deletions src/common/pickers/packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,25 +170,30 @@ function getGroupedItems(items: Installable[]): PackageQuickPickItem[] {
return result;
}

async function getInstallables(packageManager: InternalPackageManager, environment: PythonEnvironment) {
const installable = await packageManager?.getInstallable(environment);
if (installable && installable.length === 0) {
traceWarn(`No installable packages found for ${packageManager.id}: ${environment.environmentPath.fsPath}`);
}
return installable;
}

async function getWorkspacePackages(
packageManager: InternalPackageManager,
environment: PythonEnvironment,
installable: Installable[] | undefined,
preSelected?: PackageQuickPickItem[] | undefined,
): Promise<string[] | undefined> {
const items: PackageQuickPickItem[] = [];

let installable = await packageManager?.getInstallable(environment);
if (installable && installable.length > 0) {
items.push(...getGroupedItems(installable));
} else {
traceWarn(`No installable packages found for ${packageManager.id}: ${environment.environmentPath.fsPath}`);
installable = await getCommonPackages();
const common = await getCommonPackages();
items.push(
{
label: PackageManagement.commonPackages,
kind: QuickPickItemKind.Separator,
},
...installable.map(installableToQuickPickItem),
...common.map(installableToQuickPickItem),
);
}

Expand Down Expand Up @@ -240,7 +245,7 @@ async function getWorkspacePackages(
return result;
} catch (ex) {
if (ex === QuickInputButtons.Back) {
return getWorkspacePackages(packageManager, environment, selected);
return getWorkspacePackages(installable, selected);
}
return undefined;
}
Expand All @@ -250,7 +255,7 @@ async function getWorkspacePackages(
}
}

export async function getCommonPackagesToInstall(
async function getCommonPackagesToInstall(
preSelected?: PackageQuickPickItem[] | undefined,
): Promise<string[] | undefined> {
const common = await getCommonPackages();
Expand Down Expand Up @@ -315,7 +320,7 @@ export async function getCommonPackagesToInstall(
}
}

export async function getPackagesToInstall(
export async function getPackagesToInstallFromPackageManager(
packageManager: InternalPackageManager,
environment: PythonEnvironment,
): Promise<string[] | undefined> {
Expand All @@ -325,11 +330,12 @@ export async function getPackagesToInstall(

if (packageType === PackageManagement.workspaceDependencies) {
try {
const result = await getWorkspacePackages(packageManager, environment);
const installable = await getInstallables(packageManager, environment);
const result = await getWorkspacePackages(installable);
return result;
} catch (ex) {
if (packageManager.supportsGetInstallable && ex === QuickInputButtons.Back) {
return getPackagesToInstall(packageManager, environment);
return getPackagesToInstallFromPackageManager(packageManager, environment);
}
if (ex === QuickInputButtons.Back) {
throw ex;
Expand All @@ -344,7 +350,7 @@ export async function getPackagesToInstall(
return result;
} catch (ex) {
if (packageManager.supportsGetInstallable && ex === QuickInputButtons.Back) {
return getPackagesToInstall(packageManager, environment);
return getPackagesToInstallFromPackageManager(packageManager, environment);
}
if (ex === QuickInputButtons.Back) {
throw ex;
Expand All @@ -356,6 +362,13 @@ export async function getPackagesToInstall(
return undefined;
}

export async function getPackagesToInstallFromInstallable(installable: Installable[]): Promise<string[] | undefined> {
if (installable.length === 0) {
return undefined;
}
return getWorkspacePackages(installable);
}

export async function getPackagesToUninstall(packages: Package[]): Promise<Package[] | undefined> {
const items = packages.map((p) => ({
label: p.name,
Expand Down
8 changes: 4 additions & 4 deletions src/common/utils/pythonPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export async function handlePythonPath(
reporter?: Progress<{ message?: string; increment?: number }>,
token?: CancellationToken,
): Promise<PythonEnvironment | undefined> {
// Use the managers user has set for the project first. Likely, these
// managers are the ones that should be used.
for (const manager of sortManagersByPriority(projectEnvManagers)) {
if (token?.isCancellationRequested) {
return;
Expand All @@ -54,6 +56,8 @@ export async function handlePythonPath(
traceVerbose(`Manager ${manager.displayName} (${manager.id}) cannot handle ${interpreterUri.fsPath}`);
}

// If the project managers cannot handle the interpreter, then try all the managers
// that user has installed. Excluding anything that is already checked.
const checkedIds = projectEnvManagers.map((m) => m.id);
const filtered = managers.filter((m) => !checkedIds.includes(m.id));

Expand All @@ -70,10 +74,6 @@ export async function handlePythonPath(
}
}

if (token?.isCancellationRequested) {
return;
}

traceError(`Unable to handle ${interpreterUri.fsPath}`);
showErrorMessage(`Unable to handle ${interpreterUri.fsPath}`);
return undefined;
Expand Down
10 changes: 10 additions & 0 deletions src/common/window.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
ExtensionTerminalOptions,
InputBox,
InputBoxOptions,
LogOutputChannel,
OpenDialogOptions,
OutputChannel,
Progress,
ProgressOptions,
QuickInputButton,
Expand Down Expand Up @@ -279,3 +281,11 @@ export function showWarningMessage(message: string, ...items: string[]): Thenabl
export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable<string | undefined> {
return window.showInputBox(options, token);
}

export function createOutputChannel(name: string, languageId?: string): OutputChannel {
return window.createOutputChannel(name, languageId);
}

export function createLogOutputChannel(name: string): LogOutputChannel {
return window.createOutputChannel(name, { log: true });
}
19 changes: 19 additions & 0 deletions src/common/workspace.apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
ConfigurationChangeEvent,
ConfigurationScope,
Disposable,
FileDeleteEvent,
FileSystemWatcher,
GlobPattern,
Uri,
workspace,
Expand Down Expand Up @@ -39,3 +41,20 @@ export function findFiles(
): Thenable<Uri[]> {
return workspace.findFiles(include, exclude, maxResults, token);
}

export function createFileSystemWatcher(
globPattern: GlobPattern,
ignoreCreateEvents?: boolean,
ignoreChangeEvents?: boolean,
ignoreDeleteEvents?: boolean,
): FileSystemWatcher {
return workspace.createFileSystemWatcher(globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}

export function onDidDeleteFiles(
listener: (e: FileDeleteEvent) => any,
thisArgs?: any,
disposables?: Disposable[],
): Disposable {
return workspace.onDidDeleteFiles(listener, thisArgs, disposables);
}
73 changes: 23 additions & 50 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { window, commands, ExtensionContext, LogOutputChannel, TextEditor } from 'vscode';
import { commands, ExtensionContext, LogOutputChannel } from 'vscode';

import { PythonEnvironmentManagers } from './features/envManagers';
import { registerLogger } from './common/logging';
Expand Down Expand Up @@ -28,9 +28,8 @@ import { PythonProjectManagerImpl } from './features/projectManager';
import { EnvironmentManagers, ProjectCreators, PythonProjectManager } from './internal.api';
import { getPythonApi, setPythonApi } from './features/pythonApi';
import { setPersistentState } from './common/persistentState';
import { isPythonProjectFile } from './common/utils/fileNameUtils';
import { createNativePythonFinder, NativePythonFinder } from './managers/common/nativePythonFinder';
import { PythonEnvironmentApi, PythonProject } from './api';
import { PythonEnvironmentApi } from './api';
import {
ProjectCreatorsImpl,
registerAutoProjectProvider,
Expand All @@ -39,21 +38,31 @@ import {
import { WorkspaceView } from './features/views/projectView';
import { registerCompletionProvider } from './features/settings/settingCompletions';
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
import { activeTerminal, onDidChangeActiveTerminal, onDidChangeActiveTextEditor } from './common/window.apis';
import {
activeTerminal,
createLogOutputChannel,
onDidChangeActiveTerminal,
onDidChangeActiveTextEditor,
} from './common/window.apis';
import {
getEnvironmentForTerminal,
setActivateMenuButtonContext,
updateActivateMenuButtonContext,
} from './features/terminal/activateMenuButton';
import { PythonStatusBarImpl } from './features/views/pythonStatusBar';
import { updateViewsAndStatus } from './features/views/revealHandler';

export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi> {
// Logging should be set up before anything else.
const outputChannel: LogOutputChannel = window.createOutputChannel('Python Environments', { log: true });
const outputChannel: LogOutputChannel = createLogOutputChannel('Python Environments');
context.subscriptions.push(outputChannel, registerLogger(outputChannel));

// Setup the persistent state for the extension.
setPersistentState(context);

const statusBar = new PythonStatusBarImpl();
context.subscriptions.push(statusBar);

const terminalManager: TerminalManager = new TerminalManagerImpl();
context.subscriptions.push(terminalManager);

Expand Down Expand Up @@ -113,26 +122,14 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
commands.registerCommand('python-envs.set', async (item) => {
const result = await setEnvironmentCommand(item, envManagers, projectManager);
if (result) {
const projects: PythonProject[] = [];
result.forEach((r) => {
if (r.project) {
projects.push(r.project);
}
});
workspaceView.updateProject(projects);
workspaceView.updateProject();
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
}
}),
commands.registerCommand('python-envs.setEnv', async (item) => {
const result = await setEnvironmentCommand(item, envManagers, projectManager);
if (result) {
const projects: PythonProject[] = [];
result.forEach((r) => {
if (r.project) {
projects.push(r.project);
}
});
workspaceView.updateProject(projects);
workspaceView.updateProject();
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers);
}
}),
Expand Down Expand Up @@ -193,38 +190,14 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
onDidChangeActiveTerminal(async (t) => {
await updateActivateMenuButtonContext(terminalManager, projectManager, envManagers, t);
}),
onDidChangeActiveTextEditor(async (e: TextEditor | undefined) => {
if (e && !e.document.isUntitled && e.document.uri.scheme === 'file') {
if (
e.document.languageId === 'python' ||
e.document.languageId === 'pip-requirements' ||
isPythonProjectFile(e.document.uri.fsPath)
) {
const env = await workspaceView.reveal(e.document.uri);
await managerView.reveal(env);
}
}
onDidChangeActiveTextEditor(async () => {
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
}),
envManagers.onDidChangeEnvironment(async (e) => {
const activeDocument = window.activeTextEditor?.document;
if (!activeDocument || activeDocument.isUntitled || activeDocument.uri.scheme !== 'file') {
return;
}

if (
activeDocument.languageId !== 'python' &&
activeDocument.languageId !== 'pip-requirements' &&
!isPythonProjectFile(activeDocument.uri.fsPath)
) {
return;
}

const mgr1 = envManagers.getEnvironmentManager(e.uri);
const mgr2 = envManagers.getEnvironmentManager(activeDocument.uri);
if (mgr1 === mgr2 && e.new) {
const env = await workspaceView.reveal(activeDocument.uri);
await managerView.reveal(env);
}
envManagers.onDidChangeEnvironment(async () => {
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
}),
envManagers.onDidChangeEnvironments(async () => {
updateViewsAndStatus(statusBar, workspaceView, managerView, api);
}),
);

Expand Down
Loading