Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@
"command": "vscode-objectscript.loadStudioColors",
"when": "isWindows"
},
{
"command": "vscode-objectscript.newFile.class",
"when": "false"
},
{
"command": "vscode-objectscript.newFile.businessOperation",
"when": "false"
Expand Down Expand Up @@ -591,6 +595,11 @@
}
],
"file/newFile": [
{
"command": "vscode-objectscript.newFile.class",
"when": "workspaceFolderCount != 0",
"group": "file"
},
{
"command": "vscode-objectscript.newFile.kpi",
"when": "workspaceFolderCount != 0",
Expand Down Expand Up @@ -1021,6 +1030,11 @@
"command": "vscode-objectscript.loadStudioColors",
"title": "Load Studio Syntax Colors"
},
{
"category": "ObjectScript",
"command": "vscode-objectscript.newFile.class",
"title": "Class"
},
{
"category": "ObjectScript",
"command": "vscode-objectscript.newFile.kpi",
Expand Down
95 changes: 92 additions & 3 deletions src/commands/newFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DocumentContentProvider } from "../providers/DocumentContentProvider";
import { replaceFile, getWsFolder, handleError, displayableUri } from "../utils";
import { getFileName } from "./export";
import { getUrisForDocument } from "../utils/documentIndex";
import { pickDocument } from "../utils/documentPicker";

interface InputStepItem extends vscode.QuickPickItem {
value?: string;
Expand All @@ -25,7 +26,13 @@ interface QuickPickStepOptions {
items: InputStepItem[];
}

type InputStepOptions = InputBoxStepOptions | QuickPickStepOptions;
interface ClassPickStepOptions {
type: "classPick";
title: string;
api: AtelierAPI | undefined;
}

type InputStepOptions = InputBoxStepOptions | QuickPickStepOptions | ClassPickStepOptions;

/**
* Get input from the user using multiple steps.
Expand Down Expand Up @@ -101,6 +108,63 @@ async function multiStepInput(steps: InputStepOptions[]): Promise<string[] | und
});
inputBox.show();
});
} else if (stepOptions.type == "classPick") {
// Optional step: escape = skip (store ""), back = go back one step, pick = store class name
let picked: string | undefined;
if (stepOptions.api) {
picked = await pickDocument(stepOptions.api, stepOptions.title, "cls", step + 1, steps.length);
} else {
// Fallback InputBox when there's no server connection
picked = await new Promise<string | undefined>((resolve) => {
let settled = false;
const settle = (v: string | undefined) => {
if (!settled) {
settled = true;
resolve(v);
}
};
const inputBox = vscode.window.createInputBox();
inputBox.ignoreFocusOut = true;
inputBox.step = step + 1;
inputBox.totalSteps = steps.length;
inputBox.buttons = step > 0 ? [vscode.QuickInputButtons.Back] : [];
inputBox.title = stepOptions.title;
inputBox.placeholder = "Package.Subpackage.Class";
inputBox.onDidTriggerButton(() => {
settle(undefined); // Back was pressed
inputBox.hide();
});
inputBox.onDidAccept(() => {
if (typeof inputBox.validationMessage != "string") {
settle(inputBox.value); // "" = skip, or a valid class name
inputBox.hide();
}
});
inputBox.onDidHide(() => {
settle(""); // Escape = skip this optional step
inputBox.dispose();
});
inputBox.onDidChangeValue((value) => {
inputBox.validationMessage = value ? validateClassName(value) : undefined;
});
inputBox.show();
});
}
if (picked === "") {
// Back button was pressed: go back one step
step--;
} else {
// undefined = skipped, or a class name was entered/picked
if (typeof picked == "undefined") {
picked = "";
} else if (picked.slice(-4) == ".cls") {
picked = picked.slice(0, -4);
}
results[step] = picked;
step++;
}
// This is an optional step; never cancel the wizard on escape
escape = false;
} else {
// Show the QuickPick
escape = await new Promise<boolean>((resolve) => {
Expand Down Expand Up @@ -222,6 +286,7 @@ function getAdapterPrompt(adapters: InputStepItem[], type: AdapaterClassType): I

/** The types of classes we can create */
export enum NewFileType {
Class = "Class",
BusinessOperation = "Business Operation",
BusinessService = "Business Service",
BPL = "Business Process",
Expand Down Expand Up @@ -262,7 +327,7 @@ export async function newFile(type: NewFileType): Promise<void> {
api = undefined;
}

if (type != NewFileType.KPI) {
if (type != NewFileType.KPI && type != NewFileType.Class) {
// Check if we're connected to an Interoperability namespace
const ensemble: boolean = api
? await api.getNamespace().then((data) => data.result.content.features[0].enabled)
Expand Down Expand Up @@ -402,7 +467,7 @@ export async function newFile(type: NewFileType): Promise<void> {
inputSteps.push(
{
type: "inputBox",
title: `Enter a name for the new ${type} class`,
title: `Enter a name for the new ${type == NewFileType.Class ? "class" : type + " class"}`,
placeholder: "Package.Subpackage.Class",
validateInput: (value: string) => {
const valid = validateClassName(value);
Expand Down Expand Up @@ -933,6 +998,30 @@ Parameter RESPONSECLASSNAME As CLASSNAME = "${respClass}";`
/// InterSystems IRIS purges message bodies based on the class when the option to purge message bodies is enabled
Parameter ENSPURGE As BOOLEAN = 1;

}
`;
} else if (type == NewFileType.Class) {
// Add the superclass picker as the third step
inputSteps.push({
type: "classPick",
title: "Pick an optional superclass or press 'Escape' for none",
api: api,
});

// Prompt the user
const results = await multiStepInput(inputSteps);
if (!results) {
return;
}
cls = results[0];
const [, desc, superclass] = results;

// Generate the file's content
clsContent = `
${typeof desc == "string" ? "/// " + desc.replace(/\n/g, "\n/// ") : ""}
Class ${cls}${superclass ? ` Extends ${superclass}` : ""}
{

}
`;
}
Expand Down
4 changes: 4 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,10 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
sendCommandTelemetryEvent("loadStudioColors");
loadStudioColors(languageServerExt);
}),
vscode.commands.registerCommand("vscode-objectscript.newFile.class", () => {
sendCommandTelemetryEvent("newFile.class");
newFile(NewFileType.Class);
}),
vscode.commands.registerCommand("vscode-objectscript.newFile.businessOperation", () => {
sendCommandTelemetryEvent("newFile.businessOperation");
newFile(NewFileType.BusinessOperation);
Expand Down
31 changes: 25 additions & 6 deletions src/utils/documentPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,14 +285,23 @@ export async function pickDocuments(api: AtelierAPI, prompt?: string): Promise<s

/**
* Prompts the user to select a single document in server-namespace `api`
* using a custom QuickPick. An optional prompt will customize the title.
* using a custom QuickPick. An optional `prompt` will customize the title.
* `typeSuffix` can be provided to filter for specific types of documents (e.g. "cls").
* If `step` is provided and greater than 1, a back button will be included that resolves to an empty string when pressed.
* If `numberOfSteps` is provided, the title will be suffixed with the current step and total number of steps (e.g. "(2/4)") instead of the namespace and server information.
*/
export async function pickDocument(api: AtelierAPI, prompt?: string): Promise<string> {
export async function pickDocument(
api: AtelierAPI,
prompt?: string,
typeSuffix?: string,
step?: number,
numberOfSteps?: number
): Promise<string> {
let sys: "0" | "1" = "0";
let gen: "0" | "1" = "0";
let map: "0" | "1" = "1";
const query = "SELECT Name, Type FROM %Library.RoutineMgr_StudioOpenDialog(?,1,1,?,0,0,?,,0,?)";
const webApps = cspAppsForApi(api);
const webApps = (typeSuffix ?? "csp") == "csp" ? cspAppsForApi(api) : [];
const webAppRootItems = webApps.map((app: string) => {
return {
label: app,
Expand All @@ -302,9 +311,10 @@ export async function pickDocument(api: AtelierAPI, prompt?: string): Promise<st

return new Promise<string>((resolve) => {
const quickPick = vscode.window.createQuickPick<DocumentPickerItem>();
quickPick.title = `${prompt ? prompt : "Select a document"} in namespace '${api.ns}' on server '${api.serverId}'`;
quickPick.title = `${prompt ? prompt : "Select a document"} ${numberOfSteps ? `(${step}/${numberOfSteps})` : `in namespace '${api.ns}' on server '${api.serverId}'`}`;
quickPick.ignoreFocusOut = true;
quickPick.buttons = [
...((step ?? 0) > 1 ? [vscode.QuickInputButtons.Back] : []),
{
iconPath: new vscode.ThemeIcon("library"),
tooltip: "System",
Expand All @@ -327,7 +337,12 @@ export async function pickDocument(api: AtelierAPI, prompt?: string): Promise<st

const getRootItems = (): Promise<void> => {
return api
.actionQuery(`${query} WHERE Type != 5 AND Type != 10`, ["*,'*.prj", sys, gen, map])
.actionQuery(`${query} WHERE Type != 5 AND Type != 10`, [
typeSuffix ? `*.${typeSuffix}` : "*,'*.prj",
sys,
gen,
map,
])
.then((data) => {
const rootitems: DocumentPickerItem[] = data.result.content.map((i) => createSingleSelectItem(i));
const findLastIndex = (): number => {
Expand Down Expand Up @@ -356,6 +371,10 @@ export async function pickDocument(api: AtelierAPI, prompt?: string): Promise<st
quickPick.onDidTriggerButton((button) => {
quickPick.busy = true;
quickPick.enabled = false;
if (button === vscode.QuickInputButtons.Back) {
resolve(""); // signal "go back" to the caller
quickPick.hide();
}
if (button.tooltip == "System") {
sys = button.toggle.checked ? "1" : "0";
} else if (button.tooltip == "Generated") {
Expand Down Expand Up @@ -409,7 +428,7 @@ export async function pickDocument(api: AtelierAPI, prompt?: string): Promise<st
getRootItems();
} else {
api
.actionQuery(query, [`${item.fullName}/*`, sys, gen, map])
.actionQuery(query, [`${item.fullName}/*${typeSuffix ? `.${typeSuffix}` : ""}`, sys, gen, map])
.then((data) => {
const delim = item.fullName.includes("/") ? "/" : ".";
const newItems: DocumentPickerItem[] = data.result.content.map((i) =>
Expand Down