@@ -5,6 +5,43 @@ import { InternalEnvironmentManager, InternalPackageManager } from '../../intern
55import { isActivatableEnvironment } from '../common/activation' ;
66import { removable } from './utils' ;
77
8+ /**
9+ * Extracts the parent folder name from an environment path for disambiguation.
10+ *
11+ * This function handles various path formats including:
12+ * - Unix paths with bin folder: /home/user/my-project/.venv/bin/python → my-project
13+ * - Windows paths with Scripts folder: C:\Users\bob\project\.venv\Scripts\python.exe → project
14+ * - Direct venv folder paths: /home/user/project/.venv → project
15+ *
16+ * @param environment The Python environment to extract the parent folder from
17+ * @returns The name of the parent folder containing the virtual environment
18+ */
19+ export function getEnvironmentParentDirName ( environment : PythonEnvironment ) : string {
20+ const envPath = environment . environmentPath . fsPath . replace ( / \\ / g, '/' ) ;
21+ const parts = envPath . split ( '/' ) . filter ( ( p ) => p . length > 0 ) ;
22+
23+ let venvFolderIndex = - 1 ;
24+
25+ for ( let i = parts . length - 1 ; i >= 0 ; i -- ) {
26+ const part = parts [ i ] . toLowerCase ( ) ;
27+ if ( part === 'bin' || part === 'scripts' ) {
28+ venvFolderIndex = i - 1 ;
29+ break ;
30+ }
31+ if ( part . startsWith ( 'python' ) ) {
32+ continue ;
33+ }
34+ venvFolderIndex = i ;
35+ break ;
36+ }
37+
38+ if ( venvFolderIndex > 0 ) {
39+ return parts [ venvFolderIndex - 1 ] ;
40+ }
41+
42+ return parts . length >= 2 ? parts [ parts . length - 2 ] : parts [ 0 ] || '' ;
43+ }
44+
845export enum EnvTreeItemKind {
946 manager = 'python-env-manager' ,
1047 environment = 'python-env' ,
@@ -45,7 +82,10 @@ export class EnvManagerTreeItem implements EnvTreeItem {
4582export class PythonGroupEnvTreeItem implements EnvTreeItem {
4683 public readonly kind = EnvTreeItemKind . environmentGroup ;
4784 public readonly treeItem : TreeItem ;
48- constructor ( public readonly parent : EnvManagerTreeItem , public readonly group : string | EnvironmentGroupInfo ) {
85+ constructor (
86+ public readonly parent : EnvManagerTreeItem ,
87+ public readonly group : string | EnvironmentGroupInfo ,
88+ ) {
4989 const label = typeof group === 'string' ? group : group . name ;
5090 const item = new TreeItem ( label , TreeItemCollapsibleState . Collapsed ) ;
5191 item . contextValue = `pythonEnvGroup;${ this . parent . manager . id } :${ label } ;` ;
@@ -62,12 +102,21 @@ export class PythonGroupEnvTreeItem implements EnvTreeItem {
62102export class PythonEnvTreeItem implements EnvTreeItem {
63103 public readonly kind = EnvTreeItemKind . environment ;
64104 public readonly treeItem : TreeItem ;
105+ /**
106+ * Creates a tree item for a Python environment.
107+ * @param environment The Python environment to display
108+ * @param parent The parent tree item (manager or group)
109+ * @param selected If set, indicates this environment is selected ('global' or workspace path)
110+ * @param disambiguationSuffix If set, shown in description to distinguish similarly-named environments
111+ */
65112 constructor (
66113 public readonly environment : PythonEnvironment ,
67114 public readonly parent : EnvManagerTreeItem | PythonGroupEnvTreeItem ,
68115 public readonly selected ?: string ,
116+ public readonly disambiguationSuffix ?: string ,
69117 ) {
70- let name = environment . displayName ?? environment . name ;
118+ const name = environment . displayName ?? environment . name ;
119+
71120 let tooltip = environment . tooltip ?? environment . description ;
72121 if ( selected ) {
73122 const selectedTooltip =
@@ -77,7 +126,18 @@ export class PythonEnvTreeItem implements EnvTreeItem {
77126
78127 const item = new TreeItem ( name , TreeItemCollapsibleState . Collapsed ) ;
79128 item . contextValue = this . getContextValue ( ) ;
80- item . description = environment . description ;
129+
130+ // Build description with optional [uv] indicator and disambiguation suffix
131+ const uvIndicator = environment . description ?. toLowerCase ( ) . includes ( 'uv' ) ? '[uv]' : '' ;
132+ const descriptionParts : string [ ] = [ ] ;
133+ if ( uvIndicator ) {
134+ descriptionParts . push ( uvIndicator ) ;
135+ }
136+ if ( disambiguationSuffix ) {
137+ descriptionParts . push ( disambiguationSuffix ) ;
138+ }
139+ item . description = descriptionParts . length > 0 ? descriptionParts . join ( ' ' ) : undefined ;
140+
81141 item . tooltip = tooltip ;
82142 item . iconPath = environment . iconPath ;
83143 this . treeItem = item ;
@@ -240,7 +300,10 @@ export class ProjectEnvironment implements ProjectTreeItem {
240300 public readonly kind = ProjectTreeItemKind . environment ;
241301 public readonly id : string ;
242302 public readonly treeItem : TreeItem ;
243- constructor ( public readonly parent : ProjectItem , public readonly environment : PythonEnvironment ) {
303+ constructor (
304+ public readonly parent : ProjectItem ,
305+ public readonly environment : PythonEnvironment ,
306+ ) {
244307 this . id = this . getId ( parent , environment ) ;
245308 const item = new TreeItem (
246309 this . environment . displayName ?? this . environment . name ,
0 commit comments