Skip to content

Commit 396c6ab

Browse files
committed
updates
1 parent 6fec72d commit 396c6ab

File tree

6 files changed

+105
-58
lines changed

6 files changed

+105
-58
lines changed

src/common/pickers/managers.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { QuickPickItem, QuickPickItemKind } from 'vscode';
22
import { PythonProjectCreator } from '../../api';
33
import { InternalEnvironmentManager, InternalPackageManager } from '../../internal.api';
44
import { Common, Pickers } from '../localize';
5-
import { showQuickPickWithButtons, showQuickPick } from '../window.apis';
5+
import { showQuickPickWithButtons } from '../window.apis';
66

77
function getDescription(mgr: InternalEnvironmentManager | InternalPackageManager): string | undefined {
88
if (mgr.description) {
@@ -153,7 +153,7 @@ export async function pickCreator(creators: PythonProjectCreator[]): Promise<Pyt
153153
},
154154
];
155155

156-
const selected = await showQuickPick(items, {
156+
const selected = await showQuickPickWithButtons(items, {
157157
placeHolder: Pickers.Managers.selectProjectCreator,
158158
ignoreFocusOut: true,
159159
});
@@ -163,7 +163,12 @@ export async function pickCreator(creators: PythonProjectCreator[]): Promise<Pyt
163163
}
164164

165165
// Return appropriate creator based on selection
166-
switch (selected.label) {
166+
// Handle case where selected could be an array (should not happen, but for type safety)
167+
const selectedItem = Array.isArray(selected) ? selected[0] : selected;
168+
if (!selectedItem) {
169+
return undefined;
170+
}
171+
switch (selectedItem.label) {
167172
case 'Auto Find':
168173
return autoFindCreator;
169174
case 'Select Existing':
@@ -178,11 +183,24 @@ export async function pickCreator(creators: PythonProjectCreator[]): Promise<Pyt
178183
description: c.description,
179184
c: c,
180185
}));
181-
const newSelected = await showQuickPick(newItems, {
186+
const newSelected = await showQuickPickWithButtons(newItems, {
182187
placeHolder: 'Select project type for new project',
183188
ignoreFocusOut: true,
189+
showBackButton: true,
184190
});
185-
return newSelected?.c;
191+
if (!newSelected) {
192+
// User cancelled the picker
193+
return undefined;
194+
}
195+
// Handle back button
196+
if ((newSelected as any)?.kind === -1 || (newSelected as any)?.back === true) {
197+
// User pressed the back button, re-show the first menu
198+
return pickCreator(creators);
199+
}
200+
201+
// Handle case where newSelected could be an array (should not happen, but for type safety)
202+
const selectedCreator = Array.isArray(newSelected) ? newSelected[0] : newSelected;
203+
return selectedCreator?.c;
186204
}
187205

188206
return undefined;

src/features/creators/creationHelpers.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ import * as path from 'path';
44
import { EnvironmentManagers, InternalEnvironmentManager } from '../../internal.api';
55
import { CreateEnvironmentOptions } from '../../api';
66
import { traceVerbose } from '../../common/logging';
7+
import { showQuickPickWithButtons } from '../../common/window.apis';
78

89
/**
9-
* Prompts the user to choose whether to create a new virtual environment (venv) for a package.
10+
* Prompts the user to choose whether to create a new virtual environment (venv) for a package, with a clearer return and early exit.
1011
* @returns {Promise<boolean | undefined>} Resolves to true if 'Yes' is selected, false if 'No', or undefined if cancelled.
1112
*/
1213
export async function promptForVenv(): Promise<boolean | undefined> {
13-
const venvChoice = await window.showQuickPick(['Yes', 'No'], {
14+
const venvChoice = await showQuickPickWithButtons([{ label: 'Yes' }, { label: 'No' }], {
1415
placeHolder: 'Would you like to create a new virtual environment for this package?',
1516
ignoreFocusOut: true,
17+
showBackButton: true,
1618
});
1719
if (!venvChoice) {
1820
return undefined;
1921
}
20-
return venvChoice === 'Yes';
22+
if (Array.isArray(venvChoice)) {
23+
// Should not happen for single selection, but handle just in case
24+
return venvChoice.some((item) => item.label === 'Yes');
25+
}
26+
return venvChoice.label === 'Yes';
2127
}
2228

2329
/**
@@ -36,14 +42,19 @@ export async function promptForCopilotInstructions(): Promise<boolean | undefine
3642
if (!isCopilotInstalled()) {
3743
return undefined;
3844
}
39-
const copilotChoice = await window.showQuickPick(['Yes', 'No'], {
45+
const copilotChoice = await showQuickPickWithButtons([{ label: 'Yes' }, { label: 'No' }], {
4046
placeHolder: 'Would you like to create a Copilot instructions file?',
4147
ignoreFocusOut: true,
48+
showBackButton: true,
4249
});
4350
if (!copilotChoice) {
4451
return undefined;
4552
}
46-
return copilotChoice === 'Yes';
53+
if (Array.isArray(copilotChoice)) {
54+
// Should not happen for single selection, but handle just in case
55+
return copilotChoice.some((item) => item.label === 'Yes');
56+
}
57+
return copilotChoice.label === 'Yes';
4758
}
4859

4960
/**
@@ -58,6 +69,9 @@ export async function removeCopilotInstructions(destFolder: string) {
5869
}
5970
}
6071

72+
export async function insertCopilotInstructions() {}
73+
export async function insertLaunchJson() {}
74+
6175
/**
6276
* Quickly creates a new Python virtual environment (venv) in the specified destination folder using the available environment managers.
6377
* Attempts to use the venv manager if available, otherwise falls back to any manager that supports environment creation.
@@ -66,37 +80,27 @@ export async function removeCopilotInstructions(destFolder: string) {
6680
* @returns {Promise<void>} Resolves when the environment is created or an error is shown.
6781
*/
6882
export async function quickCreateNewVenv(envManagers: EnvironmentManagers, destFolder: string) {
69-
try {
70-
// get the environment manager for venv
71-
const envManager: InternalEnvironmentManager | undefined = envManagers.managers.find(
72-
(m) => m.id === 'ms-python.python:venv',
73-
);
74-
const destUri = Uri.parse(destFolder);
75-
if (envManager?.supportsQuickCreate) {
76-
// with quickCreate enabled, user will not be prompted when creating the environment
77-
const options: CreateEnvironmentOptions = { quickCreate: false };
78-
if (envManager.supportsQuickCreate) {
79-
options.quickCreate = true;
80-
}
81-
const pyEnv = await envManager.create(destUri, options);
83+
// get the environment manager for venv, should always exist
84+
const envManager: InternalEnvironmentManager | undefined = envManagers.managers.find(
85+
(m) => m.id === 'ms-python.python:venv',
86+
);
87+
const destinationUri = Uri.parse(destFolder);
88+
if (envManager?.supportsQuickCreate) {
89+
// with quickCreate enabled, user will not be prompted when creating the environment
90+
const options: CreateEnvironmentOptions = { quickCreate: false };
91+
if (envManager.supportsQuickCreate) {
92+
options.quickCreate = true;
93+
}
94+
const pyEnv = await envManager.create(destinationUri, options);
95+
// TODO: do I need to update to say this is the env for the file? Like set it?
96+
if (!pyEnv) {
8297
// comes back as undefined if this doesn't work
83-
traceVerbose(`Created venv at: ${pyEnv?.environmentPath} using ${envManager.name}`);
98+
window.showErrorMessage(`Failed to create virtual environment, please create it manually.`);
8499
} else {
85-
// // find an environment manager that supports create
86-
// const envManager = envManagers.managers.find((m) => m.supportsCreate);
87-
// if (envManager) {
88-
// const options: CreateEnvironmentOptions = { quickCreate: true, additionalPackages: [] };
89-
// const pyEnv = await envManager.create(destUri, options);
90-
// traceVerbose(`Created venv at: ${pyEnv?.environmentPath} using ${envManager.name}`);
91-
// }
92-
// // If no environment manager supports create, show an error message
93-
// window.showErrorMessage(
94-
// `No environment manager found that supports creating a new environment, skipping...`,
95-
// );
96-
//TODO: just throw error
100+
traceVerbose(`Created venv at: ${pyEnv?.environmentPath}`);
97101
}
98-
} catch (err) {
99-
window.showErrorMessage(`Failed to create virtual environment: ${err}`);
102+
} else {
103+
window.showErrorMessage(`Failed to quick create virtual environment, please create it manually.`);
100104
}
101105
}
102106

src/features/creators/newPackageProject.ts

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@ import {
1313
} from './creationHelpers';
1414
import { EXTENSION_ROOT_DIR } from '../../common/constants';
1515
import { EnvironmentManagers } from '../../internal.api';
16+
import { showInputBoxWithButtons } from '../../common/window.apis';
1617

1718
export class NewPackageProject implements PythonProjectCreator {
1819
public readonly name = 'newPackage';
1920
public readonly displayName = 'Package';
20-
public readonly description = 'Create a new Python package';
21+
public readonly description = 'Create a package folder nested in the current workspace.';
2122
public readonly tooltip = new MarkdownString('Create a new Python package');
2223

2324
constructor(private readonly envManagers: EnvironmentManagers) {}
2425

2526
async create(_options?: PythonProjectCreatorOptions): Promise<PythonProject | undefined> {
2627
// Prompt for package name (TODO: this doesn't make sense if the _options is already being passed in )
27-
const packageName = await window.showInputBox({
28+
const packageName = await showInputBoxWithButtons({
2829
prompt: 'What is the name of the package? (e.g. my_package)',
2930
ignoreFocusOut: true,
31+
showBackButton: true,
3032
});
3133
if (!packageName) {
3234
return undefined;
@@ -80,6 +82,37 @@ export class NewPackageProject implements PythonProjectCreator {
8082
}
8183
await fs.copy(templateFolder, destFolder);
8284

85+
// custom instructions
86+
const instructionsTextPath = path.join(
87+
EXTENSION_ROOT_DIR,
88+
'src',
89+
'features',
90+
'creators',
91+
'templates',
92+
'copilot-instructions-text',
93+
'package-copilot-instructions.md',
94+
);
95+
const instructionsText = `\n \n` + (await fs.readFile(instructionsTextPath, 'utf-8'));
96+
97+
// check to see if .github folder exists
98+
const githubFolderPath = path.join(destRoot, '.github');
99+
const customInstructionsPath = path.join(githubFolderPath, 'copilot-instructions.md');
100+
const ghFolder = await fs.pathExists(githubFolderPath);
101+
if (ghFolder) {
102+
const customInstructions = await fs.pathExists(customInstructionsPath);
103+
if (customInstructions) {
104+
// Append to the existing file
105+
await fs.appendFile(customInstructionsPath, instructionsText);
106+
} else {
107+
// Create the file if it doesn't exist
108+
await fs.writeFile(customInstructionsPath, instructionsText);
109+
}
110+
} else {
111+
// Create the .github folder and the file
112+
await fs.mkdir(githubFolderPath);
113+
await fs.writeFile(customInstructionsPath, instructionsText);
114+
}
115+
83116
// 2. Replace <package_name> in all files and file/folder names using helper
84117
await replaceInFilesAndNames(destFolder, 'package_name', packageName);
85118

@@ -98,21 +131,23 @@ export class NewPackageProject implements PythonProjectCreator {
98131
const pythonEnvironment = await this.envManagers.getEnvironment(Uri.parse(destFolder));
99132

100133
// 6. Replace <run_exec> and <activation_command> in README.md
101-
const readmeFilePath = path.join(destFolder, 'README.md');
134+
// const readmeFilePath = path.join(destFolder, 'README.md');
102135
if (!pythonEnvironment) {
103136
window.showErrorMessage('Python environment not found.');
104137
return undefined;
105138
}
106139
const execInfo = pythonEnvironment.execInfo;
107140
if (execInfo.run) {
108-
const { executable, args = [] } = execInfo.run;
109-
const execRunStr = [executable, ...args].join(' ');
110-
await replaceInFile(readmeFilePath, '<run_exec>', execRunStr);
111-
await replaceInFile(readmeFilePath, 'packagenamereplacementtext', packageName);
141+
// const { executable, args = [] } = execInfo.run;
142+
// const execRunStr = [executable, ...args].join(' ');
143+
// TODO: check this as I don't think I need this anymore
144+
await replaceInFile(customInstructionsPath, '<package_name>', packageName);
112145
}
113-
// TODO: replace <activation_command> in README.md ?
114146

115-
// Return a PythonProject object if needed by your API
147+
// TODO: insert copilot instructions text into the copilot instructions file
148+
// TODO: insert configs into existing launch.json file
149+
150+
// Return a PythonProject OR Uri (if no venv was created)
116151
return {
117152
name: packageName,
118153
uri: Uri.file(destFolder),

src/features/creators/templates/newPackageTemplate/.github/copilot-instructions.md renamed to src/features/creators/templates/copilot-instructions-text/package-copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copilot Instructions for package_name
1+
# Copilot Instructions for <package_name>
22

33
- The package `<package_name>` is a Python Project located in the folder `<package_name>-folder`.
44
- You need to call the `Get Python Environment Information` tool on the <package_name> path to get the Python executable details.

src/features/creators/templates/newPackageTemplate/.vscode/launch.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/features/creators/templates/newPackageTemplate/.vscode/settings.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)