forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathcopyImportPathCommand.ts
More file actions
70 lines (60 loc) · 3 KB
/
copyImportPathCommand.ts
File metadata and controls
70 lines (60 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import * as path from 'path';
import * as vscode from 'vscode';
import { inject, injectable } from 'inversify';
import { IClipboard, ICommandManager, IWorkspaceService } from '../../common/application/types';
import { IExtensionSingleActivationService } from '../../activation/types';
import { Commands } from '../../common/constants';
import { getSysPath } from '../../common/utils/pythonUtils';
@injectable()
export class CopyImportPathCommand implements IExtensionSingleActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true };
constructor(
@inject(ICommandManager) private readonly commands: ICommandManager,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
@inject(IClipboard) private readonly clipboard: IClipboard,
) {}
async activate(): Promise<void> {
this.commands.registerCommand(Commands.CopyImportPath, this.execute, this);
}
private async execute(fileUri?: vscode.Uri): Promise<void> {
const uri = fileUri ?? vscode.window.activeTextEditor?.document.uri;
if (!uri || uri.scheme !== 'file' || !uri.fsPath.endsWith('.py')) {
void vscode.window.showWarningMessage('No Python file selected for import-path copy.');
return;
}
const importPath = this.resolveImportPath(uri.fsPath);
// await vscode.env.clipboard.writeText(importPath);
await this.clipboard.writeText(importPath);
void vscode.window.showInformationMessage(`Copied: ${importPath}`);
}
/**
* Resolves a Python import-style dotted path from an absolute file path.
*
* The resolution follows a 3-level fallback strategy:
*
* 1. If the file is located under any entry in `sys.path`, the path relative to that entry is used.
* 2. If the file is located under the current workspace folder, the path relative to the workspace root is used.
* 3. Otherwise, the import path falls back to the file name (without extension).
*
* @param absPath - The absolute path to a `.py` file.
* @returns The resolved import path in dotted notation (e.g., 'pkg.module').
*/
private resolveImportPath(absPath: string): string {
// ---------- ① sys.path ----------
for (const sysRoot of getSysPath()) {
if (sysRoot && absPath.startsWith(sysRoot)) {
return CopyImportPathCommand.toDotted(path.relative(sysRoot, absPath));
}
}
// ---------- ② workspaceFolder ----------
const ws = this.workspace.getWorkspaceFolder(vscode.Uri.file(absPath));
if (ws && absPath.startsWith(ws.uri.fsPath)) {
return CopyImportPathCommand.toDotted(path.relative(ws.uri.fsPath, absPath));
}
// ---------- ③ fallback ----------
return path.basename(absPath, '.py');
}
private static toDotted(relPath: string): string {
return relPath.replace(/\.py$/i, '').split(path.sep).filter(Boolean).join('.');
}
}