Skip to content

Commit 24d7019

Browse files
authored
Merge branch 'intersystems-community:master' into master
2 parents ce80a9d + 5aaf862 commit 24d7019

7 files changed

Lines changed: 219 additions & 65 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
"editor.tabSize": 2,
55
"editor.formatOnSave": true
66
},
7+
"js/ts.tsdk.path": "node_modules/typescript/lib",
78
}

package-lock.json

Lines changed: 24 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@
239239
"command": "vscode-objectscript.loadStudioColors",
240240
"when": "isWindows"
241241
},
242+
{
243+
"command": "vscode-objectscript.newFile.class",
244+
"when": "false"
245+
},
242246
{
243247
"command": "vscode-objectscript.newFile.businessOperation",
244248
"when": "false"
@@ -591,6 +595,11 @@
591595
}
592596
],
593597
"file/newFile": [
598+
{
599+
"command": "vscode-objectscript.newFile.class",
600+
"when": "workspaceFolderCount != 0",
601+
"group": "file"
602+
},
594603
{
595604
"command": "vscode-objectscript.newFile.kpi",
596605
"when": "workspaceFolderCount != 0",
@@ -1021,6 +1030,11 @@
10211030
"command": "vscode-objectscript.loadStudioColors",
10221031
"title": "Load Studio Syntax Colors"
10231032
},
1033+
{
1034+
"category": "ObjectScript",
1035+
"command": "vscode-objectscript.newFile.class",
1036+
"title": "Class"
1037+
},
10241038
{
10251039
"category": "ObjectScript",
10261040
"command": "vscode-objectscript.newFile.kpi",

src/api/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
checkConnection,
1313
schemas,
1414
checkingConnection,
15+
inactiveServerIds,
1516
} from "../extension";
1617
import { currentWorkspaceFolder, outputChannel, outputConsole } from "../utils";
1718

@@ -232,7 +233,7 @@ export class AtelierAPI {
232233
} = getResolvedConnectionSpec(serverName, config("intersystems.servers", workspaceFolderName).get(serverName));
233234
this._config = {
234235
serverName,
235-
active: this.externalServer || conn.active,
236+
active: !inactiveServerIds.has(serverName),
236237
apiVersion: workspaceState.get(this.configName.toLowerCase() + ":apiVersion", DEFAULT_API_VERSION),
237238
serverVersion: workspaceState.get(this.configName.toLowerCase() + ":serverVersion", DEFAULT_SERVER_VERSION),
238239
https: scheme === "https",
@@ -245,13 +246,6 @@ export class AtelierAPI {
245246
pathPrefix,
246247
docker: false,
247248
};
248-
249-
// Report server as inactive when no namespace has been determined,
250-
// otherwise output channel reports the issue.
251-
// This arises when a server-only workspace is editing the user's settings.json, or the .code-workspace file.
252-
if (this._config.ns === "" && this.externalServer) {
253-
this._config.active = false;
254-
}
255249
} else if (conn["docker-compose"]) {
256250
// Provided a docker-compose type connection spec has previously been resolved we can use its values
257251
const resolvedSpec = getResolvedConnectionSpec(workspaceFolderName, undefined);

src/commands/newFile.ts

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DocumentContentProvider } from "../providers/DocumentContentProvider";
66
import { replaceFile, getWsFolder, handleError, displayableUri } from "../utils";
77
import { getFileName } from "./export";
88
import { getUrisForDocument } from "../utils/documentIndex";
9+
import { pickDocument } from "../utils/documentPicker";
910

1011
interface InputStepItem extends vscode.QuickPickItem {
1112
value?: string;
@@ -25,7 +26,13 @@ interface QuickPickStepOptions {
2526
items: InputStepItem[];
2627
}
2728

28-
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;
2936

3037
/**
3138
* Get input from the user using multiple steps.
@@ -101,6 +108,63 @@ async function multiStepInput(steps: InputStepOptions[]): Promise<string[] | und
101108
});
102109
inputBox.show();
103110
});
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 pickDocument(stepOptions.api, stepOptions.title, "cls", step + 1, steps.length);
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 === "") {
154+
// Back button was pressed: go back one step
155+
step--;
156+
} else {
157+
// undefined = skipped, or a class name was entered/picked
158+
if (typeof picked == "undefined") {
159+
picked = "";
160+
} else if (picked.slice(-4) == ".cls") {
161+
picked = picked.slice(0, -4);
162+
}
163+
results[step] = picked;
164+
step++;
165+
}
166+
// This is an optional step; never cancel the wizard on escape
167+
escape = false;
104168
} else {
105169
// Show the QuickPick
106170
escape = await new Promise<boolean>((resolve) => {
@@ -222,6 +286,7 @@ function getAdapterPrompt(adapters: InputStepItem[], type: AdapaterClassType): I
222286

223287
/** The types of classes we can create */
224288
export enum NewFileType {
289+
Class = "Class",
225290
BusinessOperation = "Business Operation",
226291
BusinessService = "Business Service",
227292
BPL = "Business Process",
@@ -262,7 +327,7 @@ export async function newFile(type: NewFileType): Promise<void> {
262327
api = undefined;
263328
}
264329

265-
if (type != NewFileType.KPI) {
330+
if (type != NewFileType.KPI && type != NewFileType.Class) {
266331
// Check if we're connected to an Interoperability namespace
267332
const ensemble: boolean = api
268333
? await api.getNamespace().then((data) => data.result.content.features[0].enabled)
@@ -402,7 +467,7 @@ export async function newFile(type: NewFileType): Promise<void> {
402467
inputSteps.push(
403468
{
404469
type: "inputBox",
405-
title: `Enter a name for the new ${type} class`,
470+
title: `Enter a name for the new ${type == NewFileType.Class ? "class" : type + " class"}`,
406471
placeholder: "Package.Subpackage.Class",
407472
validateInput: (value: string) => {
408473
const valid = validateClassName(value);
@@ -933,6 +998,30 @@ Parameter RESPONSECLASSNAME As CLASSNAME = "${respClass}";`
933998
/// InterSystems IRIS purges message bodies based on the class when the option to purge message bodies is enabled
934999
Parameter ENSPURGE As BOOLEAN = 1;
9351000
1001+
}
1002+
`;
1003+
} else if (type == NewFileType.Class) {
1004+
// Add the superclass picker as the third step
1005+
inputSteps.push({
1006+
type: "classPick",
1007+
title: "Pick an optional superclass or press 'Escape' for none",
1008+
api: api,
1009+
});
1010+
1011+
// Prompt the user
1012+
const results = await multiStepInput(inputSteps);
1013+
if (!results) {
1014+
return;
1015+
}
1016+
cls = results[0];
1017+
const [, desc, superclass] = results;
1018+
1019+
// Generate the file's content
1020+
clsContent = `
1021+
${typeof desc == "string" ? "/// " + desc.replace(/\n/g, "\n/// ") : ""}
1022+
Class ${cls}${superclass ? ` Extends ${superclass}` : ""}
1023+
{
1024+
9361025
}
9371026
`;
9381027
}

src/extension.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ export function getResolvedConnectionSpec(key: string, dflt: any): any {
338338
return dflt;
339339
}
340340

341+
/** The `api.serverId`s of all servers that are known to be inactive */
342+
export const inactiveServerIds: Set<string> = new Set();
343+
341344
export async function checkConnection(
342345
clearCookies = false,
343346
uri?: vscode.Uri,
@@ -432,9 +435,8 @@ export async function checkConnection(
432435
handleError(message);
433436
panel.text = `${PANEL_LABEL} $(error)`;
434437
panel.tooltip = `ERROR - ${message}`;
435-
if (!api.externalServer) {
436-
await setConnectionState(configName, false);
437-
}
438+
inactiveServerIds.add(api.serverId);
439+
if (!api.externalServer) await setConnectionState(configName, false);
438440
return;
439441
}
440442
checkingConnection = true;
@@ -448,9 +450,8 @@ export async function checkConnection(
448450
} else {
449451
panel.tooltip = new vscode.MarkdownString(`Connected as \`${username}\``);
450452
}
451-
if (!api.externalServer) {
452-
await setConnectionState(configName, true);
453-
}
453+
inactiveServerIds.delete(api.serverId);
454+
if (!api.externalServer) await setConnectionState(configName, true);
454455
return;
455456
};
456457

@@ -503,7 +504,8 @@ export async function checkConnection(
503504
});
504505
}
505506
} else {
506-
await setConnectionState(configName, false);
507+
inactiveServerIds.add(api.serverId);
508+
if (!api.externalServer) await setConnectionState(configName, false);
507509
}
508510
} else {
509511
success = await new Promise<boolean>((resolve) => {
@@ -535,8 +537,9 @@ export async function checkConnection(
535537
checkingConnection = false;
536538
})
537539
);
538-
} else if (!api.externalServer) {
539-
await setConnectionState(configName, false);
540+
} else {
541+
inactiveServerIds.add(api.serverId);
542+
if (!api.externalServer) await setConnectionState(configName, false);
540543
}
541544
resolve(false);
542545
});
@@ -554,7 +557,8 @@ export async function checkConnection(
554557
);
555558
panel.text = `${connInfo} $(error)`;
556559
panel.tooltip = `ERROR - ${message}`;
557-
await setConnectionState(configName, false);
560+
inactiveServerIds.add(api.serverId);
561+
if (!api.externalServer) await setConnectionState(configName, false);
558562
})
559563
.finally(() => {
560564
checkingConnection = false;
@@ -1608,6 +1612,10 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
16081612
sendCommandTelemetryEvent("loadStudioColors");
16091613
loadStudioColors(languageServerExt);
16101614
}),
1615+
vscode.commands.registerCommand("vscode-objectscript.newFile.class", () => {
1616+
sendCommandTelemetryEvent("newFile.class");
1617+
newFile(NewFileType.Class);
1618+
}),
16111619
vscode.commands.registerCommand("vscode-objectscript.newFile.businessOperation", () => {
16121620
sendCommandTelemetryEvent("newFile.businessOperation");
16131621
newFile(NewFileType.BusinessOperation);

0 commit comments

Comments
 (0)