Skip to content

Commit 3d4a402

Browse files
Introduce Settings Icon into Env Manager View (microsoft#1191)
fixes microsoft#1176 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent b3dee07 commit 3d4a402

File tree

6 files changed

+358
-107
lines changed

6 files changed

+358
-107
lines changed

package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
"python-envs.workspaceSearchPaths": {
125125
"type": "array",
126126
"description": "%python-envs.workspaceSearchPaths.description%",
127-
"default": [],
127+
"default": ["./**/.venv"],
128128
"scope": "resource",
129129
"items": {
130130
"type": "string"
@@ -213,6 +213,12 @@
213213
"category": "Python",
214214
"icon": "$(refresh)"
215215
},
216+
{
217+
"command": "python-envs.searchSettings",
218+
"title": "%python-envs.searchSettings.title%",
219+
"category": "Python",
220+
"icon": "$(gear)"
221+
},
216222
{
217223
"command": "python-envs.refreshPackages",
218224
"title": "%python-envs.refreshPackages.title%",
@@ -569,6 +575,11 @@
569575
"group": "navigation",
570576
"when": "view == env-managers"
571577
},
578+
{
579+
"command": "python-envs.searchSettings",
580+
"group": "navigation",
581+
"when": "view == env-managers"
582+
},
572583
{
573584
"command": "python-envs.refreshAllManagers",
574585
"group": "navigation",

package.nls.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
"python-envs.terminal.autoActivationType.shellStartup": "Activation using [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) or by modifying the terminal shell startup script. Enable `terminal.integrated.shellIntegration.enabled` or we may need to modify your shell startup scripts for the ideal experience.",
1212
"python-envs.terminal.autoActivationType.off": "No automatic activation of environments.",
1313
"python-envs.terminal.useEnvFile.description": "Controls whether environment variables from .env files and python.envFile setting are injected into terminals.",
14-
"python-envs.globalSearchPaths.description": "Global search paths for Python environments. Absolute directory paths that are searched at the user level.\n\n**Legacy Setting Support:** This setting is merged with the legacy `python.venvPath` and `python.venvFolders` settings. All paths from these three settings are combined into a single list of search paths. The legacy settings `python.venvPath` and `python.venvFolders` will be deprecated in the future, after which this setting will fully replace them. Please consider migrating your paths to this setting.",
15-
"python-envs.workspaceSearchPaths.description": "Workspace search paths for Python environments. Can be absolute paths or relative directory paths searched within the workspace.",
14+
"python-envs.globalSearchPaths.description": "Absolute paths to search for Python environments across all workspaces. Use for shared environment folders like `~/envs`.",
15+
"python-envs.workspaceSearchPaths.description": "Paths to search for environments in this workspace. By default, searches for a `.venv` folder in the workspace root.",
1616
"python-envs.terminal.revertStartupScriptChanges.title": "Revert Shell Startup Script Changes",
1717
"python-envs.reportIssue.title": "Report Issue",
1818
"python-envs.setEnvManager.title": "Set Environment Manager",
@@ -31,6 +31,7 @@
3131
"python-envs.setEnvSelected.title": "Set!",
3232
"python-envs.remove.title": "Delete Environment",
3333
"python-envs.refreshAllManagers.title": "Refresh All Environment Managers",
34+
"python-envs.searchSettings.title": "Configure Search Settings",
3435
"python-envs.refreshPackages.title": "Refresh Packages List",
3536
"python-envs.packages.title": "Manage Packages",
3637
"python-envs.clearCache.title": "Clear Cache",

src/extension.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { commands, ExtensionContext, LogOutputChannel, Terminal, Uri, window } from 'vscode';
2-
import { version as extensionVersion } from '../package.json';
1+
import { commands, ExtensionContext, extensions, LogOutputChannel, Terminal, Uri, window } from 'vscode';
32
import { PythonEnvironment, PythonEnvironmentApi, PythonProjectCreator } from './api';
3+
import { ENVS_EXTENSION_ID } from './common/constants';
44
import { ensureCorrectVersion } from './common/extVersion';
5-
import { registerLogger, traceError, traceInfo, traceVerbose, traceWarn } from './common/logging';
5+
import { registerLogger, traceError, traceInfo, traceWarn } from './common/logging';
66
import { clearPersistentState, setPersistentState } from './common/persistentState';
77
import { newProjectSelection } from './common/pickers/managers';
88
import { StopWatch } from './common/stopWatch';
@@ -67,6 +67,7 @@ import { TerminalEnvVarInjector } from './features/terminal/terminalEnvVarInject
6767
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
6868
import { registerTerminalPackageWatcher } from './features/terminal/terminalPackageWatcher';
6969
import { getEnvironmentForTerminal } from './features/terminal/utils';
70+
import { openSearchSettings } from './features/views/envManagerSearch';
7071
import { EnvManagerView } from './features/views/envManagersView';
7172
import { ProjectView } from './features/views/projectView';
7273
import { PythonStatusBarImpl } from './features/views/pythonStatusBar';
@@ -102,8 +103,10 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
102103

103104
ensureCorrectVersion();
104105

105-
// log extension version
106-
traceVerbose(`Python-envs extension version: ${extensionVersion}`);
106+
// Log extension version for diagnostics
107+
const extensionVersion = extensions.getExtension(ENVS_EXTENSION_ID)?.packageJSON?.version;
108+
traceInfo(`Python-envs extension version: ${extensionVersion ?? 'unknown'}`);
109+
107110
// log settings
108111
const configLevels = getEnvManagerAndPackageManagerConfigLevels();
109112
traceInfo(`\n=== ${configLevels.section} ===`);
@@ -182,6 +185,9 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
182185
await Promise.all(envManagers.managers.map((m) => m.refresh(undefined)));
183186
});
184187
}),
188+
commands.registerCommand('python-envs.searchSettings', async () => {
189+
await openSearchSettings();
190+
}),
185191
commands.registerCommand('python-envs.refreshPackages', async (item) => {
186192
await refreshPackagesCommand(item, envManagers);
187193
}),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { commands } from 'vscode';
2+
3+
/**
4+
* Opens environment search settings at workspace level.
5+
*/
6+
export async function openSearchSettings(): Promise<void> {
7+
await commands.executeCommand(
8+
'workbench.action.openWorkspaceSettings',
9+
'@ext:ms-python.vscode-python-envs "search path"',
10+
);
11+
}

