Skip to content

Custom menus have non-deterministic order #851

@broken-pen

Description

@broken-pen

Describe the bug

Orgmode has three menus that can be extended by users with custom entries: the agenda promp, the capture prompt and exports. All three are configured with a config that looks like this:

custom_entries  = {
    [shortcut_key] = { description... },
    ...
}

These are associative tables. When creating the tables, Orgmode iterates over them with the pairs() function, which pureposefully leaves the order undefined. This causes the resulting menu to have an undefined order as well. AFAICT, the order stays the same within one nvim session, but is different every time nvim is restarted.

This is usually not problematic with only one or two entries. With increasing size, however, the menu loses its functionality as a quick reminder for key presses that the user hasn't gotten used to yet. Finding the correct entry becomes difficult because every time, the entries are in different positions.

Steps to reproduce

  1. Configure more than one template in org_capture_templates.
  2. Use the capture keymap and note the order of menu entries.
  3. Restart nvim.
  4. Use the capture keymap again. The order should be different now.

Expected behavior

The order should stay the same between nvim sessions. I see multiple ways to achieve this:

  1. Turn the config into a list instead of an associative tables. This is likely impossible without a breaking change.
  2. Sort custom entries before adding them to the menu. They will always be below builtin entries.
  3. Sort menu entries before displaying them. This affects builtin entries as well.

Emacs functionality

In Emacs Lisp, associative tables are always iterated in insertion order, so this issue does not exist. The Emacs behavior is closest to option 1 of the "Expected behavior", which is not preferable due to breaking changes.

Minimal init.lua

require('orgmode').setup({
  org_capture_templates = {
    a = {
      description = 'Appointment',
      template = '* TODO %?\n%%^{Appointment}T',
    },
    c = {
      description = 'Phone call',
      template = '* %U %? :call:',
    },
    m = {
      description = 'Meeting notes',
      template = '* %U %? :meeting:',
    },
    t = {
      description = 'Task',
      template = '* TODO %?',
    },
  },
})

Screenshots and recordings

No response

OS / Distro

OS-independent

Neovim version/commit

0.10.3

Additional context

The Lua users wiki has some notes on sorted iteration over associative tables. Basically, it always comes down to using ipairs(vim.tbl_keys(assoc_table)).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions