mirror of
https://github.com/Iron-E/nvim-libmodal
synced 2024-11-12 07:10:23 +00:00
perf: general improvements
I forgot to break it up into individual commits, and I ain't going backwards now.
This commit is contained in:
parent
e535c17e84
commit
8c164b811d
@ -15,10 +15,10 @@ end
|
||||
-- this is the function that will be called whenever the user presses a button
|
||||
local function foo_mode()
|
||||
-- append to the input history, the latest button press
|
||||
input_history[#input_history + 1] = string.char(
|
||||
table.insert(input_history, string.char(
|
||||
-- the input is a character number
|
||||
vim.g.fooModeInput
|
||||
)
|
||||
))
|
||||
|
||||
-- custom logic to test for each character index to see if it matches the 'zfo' mapping
|
||||
local index = 1
|
||||
|
@ -8,7 +8,7 @@ return setmetatable(
|
||||
--- @param exit_char? string a character which can be used to exit the layer from normal mode.
|
||||
--- @return fun()|nil exit a function to exit the layer, or `nil` if `exit_char` is passed
|
||||
enter = function(keymap, exit_char)
|
||||
local layer = require('libmodal/src/Layer').new(keymap)
|
||||
local layer = require('libmodal.src.Layer').new(keymap)
|
||||
layer:enter()
|
||||
|
||||
if exit_char then
|
||||
@ -22,7 +22,7 @@ return setmetatable(
|
||||
--- @param keymap table the keymaps (e.g. `{n = {gg = {rhs = 'G', silent = true}}}`)
|
||||
--- @return libmodal.Layer
|
||||
new = function(keymap)
|
||||
return require('libmodal/src/Layer').new(keymap)
|
||||
return require('libmodal.src.Layer').new(keymap)
|
||||
end,
|
||||
},
|
||||
|
||||
@ -32,7 +32,7 @@ return setmetatable(
|
||||
--- @param name string the name of the mode.
|
||||
--- @param instruction fun()|string|table a Lua function, keymap dictionary, Vimscript command.
|
||||
enter = function(name, instruction, supress_exit)
|
||||
require('libmodal/src/Mode').new(name, instruction, supress_exit):enter()
|
||||
require('libmodal.src.Mode').new(name, instruction, supress_exit):enter()
|
||||
end
|
||||
},
|
||||
|
||||
@ -43,7 +43,7 @@ return setmetatable(
|
||||
--- @param instruction fun()|{[string]: fun()|string} what to do with user input
|
||||
--- @param user_completions? string[] a list of possible inputs, provided by the user
|
||||
enter = function(name, instruction, user_completions)
|
||||
require('libmodal/src/Prompt').new(name, instruction, user_completions):enter()
|
||||
require('libmodal.src.Prompt').new(name, instruction, user_completions):enter()
|
||||
end
|
||||
}
|
||||
},
|
||||
@ -52,12 +52,17 @@ return setmetatable(
|
||||
if key ~= 'Layer' then
|
||||
return rawget(tbl, key)
|
||||
else
|
||||
vim.notify_once(
|
||||
'`libmodal.Layer` is deprecated in favor of `libmodal.layer`. It will work FOR NOW, but uncapitalize that `L` please :)',
|
||||
vim.log.levels.WARN,
|
||||
{title = 'nvim-libmodal'}
|
||||
)
|
||||
return require 'libmodal/src/Layer'
|
||||
if vim.deprecate then
|
||||
vim.deprecate('`libmodal.Layer`', '`libmodal.layer`', '4.0.0', 'nvim-libmodal')
|
||||
else
|
||||
vim.notify_once(
|
||||
'`libmodal.Layer` is deprecated in favor of `libmodal.layer`. It will work FOR NOW, but uncapitalize that `L` please :)',
|
||||
vim.log.levels.WARN,
|
||||
{title = 'nvim-libmodal'}
|
||||
)
|
||||
end
|
||||
|
||||
return rawget(tbl, 'layer')
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
local globals = require 'libmodal.src.globals'
|
||||
|
||||
--- @type libmodal.utils
|
||||
local utils = require 'libmodal/src/utils'
|
||||
local utils = require 'libmodal.src.utils'
|
||||
|
||||
--- Normalizes a `buffer = true|false|0` argument into a number.
|
||||
--- @param buffer boolean|number the argument to normalize
|
||||
@ -52,9 +52,10 @@ end
|
||||
--- @field private active boolean whether the layer is currently applied
|
||||
--- @field private existing_keymaps_by_mode table the keymaps to restore when exiting the mode; generated automatically
|
||||
--- @field private layer_keymaps_by_mode table the keymaps to apply when entering the mode; provided by user
|
||||
local Layer = require('libmodal/src/utils/classes').new(nil)
|
||||
local Layer = require('libmodal.src.utils.classes').new()
|
||||
|
||||
--- apply the `Layer`'s keymaps buffer.
|
||||
--- @return nil
|
||||
function Layer:enter()
|
||||
if self:is_active() then
|
||||
vim.notify(
|
||||
@ -76,6 +77,7 @@ function Layer:enter()
|
||||
end
|
||||
|
||||
--- exit the layer, restoring all previous keymaps.
|
||||
--- @return nil
|
||||
function Layer:exit()
|
||||
if not self.active then
|
||||
vim.notify(
|
||||
@ -106,6 +108,7 @@ end
|
||||
--- @param lhs string the left hand side of the keymap.
|
||||
--- @param rhs fun()|string the right hand side of the keymap.
|
||||
--- @param options table options for the keymap.
|
||||
--- @return nil
|
||||
--- @see vim.keymap.set
|
||||
function Layer:map(mode, lhs, rhs, options)
|
||||
lhs = utils.api.replace_termcodes(lhs)
|
||||
@ -151,6 +154,7 @@ end
|
||||
--- @param buffer? number the buffer to unmap from (`nil` if it is not buffer-local)
|
||||
--- @param mode string the mode of the keymap.
|
||||
--- @param lhs string the keys which invoke the keymap.
|
||||
--- @return nil
|
||||
--- @see vim.api.nvim_del_keymap
|
||||
function Layer:unmap(buffer, mode, lhs)
|
||||
lhs = utils.api.replace_termcodes(lhs)
|
||||
@ -170,7 +174,7 @@ function Layer:unmap(buffer, mode, lhs)
|
||||
end)
|
||||
|
||||
if not ok and err:match 'E31: No such mapping' then
|
||||
require('libmodal/src/utils').notify_error('nvim-libmodal encountered an error while unmapping from layer', err)
|
||||
require('libmodal.src.utils').notify_error('nvim-libmodal encountered an error while unmapping from layer', err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -1,17 +1,11 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
|
||||
--- @type libmodal.collections.ParseTable
|
||||
local ParseTable = require 'libmodal/src/collections/ParseTable'
|
||||
|
||||
--- @type libmodal.utils
|
||||
local utils = require 'libmodal/src/utils'
|
||||
local globals = require 'libmodal.src.globals' --- @type libmodal.globals
|
||||
local ParseTable = require 'libmodal.src.collections.ParseTable' --- @type libmodal.collections.ParseTable
|
||||
local utils = require 'libmodal.src.utils' --- @type libmodal.utils
|
||||
|
||||
--- @class libmodal.Mode
|
||||
--- @field private exit libmodal.utils.Vars
|
||||
--- @field private flush_input_timer unknown
|
||||
--- @field private help? libmodal.utils.Help
|
||||
--- @field private indicator libmodal.utils.Indicator
|
||||
--- @field private input libmodal.utils.Vars
|
||||
--- @field private input_bytes? number[]
|
||||
--- @field private instruction fun()|{[string]: fun()|string}
|
||||
@ -23,19 +17,20 @@ local utils = require 'libmodal/src/utils'
|
||||
--- @field private timeouts_enabled boolean
|
||||
local Mode = utils.classes.new()
|
||||
|
||||
local HELP = '?'
|
||||
local HELP_CHAR = '?'
|
||||
local TIMEOUT =
|
||||
{
|
||||
CHAR = 'ø',
|
||||
CHAR = ' ',
|
||||
LEN = vim.go.timeoutlen,
|
||||
SEND = function(self) vim.api.nvim_feedkeys(self.CHAR, 'nt', false) end
|
||||
}
|
||||
TIMEOUT.CHAR_NUMBER = string.byte(TIMEOUT.CHAR)
|
||||
TIMEOUT.CHAR_NUMBER = TIMEOUT.CHAR:byte()
|
||||
|
||||
--- execute the `instruction`.
|
||||
--- @param instruction fun()|string a Lua function or Vimscript command.
|
||||
--- @return nil
|
||||
function Mode.execute_instruction(instruction)
|
||||
if type(instruction) == globals.TYPE_FUNC then
|
||||
if type(instruction) == 'function' then
|
||||
instruction()
|
||||
else
|
||||
vim.api.nvim_command(instruction)
|
||||
@ -44,6 +39,7 @@ end
|
||||
|
||||
--- check the user's input against the `self.instruction` mappings to see if there is anything to execute.
|
||||
--- if there is nothing to execute, the user's input is rendered on the screen (as does Vim by default).
|
||||
--- @return nil
|
||||
function Mode:check_input_for_mapping()
|
||||
-- stop any running timers
|
||||
self.flush_input_timer:stop()
|
||||
@ -59,12 +55,12 @@ function Mode:check_input_for_mapping()
|
||||
|
||||
-- if there was no matching command
|
||||
if not cmd then
|
||||
if #self.input_bytes < 2 and self.input_bytes[1] == string.byte(HELP) then
|
||||
if #self.input_bytes < 2 and self.input_bytes[1] == HELP_CHAR:byte() then
|
||||
self.help:show()
|
||||
end
|
||||
|
||||
self.input_bytes = {}
|
||||
elseif command_type == globals.TYPE_TBL and globals.is_true(self.timeouts_enabled) then -- the command was a table, meaning that it MIGHT match.
|
||||
elseif command_type == 'table' and globals.is_true(self.timeouts_enabled) then -- the command was a table, meaning that it MIGHT match.
|
||||
self.flush_input_timer:start( -- start the timer
|
||||
TIMEOUT.LEN, 0, vim.schedule_wrap(function()
|
||||
-- send input to interrupt a blocking `getchar`
|
||||
@ -88,9 +84,10 @@ function Mode:check_input_for_mapping()
|
||||
end
|
||||
|
||||
--- enter this mode.
|
||||
--- @return nil
|
||||
function Mode:enter()
|
||||
-- intialize variables that are needed for each recurse of a function
|
||||
if type(self.instruction) == globals.TYPE_TBL then
|
||||
if type(self.instruction) == 'table' then
|
||||
-- initialize the input history variable.
|
||||
self.popups:push(utils.Popup.new())
|
||||
end
|
||||
@ -151,9 +148,9 @@ function Mode:get_user_input()
|
||||
over the instruction and it may change over the course of execution. ]]
|
||||
local instruction_type = type(self.instruction)
|
||||
|
||||
if instruction_type == globals.TYPE_TBL then -- the instruction was provided as a was a set of mappings.
|
||||
if instruction_type == 'table' then -- the instruction was provided as a was a set of mappings.
|
||||
self:check_input_for_mapping()
|
||||
elseif instruction_type == globals.TYPE_STR then -- the instruction is the name of a Vimscript function.
|
||||
elseif instruction_type == 'string' then -- the instruction is the name of a Vimscript function.
|
||||
vim.fn[self.instruction]()
|
||||
else -- the instruction is a function.
|
||||
self.instruction()
|
||||
@ -164,8 +161,9 @@ function Mode:get_user_input()
|
||||
end
|
||||
|
||||
--- uninitialize variables from after exiting the mode.
|
||||
--- @return nil
|
||||
function Mode:tear_down()
|
||||
if type(self.instruction) == globals.TYPE_TBL then
|
||||
if type(self.instruction) == 'table' then
|
||||
self.flush_input_timer:stop()
|
||||
self.input_bytes = nil
|
||||
|
||||
@ -192,7 +190,6 @@ function Mode.new(name, instruction, supress_exit)
|
||||
local self = setmetatable(
|
||||
{
|
||||
exit = utils.Vars.new('exit', name),
|
||||
indicator = utils.Indicator.new('LibmodalPrompt', '-- ' .. name .. ' --'),
|
||||
input = utils.Vars.new('input', name),
|
||||
instruction = instruction,
|
||||
name = name,
|
||||
@ -200,23 +197,23 @@ function Mode.new(name, instruction, supress_exit)
|
||||
Mode
|
||||
)
|
||||
|
||||
self.show_name = (not vim.o.showmode) and utils.api.redraw or function()
|
||||
utils.api.redraw()
|
||||
|
||||
vim.api.nvim_command('echohl ' .. self.indicator.hl .. " | echon '" .. self.indicator.str .. "'")
|
||||
vim.api.nvim_command 'echohl None'
|
||||
end
|
||||
self.show_name = vim.o.showmode and
|
||||
function()
|
||||
utils.api.redraw()
|
||||
vim.api.nvim_echo({{'-- ' .. name .. ' --', 'LibmodalPrompt'}}, false, {})
|
||||
end or
|
||||
utils.api.redraw
|
||||
|
||||
-- define the exit flag
|
||||
self.supress_exit = supress_exit or false
|
||||
|
||||
-- if the user provided keymaps
|
||||
if type(instruction) == globals.TYPE_TBL then
|
||||
if type(instruction) == 'table' then
|
||||
-- create a timer to perform actions with.
|
||||
self.flush_input_timer = vim.loop.new_timer()
|
||||
|
||||
-- determine if a default `Help` should be created.
|
||||
if not self.instruction[HELP] then
|
||||
if not self.instruction[HELP_CHAR] then
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we checked that `instruction` is a table above
|
||||
self.help = utils.Help.new(self.instruction, 'KEY MAP')
|
||||
end
|
||||
@ -228,7 +225,7 @@ function Mode.new(name, instruction, supress_exit)
|
||||
self.mappings = ParseTable.new(self.instruction)
|
||||
|
||||
-- create a table for mode-specific data.
|
||||
self.popups = require('libmodal/src/collections/Stack').new()
|
||||
self.popups = require('libmodal.src.collections.Stack').new()
|
||||
|
||||
-- create a variable for whether or not timeouts are enabled.
|
||||
self.timeouts = utils.Vars.new('timeouts', self.name)
|
||||
|
@ -1,18 +1,14 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
|
||||
--- @type libmodal.utils
|
||||
local utils = require 'libmodal/src/utils'
|
||||
local utils = require 'libmodal.src.utils' --- @type libmodal.utils
|
||||
|
||||
--- @class libmodal.Prompt
|
||||
--- @field private completions? string[]
|
||||
--- @field private indicator {hl: string, text: string}
|
||||
--- @field private exit libmodal.utils.Vars
|
||||
--- @field private help? libmodal.utils.Help
|
||||
--- @field private indicator libmodal.utils.Indicator
|
||||
--- @field private input libmodal.utils.Vars
|
||||
--- @field private instruction fun()|{[string]: fun()|string}
|
||||
--- @field private name string
|
||||
local Prompt = utils.classes.new(nil)
|
||||
local Prompt = utils.classes.new()
|
||||
|
||||
local HELP = 'help'
|
||||
local REPLACEMENTS =
|
||||
@ -28,11 +24,12 @@ end
|
||||
|
||||
--- execute the instruction specified by the `user_input`.
|
||||
--- @param user_input string
|
||||
--- @return nil
|
||||
function Prompt:execute_instruction(user_input)
|
||||
if type(self.instruction) == globals.TYPE_TBL then -- the self.instruction is a command table.
|
||||
if type(self.instruction) == 'table' then -- the self.instruction is a command table.
|
||||
if self.instruction[user_input] then -- there is a defined command for the input.
|
||||
local to_execute = self.instruction[user_input]
|
||||
if type(to_execute) == globals.TYPE_FUNC then
|
||||
if type(to_execute) == 'function' then
|
||||
to_execute()
|
||||
else
|
||||
vim.api.nvim_command(to_execute)
|
||||
@ -42,7 +39,7 @@ function Prompt:execute_instruction(user_input)
|
||||
else -- show an error.
|
||||
vim.notify('nvim-libmodal prompt: unkown command', vim.log.levels.ERROR, {title = 'nvim-libmodal'})
|
||||
end
|
||||
elseif type(self.instruction) == globals.TYPE_STR then -- the self.instruction is a function.
|
||||
elseif type(self.instruction) == 'string' then -- the self.instruction is a function.
|
||||
vim.fn[self.instruction]()
|
||||
else -- attempt to call the self.instruction.
|
||||
self.instruction()
|
||||
@ -62,7 +59,7 @@ function Prompt:get_user_input()
|
||||
--- 3. Read `g:<mode_name>ModeExit` to see if we should `continue_prompt`
|
||||
--- @param user_input string
|
||||
local function user_input_callback(user_input)
|
||||
if user_input and string.len(user_input) > 0 then -- the user actually entered something.
|
||||
if user_input and user_input:len() > 0 then -- the user actually entered something.
|
||||
self.input:set(user_input)
|
||||
self:execute_instruction(user_input)
|
||||
|
||||
@ -75,21 +72,20 @@ function Prompt:get_user_input()
|
||||
end
|
||||
end
|
||||
|
||||
-- echo the highlighting
|
||||
vim.api.nvim_command('echohl ' .. self.indicator.hl)
|
||||
|
||||
-- set the user input variable
|
||||
if self.completions then
|
||||
vim.api.nvim_command('echo "' .. self.indicator.str .. '"')
|
||||
vim.api.nvim_echo({{self.indicator.text, self.indicator.hl}}, false, {})
|
||||
vim.ui.select(self.completions, {}, user_input_callback)
|
||||
else
|
||||
vim.ui.input({prompt = self.indicator.str}, user_input_callback)
|
||||
vim.api.nvim_command('echohl ' .. self.indicator.hl)
|
||||
vim.ui.input({prompt = self.indicator.text}, user_input_callback)
|
||||
end
|
||||
|
||||
return continue_prompt == nil and true or continue_prompt
|
||||
end
|
||||
|
||||
--- enter the prompt.
|
||||
--- @return nil
|
||||
function Prompt:enter()
|
||||
-- enter the mode using a loop.
|
||||
local continue_mode = true
|
||||
@ -107,53 +103,52 @@ function Prompt:enter()
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
{
|
||||
--- enter a prompt.
|
||||
--- @param name string the name of the prompt
|
||||
--- @param instruction fun()|{[string]: fun()|string} what to do with user input
|
||||
--- @param user_completions? string[] a list of possible inputs, provided by the user
|
||||
--- @return libmodal.Prompt
|
||||
new = function(name, instruction, user_completions)
|
||||
name = vim.trim(name)
|
||||
--- enter a prompt.
|
||||
--- @param name string the name of the prompt
|
||||
--- @param instruction fun()|{[string]: fun()|string} what to do with user input
|
||||
--- @param user_completions? string[] a list of possible inputs, provided by the user
|
||||
--- @return libmodal.Prompt
|
||||
function Prompt.new(name, instruction, user_completions)
|
||||
name = vim.trim(name)
|
||||
|
||||
local self = setmetatable(
|
||||
{
|
||||
exit = utils.Vars.new('exit', name),
|
||||
indicator = utils.Indicator.new('LibmodalStar', '* ' .. name .. ' > '),
|
||||
input = utils.Vars.new('input', name),
|
||||
instruction = instruction,
|
||||
name = name
|
||||
},
|
||||
Prompt
|
||||
)
|
||||
local self = setmetatable(
|
||||
{
|
||||
exit = utils.Vars.new('exit', name),
|
||||
indicator = {hl = 'LibmodalStar', text = '* ' .. name .. ' > '},
|
||||
input = utils.Vars.new('input', name),
|
||||
instruction = instruction,
|
||||
name = name
|
||||
},
|
||||
Prompt
|
||||
)
|
||||
|
||||
-- get the completion list.
|
||||
if type(instruction) == globals.TYPE_TBL then -- unload the keys of the mode command table.
|
||||
-- create one if the user specified a command table.
|
||||
local completions = {}
|
||||
local contained_help = false
|
||||
-- get the completion list.
|
||||
if type(instruction) == 'table' then -- unload the keys of the mode command table.
|
||||
-- create one if the user specified a command table.
|
||||
local completions = {}
|
||||
local contained_help = false
|
||||
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we check `instruction` is `table`
|
||||
for command, _ in pairs(instruction) do
|
||||
completions[#completions + 1] = command
|
||||
if command == HELP then
|
||||
contained_help = true
|
||||
end
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we check `instruction` is `table`
|
||||
for command, _ in pairs(instruction) do
|
||||
table.insert(completions, command)
|
||||
if command == HELP then
|
||||
contained_help = true
|
||||
end
|
||||
|
||||
if not contained_help then -- assign it.
|
||||
completions[#completions + 1] = HELP
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we checked that `instruction` is a table above
|
||||
self.help = utils.Help.new(instruction, 'COMMAND')
|
||||
end
|
||||
|
||||
self.completions = completions
|
||||
elseif user_completions then
|
||||
-- use the table that the user gave.
|
||||
self.completions = user_completions
|
||||
end
|
||||
|
||||
return self
|
||||
if not contained_help then -- assign it.
|
||||
table.insert(completions, HELP)
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we checked that `instruction` is a table above
|
||||
self.help = utils.Help.new(instruction, 'COMMAND')
|
||||
end
|
||||
|
||||
self.completions = completions
|
||||
elseif user_completions then
|
||||
-- use the table that the user gave.
|
||||
self.completions = user_completions
|
||||
end
|
||||
}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return Prompt
|
||||
|
@ -1,34 +1,33 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
local utils = require('libmodal.src.utils') --- @type libmodal.utils
|
||||
|
||||
--- the number corresponding to <CR> in vim.
|
||||
local CR = string.byte(vim.api.nvim_replace_termcodes('<CR>', true, true, true))
|
||||
--- the number corresponding to `<CR>` in vim.
|
||||
local CR = utils.api.replace_termcodes('<CR>'):byte()
|
||||
|
||||
--- @class libmodal.collections.ParseTable
|
||||
--- @field CR number the byte representation of `<CR>`
|
||||
local ParseTable = require('libmodal/src/utils/classes').new {CR = CR}
|
||||
local ParseTable = utils.classes.new {CR = CR}
|
||||
|
||||
--- reverse the order of elements in some `tbl`
|
||||
--- @param tbl table the table to reverse
|
||||
--- @return table tbl_reversed
|
||||
local function table_reverse(tbl)
|
||||
--- reverse the order of elements in some `list`.
|
||||
--- @generic T
|
||||
--- @param list T[]
|
||||
--- @return T[] reversed
|
||||
local function table_reverse(list)
|
||||
local reversed = {}
|
||||
while #reversed < #tbl do
|
||||
-- look, no variables!
|
||||
reversed[#reversed + 1] = tbl[#tbl - #reversed]
|
||||
for i = #list, 1, -1 do
|
||||
table.insert(reversed, list[i])
|
||||
end
|
||||
return reversed
|
||||
end
|
||||
|
||||
--- @param str string
|
||||
--- @param s string
|
||||
--- @return string[] chars of `str`
|
||||
local function chars(str)
|
||||
return vim.split(str, '')
|
||||
local function chars(s)
|
||||
return vim.split(s, '', {plain = true, trimempty = true})
|
||||
end
|
||||
|
||||
--- retrieve the mapping of `lhs_reversed_bytes`
|
||||
--- @param parse_table libmodal.collections.ParseTable the table to fetch `lhs_reversed_bytes` from.
|
||||
--- @param lhs_reversed_bytes string[] the characters of the left-hand side of the mapping reversed passed to `string.byte`
|
||||
--- @param lhs_reversed_bytes string[]|integer[] the characters of the left-hand side of the mapping reversed passed to `string.byte`
|
||||
--- @return false|fun()|nil|string|table match a string/func when fully matched; a table when partially matched; false when no match.
|
||||
local function get(parse_table, lhs_reversed_bytes)
|
||||
--[[ Get the next character in the keymap string. ]]
|
||||
@ -47,13 +46,13 @@ local function get(parse_table, lhs_reversed_bytes)
|
||||
local val = parse_table[k]
|
||||
local val_type = type(val)
|
||||
|
||||
if val_type == globals.TYPE_TBL then
|
||||
if val_type == 'table' then
|
||||
if val[CR] and #lhs_reversed_bytes < 1 then
|
||||
return val
|
||||
else
|
||||
return get(val, lhs_reversed_bytes)
|
||||
end
|
||||
elseif val_type == globals.TYPE_STR or val_type == globals.TYPE_FUNC and #lhs_reversed_bytes < 1 then
|
||||
elseif val_type == 'string' or val_type == 'function' and #lhs_reversed_bytes < 1 then
|
||||
return val
|
||||
end
|
||||
end
|
||||
@ -64,16 +63,17 @@ end
|
||||
--- insert a `value` into `parse_table` at the position indicated by `lhs_reversed_bytes`
|
||||
--- @param lhs_reversed_bytes string[] the characters of the left-hand side of the mapping reversed passed to `string.byte`
|
||||
--- @param value fun()|string the right-hand-side of the mapping
|
||||
--- @return nil
|
||||
local function put(parse_table, lhs_reversed_bytes, value)
|
||||
--[[ Get the next character in the table. ]]
|
||||
local byte = string.byte(table.remove(lhs_reversed_bytes))
|
||||
local byte = table.remove(lhs_reversed_bytes):byte()
|
||||
|
||||
if #lhs_reversed_bytes > 0 then -- there are still characters left in the key.
|
||||
if not parse_table[byte] then -- this is a new mapping
|
||||
parse_table[byte] = {}
|
||||
else -- if there is a previous command mapping in place
|
||||
local value_type = type(parse_table[byte])
|
||||
if value_type == globals.TYPE_STR or value_type == globals.TYPE_FUNC then -- if this is not a tree of inputs already
|
||||
if value_type == 'string' or value_type == 'function' then -- if this is not a tree of inputs already
|
||||
-- make the mapping require hitting enter before executing
|
||||
parse_table[byte] = {[CR] = parse_table[byte]}
|
||||
end
|
||||
@ -82,7 +82,7 @@ local function put(parse_table, lhs_reversed_bytes, value)
|
||||
-- run put() again
|
||||
put(parse_table[byte], lhs_reversed_bytes, value)
|
||||
-- if parse_Table[k] is a pre-existing table, don't clobber the table— clobber the `CR` value.
|
||||
elseif type(parse_table[byte]) == globals.TYPE_TBL then
|
||||
elseif type(parse_table[byte]) == 'table' then
|
||||
parse_table[byte][CR] = value
|
||||
else
|
||||
parse_table[byte] = value -- parse_table[k] is not a table, go ahead and clobber the value.
|
||||
@ -114,11 +114,11 @@ end
|
||||
--- @return false|fun()|nil|string|table match a string/func when fully found; a table when partially found; false when not found.
|
||||
function ParseTable:parse_get(key)
|
||||
--- @type table<number|string>
|
||||
local parsed_table = chars(string.reverse(key))
|
||||
local parsed_table = chars(key:reverse())
|
||||
|
||||
-- convert all of the strings to bytes.
|
||||
for i, v in ipairs(parsed_table) do
|
||||
parsed_table[i] = string.byte(v)
|
||||
parsed_table[i] = v:byte()
|
||||
end
|
||||
|
||||
return get(self, parsed_table)
|
||||
@ -127,12 +127,14 @@ end
|
||||
--- parse `key` and assign it to `value`.
|
||||
--- @param key string the left-hand-side of the mapping
|
||||
--- @param value fun()|string the right-hand-side of the mapping
|
||||
--- @return nil
|
||||
function ParseTable:parse_put(key, value)
|
||||
put(self, chars(string.reverse(key)), value)
|
||||
put(self, chars(key:reverse()), value)
|
||||
end
|
||||
|
||||
--- `:parse_put` all `{key, value}` pairs in `keys_and_values`.
|
||||
--- @param keys_and_values {[string]: fun()|string}
|
||||
--- @return nil
|
||||
function ParseTable:parse_put_all(keys_and_values)
|
||||
for k, v in pairs(keys_and_values) do
|
||||
self:parse_put(k, v)
|
||||
|
@ -1,27 +1,30 @@
|
||||
--- @class libmodal.collections.Stack
|
||||
local Stack = require('libmodal/src/utils/classes').new(nil)
|
||||
local Stack = require('libmodal.src.utils.classes').new()
|
||||
|
||||
--- @return libmodal.collections.Stack
|
||||
function Stack.new()
|
||||
return setmetatable({}, Stack)
|
||||
end
|
||||
|
||||
--- @return unknown top the foremost value of the stack
|
||||
--- @generic T
|
||||
--- @return T top the foremost value of the stack
|
||||
function Stack:peek()
|
||||
return self[#self]
|
||||
end
|
||||
|
||||
--- remove the foremost value from the stack and return it.
|
||||
--- @return unknown top the foremost value of the stack
|
||||
--- @generic T
|
||||
--- @return T top the foremost value of the stack
|
||||
function Stack:pop()
|
||||
return table.remove(self)
|
||||
end
|
||||
|
||||
--- push some `value` on to the stack.
|
||||
--- @param value unknown the value to push onto the stack.
|
||||
--- @generic T
|
||||
--- @param value T the value to push onto the stack.
|
||||
function Stack:push(value)
|
||||
-- push to the stack
|
||||
self[#self + 1] = value
|
||||
table.insert(self, value)
|
||||
end
|
||||
|
||||
return Stack
|
||||
|
@ -1,8 +1,10 @@
|
||||
--- @class libmodal.collections
|
||||
--- @field private ParseTable libmodal.collections.ParseTable
|
||||
--- @field private Stack libmodal.collections.Stack
|
||||
return
|
||||
--- @field ParseTable libmodal.collections.ParseTable
|
||||
--- @field Stack libmodal.collections.Stack
|
||||
local collections =
|
||||
{
|
||||
ParseTable = require 'libmodal/src/collections/ParseTable',
|
||||
Stack = require 'libmodal/src/collections/Stack'
|
||||
ParseTable = require 'libmodal.src.collections.ParseTable',
|
||||
Stack = require 'libmodal.src.collections.Stack'
|
||||
}
|
||||
|
||||
return collections
|
||||
|
@ -1,31 +1,23 @@
|
||||
--- @class libmodal.globals
|
||||
--- @field ESC_NR number the key-code for the escape character.
|
||||
--- @field TYPE_FUNC string the string which is returned by `type(function() end)` (or any function)
|
||||
--- @field TYPE_NUM string the string which is returned by `type(0)` (or any number)
|
||||
--- @field TYPE_STR string the string which is returned by `type ''` (or any string)
|
||||
--- @field TYPE_TBL string the string which is returned by `type {}` (or any table)
|
||||
--- @field VIM_FALSE number the value of Vimscript's `v:false`
|
||||
--- @field VIM_TRUE number the value of Vimscript's `v:true`
|
||||
--- @field ESC_NR integer the key-code for the escape character.
|
||||
--- @field VIM_FALSE integer the value of Vimscript's `v:false`
|
||||
--- @field VIM_TRUE integer the value of Vimscript's `v:true`
|
||||
local globals =
|
||||
{
|
||||
ESC_NR = 27,
|
||||
TYPE_FUNC = type(function() end),
|
||||
TYPE_NUM = type(0),
|
||||
TYPE_STR = type '',
|
||||
TYPE_TBL = type {},
|
||||
ESC_NR = vim.api.nvim_replace_termcodes('<Esc>', true, true, true):byte(),
|
||||
VIM_FALSE = 0,
|
||||
VIM_TRUE = 1,
|
||||
}
|
||||
|
||||
--- assert some value is either `false` or `v:false`.
|
||||
--- @param val boolean|number
|
||||
--- @param val boolean|integer
|
||||
--- @return boolean
|
||||
function globals.is_false(val)
|
||||
return val == false or val == globals.VIM_FALSE
|
||||
end
|
||||
|
||||
--- assert some value is either `true` or `v:true`.
|
||||
--- @param val boolean|number
|
||||
--- @param val boolean|integer
|
||||
--- @return boolean
|
||||
function globals.is_true(val)
|
||||
return val == true or val == globals.VIM_TRUE
|
||||
|
@ -7,12 +7,12 @@
|
||||
--- @field utils libmodal.utils
|
||||
local libmodal =
|
||||
{
|
||||
collections = require 'libmodal/src/collections',
|
||||
globals = require 'libmodal/src/globals',
|
||||
Layer = require 'libmodal/src/Layer',
|
||||
Mode = require 'libmodal/src/Mode',
|
||||
Prompt = require 'libmodal/src/Prompt',
|
||||
utils = require 'libmodal/src/utils',
|
||||
collections = require 'libmodal.src.collections',
|
||||
globals = require 'libmodal.src.globals',
|
||||
Layer = require 'libmodal.src.Layer',
|
||||
Mode = require 'libmodal.src.Mode',
|
||||
Prompt = require 'libmodal.src.Prompt',
|
||||
utils = require 'libmodal.src.utils',
|
||||
}
|
||||
|
||||
return libmodal
|
||||
|
@ -1,31 +1,31 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
|
||||
--- @class libmodal.utils.Help
|
||||
local Help = require('libmodal/src/utils/classes').new(nil)
|
||||
local Help = require('libmodal.src.utils.classes').new()
|
||||
|
||||
--- align `tbl` according to the `longest_key_len`.
|
||||
--- @param tbl table what to align.
|
||||
--- @param tbl {[string]: string|fun()} what to align.
|
||||
--- @param longest_key_len number how long the longest key is.
|
||||
--- @return table aligned
|
||||
--- @return string aligned
|
||||
local function align_columns(tbl, longest_key_len)
|
||||
local to_print = {}
|
||||
|
||||
for key, value in pairs(tbl) do
|
||||
to_print[#to_print + 1] = key
|
||||
local len = string.len(key)
|
||||
local byte = string.byte(key)
|
||||
table.insert(to_print, key)
|
||||
local len = key:len()
|
||||
local byte = key:byte()
|
||||
|
||||
-- account for ASCII chars that take up more space.
|
||||
if byte <= 32 or byte == 127 then
|
||||
len = len + 1
|
||||
end
|
||||
|
||||
for _ = len, longest_key_len do
|
||||
to_print[#to_print + 1] = ' '
|
||||
table.insert(to_print, ' ')
|
||||
end
|
||||
|
||||
to_print[#to_print + 1] = ' │ ' .. (type(value) == globals.TYPE_STR and value or vim.inspect(value)) .. '\n'
|
||||
table.insert(to_print, ' │ ' .. (type(value) == 'string' and value or vim.inspect(value)) .. '\n')
|
||||
end
|
||||
return to_print
|
||||
|
||||
return table.concat(to_print)
|
||||
end
|
||||
|
||||
--- create a default help table with `commands_or_maps` and vim expressions.
|
||||
@ -33,12 +33,13 @@ end
|
||||
--- @param title string
|
||||
--- @return libmodal.utils.Help
|
||||
function Help.new(commands_or_maps, title)
|
||||
-- find the longest key in the table, or at least the length of the title
|
||||
local longest_key_maps = string.len(title)
|
||||
--- the longest key in the table
|
||||
local longest_key = title:len()
|
||||
|
||||
for key, _ in pairs(commands_or_maps) do
|
||||
local key_len = string.len(key)
|
||||
if key_len > longest_key_maps then
|
||||
longest_key_maps = key_len
|
||||
local key_len = key:len()
|
||||
if key_len > longest_key then
|
||||
longest_key = key_len
|
||||
end
|
||||
end
|
||||
|
||||
@ -46,19 +47,18 @@ function Help.new(commands_or_maps, title)
|
||||
return setmetatable(
|
||||
{
|
||||
[1] = ' ',
|
||||
[2] = table.concat(align_columns({[title] = 'VIM EXPRESSION'}, longest_key_maps)),
|
||||
[3] = table.concat(align_columns({[string.rep('-', string.len(title))] = '--------------'}, longest_key_maps)),
|
||||
[4] = table.concat(align_columns(commands_or_maps, longest_key_maps)),
|
||||
[2] = align_columns({[title] = 'VIM EXPRESSION'}, longest_key),
|
||||
[3] = align_columns({[('-'):rep(title:len())] = '--------------'}, longest_key),
|
||||
[4] = align_columns(commands_or_maps, longest_key),
|
||||
},
|
||||
Help
|
||||
)
|
||||
end
|
||||
|
||||
--- show the contents of this `Help`.
|
||||
--- @return nil
|
||||
function Help:show()
|
||||
for _, help_text in ipairs(self) do
|
||||
print(help_text)
|
||||
end
|
||||
vim.api.nvim_echo(self, false, {})
|
||||
vim.fn.getchar()
|
||||
end
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
--- @class libmodal.utils.Indicator
|
||||
--- @field hl string the highlight group to use when printing `str`
|
||||
--- @field str string the text to write
|
||||
local Indicator = {}
|
||||
|
||||
--- @param highlight_group string the highlight group to use when printing `str`
|
||||
--- @param str string what to print
|
||||
--- @return libmodal.utils.Indicator
|
||||
function Indicator.new(highlight_group, str)
|
||||
return {hl = highlight_group, str = str}
|
||||
end
|
||||
|
||||
return Indicator
|
@ -2,7 +2,7 @@
|
||||
--- @field private buffer number the number of the window which this popup is rendered on.
|
||||
--- @field private input_chars string[] the characters input by the user.
|
||||
--- @field private window number the number of the window which this popup is rendered on.
|
||||
local Popup = require('libmodal/src/utils/classes').new(nil)
|
||||
local Popup = require('libmodal.src.utils.classes').new()
|
||||
|
||||
--- @param window number
|
||||
--- @return boolean `true` if the window is non-`nil` and `nvim_win_is_valid`
|
||||
@ -13,6 +13,7 @@ end
|
||||
--- Close `self.window`
|
||||
--- The `self` is inert after calling this.
|
||||
--- @param keep_buffer boolean `self.buffer` is passed to `nvim_buf_delete` unless `keep_buffer` is `false`
|
||||
--- @return nil
|
||||
function Popup:close(keep_buffer)
|
||||
if valid(self.window) then
|
||||
vim.api.nvim_win_close(self.window, false)
|
||||
@ -35,6 +36,7 @@ function Popup.new(config)
|
||||
end
|
||||
|
||||
--- attempt to open this popup. If the popup was already open, close it and re-open it.
|
||||
--- @return nil
|
||||
function Popup:open(config)
|
||||
if not config then
|
||||
config =
|
||||
@ -62,6 +64,7 @@ end
|
||||
|
||||
--- display `input_bytes` in `self.buffer`
|
||||
--- @param input_bytes number[] a list of character codes to display
|
||||
--- @return nil
|
||||
function Popup:refresh(input_bytes)
|
||||
-- the user simply typed one more character onto the last one.
|
||||
if #input_bytes == #self.input_chars + 1 then
|
||||
|
@ -1,7 +1,7 @@
|
||||
--- @class libmodal.utils.Vars
|
||||
--- @field private mode_name string the highlight group to use when printing `str`
|
||||
--- @field private var_name string the highlight group to use when printing `str`
|
||||
local Vars = require('libmodal/src/utils/classes').new(nil)
|
||||
local Vars = require('libmodal.src.utils.classes').new()
|
||||
|
||||
--- @return unknown `vim.g[self:name()])`
|
||||
function Vars:get()
|
||||
@ -20,11 +20,15 @@ end
|
||||
function Vars.new(var_name, mode_name)
|
||||
local self = setmetatable({}, Vars)
|
||||
|
||||
--- @param str_with_spaces string
|
||||
--- @param first_letter_modifier fun(s: string): string
|
||||
local function no_spaces(str_with_spaces, first_letter_modifier)
|
||||
local split_str = vim.split(string.gsub(str_with_spaces, vim.pesc '_', vim.pesc ' '), ' ')
|
||||
local split_str = vim.split(str_with_spaces:gsub(vim.pesc '_', vim.pesc ' '), ' ')
|
||||
|
||||
--- @param str string
|
||||
--- @param func fun(s: string): string
|
||||
local function camel_case(str, func)
|
||||
return func(string.sub(str, 0, 1) or '') .. string.lower(string.sub(str, 2) or '')
|
||||
return func(str:sub(0, 1) or '') .. (str:sub(2) or ''):lower()
|
||||
end
|
||||
|
||||
split_str[1] = camel_case(split_str[1], first_letter_modifier)
|
||||
@ -42,9 +46,11 @@ function Vars.new(var_name, mode_name)
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param val unknown set `vim.g[self:name()])` equal to this value
|
||||
--- @generic T
|
||||
--- @param val T set `g:{self:name()})` equal to this value
|
||||
--- @return nil
|
||||
function Vars:set(val)
|
||||
vim.g[self:name()] = val
|
||||
vim.api.nvim_set_var(self:name(), val)
|
||||
end
|
||||
|
||||
return Vars
|
||||
|
@ -1,14 +1,14 @@
|
||||
--- @type libmodal.globals
|
||||
local globals = require 'libmodal/src/globals'
|
||||
local globals = require 'libmodal.src.globals' --- @type libmodal.globals
|
||||
|
||||
--- @class libmodal.utils.api
|
||||
local api = {}
|
||||
|
||||
--- send a character to exit a mode.
|
||||
--- @param exit_char? number|string the character used to exit the mode, or ESCAPE if none was provided.
|
||||
--- @return nil
|
||||
function api.mode_exit(exit_char)
|
||||
-- if there was no provided `exit_char`, or it is a character code.
|
||||
if type(exit_char) == globals.TYPE_NUM then
|
||||
if type(exit_char) == 'number' then
|
||||
-- translate the character code or default to escape.
|
||||
--- @diagnostic disable-next-line:param-type-mismatch we just checked `exit_char` == `number`
|
||||
exit_char = string.char(exit_char)
|
||||
@ -22,6 +22,7 @@ function api.mode_exit(exit_char)
|
||||
end
|
||||
|
||||
--- run the `mode` command to refresh the screen.
|
||||
--- @return nil
|
||||
function api.redraw()
|
||||
vim.api.nvim_command 'mode'
|
||||
end
|
||||
|
@ -1,18 +1,18 @@
|
||||
--- @class libmodal.utils.classes
|
||||
local classes = {}
|
||||
local classes =
|
||||
{
|
||||
--- define a metatable.
|
||||
--- @param template? table the default value
|
||||
--- @return table class
|
||||
new = function(template)
|
||||
-- set self to `template`, or `{}` if nil.
|
||||
local self = template or {}
|
||||
|
||||
--- define a metatable.
|
||||
--- @param template? table the default value
|
||||
function classes.new(template)
|
||||
-- set self to `template`, or `{}` if nil.
|
||||
local self = template or {}
|
||||
|
||||
-- set `__index`.
|
||||
if not self.__index then
|
||||
-- set `__index`.
|
||||
self.__index = self
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
return self
|
||||
end,
|
||||
}
|
||||
|
||||
return classes
|
||||
|
@ -1,23 +1,22 @@
|
||||
--- @class libmodal.utils
|
||||
--- @field api libmodal.utils.api
|
||||
--- @field classes libmodal.utils.classes
|
||||
--- @field Indicator libmodal.utils.Indicator
|
||||
--- @field Help libmodal.utils.Help
|
||||
--- @field Popup libmodal.utils.Popup
|
||||
--- @field Vars libmodal.utils.Vars
|
||||
local utils =
|
||||
{
|
||||
api = require 'libmodal/src/utils/api',
|
||||
classes = require 'libmodal/src/utils/classes',
|
||||
Indicator = require 'libmodal/src/utils/Indicator',
|
||||
Help = require 'libmodal/src/utils/Help',
|
||||
Popup = require 'libmodal/src/utils/Popup',
|
||||
Vars = require 'libmodal/src/utils/Vars',
|
||||
api = require 'libmodal.src.utils.api',
|
||||
classes = require 'libmodal.src.utils.classes',
|
||||
Help = require 'libmodal.src.utils.Help',
|
||||
Popup = require 'libmodal.src.utils.Popup',
|
||||
Vars = require 'libmodal.src.utils.Vars',
|
||||
}
|
||||
|
||||
--- `vim.notify` with a `msg` some `error` which has a `vim.v.throwpoint` and `vim.v.exception`.
|
||||
--- @param msg string
|
||||
--- @param err string
|
||||
--- @return nil
|
||||
function utils.notify_error(msg, err)
|
||||
vim.notify(
|
||||
msg .. ': ' .. vim.v.throwpoint .. '\n' .. vim.v.exception .. '\n' .. err,
|
||||
|
@ -1,6 +1,3 @@
|
||||
if vim.g.loaded_libmodal then return end
|
||||
vim.g.loaded_libmodal = true
|
||||
|
||||
vim.g.libmodalTimeouts = vim.g.libmodalTimeouts or vim.go.timeout
|
||||
|
||||
-- The default highlight groups (for colors) are specified below.
|
||||
|
Loading…
Reference in New Issue
Block a user