src/managers/common/nativePythonFinder.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { PythonProjectApi } from '../../api';
88
import { spawnProcess } from '../../common/childProcess.apis';
99
import { ENVS_EXTENSION_ID, PYTHON_EXTENSION_ID } from '../../common/constants';
1010
import { getExtension } from '../../common/extension.apis';
11-
import { traceError, traceLog, traceWarn } from '../../common/logging';
11+
import { traceError, traceVerbose, traceWarn } from '../../common/logging';
1212
import { untildify, untildifyArray } from '../../common/utils/pathUtils';
1313
import { isWindows } from '../../common/utils/platformUtils';
1414
import { createRunningWorkerPool, WorkerPool } from '../../common/utils/workerPool';
@@ -671,10 +671,23 @@ function getPythonSettingAndUntildify<T>(name: string, scope?: Uri): T | undefin
671671
return value;
672672
}
673673

674+
/**
675+
* Cross-platform check for absolute paths.
676+
* Uses both current platform's check and Windows-specific check to handle
677+
* Windows paths (e.g., C:\path) when running on Unix systems.
678+
*/
679+
function isAbsolutePath(inputPath: string): boolean {
680+
return path.isAbsolute(inputPath) || path.win32.isAbsolute(inputPath);
681+
}
682+
674683
/**
675684
* Gets all extra environment search paths from various configuration sources.
676685
* Combines legacy python settings (with migration), globalSearchPaths, and workspaceSearchPaths.
677-
* @returns Array of search directory paths
686+
*
687+
* Paths can include glob patterns which are expanded by the native
688+
* Python Environment Tool (PET) during environment discovery.
689+
*
690+
* @returns Array of search paths (may include glob patterns)
678691
*/
679692
export async function getAllExtraSearchPaths(): Promise<string[]> {
680693
const searchDirectories: string[] = [];
@@ -698,7 +711,7 @@ export async function getAllExtraSearchPaths(): Promise<string[]> {
698711

699712
const trimmedPath = searchPath.trim();
700713

701-
if (path.isAbsolute(trimmedPath)) {
714+
if (isAbsolutePath(trimmedPath)) {
702715
// Absolute path - use as is
703716
searchDirectories.push(trimmedPath);
704717
} else {
@@ -710,20 +723,16 @@ export async function getAllExtraSearchPaths(): Promise<string[]> {
710723
searchDirectories.push(resolvedPath);
711724
}
712725
} else {
713-
traceWarn('Warning: No workspace folders found for relative path:', trimmedPath);
726+
traceWarn('No workspace folders found for relative search path:', trimmedPath);
714727
}
715728
}
716729
}
717730

718-
// Remove duplicates and return
731+
// Remove duplicates and normalize to forward slashes for cross-platform glob compatibility
719732
const uniquePaths = Array.from(new Set(searchDirectories));
720-
traceLog(
721-
'getAllExtraSearchPaths completed. Total unique search directories:',
722-
uniquePaths.length,
723-
'Paths:',
724-
uniquePaths,
725-
);
726-
return uniquePaths;
733+
const normalizedPaths = uniquePaths.map((p) => p.replace(/\\/g, '/'));
734+
traceVerbose('Environment search directories:', normalizedPaths.length, 'paths');
735+
return normalizedPaths;
727736
}
728737

729738
/**
@@ -745,6 +754,7 @@ function getGlobalSearchPaths(): string[] {
745754

746755
/**
747756
* Gets the most specific workspace-level setting available for workspaceSearchPaths.
757+
* Supports glob patterns which are expanded by PET.
748758
*/
749759
function getWorkspaceSearchPaths(): string[] {
750760
try {
@@ -753,11 +763,11 @@ function getWorkspaceSearchPaths(): string[] {
753763

754764
if (inspection?.globalValue) {
755765
traceError(
756-
'Error: python-envs.workspaceSearchPaths is set at the user/global level, but this setting can only be set at the workspace or workspace folder level.',
766+
'python-envs.workspaceSearchPaths is set at the user/global level, but this setting can only be set at the workspace or workspace folder level.',
757767
);
758768
}
759769

760-
// For workspace settings, prefer workspaceFolder > workspace
770+
// For workspace settings, prefer workspaceFolder > workspace > default
761771
if (inspection?.workspaceFolderValue) {
762772
return inspection.workspaceFolderValue;
763773
}
@@ -766,8 +776,8 @@ function getWorkspaceSearchPaths(): string[] {
766776
return inspection.workspaceValue;
767777
}
768778

769-
// Default empty array (don't use global value for workspace settings)
770-
return [];
779+
// Use the default value from package.json
780+
return inspection?.defaultValue ?? [];
771781
} catch (error) {
772782
traceError('Error getting workspaceSearchPaths:', error);
773783
return [];

0 commit comments

Comments
 (0)