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