Skip to content

Commit 60db6b1

Browse files
Integrate classPick into multiStepInput via ClassPickStepOptions
Co-authored-by: gjsjohnmurray <6726799+gjsjohnmurray@users.noreply.github.com>
1 parent e73b96c commit 60db6b1

File tree

2 files changed

+94
-25
lines changed

2 files changed

+94
-25
lines changed

src/commands/newFile.ts

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ interface QuickPickStepOptions {
2626
items: InputStepItem[];
2727
}
2828

29-
type InputStepOptions = InputBoxStepOptions | QuickPickStepOptions;
29+
interface ClassPickStepOptions {
30+
type: "classPick";
31+
title: string;
32+
api: AtelierAPI | undefined;
33+
}
34+
35+
type InputStepOptions = InputBoxStepOptions | QuickPickStepOptions | ClassPickStepOptions;
3036

3137
/**
3238
* Get input from the user using multiple steps.
@@ -102,6 +108,58 @@ async function multiStepInput(steps: InputStepOptions[]): Promise<string[] | und
102108
});
103109
inputBox.show();
104110
});
111+
} else if (stepOptions.type == "classPick") {
112+
// Optional step: escape = skip (store ""), back = go back one step, pick = store class name
113+
let picked: string | undefined;
114+
if (stepOptions.api) {
115+
picked = await pickClass(stepOptions.api, stepOptions.title, step > 0);
116+
} else {
117+
// Fallback InputBox when there's no server connection
118+
picked = await new Promise<string | undefined>((resolve) => {
119+
let settled = false;
120+
const settle = (v: string | undefined) => {
121+
if (!settled) {
122+
settled = true;
123+
resolve(v);
124+
}
125+
};
126+
const inputBox = vscode.window.createInputBox();
127+
inputBox.ignoreFocusOut = true;
128+
inputBox.step = step + 1;
129+
inputBox.totalSteps = steps.length;
130+
inputBox.buttons = step > 0 ? [vscode.QuickInputButtons.Back] : [];
131+
inputBox.title = stepOptions.title;
132+
inputBox.placeholder = "Package.Subpackage.Class";
133+
inputBox.onDidTriggerButton(() => {
134+
settle(undefined); // Back was pressed
135+
inputBox.hide();
136+
});
137+
inputBox.onDidAccept(() => {
138+
if (typeof inputBox.validationMessage != "string") {
139+
settle(inputBox.value); // "" = skip, or a valid class name
140+
inputBox.hide();
141+
}
142+
});
143+
inputBox.onDidHide(() => {
144+
settle(""); // Escape = skip this optional step
145+
inputBox.dispose();
146+
});
147+
inputBox.onDidChangeValue((value) => {
148+
inputBox.validationMessage = value ? validateClassName(value) : undefined;
149+
});
150+
inputBox.show();
151+
});
152+
}
153+
if (picked === undefined) {
154+
// Back button was pressed: go back one step
155+
step--;
156+
} else {
157+
// "" = skipped, or a class name was entered/picked
158+
results[step] = picked;
159+
step++;
160+
}
161+
// This is an optional step; never cancel the wizard on escape
162+
escape = false;
105163
} else {
106164
// Show the QuickPick
107165
escape = await new Promise<boolean>((resolve) => {
@@ -938,26 +996,20 @@ Parameter ENSPURGE As BOOLEAN = 1;
938996
}
939997
`;
940998
} else if (type == NewFileType.Class) {
941-
// Prompt the user for the class name and description
999+
// Add the superclass picker as the third step
1000+
inputSteps.push({
1001+
type: "classPick",
1002+
title: "Pick an optional superclass. Press 'Escape' to skip.",
1003+
api: api,
1004+
});
1005+
1006+
// Prompt the user
9421007
const results = await multiStepInput(inputSteps);
9431008
if (!results) {
9441009
return;
9451010
}
9461011
cls = results[0];
947-
const [, desc] = results;
948-
949-
// Prompt for an optional superclass
950-
let superclass: string | undefined;
951-
if (api) {
952-
superclass = await pickClass(api, "Pick an optional superclass. Press 'Escape' to skip.");
953-
} else {
954-
superclass = await vscode.window.showInputBox({
955-
title: "Enter an optional superclass. Press 'Escape' to skip.",
956-
ignoreFocusOut: true,
957-
placeHolder: "Package.Subpackage.Class",
958-
validateInput: (value: string) => (value ? validateClassName(value) : undefined),
959-
});
960-
}
1012+
const [, desc, superclass] = results;
9611013

9621014
// Generate the file's content
9631015
clsContent = `

src/commands/project.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -653,20 +653,32 @@ async function pickAdditions(
653653
/**
654654
* Show an expandable QuickPick to let the user pick a single class from the server.
655655
* Packages can be expanded to reveal their classes. Only leaf class items can be accepted.
656-
* Returns the class name (without the `.cls` extension), or `undefined` if the user escapes.
656+
* - Returns the class name (without the `.cls` extension) when a class is picked.
657+
* - Returns `""` when the user presses Escape (skip this optional step).
658+
* - Returns `undefined` when the user presses the Back button (only when `canGoBack` is `true`).
657659
*/
658-
export async function pickClass(api: AtelierAPI, title: string): Promise<string | undefined> {
660+
export async function pickClass(api: AtelierAPI, title: string, canGoBack = false): Promise<string | undefined> {
659661
const query = "SELECT Name, Type FROM %Library.RoutineMgr_StudioOpenDialog(?,1,1,?,0,0,?)";
660662
let sys: "0" | "1" = "0";
661663
let gen: "0" | "1" = "0";
662664

663665
return new Promise<string | undefined>((resolve) => {
666+
// Use a settled flag so that onDidHide (which always fires) never double-resolves.
667+
let settled = false;
668+
const settle = (value: string | undefined) => {
669+
if (!settled) {
670+
settled = true;
671+
resolve(value);
672+
}
673+
};
674+
664675
const quickPick = vscode.window.createQuickPick<PickAdditionsItem>();
665676
quickPick.title = title;
666677
quickPick.ignoreFocusOut = true;
667678
quickPick.keepScrollPosition = true;
668679
quickPick.matchOnDescription = true;
669680
quickPick.buttons = [
681+
...(canGoBack ? [vscode.QuickInputButtons.Back] : []),
670682
{
671683
iconPath: new vscode.ThemeIcon("library"),
672684
tooltip: "System",
@@ -721,13 +733,18 @@ export async function pickClass(api: AtelierAPI, title: string): Promise<string
721733
};
722734

723735
quickPick.onDidTriggerButton((button) => {
724-
quickPick.busy = true;
725-
if (button.tooltip == "System") {
726-
sys = button.toggle.checked ? "1" : "0";
736+
if (button === vscode.QuickInputButtons.Back) {
737+
settle(undefined); // signal "go back" to the caller
738+
quickPick.hide();
727739
} else {
728-
gen = button.toggle.checked ? "1" : "0";
740+
quickPick.busy = true;
741+
if (button.tooltip == "System") {
742+
sys = button.toggle.checked ? "1" : "0";
743+
} else {
744+
gen = button.toggle.checked ? "1" : "0";
745+
}
746+
getRootItems();
729747
}
730-
getRootItems();
731748
});
732749

733750
quickPick.onDidTriggerItemButton((event) => {
@@ -771,13 +788,13 @@ export async function pickClass(api: AtelierAPI, title: string): Promise<string
771788
if (selected && !selected.buttons?.length) {
772789
// Leaf class item (no expand button): strip the .cls extension and resolve
773790
const name = selected.fullName.endsWith(".cls") ? selected.fullName.slice(0, -4) : selected.fullName;
774-
resolve(name);
791+
settle(name);
775792
quickPick.hide();
776793
}
777794
});
778795

779796
quickPick.onDidHide(() => {
780-
resolve(undefined);
797+
settle(""); // Escape pressed: resolve with "" to signal "skip this optional step"
781798
quickPick.dispose();
782799
});
783800

0 commit comments

Comments
 (0)