stevearc / dressing.nvim

Neovim plugin to improve the default vim.ui interfaces
MIT License
1.7k stars 32 forks source link

[Feature Request] Per-instance or dynamic option for vim.ui.input #71

Closed wookayin closed 1 year ago

wookayin commented 1 year ago

We can configure many options for vim.ui.input via dressing.setup { input = ... }, but this applies globally only and there seems no way to override with per-instance options at this moment. For example, one may want to make the vim.ui.input window have a different size, different position (relative) just temporarily.

I tried something like

vim.ui.input ({
    prompt = "Enter the new value >",   -- this works
    relative = "editor",  -- no effects
    width = 100,  -- no effects,
    winblend = 50,  -- no effects
    override = function(conf)
         -- This is the config that will be passed to nvim_open_win.
         return {}
    end,  -- no effects
})

but most of the options other than prompt, default, etc. (as per the "standard" spec of vim.ui.input()) do not work. Although those options would work only when dressing.nvim is used, but I think it's perfectly fine to have the opts table contains some specific and optional arguments.

wookayin commented 1 year ago

Actually it's not a bug (the GH issue template does not have that category) -- please feel free to remove the label.

stevearc commented 1 year ago

API extensions are something that I'm willing to add (I've already added one for telescope-specific behavior in vim.ui.select), but it's something I still want to be very careful about. Any extensions should

The telescope extension will never conflict with core (the telescope key is safe because they will never write code in core that depends on a plugin), and gracefully degrades for other implementations. It can conflict with user settings, but it unlocks the ability for callsites to use telescope-specific features (e.g. preview window), which I thought was significant enough upside. Additionally, the API extension is extremely simple: just a single 'telescope' parameter that is passed to telescope directly.

My main concerns about the proposed extensions to vim.ui.input are the conflicts with user-supplied options and the relatively large addition to the API surface area.

Why do you think it makes sense for the callsite to be able to override layout and styling options set by the user? What is your specific use case?

wookayin commented 1 year ago

Thank you @stevearc for detailed explanation. I totally agree with your rationale to make it as compatible and as same as the built-in vim.ui functions, and they make lots of sense to me.

I was trying to use some dynamic, or semi-dynamic way of configuring those UI attributes so that I can have different layouts, width, position, etc. for different use cases. Some input dialogs are better with relative = cursor (e.g. entering some buffer- or window-local variables) and others are better with relative = editor (e.g. entering some global values), for instance.

Actually this is already possible through get_config, mentioned in README: :help dressing_get_config.

dressing.setup {
  input = {
    relative = 'editor', -- default
    get_config = function(opts)
      -- opts: the lua table passed to vim.ui.input(opts, ...)
      -- returns a specific configuration to override with
      if opts.kind == 'codeaction':
        return { relative = 'cursor' }
      end
      return {}
    end
  }
}

So this is already more than enough because I can use different kind (or any other field) to tell where vim.ui.input was called from.

Indeed, one could take advantage of this feature to implement more dynamic, per-instance configuration overriding if they would really want it. It's kind of a hack (at users' own risk), but officially the plugin itself does not support dynamic options against potential discrepancy with the original behavior. For instance,

dressing.setup {
  input = {
    width = 40, -- default
    relative = 'editor', -- default
    get_config = function(opts)
      -- Just a demonstration: allow overriding `width` and `relative` only
      local extra_opts = {}
      if opts.width ~= nil then extra_opts.width = opts.width end
      if opts.relative ~= nil then extra_opts.relative = opts.relative end
      return extra_opts
    end
  }
}

With such a config, we can do something like:

local on_confirm = function(x) vim.notify(vim.inspect(x)) end
vim.ui.input({ default_prompt = "Enter >", width = 100, relative = 'cursor' }, on_confirm)
stevearc commented 1 year ago

I didn't think to do that, but you're right the get_config can be modified to do your own customizations. I think the only thing standing in the way of making this a feature instead of a hack is that officially (in the Neovim docs) vim.ui.input does not support/mention the use of a kind option the way vim.ui.select does.

Liquidmantis commented 1 year ago

Thanks for this, @wookayin ! I was trying to have a customized input for creating notes and needed the example of using get_config to sort it out. Wouldn't have figured it out without this issue.

jan-xyz commented 1 year ago

@wookayin I generalised your idea and with

        require("dressing").setup({
            ...
            input = {
                win_options = {
                    sidescrolloff = 4,
                },
                get_config = function(extra_opts)
                    local opts = require("dressing.config").input
                    return vim.tbl_deep_extend("force", opts, extra_opts or {})
                end,
            },
            ...
            select = {
                get_config = function(extra_opts)
                    local opts = require("dressing.config").select
                    return vim.tbl_deep_extend("force", opts, extra_opts or {})
                end,
            }
        })

it's possible to fully customise the config from the call side. I am well aware that this is dangerous and might lead to breaking things when it clashes with newly introduced parameters to the Neovim API, but I think it's fine to To This At Your Own Risk, kind of thing! Thanks for the initial idea!