Skip to content

Question about auto sizing for vim.ui.select. #793

Description

@EmreDogann

This is not a bug report or issue,
I wanted to know if it was possible to have the height of fzf-lua's vim.ui.select menu fit the content.

The way I do it now is as follows:

require("fzf-lua").register_ui_select({
	winopts = {
		height = 0.3,
		width = 0.2,
		row = 0.5,
		col = 0.5,
	},
	fzf_opts = {
		["--layout"] = "reverse-list",
		["--info"] = "hidden",
	},
})

This is what it looks like using cmake-tools.nvim:
image
As can be seen above, there is a lot of space due to the height = 0.3 option.

Would it be possible to have an 'auto height' kind of feature so the height of the window is set to the maximum size of the content? Maybe this is already possible and I missed it?

Activity

  1. ibhagwan commented on Jun 23, 2023

    @ibhagwan
    Owner

    Would it be possible to have an 'auto height' kind of feature so the height of the window is set to the maximum size of the content? Maybe this is already possible and I missed it?

    You didn’t miss anything, it’s not possible ATM, not hard to implement, I’ll look into it.

  2. ibhagwan commented on Jun 24, 2023

    @ibhagwan
    Owner

    I struggled a bit to come up with the exact solution for this, not because it's hard but because there are many options:

    • Should we take into consideration min/max height?
    • As long as we're willing to take the performance penalty (enumerating all items), why not have adjustable width too?
    • Line padding (do you wish to have the exact length or a few lines extra?)
    • Other nuances...

    I decided the best solution would be to add the ui select items to the already existing options callback, this way each user can customize this to their liking, as for the request in the OP, below is what I came up with:

    Make sure to update to f9056ff or the below won't work

    • min height of 0.15
    • max height of 0.70
    • 4 lines of padding (prompt/query line, borders and one extra empty line)
      require("fzf-lua").register_ui_select(function(_, items)
        local min_h, max_h = 0.15, 0.70
        local h = (#items + 4) / vim.o.lines
        if h < min_h then
          h = min_h
        elseif h > max_h then
          h = max_h
        end
        return { winopts = { height = h, width = 0.60, row = 0.40 } }
      end)

    Modify the last return to add the other options you wish to register:

     return {
        winopts = { height = h, width = 0.60, row = 0.40 },
        fzf_opts = {
          ["--layout"] = "reverse-list",
          ["--info"] = "hidden",
        },
      }

    Test with different length items using:

    -- `:=` is equal to `:lua` since neovim 0.9
    := vim.ui.select({1,2}, {}, function() end)
    := vim.ui.select({1,2,3,4,5,6,7,8,9}, {}, function() end)
  3. ibhagwan commented on Jun 24, 2023

    @ibhagwan
    Owner
  4. EmreDogann commented on Jun 24, 2023

    @EmreDogann
    Author

    Just played around with it and it seems to work no problem! Thanks a lot!
    It might also be worth putting your example up on the wiki.

    image

    require("fzf-lua").register_ui_select(function(_, items)
    	-- Auto-height
    	local min_h, max_h = 0.15, 0.70
    	local h = (#items + 2) / vim.o.lines
    	if h < min_h then
    		h = min_h
    	elseif h > max_h then
    		h = max_h
    	end
    
    	-- Auto-width
    	-- if (#items < 10000) then	-- Maybe disable auto-width on large results?
    	local min_w, max_w = 0.05, 0.70
    	local longest = 0
    	for i, v in ipairs(items) do
    		local length = #v
    		if length > longest then
    			longest = length
    		end
    	end
    	-- needs minimum 7 in my case due to the extra stuff fzf adds on the left side (markers, numbers, extra padding, etc).
    	local w = (longest + 9) / vim.o.columns
    	if w < min_w then
    		w = min_w
    	elseif w > max_w then
    		w = max_w
    	end
    	-- end
    
    	return {
    		winopts = {
    			height = h,
    			width = w,
    			row = 0.5,
    			col = 0.5,
    		},
    		fzf_opts = {
    			["--layout"] = "reverse-list",
    			["--info"] = "hidden",
    		},
    	}
    end)

    The only problem I see here is that the auto-width cannot adjust for the extra items taken up by the left side of the fzf window (markers/prompts, numbers, etc). Not a huge deal but it was something I noticed.

    • Should we take into consideration min/max height?
    • As long as we're willing to take the performance penalty (enumerating all items), why not have adjustable width too?
    • Line padding (do you wish to have the exact length or a few lines extra?)
    • Other nuances...

    I decided the best solution would be to add the ui select items to the already existing options callback, this way each user can customize this to their liking, as for the request in the OP, below is what I came up with:

    Yeah, I was wondering about those questions as well before. I think this is the best of both worlds, otherwise yeah so many edge cases to get this to work. This solution also follows similarly with the example in the wiki on dynamic resizing of the windows so things are more consistent.

    Now that I think about it, this approach might also work for winopts_fn although to be honest I see it being less useful there.

  5. ibhagwan commented on Jun 24, 2023

    @ibhagwan
    Owner

    Now that I think about it, this approach might also work for winopts_fn although to be honest I see it being less useful there.

    If you take a look at the Wiki’s advanced API you’d notice three types of content: table, function and string - this can work only for table contents when you know the number of items in advance, the other types generate async contents (for performance and UX).

  6. EmreDogann commented on Jun 24, 2023

    @EmreDogann
    Author

    Ah I see, that makes sense. Thanks.

  7. ibhagwan commented on Jun 24, 2023

    @ibhagwan
    Owner
  8. ShaolunWang commented on Jun 12, 2024

    @ShaolunWang

    RE: auto resize width

    The one above from @EmreDogann didn't work for me. I spent some time investigating, and the issue was that items parameter was not formatted here. I pre-formatted the string, and use the string length to resize it. It seems to be working:

    local min_w, max_w = 0.05, 0.70
    local longest = 0
    for i, e in ipairs(items) do
    -- this bit is from fzf-lua's source code for register ui select
    	local format_entry = ui_opts.format_item and ui_opts.format_item(e) or tostring(e)
    --
    	local length = tostring(format_entry):len()
    	if length > longest then
    		longest = length
    	end
    end
    
    local w = (longest + 9) / vim.o.columns
    if w < min_w then
    	w = min_w
    elseif w > max_w then
    	w = max_w
    end
    
  9. ibhagwan commented on Jun 12, 2024

    @ibhagwan
    Owner

    The one above from @EmreDogann didn't work for me. I spent some time investigating, and the issue was that items parameter was not formatted here. I pre-formatted the string, and use the string length to resize it. It seems to be working:

    @ShaolunWang, the snippet in the wiki only auto-adjusts the height, so you’re correct in the sense it wasn’t supposed to format the items and look at the width.

  10. joelostblom commented on Feb 7, 2026

    @joelostblom

    I'm failing when trying to use the snippet in the wiki to autosize the height of the vim.ui.select window. This is what the window looks like by default before trying to resize it:

    Image

    After applying the wiki snippet, the window now becomes too small so that not all the three lines are visible in code actions and almost nothing of the diff is visible:

    Image

    What I was expecting was something like this where both the section for the code actions and the section for the diff preview autosized based on the number of lines inside each:

    Image

    I tested on both neovim 11.5 and 12 nightly with the same result. Any idea of what might be going awry here?

  11. ibhagwan commented on Feb 7, 2026

    @ibhagwan
    Owner

    I tested on both neovim 11.5 and 12 nightly with the same result. Any idea of what might be going awry here?

    The snippet only takes into account the amount of entires, for code preview you have to add a few more lines for the preview, increase height until it looks like what you want.

  12. joelostblom commented on Feb 8, 2026

    @joelostblom

    Thanks for the quick reply! The issue with just increasing the number of lines is that the extra vertical space seems to be divided evenly between between the two boxes in the pop up. So if I add extra lines to fit the output from the diff, it leads to several blank lines below the code actions, which pushes the diff preview further down on the screen:

    Image

    The desired output would be something like this where each box is autosized to its content:

    Image
  13. ibhagwan commented on Feb 8, 2026

    @ibhagwan
    Owner

    The issue with just increasing the number of lines is that the extra vertical space seems to be divided evenly between between the two boxes in the pop up. So if I add extra lines to fit the output from the diff, it leads to several blank lines below the code actions, which pushes the diff preview further down on the screen:

    The issue here is that the dynamic sizing in ui.select wiki wasn’t meant for code actions with previews, the preview size is a percentage of the total window, if you want this to fit perfectly you’ll need to adjust the code for code actions differently (you can use the ui_opts.kind to determine the type of interface) and reverse calculate the total size of window height needed for both the items+preview.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions