noice.nvim icon indicating copy to clipboard operation
noice.nvim copied to clipboard

feature: Override vim.ui.select with a nui menu (very basic implementation shown)

Open theKnightsOfRohan opened this issue 1 month ago • 0 comments

Did you check the docs?

  • [X] I have read all the noice.nvim docs

Is your feature request related to a problem? Please describe.

Whether using notify or mini as the message mechanism, it's a little disconcerting to have the selection dialog appear in the same place as your notifications, and have no visual indication of escaping out/selecting an item. I've tried doing this myself, and it works okay, but I think integrating it into noice as a preset or something would be "noice". 🙃

Describe the solution you'd like

Since the nui menu is pretty robust, I think just the ability to create a menu at the cursor or at an arbitrary position on the screen whenever vim.ui.select() is called would be enough. The reason I care about cursor position is for vim.lsp.buf.code_action(), which I use a lot, so that eye position is maintained.

Describe alternatives you've considered

I guess custom creating a menu from scratch, or a special input buffer would be an alternative? But the nui menu seems like a pretty 1:1 layer between inputs, so I don't see anything that would be too much better. I guess you could make the selection menu part of the configuration options if there are multiple ways to implement it.

Additional context

Here's a very basic 1-afternoon implementation of what I mean (note that I am no lua whiz):

return {
    "MunifTanjim/nui.nvim",
    config = function()
        local Menu = require("nui.menu")

        vim.ui.select = function(items, opts, on_choice)
            vim.validate({
                items = { items, 'table', false },
                on_choice = { on_choice, 'function', false },
            })
            opts = opts or {}
            local format_item = opts.format_item or tostring

            Menu({
                relative = "cursor",
                position = {
                    row = 2,
                    col = 0,
                },
                size = {
                    width = 40,
                },
                border = {
                    style = "rounded",
                    text = {
                        top = opts.prompt or "Select an item",
                        top_align = "center",
                    },
                },
            }, {
                lines = (function()
                    ---@type NuiTree.Node[]
                    local selections = {}

                    for _, item in ipairs(items) do
                        if type(item) == "string" then
                            table.insert(selections, Menu.item(item))
                        elseif type(item) == "table" then
                            table.insert(selections, Menu.item(format_item(item), item))
                        end
                    end

                    return selections
                end)(),

                on_close = function()
                    print("Closed")
                end,
                on_submit = function(selected)
                    on_choice(selected)
                end,
            }):mount()
        end

        vim.keymap.set("n", "<leader><leader>", function()
            vim.ui.select({ 'One', 'Two', 'Three' }, {}, function(selected)
                print("Selected: " .. selected)
            end)
        end)
    end,
}

This implementation works with both vim.lsp.buf.code_action() and the string example keymapped below, which should mean that it works with both the table and string versions of nui's Menu.

theKnightsOfRohan avatar May 13 '24 20:05 theKnightsOfRohan