`Menu.addItem()`: Support asynchronous callback functions?

From Obsidian API 1.1.7:

 /**
   * Adds a menu item. Only works when menu is not shown yet.
   * @public
   */
   addItem(cb: (item: MenuItem) => any): this;

A TypeScript example of passing a callback to Menu.addItem():

this.app.workspace.on('file-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
    menu.addItem((menuItem: MenuItem) => {
        menuItem.setTitle('My menu item');
    });
});

A simple use-case like this works ok as it’s not asynchronous.

The problem

  • My Shell commands plugin can generate menu items for shell commands that a user wants to execute via a context menu (such as file-menu).
  • Each shell command may include {{variables}} in the menu title. The idea is that variables are parsed when a menu is opened, so the menu item shows the values of the parsed variables. Example: Open image {{event_file_name}} in GIMP will be parsed to Open image MyPhoto.jpg in GIMP.
  • Some variables - e.g. {{event_file_content}} read values asynchronously, e.g. await app.vault.read(putFileObjectHere).
  • If asynchronous menu title generating takes too long, the menu is shown before the async function call is finished. On Windows and Linux this seems to be ok - the menu title is shown correctly even if it “arrives” a few fractions of a second after the menu is opened. But on macOS the late assigned title will not show anywhere. I know this is currently the expected behavior (so not a bug of Obsidian), as it’s mentioned in the Obsidian API I linked above.

Suggesting a change to Obsidian API

I’d like to ask if you could consider making Menu.addItem() support asynchronous callback functions? Maybe Menu.addItem() could delay showing a menu until a Promise returned by a callback function is resolved?

A possible downside of this is that if a callback function is really slow (e.g. one second), it could cause a noticeable slow down to the menu appearing, which would result in bad user experience.

Perhaps a better (but more work requiring) solution would be to open the menu immediately, but then update the visual elements after the Promise is resolved?

Thank you for considering this and please let me know if you have any comments or questions. :slightly_smiling_face::+1:

There are two main reasons why it is not async:

  • Sorting, which happens on show
  • native menus, like used in the linked discussions cannot be updated, they need to be fully recompiled and retriggered.

“Native menus” is a setting in Apperance, it is not exclusive to macOS.

When using your second solution the menu will close & open again when a Promise is resolved, which results in bad UX.

1 Like