forked from microsoft/vscode-python-environments
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnewScriptProject.ts
More file actions
118 lines (109 loc) · 5.8 KB
/
newScriptProject.ts
File metadata and controls
118 lines (109 loc) · 5.8 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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import * as fs from 'fs-extra';
import * as path from 'path';
import { commands, l10n, MarkdownString, QuickInputButtons, Uri, window, workspace } from 'vscode';
import { PythonProject, PythonProjectCreator, PythonProjectCreatorOptions } from '../../api';
import { NEW_PROJECT_TEMPLATES_FOLDER } from '../../common/constants';
import { showInputBoxWithButtons } from '../../common/window.apis';
import { isCopilotInstalled, manageCopilotInstructionsFile, replaceInFilesAndNames } from './creationHelpers';
export class NewScriptProject implements PythonProjectCreator {
public readonly name = l10n.t('newScript');
public readonly displayName = l10n.t('Script');
public readonly description = l10n.t('Creates a new script folder in your current workspace with PEP 723 support');
public readonly tooltip = new MarkdownString(l10n.t('Create a new Python script'));
constructor() {}
async create(options?: PythonProjectCreatorOptions): Promise<PythonProject | Uri | undefined> {
// quick create (needs name, will always create venv and copilot instructions)
// not quick create
// ask for script file name
// ask if they want venv
let scriptFileName = options?.name;
let createCopilotInstructions: boolean | undefined;
if (options?.quickCreate === true) {
// If quickCreate is true, we should not prompt for any input
if (!scriptFileName) {
throw new Error('Script file name is required in quickCreate mode.');
}
createCopilotInstructions = true;
} else {
//Prompt as quickCreate is false
if (!scriptFileName) {
try {
scriptFileName = await showInputBoxWithButtons({
prompt: l10n.t('What is the name of the script? (e.g. my_script.py)'),
ignoreFocusOut: true,
showBackButton: true,
validateInput: (value) => {
// Ensure the filename ends with .py and follows valid naming conventions
if (!value.endsWith('.py')) {
return l10n.t('Script name must end with ".py".');
}
const baseName = value.replace(/\.py$/, '');
// following PyPI (PEP 508) rules for package names
if (!/^([a-z_]|[a-z0-9_][a-z0-9._-]*[a-z0-9_])$/i.test(baseName)) {
return l10n.t(
'Invalid script name. Use only letters, numbers, underscores, hyphens, or periods. Must start and end with a letter or number.',
);
}
if (/^[-._0-9]$/i.test(baseName)) {
return l10n.t('Single-character script names cannot be a number, hyphen, or period.');
}
return null;
},
});
} catch (ex) {
if (ex === QuickInputButtons.Back) {
await commands.executeCommand('python-envs.createNewProjectFromTemplate');
}
}
if (!scriptFileName) {
return undefined;
}
if (isCopilotInstalled()) {
createCopilotInstructions = true;
}
}
// 1. Copy template folder
const newScriptTemplateFile = path.join(NEW_PROJECT_TEMPLATES_FOLDER, 'new723ScriptTemplate', 'script.py');
if (!(await fs.pathExists(newScriptTemplateFile))) {
window.showErrorMessage(l10n.t('Template file does not exist, aborting creation.'));
return undefined;
}
// Check if the destination folder is provided, otherwise use the first workspace folder
let destRoot = options?.rootUri.fsPath;
if (!destRoot) {
const workspaceFolders = workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
window.showErrorMessage(l10n.t('No workspace folder is open or provided, aborting creation.'));
return undefined;
}
destRoot = workspaceFolders[0].uri.fsPath;
}
// Check if the destination folder already exists
const scriptDestination = path.join(destRoot, scriptFileName);
if (await fs.pathExists(scriptDestination)) {
window.showErrorMessage(
l10n.t(
'A script file by that name already exists, aborting creation. Please retry with a unique script name given your workspace.',
),
);
return undefined;
}
await fs.copy(newScriptTemplateFile, scriptDestination);
// 2. Replace 'script_name' in the file using a helper (just script name remove .py)
await replaceInFilesAndNames(scriptDestination, 'script_name', scriptFileName.replace(/\.py$/, ''));
// 3. add custom github copilot instructions
if (createCopilotInstructions) {
const packageInstructionsPath = path.join(
NEW_PROJECT_TEMPLATES_FOLDER,
'copilot-instructions-text',
'script-copilot-instructions.md',
);
await manageCopilotInstructionsFile(destRoot, packageInstructionsPath, [
{ searchValue: '<script_name>', replaceValue: scriptFileName },
]);
}
return Uri.file(scriptDestination);
}
return undefined;
}
}