1

Refresh generated neovim config

This commit is contained in:
2024-08-15 13:01:03 +02:00
parent 64b51cf53a
commit f5af8e2b28
1836 changed files with 38979 additions and 31094 deletions

View File

@ -1,175 +1,182 @@
local M = {}
local uv = vim.uv or vim.loop
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local api = vim.api
M.waiting = false
local settings = {
timeout = vim.o.timeoutlen,
mapping = { "jk", "jj" },
clear_empty_lines = false,
---@type string|function
keys = "<Esc>",
default_mappings = true,
mappings = {
i = {
-- first_key[s]
j = {
-- second_key[s]
k = "<Esc>",
j = "<Esc>",
},
},
c = {
j = {
k = "<Esc>",
j = "<Esc>",
},
},
t = {
j = {
k = "<C-\\><C-n>",
},
},
v = {
j = {
k = "<Esc>",
},
},
s = {
j = {
k = "<Esc>",
},
},
},
}
local first_chars = {}
local second_chars = {}
---@class State
---@field char string
---@field modified boolean
local timer
local waiting = false
---@type State[]
local input_states = {}
---@param tbl table table to search through
---@param element any element to search in tbl
---@return table indices
--- Search for indices in tbl where element occurs
local function get_indices(tbl, element)
local indices = {}
for idx, value in ipairs(tbl) do
if element == value then
table.insert(indices, idx)
end
end
return indices
end
---@param keys string keys to feed
--- Replace keys with termcodes and feed them
local function feed(keys, mode)
api.nvim_feedkeys(
api.nvim_replace_termcodes(keys, true, true, true),
mode or "n",
false
)
end
local function start_timer()
waiting = true
if timer then
timer:stop()
end
timer = vim.defer_fn(function()
waiting = false
end, settings.timeout)
end
local function get_keys()
-- if keys is string use it, else use it as a function
return type(settings.keys) == "string" and settings.keys or settings.keys()
end
local function check_timeout()
if waiting then
local current_line = api.nvim_get_current_line()
if settings.clear_empty_lines and current_line:match("^%s+j$") then
vim.schedule(function()
api.nvim_set_current_line("")
feed(get_keys(), "in")
end)
else
feed("<BS><BS>" .. get_keys(), "in") -- delete the characters from the mapping
end
waiting = false -- more timely
return true
end
return false
end
function M.check_charaters()
local char = vim.v.char
table.insert(input_states, { char = char, modified = vim.bo.modified })
local matched = false
if #input_states >= 2 then
---@type State
local prev_state = input_states[#input_states - 1]
local indices = get_indices(second_chars, char)
-- if char == second_chars[idx] and prev_char == first_chars[idx] as well
-- then matched = true
for _, idx in ipairs(indices) do
if first_chars[idx] == prev_state.char then
matched = check_timeout()
break
local function unmap_keys()
for mode, keys in pairs(settings.mappings) do
for key, subkeys in pairs(keys) do
pcall(vim.keymap.del, mode, key)
for subkey, _ in pairs(subkeys) do
pcall(vim.keymap.del, mode, subkey)
end
end
end
end
if matched then
input_states = {}
vim.schedule(function()
vim.bo.modified = prev_state.modified
end)
-- WIP: move this into recorder.lua ?
-- When a first_key is pressed, `recorded_key` is set to it
-- (e.g. if jk is a mapping, when 'j' is pressed, `recorded_key` is set to 'j')
local recorded_key = nil
local bufmodified = nil
local timeout_timer = uv.new_timer()
local has_recorded = false -- See `vim.on_key` below
local function record_key(key)
if timeout_timer:is_active() then
timeout_timer:stop()
end
bufmodified = vim.bo.modified
recorded_key = key
has_recorded = true
M.waiting = true
timeout_timer:start(settings.timeout, 0, function()
M.waiting = false
recorded_key = nil
end)
end
vim.on_key(function(_, typed)
if typed == "" then
return
end
if has_recorded == false then
-- If the user presses a key that doesn't get recorded, remove the previously recorded key.
recorded_key = nil
return
end
has_recorded = false
end)
-- List of keys that undo the effect of pressing first_key
local undo_key = {
i = "<bs>",
c = "<bs>",
t = "<bs>",
}
local function map_keys()
for mode, first_keys in pairs(settings.mappings) do
local map_opts = { expr = true }
for first_key, _ in pairs(first_keys) do
vim.keymap.set(mode, first_key, function()
record_key(first_key)
return first_key
end, map_opts)
end
for _, second_keys in pairs(first_keys) do
for second_key, mapping in pairs(second_keys) do
if not mapping then
goto continue
end
vim.keymap.set(mode, second_key, function()
-- If a first_key wasn't recorded, record second_key because it might be a first_key for another sequence.
-- TODO: Explicitly, check if it's a starting key. I don't think that's necessary right now.
if recorded_key == nil then
record_key(second_key)
return second_key
end
-- If a key was recorded, but it isn't the first_key for second_key, record second_key(second_key might be a first_key for another sequence)
-- Or if the recorded_key was just a second_key
if
not (
first_keys[recorded_key]
and first_keys[recorded_key][second_key]
)
then
record_key(second_key)
return second_key
end
local keys = ""
keys = keys
.. t(
(undo_key[mode] or "")
.. (
("<cmd>setlocal %smodified<cr>"):format(
bufmodified and "" or "no"
)
)
)
if type(mapping) == "string" then
keys = keys .. t(mapping)
elseif type(mapping) == "function" then
keys = keys .. t(mapping() or "")
end
vim.api.nvim_feedkeys(keys, "in", false)
end, map_opts)
::continue::
end
end
end
-- if can't find a match, and the typed char is first in a mapping, start the timeout
if not matched and vim.tbl_contains(first_chars, char) then
start_timer()
end
end
local function char_at(str, pos)
return vim.fn.nr2char(vim.fn.strgetchar(str, pos))
end
local function validate_settings()
assert(type(settings.mapping) == "table", "Mapping must be a table.")
for _, mapping in ipairs(settings.mapping) do
-- replace all multibyte chars to `A` char
local length = #vim.fn.substitute(mapping, ".", "A", "g")
assert(length == 2, "Mapping must be 2 keys.")
end
if settings.timeout then
assert(type(settings.timeout) == "number", "Timeout must be a number.")
assert(
settings.timeout >= 100,
"Timeout must be greater than or equal to 100."
)
end
assert(
vim.tbl_contains({ "string", "function" }, type(settings.keys)),
"Keys must be a function or string."
)
end
function M.setup(update)
if update and update.default_mappings == false then
settings.mappings = {}
end
settings = vim.tbl_deep_extend("force", settings, update or {})
-- if mapping is a string (single mapping) make it a table
if type(settings.mapping) == "string" then
settings.mapping = { settings.mapping }
if settings.keys or settings.clear_empty_lines then
vim.notify(
"[better-escape.nvim]: Rewrite! Check: https://github.com/max397574/better-escape.nvim",
vim.log.levels.WARN,
{}
)
end
local ok, msg = pcall(validate_settings)
if ok then
-- create tables with the first and seconds chars of the mappings
for _, shortcut in ipairs(settings.mapping) do
vim.cmd("silent! iunmap " .. shortcut)
table.insert(first_chars, char_at(shortcut, 0))
table.insert(second_chars, char_at(shortcut, 1))
if settings.mapping then
vim.notify(
"[better-escape.nvim]: Rewrite! Check: https://github.com/max397574/better-escape.nvim",
vim.log.levels.WARN,
{}
)
if type(settings.mapping) == "string" then
settings.mapping = { settings.mapping }
end
for _, mapping in ipairs(settings.mappings) do
settings.mappings.i[mapping:sub(1, 2)] = {}
settings.mappings.i[mapping:sub(1, 1)][mapping:sub(2, 2)] =
settings.keys
end
vim.cmd([[
augroup better_escape
autocmd!
autocmd InsertCharPre * lua require"better_escape".check_charaters()
augroup END
]])
else
vim.notify("Error(better-escape.nvim): " .. msg, vim.log.levels.ERROR)
end
unmap_keys()
map_keys()
end
return setmetatable(M, {
__index = function(_, k)
if k == "waiting" then
return waiting
end
end,
})
return M

View File

@ -1,21 +1,24 @@
# 🚪better-escape.nvim
# better-escape.nvim
This plugin is the lua version of [better_escape.vim](https://github.com/jdhao/better-escape.vim),
with some additional features and optimizations
![better-escape](https://github.com/max397574/better-escape.nvim/assets/81827001/8863a620-b075-4417-92d0-7eb2d2646186)
A lot of people have mappings like `jk` or `jj` to escape insert mode.
The problem with this mappings is that whenever you type a `j`, neovim wait about 100-500ms (depending on your timeoutlen) to see, if you type a `j` or a `k` because these are mapped.
Only after that time the `j` will be inserted.
Then you always get a delay when typing a `j`.
A lot of people have mappings like `jk` or `jj` to escape insert mode. The
problem with this mappings is that whenever you type a `j`, neovim wait about
100-500ms (depending on your timeoutlen) to see, if you type a `j` or a `k`
because these are mapped. Only after that time the `j` will be inserted. Then
you always get a delay when typing a `j`.
This looks like this (see below for a gif):
An example where this has a big impact is e.g. telescope. Because the characters
which are mapped aren't really inserted at first the whole filtering isn't
instant.
![Screen Shot 2021-10-08 at 16 21 23](https://user-images.githubusercontent.com/81827001/136576543-c8b4e802-84a8-4087-a7a4-f7d069931885.png)
![better-escape-tele](https://github.com/max397574/better-escape.nvim/assets/81827001/390f115d-87cd-43d8-aadf-fffb12bd84c9)
## ✨Features
- Escape without getting delay when typing in insert mode
- Customizable mapping and timeout
- Write mappings in many modes without having a delay when typing
- Customizable timeout
- Map key sequences and lua functions
- Use multiple mappings
- Really small and fast
@ -24,8 +27,8 @@ This looks like this (see below for a gif):
Use your favourite package manager and call the setup function.
```lua
-- lua with packer.nvim
use {
-- lua with lazy.nvim
{
"max397574/better-escape.nvim",
config = function()
require("better_escape").setup()
@ -33,31 +36,107 @@ use {
}
```
## ❗Rewrite
There was a big rewrite which allows much more flexibility now. You can now
define mappings in most modes and also use functions.
The biggest change was that the `mapping` config option was removed. Check the
default configuration below to see the new structure.
This also deprecated the `clear_empty_lines` setting. You can replicate this
behavior by setting a mapping to a function like this:
```lua
-- `k` would be the second key of a mapping
k = function()
vim.api.nvim_input("<esc>")
local current_line = vim.api.nvim_get_current_line()
if current_line:match("^%s+j$") then
vim.schedule(function()
vim.api.nvim_set_current_line("")
end)
end
end
```
## ⚙Customization
Call the setup function with your options as arguments.
After the rewrite you can also use any function. So you could for example map
`<space><tab>` to jump with luasnip like this:
```lua
i = {
[" "] = {
["<tab>"] = function()
-- Defer execution to avoid side-effects
vim.defer_fn(function()
-- set undo point
vim.o.ul = vim.o.ul
require("luasnip").expand_or_jump()
end, 1)
end
}
}
```
### Disable mappings
To disable keys set them to `false` in the configuration.
You can also disable all default mappings by setting the `default_mappings` option to false.
<details>
<summary>Default Config</summary>
```lua
-- lua, default settings
require("better_escape").setup {
mapping = {"jk", "jj"}, -- a table with mappings to use
timeout = vim.o.timeoutlen, -- the time in which the keys must be hit in ms. Use option timeoutlen by default
clear_empty_lines = false, -- clear line after escaping if there is only whitespace
keys = "<Esc>", -- keys used for escaping, if it is a function will use the result everytime
-- example(recommended)
-- keys = function()
-- return vim.api.nvim_win_get_cursor(0)[2] > 1 and '<esc>l' or '<esc>'
-- end,
timeout = vim.o.timeoutlen,
default_mappings = true,
mappings = {
i = {
j = {
-- These can all also be functions
k = "<Esc>",
j = "<Esc>",
},
},
c = {
j = {
k = "<Esc>",
j = "<Esc>",
},
},
t = {
j = {
k = "<Esc>",
j = "<Esc>",
},
},
v = {
j = {
k = "<Esc>",
},
},
s = {
j = {
k = "<Esc>",
},
},
},
}
```
</details>
## API
`require("better_escape").waiting` is a boolean indicating that it's waiting for
a mapped sequence to complete.
<details>
<summary>statusline example</summary>
<summary>Statusline example</summary>
```lua
function escape_status()
@ -68,28 +147,15 @@ end
</details>
## 👀Demo
![mapping](https://user-images.githubusercontent.com/81827001/135870002-07c1dc41-f3e7-4ece-af6f-50e9b0711a66.gif)
![plugin](https://user-images.githubusercontent.com/81827001/135870101-febf3507-9327-4b80-aa9a-ba08bff6b8d4.gif)
## 🎓How it works
With the mappings there are two tables created.
One contains all first characters and one all second characters.
Whenever you type a character the plugin checks if it's in any of the two tables
If it is in the first one, the plugin starts a timer.
If it is in the second, the plugin checks whether the character you typed before is in the table with the first characters.
If this is the case the plugin gets all the indices where the characters are in the tables, then is searches for matches.
If there is a match, that means that there is a mapping which has the typed character as second and the previous typed character as first character.
The plugin then checks if the time passed since the first character was types is smaller than `timoutlen`.
If this is the case the two characters get deleted and `keys` get feed or executed.
Like this it is possible that the characters really get inserted and therefore you have no delay after typing one of the characters of your mapping.
With the `timeoutlen` it's still possible to type the characters of your mappings.
## ❤️ Support
If you like the projects I do and they can help you in your life you can support my work with [github sponsors](https://github.com/sponsors/max397574).
Every support motivates me to continue working on my open source projects.
If you like the projects I do and they can help you in your life you can support
my work with [github sponsors](https://github.com/sponsors/max397574). Every
support motivates me to continue working on my open source projects.
## Similar plugins
The old version of this plugin was a lua version of
[better_escape.vim](https://github.com/jdhao/better-escape.vim), with some
additional features and optimizations. This changed with the rewrite though. Now
it has much more features.