Skip to content

Commit

Permalink
Merge pull request #2182 from codefori/fix/eventFunctionsCrash
Browse files Browse the repository at this point in the history
Fix event functions chain crash
  • Loading branch information
sebjulliand authored Jul 29, 2024
2 parents e265f56 + 53edeff commit e513d0f
Show file tree
Hide file tree
Showing 16 changed files with 199 additions and 133 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"icon": "icon.png",
"displayName": "Code for IBM i",
"description": "Maintain your RPGLE, CL, COBOL, C/CPP on IBM i right from Visual Studio Code.",
"version": "2.12.0",
"version": "2.12.1-dev.0",
"keywords": [
"ibmi",
"rpgle",
Expand Down
58 changes: 51 additions & 7 deletions src/api/Instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ import IBMi from "./IBMi";
import { ConnectionStorage, GlobalStorage } from "./Storage";

type IBMiEventSubscription = {
event: IBMiEvent,
func: Function
func: Function,
transient?: boolean
};

type SubscriptionMap = Map<string, IBMiEventSubscription>

export default class Instance {
private connection: IBMi | undefined;
private storage: ConnectionStorage;
private emitter: vscode.EventEmitter<IBMiEvent> = new vscode.EventEmitter();
private subscribers: IBMiEventSubscription[] = [];
private subscribers: Map<IBMiEvent, SubscriptionMap> = new Map;

private deprecationCount = 0; //TODO: remove in v3.0.0

constructor(context: vscode.ExtensionContext) {
this.subscribers = [];
this.storage = new ConnectionStorage(context);
this.emitter.event(e => this.processEvent(e));
}
Expand Down Expand Up @@ -56,17 +59,58 @@ export default class Instance {
return this.storage.ready ? this.storage : undefined;
}

/**
* Subscribe to an {@link IBMiEvent}. When the event is triggerred, the `func` function gets executed.
*
* Each `context`/`name` couple must be unique.
* @param context the extension subscribing to the event
* @param event the {@link IBMiEvent} to subscribe to
* @param name a human-readable name summarizing the function
* @param func the function to execute when the {@link IBMiEvent} is triggerred
* @param transient if `true`, the function will only be executed once during the lifetime of a connection
*/
subscribe(context: vscode.ExtensionContext, event: IBMiEvent, name: string, func: Function, transient?: boolean) {
this.getSubscribers(event).set(`${context.extension.id} - ${name}`, { func, transient });
}

private getSubscribers(event: IBMiEvent) {
let eventSubscribers: SubscriptionMap = this.subscribers.get(event) || new Map;
if (!this.subscribers.has(event)) {
this.subscribers.set(event, eventSubscribers);
}
return eventSubscribers;
}

/**
* @deprecated Will be removed in `v3.0.0`; use {@link subscribe} instead
*/
onEvent(event: IBMiEvent, func: Function): void {
this.subscribers.push({ event, func });
this.getSubscribers(event).set(`deprecated - ${func.name || "unknown"}_${this.deprecationCount++}`, { func });
console.warn("[Code for IBM i] Deprecation warning: you are using Instance::onEvent which is deprecated and will be removed in v3.0.0. Please use Instance::subscribe instead.");
}

fire(event: IBMiEvent) {
this.emitter?.fire(event);
}

async processEvent(event: IBMiEvent) {
for (const subscriber of this.subscribers.filter(s => s.event === event)) {
await subscriber.func();
const eventSubscribers = this.getSubscribers(event)
console.time(event);
for (const [identity, callable] of eventSubscribers.entries()) {
try {
console.time(identity);
await callable.func();
console.timeEnd(identity);
}
catch (error) {
console.error(`${event} event function ${identity} failed`, error);
}
finally {
if (callable.transient) {
eventSubscribers.delete(identity);
}
}
}
console.timeEnd(event);
}
}
21 changes: 14 additions & 7 deletions src/api/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export namespace Terminal {
connectionString?: string
}

let terminalCount = 0;

function setHalted(state: boolean) {
commands.executeCommand(`setContext`, `code-for-ibmi:term5250Halted`, state);
}
Expand All @@ -43,17 +45,17 @@ export namespace Terminal {
const ATTENTION = 1;
const TAB = 9;

export function registerTerminalCommands() {
export function registerTerminalCommands(context: vscode.ExtensionContext) {
return [
vscode.commands.registerCommand(`code-for-ibmi.launchTerminalPicker`, () => {
return selectAndOpen(instance);
return selectAndOpen(context, instance);
}),

vscode.commands.registerCommand(`code-for-ibmi.openTerminalHere`, async (ifsNode) => {
const content = instance.getContent();
if (content) {
const ifsPath = (await content.isDirectory(ifsNode.path)) ? ifsNode.path : path.dirname(ifsNode.path);
const terminal = await selectAndOpen(instance, TerminalType.PASE);
const terminal = await selectAndOpen(context, instance, TerminalType.PASE);
terminal?.sendText(`cd ${ifsPath}`);
}
}),
Expand All @@ -68,7 +70,7 @@ export namespace Terminal {
];
}

export async function selectAndOpen(instance: Instance, openType?: TerminalType) {
async function selectAndOpen(context: vscode.ExtensionContext, instance: Instance, openType?: TerminalType) {
const connection = instance.getConnection();
const configuration = instance.getConfig();
if (connection && configuration) {
Expand Down Expand Up @@ -104,14 +106,14 @@ export namespace Terminal {
vscode.workspace.getConfiguration().update(`terminal.integrated.sendKeybindingsToShell`, true, true);
}

return createTerminal(connection, terminalSettings);
return createTerminal(context, connection, terminalSettings);
}
}
}

const HALTED = ` II`;

async function createTerminal(connection: IBMi, terminalSettings: TerminalSettings) {
async function createTerminal(context: vscode.ExtensionContext, connection: IBMi, terminalSettings: TerminalSettings) {
const writeEmitter = new vscode.EventEmitter<string>();
const paseWelcomeMessage = 'echo "Terminal started. Thanks for using Code for IBM i"';

Expand Down Expand Up @@ -201,7 +203,12 @@ export namespace Terminal {
channel.stdin.write(`${paseWelcomeMessage}\n`);
}

instance.onEvent('disconnected', () => emulatorTerminal.dispose());
instance.subscribe(
context,
'disconnected',
`Dispose Terminal ${terminalCount++}`,
() => emulatorTerminal.dispose(),
true);

return emulatorTerminal;
}
Expand Down
56 changes: 32 additions & 24 deletions src/api/debug/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,36 +371,44 @@ export async function initialize(context: ExtensionContext) {
);

// Run during startup:
instance.onEvent("connected", async () => {
activateDebugExtension();
const connection = instance.getConnection();
const content = instance.getContent();
if (connection && content && server.debugPTFInstalled()) {
vscode.commands.executeCommand(`setContext`, ptfContext, true);
instance.subscribe(
context,
'connected',
`Load debugger status`,
async () => {
activateDebugExtension();
const connection = instance.getConnection();
const content = instance.getContent();
if (connection && content && server.debugPTFInstalled()) {
vscode.commands.executeCommand(`setContext`, ptfContext, true);

//Enable debug related commands
vscode.commands.executeCommand(`setContext`, debugContext, true);
//Enable debug related commands
vscode.commands.executeCommand(`setContext`, debugContext, true);

//Enable service entry points related commands
vscode.commands.executeCommand(`setContext`, debugSEPContext, await server.isSEPSupported());
//Enable service entry points related commands
vscode.commands.executeCommand(`setContext`, debugSEPContext, await server.isSEPSupported());

const isDebugManaged = isManaged();
vscode.commands.executeCommand(`setContext`, `code-for-ibmi:debugManaged`, isDebugManaged);
if (!isDebugManaged) {
if (validateIPv4address(connection.currentHost)) {
vscode.window.showWarningMessage(`You are using an IPv4 address to connect to this system. This may cause issues with debugging. Please use a hostname in the Login Settings instead.`);
}
const isDebugManaged = isManaged();
vscode.commands.executeCommand(`setContext`, `code-for-ibmi:debugManaged`, isDebugManaged);
if (!isDebugManaged) {
if (validateIPv4address(connection.currentHost)) {
vscode.window.showWarningMessage(`You are using an IPv4 address to connect to this system. This may cause issues with debugging. Please use a hostname in the Login Settings instead.`);
}

certificates.sanityCheck(connection, content);
certificates.sanityCheck(connection, content);
}
}
}
});
});

instance.onEvent("disconnected", () => {
resetDebugServiceDetails();
vscode.commands.executeCommand(`setContext`, debugContext, false);
vscode.commands.executeCommand(`setContext`, debugSEPContext, false);
});
instance.subscribe(
context,
'disconnected',
`Clear debugger status`,
() => {
resetDebugServiceDetails();
vscode.commands.executeCommand(`setContext`, debugContext, false);
vscode.commands.executeCommand(`setContext`, debugSEPContext, false);
});
}

function validateIPv4address(ipaddress: string) {
Expand Down
90 changes: 49 additions & 41 deletions src/api/local/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,55 +45,63 @@ export namespace Deployment {
buildWatcher().then(bw => context.subscriptions.push(bw));
}

instance.onEvent("connected", () => {
const workspaces = vscode.workspace.workspaceFolders;
const connection = instance.getConnection();
const config = instance.getConfig();
const storage = instance.getStorage();

if (workspaces && connection && storage && config) {
if (workspaces.length > 0) {
buildWatcher().then(bw => context.subscriptions.push(bw));
button.show();
}
instance.subscribe(
context,
'connected',
`Initialize deployment`,
() => {
const workspaces = vscode.workspace.workspaceFolders;
const connection = instance.getConnection();
const config = instance.getConfig();
const storage = instance.getStorage();

if (workspaces && connection && storage && config) {
if (workspaces.length > 0) {
buildWatcher().then(bw => context.subscriptions.push(bw));
button.show();
}

const existingPaths = storage.getDeployment();
const existingPaths = storage.getDeployment();

if (workspaces.length === 1) {
const workspace = workspaces[0];

if (existingPaths && !existingPaths[workspace.uri.fsPath]) {
const possibleDeployDir = DeployTools.buildPossibleDeploymentDirectory(workspace);
vscode.window.showInformationMessage(
`Deploy directory for Workspace not setup. Would you like to default to '${possibleDeployDir}'?`,
`Yes`,
`Ignore`
).then(async result => {
if (result === `Yes`) {
DeployTools.setDeployLocation({ path: possibleDeployDir }, workspace);
}
});
}
if (workspaces.length === 1) {
const workspace = workspaces[0];

getLocalActions(workspace).then(result => {
if (result.length === 0) {
if (existingPaths && !existingPaths[workspace.uri.fsPath]) {
const possibleDeployDir = DeployTools.buildPossibleDeploymentDirectory(workspace);
vscode.window.showInformationMessage(
`There are no local Actions defined for this project.`,
`Run Setup`
).then(result => {
if (result === `Run Setup`)
vscode.commands.executeCommand(`code-for-ibmi.launchActionsSetup`);
`Deploy directory for Workspace not setup. Would you like to default to '${possibleDeployDir}'?`,
`Yes`,
`Ignore`
).then(async result => {
if (result === `Yes`) {
DeployTools.setDeployLocation({ path: possibleDeployDir }, workspace);
}
});
}
})

getLocalActions(workspace).then(result => {
if (result.length === 0) {
vscode.window.showInformationMessage(
`There are no local Actions defined for this project.`,
`Run Setup`
).then(result => {
if (result === `Run Setup`)
vscode.commands.executeCommand(`code-for-ibmi.launchActionsSetup`);
});
}
})
}
}
}
});
});

instance.onEvent("disconnected", () => {
fixCCSID = undefined;
button.hide();
})
instance.subscribe(
context,
'disconnected',
`Clear deployment`,
() => {
fixCCSID = undefined;
button.hide();
})
}

export function getConnection(): IBMi {
Expand Down
20 changes: 11 additions & 9 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function activate(context: ExtensionContext): Promise<CodeForIBMi>
context.subscriptions.push(
window.registerTreeDataProvider(
`helpView`,
new HelpView()
new HelpView(context)
),
window.registerTreeDataProvider(
`libraryListView`,
Expand Down Expand Up @@ -114,14 +114,16 @@ export async function activate(context: ExtensionContext): Promise<CodeForIBMi>
initialise(context);
}

instance.onEvent(`connected`, () => {
Promise.all([
commands.executeCommand("code-for-ibmi.refreshObjectBrowser"),
commands.executeCommand("code-for-ibmi.refreshLibraryListView"),
commands.executeCommand("code-for-ibmi.refreshIFSBrowser"),
commands.executeCommand("code-for-ibmi.refreshProfileView")
]);
});
instance.subscribe(
context,
'connected',
`Refresh views`,
() => {
commands.executeCommand("code-for-ibmi.refreshObjectBrowser");
commands.executeCommand("code-for-ibmi.refreshLibraryListView");
commands.executeCommand("code-for-ibmi.refreshIFSBrowser");
commands.executeCommand("code-for-ibmi.refreshProfileView");
});

return { instance, customUI: () => new CustomUI(), deployTools: DeployTools, evfeventParser: parseErrors, tools: Tools };
}
Expand Down
Loading

0 comments on commit e513d0f

Please sign in to comment.