@@ -7,6 +7,8 @@ import { IExtensionSingleActivationService } from '../../activation/types';
77import { Commands } from '../../common/constants' ;
88import { getSysPath } from '../../common/utils/pythonUtils' ;
99import { IInterpreterPathService } from '../../common/types' ;
10+ import { sendTelemetryEvent } from '../../telemetry' ;
11+ import { EventName } from '../../telemetry/constants' ;
1012
1113@injectable ( )
1214export class CopyImportPathCommand implements IExtensionSingleActivationService {
@@ -24,17 +26,42 @@ export class CopyImportPathCommand implements IExtensionSingleActivationService
2426 }
2527
2628 private async execute ( fileUri ?: vscode . Uri ) : Promise < void > {
27- const uri = fileUri ?? vscode . window . activeTextEditor ?. document . uri ;
28- if ( ! uri || ! uri . fsPath . endsWith ( '.py' ) ) {
29- void vscode . window . showWarningMessage ( 'No Python file selected for import-path copy.' ) ;
30- return ;
31- }
29+ const trigger = fileUri ? 'api' : vscode . window . activeTextEditor ? 'contextMenu' : 'palette' ;
30+ let outcome : 'success' | 'noFile' | 'notPy' | 'error' = 'success' ;
31+ let strategy : 'sysPath' | 'workspace' | 'fallback' | undefined = undefined ;
32+ let exObj : Error | undefined = undefined ;
3233
33- const resource : vscode . Uri | undefined = uri ?? this . workspace . workspaceFolders ?. [ 0 ] ?. uri ;
34- const pythonPath = this . interpreterPathService . get ( resource ) ;
35- const importPath = this . resolveImportPath ( uri . fsPath , pythonPath ) ;
36- await this . clipboard . writeText ( importPath ) ;
37- void vscode . window . showInformationMessage ( `Copied: ${ importPath } ` ) ;
34+ try {
35+ const uri = fileUri ?? vscode . window . activeTextEditor ?. document . uri ;
36+ if ( ! uri ) {
37+ outcome = 'noFile' ;
38+ return ;
39+ }
40+ if ( ! uri . fsPath . endsWith ( '.py' ) ) {
41+ outcome = 'notPy' ;
42+ return ;
43+ }
44+ const resource = uri ?? this . workspace . workspaceFolders ?. [ 0 ] ?. uri ;
45+ const pythonPath = this . interpreterPathService . get ( resource ) ;
46+ const [ importPath , strat ] = this . resolveImportPath ( uri . fsPath , pythonPath ) ;
47+ strategy = strat ;
48+ await this . clipboard . writeText ( importPath ) ;
49+ void vscode . window . showInformationMessage ( `Copied: ${ importPath } ` ) ;
50+ } catch ( ex ) {
51+ outcome = 'error' ;
52+ exObj = ex as Error ;
53+ } finally {
54+ sendTelemetryEvent (
55+ EventName . COPY_IMPORT_PATH ,
56+ undefined ,
57+ {
58+ trigger,
59+ outcome,
60+ strategy,
61+ } ,
62+ exObj ,
63+ ) ;
64+ }
3865 }
3966
4067 /**
@@ -46,25 +73,26 @@ export class CopyImportPathCommand implements IExtensionSingleActivationService
4673 * 2. If the file is located under the current workspace folder, the path relative to the workspace root is used.
4774 * 3. Otherwise, the import path falls back to the file name (without extension).
4875 *
49- * @param absPath - The absolute path to a `.py` file.
50- * @returns The resolved import path in dotted notation (e.g., 'pkg.module').
76+ * @param absPath Absolute path to a `.py` file.
77+ * @param pythonPath Optional Python interpreter path to determine `sys.path`.
78+ * @returns A tuple: [import path in dotted notation, resolution source: 'sysPath' | 'workspace' | 'fallback']
5179 */
52- private resolveImportPath ( absPath : string , pythonPath ?: string ) : string {
80+ private resolveImportPath ( absPath : string , pythonPath ?: string ) : [ string , 'sysPath' | 'workspace' | 'fallback' ] {
5381 // ---------- ① sys.path ----------
5482 for ( const sysRoot of getSysPath ( pythonPath ) ) {
5583 if ( sysRoot && absPath . startsWith ( sysRoot ) ) {
56- return CopyImportPathCommand . toDotted ( path . relative ( sysRoot , absPath ) ) ;
84+ return [ CopyImportPathCommand . toDotted ( path . relative ( sysRoot , absPath ) ) , 'sysPath' ] ;
5785 }
5886 }
5987
60- // ---------- ② workspaceFolder ----------
88+ // ---------- ② workspace ----------
6189 const ws = this . workspace . getWorkspaceFolder ( vscode . Uri . file ( absPath ) ) ;
6290 if ( ws && absPath . startsWith ( ws . uri . fsPath ) ) {
63- return CopyImportPathCommand . toDotted ( path . relative ( ws . uri . fsPath , absPath ) ) ;
91+ return [ CopyImportPathCommand . toDotted ( path . relative ( ws . uri . fsPath , absPath ) ) , 'workspace' ] ;
6492 }
6593
6694 // ---------- ③ fallback ----------
67- return path . basename ( absPath , '.py' ) ;
95+ return [ path . basename ( absPath , '.py' ) , 'fallback' ] ;
6896 }
6997
7098 private static toDotted ( relPath : string ) : string {
0 commit comments