You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nvim-libmodal/lua/libmodal/src/Prompt.lua

240 lines
6.3 KiB
Lua

--[[/* IMPORTS */]]
4 years ago
local globals = require('libmodal/src/globals')
local utils = require('libmodal/src/utils')
local Vars = require('libmodal/src/Vars')
--[[/* MODULE */]]
local Prompt = {TYPE = 'libmodal-prompt'}
local _HELP = 'help'
4 years ago
local _REPLACEMENTS = {
'(', ')', '[', ']', '{', '}',
'=', '+', '<', '>', '^',
',', '/', ':', '?', '@', '!', '$', '*', '.', '%', '&', '\\',
}
4 years ago
for i, replacement in ipairs(_REPLACEMENTS) do
_REPLACEMENTS[i], _ = vim.pesc(replacement)
4 years ago
end
--[[
/*
* META `Prompt`
*/
--]]
4 years ago
local _metaPrompt = require('libmodal/src/classes').new(Prompt.TYPE)
---------------------------------------------------
--[[ SUMMARY:
* Execute the specified instruction based on user input.
]]
--[[ PARAMS:
* `userInput` => the input from the user, used to determine how to execute the `self._instruction`.
]]
---------------------------------------------------
function _metaPrompt:_executeInstruction(userInput)
-- get the instruction for the mode.
local instruction = self._instruction
if type(instruction) == globals.TYPE_TBL then -- The instruction is a command table.
if instruction[userInput] then -- There is a defined command for the input.
local to_execute = instruction[userInput]
if type(to_execute) == globals.TYPE_FUNC then
to_execute()
else
vim.api.nvim_command(instruction[userInput])
end
elseif userInput == _HELP then -- The user did not define a 'help' command, so use the default.
self._help:show()
else -- show an error.
utils.api.nvim_show_err(globals.DEFAULT_ERROR_TITLE, 'Unknown command')
end
elseif type(instruction) == globals.TYPE_STR then -- The instruction is a function. Works on Neovim 0.5+.
vim.fn[instruction]()
else -- attempt to call the instruction.
instruction()
end
end
4 years ago
---------------------------------
--[[ SUMMARY:
* Loop to get user input with `input()`.
]]
---------------------------------
function _metaPrompt:_inputLoop()
-- If the mode is not handling exit events automatically and the global exit var is true.
if self.exit.supress and globals.is_true(self.exit:nvimGet()) then
return false
end
-- clear previous `echo`s.
utils.api.nvim_redraw()
4 years ago
-- define a placeholder for user input
local userInput = ''
4 years ago
-- echo the highlighting
vim.api.nvim_command('echohl ' .. self.indicator.hl)
4 years ago
-- set the user input variable
if self._completions then userInput =
vim.fn['libmodal#_inputWith'](self.indicator.str, self._completions)
else userInput =
vim.fn.input(self.indicator)
end
4 years ago
-- determine what to do with the input
if string.len(userInput) > 0 then -- The user actually entered something.
4 years ago
self.input:nvimSet(userInput)
self:_executeInstruction(userInput)
4 years ago
else -- indicate we want to leave the prompt
return false
end
return true
end
4 years ago
----------------------------
--[[ SUMMARY:
* Enter a prompt 'mode'.
]]
----------------------------
function _metaPrompt:enter()
-- enter the mode using a loop.
4 years ago
local continueMode = true
while continueMode do
local noErrors, promptResult = pcall(self._inputLoop, self)
-- if there were errors.
if not noErrors then
4 years ago
utils.show_error(promptResult)
continueMode = true
else
4 years ago
continueMode = promptResult
end
end
end
--[[
/*
* CLASS `Prompt`
*/
--]]
------------------------------------------------------
--[[ SUMMARY:
* Provide completions for a `libmodal.prompt`.
]]
--[[ PARAMS:
* `completions` => the list of completions.
]]
--[[ RETURNS:
* A function that accepts:
* `argLead` => the current line being edited, stops at the cursor.
* `cmdLine` => the current line being edited
* `cursorPos` => the position of the cursor
* Used for `input()` VimL.
]]
------------------------------------------------------
function Prompt.createCompletionsProvider(completions)
return function(argLead, cmdLine, _)
if string.len(cmdLine) < 1 then return completions
end
-- replace conjoining characters with spaces.
local spacedArgLead = argLead
4 years ago
for _, replacement in ipairs(_REPLACEMENTS) do
-- _REPLACEMENTS are already `vim.pesc`aped
spacedArgLead, _ = string.gsub(spacedArgLead, replacement, ' ')
end
-- split the spaced version of `argLead`.
local splitArgLead = vim.split(spacedArgLead, ' ', true)
4 years ago
--[[ make sure the user is in a position were this function
will provide accurate completions.]]
if #splitArgLead > 1 then return nil
end
-- get the word selected by the user. (don't compare case)
local word = string.upper(splitArgLead[1])
-- get all matches from the completions list.
local matches = {}
4 years ago
for _, completion in ipairs(completions) do
-- test if `word` is inside of `completions`:`v`, ignoring case.
local escapedCompletion, _ = vim.pesc(string.upper(completion))
if string.match(escapedCompletion, word) then
4 years ago
matches[#matches + 1] = completion -- preserve case when providing completions.
end
end
return matches
end
end
-------------------------------------------
--[[ SUMMARY:
* Enter a prompt.
]]
--[[ PARAMS:
* `name` => the prompt name.
* `instruction` => the prompt callback, or mode command table.
* `...` => a completions table.
]]
-------------------------------------------
function Prompt.new(name, instruction, ...)
name = vim.trim(name)
4 years ago
local self = setmetatable(
{
exit = Vars.new('exit', name),
indicator = require('libmodal/src/Indicator').prompt(name),
input = require('libmodal/src/Vars').new('input', name),
_instruction = instruction,
_name = name
4 years ago
},
_metaPrompt
)
-- get the arguments
local userCompletions, supressExit = unpack({...})
self.exit.supress = supressExit or false
-- 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 containedHelp = false
4 years ago
for command, _ in pairs(instruction) do
completions[#completions + 1] = command
if command == _HELP then containedHelp = true
end
end
if not containedHelp then -- assign it.
completions[#completions + 1] = _HELP
4 years ago
self._help = utils.Help.new(instruction, 'COMMAND')
end
self._completions = completions
elseif userCompletions then
-- Use the table that the user gave.
self._completions = userCompletions
end
4 years ago
return self
end
--[[
/*
* PUBLICIZE MODULE
*/
--]]
return Prompt