Initial commit
This commit is contained in:
commit
7f6cf7fb80
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
luacheck .
|
||||||
|
stylua --check .
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
HERE = os.path.dirname(__file__)
|
||||||
|
ROOT = os.path.abspath(os.path.join(HERE, os.path.pardir))
|
||||||
|
README = os.path.join(ROOT, "README.md")
|
||||||
|
DOC = os.path.join(ROOT, "doc", "dressing.txt")
|
||||||
|
|
||||||
|
|
||||||
|
def indent(lines: List[str], amount: int) -> List[str]:
|
||||||
|
ret = []
|
||||||
|
for line in lines:
|
||||||
|
if amount >= 0:
|
||||||
|
ret.append(" " * amount + line)
|
||||||
|
else:
|
||||||
|
space = re.match(r"[ \t]+", line)
|
||||||
|
if space:
|
||||||
|
ret.append(line[min(abs(amount), space.span()[1]) :])
|
||||||
|
else:
|
||||||
|
ret.append(line)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def replace_section(file: str, start_pat: str, end_pat: str, lines: List[str]) -> None:
|
||||||
|
prefix_lines: List[str] = []
|
||||||
|
postfix_lines: List[str] = []
|
||||||
|
file_lines = prefix_lines
|
||||||
|
found_section = False
|
||||||
|
with open(file, "r", encoding="utf-8") as ifile:
|
||||||
|
inside_section = False
|
||||||
|
for line in ifile:
|
||||||
|
if inside_section:
|
||||||
|
if re.match(end_pat, line):
|
||||||
|
inside_section = False
|
||||||
|
file_lines = postfix_lines
|
||||||
|
file_lines.append(line)
|
||||||
|
else:
|
||||||
|
if not found_section and re.match(start_pat, line):
|
||||||
|
inside_section = True
|
||||||
|
found_section = True
|
||||||
|
file_lines.append(line)
|
||||||
|
|
||||||
|
if inside_section or not found_section:
|
||||||
|
raise Exception(f"could not find file section {start_pat}")
|
||||||
|
|
||||||
|
all_lines = prefix_lines + lines + postfix_lines
|
||||||
|
with open(file, "w", encoding="utf-8") as ofile:
|
||||||
|
ofile.write("".join(all_lines))
|
||||||
|
|
||||||
|
|
||||||
|
def read_section(filename: str, start_pat: str, end_pat: str) -> List[str]:
|
||||||
|
lines = []
|
||||||
|
with open(filename, "r", encoding="utf-8") as ifile:
|
||||||
|
inside_section = False
|
||||||
|
for line in ifile:
|
||||||
|
if inside_section:
|
||||||
|
if re.match(end_pat, line):
|
||||||
|
break
|
||||||
|
lines.append(line)
|
||||||
|
elif re.match(start_pat, line):
|
||||||
|
inside_section = True
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Update the README"""
|
||||||
|
config_file = os.path.join(ROOT, "lua", "dressing", "config.lua")
|
||||||
|
opt_lines = read_section(config_file, r"^\s*local default_config =", r"^}$")
|
||||||
|
replace_section(README, r"^require\('dressing'\)\.setup", r"^}\)$", opt_lines)
|
||||||
|
replace_section(
|
||||||
|
DOC, r"^\s*require\('dressing'\)\.setup", r"^\s*}\)$", indent(opt_lines, 4)
|
||||||
|
)
|
||||||
|
|
||||||
|
get_config_lines = read_section(DOC, r"^dressing.get_config", "^===")
|
||||||
|
for i, line in enumerate(get_config_lines):
|
||||||
|
if re.match(r"^\s*>$", line):
|
||||||
|
get_config_lines[i] = "```lua\n"
|
||||||
|
break
|
||||||
|
get_config_lines.append("```\n")
|
||||||
|
replace_section(README, r"^## Custom config", r"^#", indent(get_config_lines, -4))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Lint
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
luacheck:
|
||||||
|
name: Luacheck
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo add-apt-repository universe
|
||||||
|
sudo apt install luarocks -y
|
||||||
|
sudo luarocks install luacheck
|
||||||
|
|
||||||
|
- name: Run Luacheck
|
||||||
|
run: luacheck .
|
||||||
|
|
||||||
|
stylua:
|
||||||
|
name: StyLua
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Stylua
|
||||||
|
uses: JohnnyMorganz/stylua-action@1.0.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --check .
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
name: Update README
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-readme:
|
||||||
|
name: Update README
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Update README
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
COMMIT_MSG: |
|
||||||
|
[docgen] Update README.md
|
||||||
|
skip-checks: true
|
||||||
|
run: |
|
||||||
|
git config user.email "actions@github"
|
||||||
|
git config user.name "Github Actions"
|
||||||
|
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
|
||||||
|
python .github/update_readme.py
|
||||||
|
git add README.md
|
||||||
|
# Only commit and push if we have changes
|
||||||
|
git diff --quiet && git diff --staged --quiet || (git commit -m "${COMMIT_MSG}"; git push origin HEAD:${GITHUB_REF})
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Compiled Lua sources
|
||||||
|
luac.out
|
||||||
|
|
||||||
|
# luarocks build files
|
||||||
|
*.src.rock
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.os
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
*.def
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
max_comment_line_length = false
|
||||||
|
codes = true
|
||||||
|
|
||||||
|
exclude_files = {
|
||||||
|
"tests/",
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore = {
|
||||||
|
"212", -- Unused argument
|
||||||
|
"631", -- Line is too long
|
||||||
|
"122", -- Setting a readonly global
|
||||||
|
}
|
||||||
|
|
||||||
|
read_globals = {
|
||||||
|
"vim",
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
column_width = 100
|
||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 2
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Steven Arcangeli
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,180 @@
|
||||||
|
# Dressing.nvim
|
||||||
|
|
||||||
|
With the release of Neovim 0.6 we were given the start of extensible core UI
|
||||||
|
hooks ([vim.ui.select](https://github.com/neovim/neovim/pull/15771) and
|
||||||
|
[vim.ui.input](https://github.com/neovim/neovim/pull/15959)). 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 separate
|
||||||
|
[configuration](#configuration) method.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Neovim 0.5+
|
||||||
|
|
||||||
|
On versions prior to 0.6, this plugin will act as a polyfill for `vim.ui`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
dressing.nvim supports all the usual plugin managers
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Packer</summary>
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('packer').startup(function()
|
||||||
|
use {'stevearc/dressing.nvim'}
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Paq</summary>
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require "paq" {
|
||||||
|
{'stevearc/dressing.nvim'};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>vim-plug</summary>
|
||||||
|
|
||||||
|
```vim
|
||||||
|
Plug 'stevearc/dressing.nvim'
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>dein</summary>
|
||||||
|
|
||||||
|
```vim
|
||||||
|
call dein#add('stevearc/dressing.nvim')
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Pathogen</summary>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth=1 https://github.com/stevearc/dressing.nvim.git ~/.vim/bundle/
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Neovim native package</summary>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
If you're fine with the defaults, you're good to go after installation. If you
|
||||||
|
want to tweak, call this function:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('dressing').setup({
|
||||||
|
input = {
|
||||||
|
-- Default prompt string
|
||||||
|
default_prompt = "➤ ",
|
||||||
|
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "SW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
prefer_width = 40,
|
||||||
|
max_width = nil,
|
||||||
|
min_width = 20,
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
select = {
|
||||||
|
-- Priority list of preferred vim.select implementations
|
||||||
|
backend = { "telescope", "fzf", "builtin", "nui" },
|
||||||
|
|
||||||
|
-- Options for telescope selector
|
||||||
|
telescope = {},
|
||||||
|
|
||||||
|
-- Options for fzf selector
|
||||||
|
fzf = {
|
||||||
|
width = 0.5,
|
||||||
|
height = 0.4,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for nui Menu
|
||||||
|
nui = {
|
||||||
|
position = "50%",
|
||||||
|
size = nil,
|
||||||
|
relative = "editor",
|
||||||
|
border = {
|
||||||
|
style = "rounded",
|
||||||
|
},
|
||||||
|
max_width = 80,
|
||||||
|
max_height = 40,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for built-in selector
|
||||||
|
builtin = {
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "NW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
width = nil,
|
||||||
|
max_width = 0.8,
|
||||||
|
min_width = 40,
|
||||||
|
height = nil,
|
||||||
|
max_height = 0.9,
|
||||||
|
min_height = 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom config per usage
|
||||||
|
For each of the `input` and `select` configs, there is an option
|
||||||
|
`get_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:
|
||||||
|
```lua
|
||||||
|
require('dressing').setup({
|
||||||
|
select = {
|
||||||
|
get_config = function(opts)
|
||||||
|
if opts.kind == 'codeaction' then
|
||||||
|
return {
|
||||||
|
backend = 'nui',
|
||||||
|
nui = {
|
||||||
|
relative = 'cursor',
|
||||||
|
max_width = 40,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
```
|
||||||
|
## Similar projects
|
||||||
|
* [nvim-notify](https://github.com/rcarriga/nvim-notify) - doing pretty much the
|
||||||
|
same thing but for `vim.notify`
|
||||||
|
* [nui.nvim](https://github.com/MunifTanjim/nui.nvim) - provides common UI
|
||||||
|
components for plugin authors
|
|
@ -0,0 +1,20 @@
|
||||||
|
func! dressing#prompt_confirm(text) abort
|
||||||
|
call v:lua.dressing_prompt_confirm(a:text)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! dressing#prompt_cancel() abort
|
||||||
|
lua dressing_prompt_cancel()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function! dressing#fzf_run(labels, options, window) abort
|
||||||
|
call fzf#run({
|
||||||
|
\ 'source': a:labels,
|
||||||
|
\ 'sink': funcref('dressing#fzf_choice'),
|
||||||
|
\ 'options': a:options,
|
||||||
|
\ 'window': a:window,
|
||||||
|
\ })
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! dressing#fzf_choice(label) abort
|
||||||
|
call v:lua.dressing_fzf_choice(a:label)
|
||||||
|
endfunction
|
|
@ -0,0 +1,102 @@
|
||||||
|
*dressing.txt*
|
||||||
|
*Dressing* *dressing* *dressing.nvim*
|
||||||
|
===============================================================================
|
||||||
|
CONFIGURATION *dressing-configuration*
|
||||||
|
|
||||||
|
Configure dressing.nvim by calling the setup() function.
|
||||||
|
>
|
||||||
|
require('dressing').setup({
|
||||||
|
input = {
|
||||||
|
-- Default prompt string
|
||||||
|
default_prompt = "➤ ",
|
||||||
|
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "SW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
prefer_width = 40,
|
||||||
|
max_width = nil,
|
||||||
|
min_width = 20,
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
select = {
|
||||||
|
-- Priority list of preferred vim.select implementations
|
||||||
|
backend = { "telescope", "fzf", "builtin", "nui" },
|
||||||
|
|
||||||
|
-- Options for telescope selector
|
||||||
|
telescope = {},
|
||||||
|
|
||||||
|
-- Options for fzf selector
|
||||||
|
fzf = {
|
||||||
|
width = 0.5,
|
||||||
|
height = 0.4,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for nui Menu
|
||||||
|
nui = {
|
||||||
|
position = "50%",
|
||||||
|
size = nil,
|
||||||
|
relative = "editor",
|
||||||
|
border = {
|
||||||
|
style = "rounded",
|
||||||
|
},
|
||||||
|
max_width = 80,
|
||||||
|
max_height = 40,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for built-in selector
|
||||||
|
builtin = {
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "NW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
width = nil,
|
||||||
|
max_width = 0.8,
|
||||||
|
min_width = 40,
|
||||||
|
height = nil,
|
||||||
|
max_height = 0.9,
|
||||||
|
min_height = 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
dressing.get_config() *dressing_get_config()*
|
||||||
|
For each of the `input` and `select` configs, there is an option
|
||||||
|
`get_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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
vim:ft=help:et:ts=2:sw=2:sts=2:norl
|
|
@ -0,0 +1,6 @@
|
||||||
|
Dressing dressing.txt /*Dressing*
|
||||||
|
dressing dressing.txt /*dressing*
|
||||||
|
dressing-configuration dressing.txt /*dressing-configuration*
|
||||||
|
dressing.nvim dressing.txt /*dressing.nvim*
|
||||||
|
dressing.txt dressing.txt /*dressing.txt*
|
||||||
|
dressing_get_config() dressing.txt /*dressing_get_config()*
|
|
@ -0,0 +1,92 @@
|
||||||
|
local default_config = {
|
||||||
|
input = {
|
||||||
|
-- Default prompt string
|
||||||
|
default_prompt = "➤ ",
|
||||||
|
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "SW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
prefer_width = 40,
|
||||||
|
max_width = nil,
|
||||||
|
min_width = 20,
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
select = {
|
||||||
|
-- Priority list of preferred vim.select implementations
|
||||||
|
backend = { "telescope", "fzf", "builtin", "nui" },
|
||||||
|
|
||||||
|
-- Options for telescope selector
|
||||||
|
telescope = {},
|
||||||
|
|
||||||
|
-- Options for fzf selector
|
||||||
|
fzf = {
|
||||||
|
window = {
|
||||||
|
width = 0.5,
|
||||||
|
height = 0.4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for nui Menu
|
||||||
|
nui = {
|
||||||
|
position = "50%",
|
||||||
|
size = nil,
|
||||||
|
relative = "editor",
|
||||||
|
border = {
|
||||||
|
style = "rounded",
|
||||||
|
},
|
||||||
|
max_width = 80,
|
||||||
|
max_height = 40,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Options for built-in selector
|
||||||
|
builtin = {
|
||||||
|
-- These are passed to nvim_open_win
|
||||||
|
anchor = "NW",
|
||||||
|
relative = "cursor",
|
||||||
|
row = 0,
|
||||||
|
col = 0,
|
||||||
|
border = "rounded",
|
||||||
|
|
||||||
|
-- These can be integers or a float between 0 and 1 (e.g. 0.4 for 40%)
|
||||||
|
width = nil,
|
||||||
|
max_width = 0.8,
|
||||||
|
min_width = 40,
|
||||||
|
height = nil,
|
||||||
|
max_height = 0.9,
|
||||||
|
min_height = 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
-- see :help dressing_get_config
|
||||||
|
get_config = nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local M = vim.deepcopy(default_config)
|
||||||
|
|
||||||
|
M.update = function(opts)
|
||||||
|
local newconf = vim.tbl_deep_extend("force", default_config, opts or {})
|
||||||
|
for k, v in pairs(newconf) do
|
||||||
|
M[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
M.get_mod_config = function(key, ...)
|
||||||
|
if not M[key].get_config then
|
||||||
|
return M[key]
|
||||||
|
end
|
||||||
|
local conf = M[key].get_config(...)
|
||||||
|
if conf then
|
||||||
|
return vim.tbl_deep_extend("force", M[key], conf)
|
||||||
|
else
|
||||||
|
return M[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,18 @@
|
||||||
|
local config = require("dressing.config")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.setup = function(opts)
|
||||||
|
config.update(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.patch = function()
|
||||||
|
-- For Neovim before 0.6
|
||||||
|
if not vim.ui then
|
||||||
|
vim.ui = {}
|
||||||
|
end
|
||||||
|
vim.ui.input = require("dressing.input")
|
||||||
|
vim.ui.select = require("dressing.select")
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,83 @@
|
||||||
|
local global_config = require("dressing.config")
|
||||||
|
local util = require("dressing.util")
|
||||||
|
|
||||||
|
local function clear_callbacks()
|
||||||
|
_G.dressing_prompt_confirm = function() end
|
||||||
|
_G.dressing_prompt_cancel = function() end
|
||||||
|
_G.dressing_prompt_hl = function() end
|
||||||
|
end
|
||||||
|
|
||||||
|
clear_callbacks()
|
||||||
|
|
||||||
|
return function(opts, on_confirm)
|
||||||
|
vim.validate({
|
||||||
|
on_confirm = { on_confirm, "function", false },
|
||||||
|
})
|
||||||
|
|
||||||
|
opts = opts or {}
|
||||||
|
local config = global_config.get_mod_config("input", opts)
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
local prompt = opts.prompt or config.default_prompt
|
||||||
|
local width = util.calculate_width(config.prefer_width + vim.api.nvim_strwidth(prompt), config)
|
||||||
|
local winopt = {
|
||||||
|
relative = config.relative,
|
||||||
|
anchor = config.anchor,
|
||||||
|
row = config.row,
|
||||||
|
col = config.col,
|
||||||
|
border = config.border,
|
||||||
|
width = width,
|
||||||
|
height = 1,
|
||||||
|
zindex = 150,
|
||||||
|
style = "minimal",
|
||||||
|
}
|
||||||
|
local winnr = vim.api.nvim_open_win(bufnr, true, winopt)
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "buftype", "prompt")
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "swapfile", false)
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
|
||||||
|
vim.fn.prompt_setprompt(bufnr, prompt)
|
||||||
|
_G.dressing_prompt_confirm = function(text)
|
||||||
|
clear_callbacks()
|
||||||
|
vim.api.nvim_win_close(winnr, true)
|
||||||
|
on_confirm(text)
|
||||||
|
end
|
||||||
|
_G.dressing_prompt_cancel = function()
|
||||||
|
clear_callbacks()
|
||||||
|
vim.api.nvim_win_close(winnr, true)
|
||||||
|
on_confirm(nil)
|
||||||
|
end
|
||||||
|
if opts.highlight then
|
||||||
|
_G.dressing_prompt_hl = function()
|
||||||
|
local text = vim.api.nvim_buf_get_lines(bufnr, 0, 1, true)[1]
|
||||||
|
local ns = vim.api.nvim_create_namespace("DressingHl")
|
||||||
|
local highlights
|
||||||
|
if type(opts.highlight) == "function" then
|
||||||
|
highlights = opts.highlight(text)
|
||||||
|
else
|
||||||
|
highlights = vim.fn[opts.highlight](text)
|
||||||
|
end
|
||||||
|
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
|
||||||
|
for _, highlight in ipairs(highlights) do
|
||||||
|
local start = highlight[1]
|
||||||
|
local stop = highlight[2]
|
||||||
|
local group = highlight[3]
|
||||||
|
vim.api.nvim_buf_add_highlight(bufnr, ns, group, 0, start, stop)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vim.cmd([[
|
||||||
|
autocmd TextChanged <buffer> lua dressing_prompt_hl()
|
||||||
|
autocmd TextChangedI <buffer> lua dressing_prompt_hl()
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
-- Would prefer to use v:lua directly here, but it doesn't work :(
|
||||||
|
vim.fn.prompt_setcallback(bufnr, "dressing#prompt_confirm")
|
||||||
|
vim.fn.prompt_setinterrupt(bufnr, "dressing#prompt_cancel")
|
||||||
|
vim.cmd([[
|
||||||
|
autocmd BufLeave <buffer> call dressing#prompt_cancel()
|
||||||
|
]])
|
||||||
|
vim.cmd("startinsert!")
|
||||||
|
if opts.default then
|
||||||
|
vim.fn.feedkeys(opts.default)
|
||||||
|
end
|
||||||
|
_G.dressing_prompt_hl()
|
||||||
|
end
|
|
@ -0,0 +1,74 @@
|
||||||
|
local util = require("dressing.util")
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.is_supported = function()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local _callback = function() end
|
||||||
|
local _items = {}
|
||||||
|
local function clear_callback()
|
||||||
|
_callback = function() end
|
||||||
|
_items = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
M.select = function(config, items, opts, on_choice)
|
||||||
|
_callback = function(item, idx)
|
||||||
|
clear_callback()
|
||||||
|
on_choice(item, idx)
|
||||||
|
end
|
||||||
|
_items = items
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "swapfile", false)
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
|
||||||
|
local lines = {}
|
||||||
|
local max_width = 1
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
local line = opts.format_item(item)
|
||||||
|
max_width = math.max(max_width, vim.api.nvim_strwidth(line))
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
|
||||||
|
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
|
||||||
|
local winopt = {
|
||||||
|
relative = config.relative,
|
||||||
|
anchor = config.anchor,
|
||||||
|
row = config.row,
|
||||||
|
col = config.col,
|
||||||
|
border = config.border,
|
||||||
|
width = util.calculate_width(max_width, config),
|
||||||
|
height = util.calculate_height(#lines, config),
|
||||||
|
zindex = 150,
|
||||||
|
style = "minimal",
|
||||||
|
}
|
||||||
|
local winnr = vim.api.nvim_open_win(bufnr, true, winopt)
|
||||||
|
vim.api.nvim_win_set_option(winnr, "cursorline", true)
|
||||||
|
if vim.fn.exists("&cursorlineopt") ~= 0 then
|
||||||
|
vim.api.nvim_win_set_option(winnr, "cursorlineopt", "both")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function map(lhs, rhs)
|
||||||
|
vim.api.nvim_buf_set_keymap(bufnr, "n", lhs, rhs, { silent = true, noremap = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
map("<CR>", [[<cmd>lua require('dressing.select.builtin').choose()<CR>]])
|
||||||
|
map("<C-c>", [[<cmd>lua require('dressing.select.builtin').cancel()<CR>]])
|
||||||
|
map("<Esc>", [[<cmd>lua require('dressing.select.builtin').cancel()<CR>]])
|
||||||
|
vim.cmd([[
|
||||||
|
autocmd BufLeave <buffer> lua require('dressing.select.builtin').cancel()
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
|
||||||
|
M.choose = function()
|
||||||
|
local cursor = vim.api.nvim_win_get_cursor(0)
|
||||||
|
local idx = cursor[1]
|
||||||
|
_callback(_items[idx], idx)
|
||||||
|
vim.api.nvim_win_close(0, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.cancel = function()
|
||||||
|
vim.api.nvim_win_close(0, true)
|
||||||
|
_callback(nil, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,36 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.is_supported = function()
|
||||||
|
return vim.fn.exists("*fzf#run") ~= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function clear_callback()
|
||||||
|
_G.dressing_fzf_choice = function() end
|
||||||
|
_G.dressing_fzf_cancel = function() end
|
||||||
|
end
|
||||||
|
|
||||||
|
clear_callback()
|
||||||
|
|
||||||
|
M.select = function(config, items, opts, on_choice)
|
||||||
|
local labels = {}
|
||||||
|
for i, item in ipairs(items) do
|
||||||
|
table.insert(labels, string.format("%d: %s", i, opts.format_item(item)))
|
||||||
|
end
|
||||||
|
_G.dressing_fzf_cancel = function()
|
||||||
|
clear_callback()
|
||||||
|
on_choice(nil, nil)
|
||||||
|
end
|
||||||
|
_G.dressing_fzf_choice = function(label)
|
||||||
|
clear_callback()
|
||||||
|
local colon = string.find(label, ":")
|
||||||
|
local lnum = tonumber(string.sub(label, 1, colon - 1))
|
||||||
|
local item = items[lnum]
|
||||||
|
on_choice(item, lnum)
|
||||||
|
end
|
||||||
|
vim.fn["dressing#fzf_run"](labels, string.format('--prompt="%s"', opts.prompt), config.window)
|
||||||
|
-- fzf doesn't have a cancel callback, so we have to make one.
|
||||||
|
-- the defer_fn is so that we can process the confirm event first
|
||||||
|
vim.cmd([[autocmd BufLeave <buffer> lua vim.defer_fn(function() dressing_fzf_cancel() end, 5)]])
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,39 @@
|
||||||
|
local global_config = require("dressing.config")
|
||||||
|
|
||||||
|
local function get_backend(config)
|
||||||
|
local backends = config.backend
|
||||||
|
if type(backends) ~= "table" then
|
||||||
|
backends = { backends }
|
||||||
|
end
|
||||||
|
for _, backend in ipairs(backends) do
|
||||||
|
local ok, mod = pcall(require, string.format("dressing.select.%s", backend))
|
||||||
|
if ok and mod.is_supported() then
|
||||||
|
return mod, backend
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return require("dressing.select.builtin"), "builtin"
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(items, opts, on_choice)
|
||||||
|
vim.validate({
|
||||||
|
items = { items, "table", false },
|
||||||
|
on_choice = { on_choice, "function", false },
|
||||||
|
})
|
||||||
|
opts = opts or {}
|
||||||
|
local config = global_config.get_mod_config("select", opts)
|
||||||
|
opts.prompt = opts.prompt or "Select one of:"
|
||||||
|
opts.format_item = opts.format_item or tostring
|
||||||
|
|
||||||
|
local backend, name = get_backend(config)
|
||||||
|
backend.select(config[name], items, opts, on_choice)
|
||||||
|
|
||||||
|
-- for i, item in pairs(items) do
|
||||||
|
-- table.insert(choices, string.format("%d: %s", i, format_item(item)))
|
||||||
|
-- end
|
||||||
|
-- local choice = vim.fn.inputlist(choices)
|
||||||
|
-- if choice < 1 or choice > #items then
|
||||||
|
-- on_choice(nil, nil)
|
||||||
|
-- else
|
||||||
|
-- on_choice(items[choice], choice)
|
||||||
|
-- end
|
||||||
|
end
|
|
@ -0,0 +1,55 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.is_supported = function()
|
||||||
|
return pcall(require, "nui.menu")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.select = function(config, items, opts, on_choice)
|
||||||
|
local Menu = require("nui.menu")
|
||||||
|
local event = require("nui.utils.autocmd").event
|
||||||
|
local lines = {}
|
||||||
|
for i, item in ipairs(items) do
|
||||||
|
table.insert(lines, Menu.item(opts.format_item(item), { value = item, idx = i }))
|
||||||
|
end
|
||||||
|
|
||||||
|
local border = vim.deepcopy(config.border)
|
||||||
|
border.text = {
|
||||||
|
top = opts.prompt,
|
||||||
|
top_align = "center",
|
||||||
|
}
|
||||||
|
local menu = Menu({
|
||||||
|
position = config.position,
|
||||||
|
size = config.size,
|
||||||
|
relative = config.relative,
|
||||||
|
border = border,
|
||||||
|
buf_options = {
|
||||||
|
swapfile = false,
|
||||||
|
},
|
||||||
|
win_options = {
|
||||||
|
winblend = 10,
|
||||||
|
},
|
||||||
|
enter = true,
|
||||||
|
}, {
|
||||||
|
lines = lines,
|
||||||
|
max_width = config.max_width,
|
||||||
|
max_height = config.max_height,
|
||||||
|
keymap = {
|
||||||
|
focus_next = { "j", "<Down>", "<Tab>" },
|
||||||
|
focus_prev = { "k", "<Up>", "<S-Tab>" },
|
||||||
|
close = { "<Esc>", "<C-c>" },
|
||||||
|
submit = { "<CR>" },
|
||||||
|
},
|
||||||
|
on_close = function()
|
||||||
|
on_choice(nil, nil)
|
||||||
|
end,
|
||||||
|
on_submit = function(item)
|
||||||
|
on_choice(item.value, item.idx)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
menu:mount()
|
||||||
|
|
||||||
|
menu:on(event.BufLeave, menu.menu_props.on_close, { once = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,58 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.is_supported = function()
|
||||||
|
return pcall(require, "telescope")
|
||||||
|
end
|
||||||
|
|
||||||
|
M.select = function(config, items, opts, on_choice)
|
||||||
|
local themes = require("telescope.themes")
|
||||||
|
local actions = require("telescope.actions")
|
||||||
|
local state = require("telescope.actions.state")
|
||||||
|
local pickers = require("telescope.pickers")
|
||||||
|
local finders = require("telescope.finders")
|
||||||
|
local conf = require("telescope.config").values
|
||||||
|
|
||||||
|
local entry_maker = function(item)
|
||||||
|
local formatted = opts.format_item(item)
|
||||||
|
return {
|
||||||
|
display = formatted,
|
||||||
|
ordinal = formatted,
|
||||||
|
value = item,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local picker_opts = themes.get_dropdown({
|
||||||
|
previewer = false,
|
||||||
|
})
|
||||||
|
pickers.new(picker_opts, {
|
||||||
|
prompt_title = opts.prompt,
|
||||||
|
finder = finders.new_table({
|
||||||
|
results = items,
|
||||||
|
entry_maker = entry_maker,
|
||||||
|
}),
|
||||||
|
sorter = conf.generic_sorter(opts),
|
||||||
|
attach_mappings = function(prompt_bufnr)
|
||||||
|
actions.select_default:replace(function()
|
||||||
|
local selection = state.get_selected_entry()
|
||||||
|
actions._close(prompt_bufnr, false)
|
||||||
|
local idx = nil
|
||||||
|
for i, item in ipairs(items) do
|
||||||
|
if item == selection.value then
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
on_choice(selection.value, idx)
|
||||||
|
end)
|
||||||
|
|
||||||
|
actions.close:replace(function()
|
||||||
|
actions._close(prompt_bufnr, false)
|
||||||
|
on_choice(nil, nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
}):find()
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1,46 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function is_float(value)
|
||||||
|
local _, p = math.modf(value)
|
||||||
|
return p ~= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calc_float(value, max_value)
|
||||||
|
if value and is_float(value) then
|
||||||
|
return math.min(max_value, value * max_value)
|
||||||
|
else
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function calculate_dim(desired_size, size, min_size, max_size, total_size)
|
||||||
|
local ret = calc_float(size, total_size)
|
||||||
|
if not ret then
|
||||||
|
ret = calc_float(desired_size, total_size)
|
||||||
|
end
|
||||||
|
ret = math.min(ret, calc_float(max_size, total_size) or total_size)
|
||||||
|
ret = math.max(ret, calc_float(min_size, total_size) or 1)
|
||||||
|
return math.floor(ret)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.calculate_width = function(desired_width, config)
|
||||||
|
return calculate_dim(
|
||||||
|
desired_width,
|
||||||
|
config.width,
|
||||||
|
config.min_width,
|
||||||
|
config.max_width,
|
||||||
|
vim.o.columns
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.calculate_height = function(desired_height, config)
|
||||||
|
return calculate_dim(
|
||||||
|
desired_height,
|
||||||
|
config.height,
|
||||||
|
config.min_height,
|
||||||
|
config.max_height,
|
||||||
|
vim.o.lines - vim.o.cmdheight
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
|
@ -0,0 +1 @@
|
||||||
|
require("dressing").patch()
|
Loading…
Reference in New Issue