jupyterlab / lumino

Lumino is a library for building interactive web applications
https://lumino.readthedocs.io/
Other
598 stars 127 forks source link

Should commands receive an event that triggered them? #554

Open krassowski opened 1 year ago

krassowski commented 1 year ago

Problem

A number of commands, e.g. in jupterlab extensions (especially editor integrations such as spellchecker or LSP), need to know the node they were triggered on. Currently JupyterLab implements a custom caching logic that holds the most recent pointer event that triggered the context menu forever (until another context menu gets opened). This leads to a memory leak.

Proposed Solution

We could reserve a special argument in the args of commands for the event the command was triggered by (if any). Since most events contain a target property this would solve a major headache of extension developers.

This would mean that calls to CommandRegistry.execute would need to be adjusted to pass the event on. Conceptually this could be something like:

    // Close the root menu before executing the command.
    this.rootMenu.close();

    // Execute the command for the item.
-    let { command, args } = item;
+    let { command, originalArgs } = item;
+    const args = {...originalArgs, triggerEvent: event};
    if (this.commands.isEnabled(command, args)) {
      this.commands.execute(command, args);
    }

but in practice we would probably want to adjust the signature of execute to avoid repetition of this operation. We would also want to make similar adjustments in isEnabled and similar methods.

Additional context

fcollonval commented 1 year ago

Link to the code mentioned above: https://github.com/jupyterlab/lumino/blob/133d8725f4dd2c3a56b10d917f1a7b116ff17ce8/packages/widgets/src/menu.ts#L316

Somehow related for the discussion: https://github.com/jupyterlab/lumino/issues/180

krassowski commented 1 year ago

During the team call an issue of serialisation of commands (e.g. in command-linker in JupyterLab) was raised. If we just pass events as-is, it would potentially limit some functionality for use cases where commands are sent via JSON (or loaded from file); while for the use-case of retrieving context menu event this is not a new limitation, we could consider declaring a custom IEvent interface which would wrap the event and potentially expose some additional information, e.g. exposing a subset of event.target DOM node as a serialisable object. This could be:

interface INodeData {
  boundingBox: {x: number; y: number; height: number; width: number};
  htmlString: string;
  selector: string;
  classNames: readonly string[];
}

interface IEvent {
  target?: INodeData
  // other common event data
}

thoughts? CC @afshin