GitHub - stevearc/dressing.nvim: Neovim plugin to improve the default vim.ui interfaces (original) (raw)

Warning

This plugin is archived! It still works, but I recommend that you use snacks.nvim instead for your vim.ui.* interfaces. To read more about why this decision was made, see #190

With the release of Neovim 0.6 we were given the start of extensible core UI hooks (vim.ui.select andvim.ui.input). They exist to allow plugin authors to override them with improvements upon the default behavior, so that's exactly what we're going to do.

It is a goal to match and not extend the core Neovim API. All options that core respects will be respected, and we will not accept any custom parameters or options in the functions. Customization will be done entirely using a separateconfiguration method.

Requirements

Neovim 0.8.0+ (for earlier versions, use the nvim-0.7 or nvim-0.5 branch)

Screenshots

vim.input replacement (handling a LSP rename)

Screenshot from 2021-12-09 17-36-16

vim.select (telescope)

Screenshot from 2021-12-02 19-46-01

vim.select (fzf)

Screenshot from 2021-12-02 19-46-54

vim.select (nui)

Screenshot from 2021-12-02 19-47-56

vim.select (snacks_picker)

Screenshot from 2025-01-29 03-08-00

vim.select (built-in)

Screenshot from 2021-12-04 17-14-32

Installation

dressing.nvim supports all the usual plugin managers

lazy.nvim

{ 'stevearc/dressing.nvim', opts = {}, }

Packer

require('packer').startup(function() use {'stevearc/dressing.nvim'} end)

Paq

require "paq" { {'stevearc/dressing.nvim'}; }

vim-plug

Plug 'stevearc/dressing.nvim'

dein

call dein#add('stevearc/dressing.nvim')

Pathogen

git clone --depth=1 https://github.com/stevearc/dressing.nvim.git ~/.vim/bundle/

Neovim native package

git clone --depth=1 https://github.com/stevearc/dressing.nvim.git
"${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/dressing.nvim/start/dressing.nvim

Configuration

If you're fine with the defaults, you're good to go after installation. If you want to tweak, call this function:

require("dressing").setup({ input = { -- Set to false to disable the vim.ui.input implementation enabled = true,

-- Default prompt string
default_prompt = "Input",

-- Trim trailing `:` from prompt
trim_prompt = true,

-- Can be 'left', 'right', or 'center'
title_pos = "left",

-- The initial mode when the window opens (insert|normal|visual|select).
start_mode = "insert",

-- These are passed to nvim_open_win
border = "rounded",
-- 'editor' and 'win' will default to being centered
relative = "cursor",

-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
prefer_width = 40,
width = nil,
-- min_width and max_width can be a list of mixed types.
-- min_width = {20, 0.2} means "the greater of 20 columns or 20% of total"
max_width = { 140, 0.9 },
min_width = { 20, 0.2 },

buf_options = {},
win_options = {
  -- Disable line wrapping
  wrap = false,
  -- Indicator for when text exceeds window
  list = true,
  listchars = "precedes:…,extends:…",
  -- Increase this for more context when text scrolls off the window
  sidescrolloff = 0,
},

-- Set to `false` to disable
mappings = {
  n = {
    ["<Esc>"] = "Close",
    ["<CR>"] = "Confirm",
  },
  i = {
    ["<C-c>"] = "Close",
    ["<CR>"] = "Confirm",
    ["<Up>"] = "HistoryPrev",
    ["<Down>"] = "HistoryNext",
  },
},

override = function(conf)
  -- This is the config that will be passed to nvim_open_win.
  -- Change values here to customize the layout
  return conf
end,

-- see :help dressing_get_config
get_config = nil,

}, select = { -- Set to false to disable the vim.ui.select implementation enabled = true,

-- Priority list of preferred vim.select implementations
backend = { "telescope", "fzf_lua", "fzf", "builtin", "nui" },

-- Trim trailing `:` from prompt
trim_prompt = true,

-- Options for telescope selector
-- These are passed into the telescope picker directly. Can be used like:
-- telescope = require('telescope.themes').get_ivy({...})
telescope = nil,

-- Options for fzf selector
fzf = {
  window = {
    width = 0.5,
    height = 0.4,
  },
},

-- Options for fzf-lua
fzf_lua = {
  -- winopts = {
  --   height = 0.5,
  --   width = 0.5,
  -- },
},

-- Options for nui Menu
nui = {
  position = "50%",
  size = nil,
  relative = "editor",
  border = {
    style = "rounded",
  },
  buf_options = {
    swapfile = false,
    filetype = "DressingSelect",
  },
  win_options = {
    winblend = 0,
  },
  max_width = 80,
  max_height = 40,
  min_width = 40,
  min_height = 10,
},

-- Options for built-in selector
builtin = {
  -- Display numbers for options and set up keymaps
  show_numbers = true,
  -- These are passed to nvim_open_win
  border = "rounded",
  -- 'editor' and 'win' will default to being centered
  relative = "editor",

  buf_options = {},
  win_options = {
    cursorline = true,
    cursorlineopt = "both",
    -- disable highlighting for the brackets around the numbers
    winhighlight = "MatchParen:",
    -- adds padding at the left border
    statuscolumn = " ",
  },

  -- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
  -- the min_ and max_ options can be a list of mixed types.
  -- max_width = {140, 0.8} means "the lesser of 140 columns or 80% of total"
  width = nil,
  max_width = { 140, 0.8 },
  min_width = { 40, 0.2 },
  height = nil,
  max_height = 0.9,
  min_height = { 10, 0.2 },

  -- Set to `false` to disable
  mappings = {
    ["<Esc>"] = "Close",
    ["<C-c>"] = "Close",
    ["<CR>"] = "Confirm",
  },

  override = function(conf)
    -- This is the config that will be passed to nvim_open_win.
    -- Change values here to customize the layout
    return conf
  end,
},

-- Used to override format_item. See :help dressing-format
format_item_override = {},

-- see :help dressing_get_config
get_config = nil,

}, })

Highlights

A common way to adjust the highlighting of just the dressing windows is by providing a winhighlight option in the config. See :help winhighlightfor more details. Example:

require('dressing').setup({ input = { win_options = { winhighlight = 'NormalFloat:DiagnosticError' } } })

Advanced configuration

For each of the input and select configs, there is an optionget_config. This can be a function that accepts the opts parameter that is passed in to vim.select or vim.input. It must return either nil (to no-op) or config values to use in place of the global config values for that module.

For example, if you want to use a specific configuration for code actions:

require('dressing').setup({ select = { get_config = function(opts) if opts.kind == 'codeaction' then return { backend = 'nui', nui = { relative = 'cursor', max_width = 40, } } end end } })

Notes for plugin authors

TL;DR: you can customize the telescope vim.ui.select implementation by passing telescope into opts.

The vim.ui hooks are a great boon for us because we can now assume that users will have a reasonable UI available for simple input operations. We no longer have to build separate implementations for each of fzf, telescope, ctrlp, etc. The tradeoff is that vim.ui.select is less customizable than any of these options, so if you wanted to have a preview window (like telescope supports), it is no longer an option.

My solution to this is extending the opts that are passed to vim.ui.select. You can add a telescope field that will be passed directly into the picker, allowing you to customize any part of the UI. If a user has both dressing and telescope installed, they will get your custom picker UI. If either of those are not true, the selection UI will gracefully degrade to whatever the user has configured for vim.ui.select.

An example of usage:

vim.ui.select({'apple', 'banana', 'mango'}, { prompt = "Title", telescope = require("telescope.themes").get_cursor(), }, function(selected) end)

For now this is available only for the telescope backend, but feel free to request additions.