perf: general improvements

I forgot to break it up into individual commits, and I ain't going
backwards now.
This commit is contained in:
Iron-E 2023-03-12 19:26:38 -04:00
parent e535c17e84
commit 8c164b811d
No known key found for this signature in database
GPG Key ID: 83A6AEB40395D40D
18 changed files with 218 additions and 225 deletions

View File

@ -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

View File

@ -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,
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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.