From 481ce853ca8b92874164a94c267ba3f377b48c4a Mon Sep 17 00:00:00 2001 From: Iron-E Date: Wed, 20 May 2020 18:45:32 -0400 Subject: [PATCH] Bug fixes --- autoload/libmodal.vim | 17 +-- doc/libmodal.txt | 32 ++-- examples/lua/submodes.lua | 2 +- lua/libmodal/src/Indicator/init.lua | 45 +++--- lua/libmodal/src/Mode/Popup.lua | 125 ++++++++++------ lua/libmodal/src/Mode/init.lua | 140 +++++++++--------- lua/libmodal/src/Prompt.lua | 107 ++++++++----- lua/libmodal/src/Vars.lua | 27 ++-- .../src/{Mode => collections}/ParseTable.lua | 0 lua/libmodal/src/collections/Stack.lua | 44 ++++-- lua/libmodal/src/collections/init.lua | 5 +- lua/libmodal/src/deprecated/mode.lua | 46 ------ lua/libmodal/src/init.lua | 26 +++- lua/libmodal/src/{deprecated => }/prompt.lua | 0 lua/libmodal/src/utils/Help.lua | 55 ++++--- lua/libmodal/src/utils/WindowState.lua | 45 +++--- lua/libmodal/src/utils/api.lua | 15 +- 17 files changed, 398 insertions(+), 333 deletions(-) rename lua/libmodal/src/{Mode => collections}/ParseTable.lua (100%) delete mode 100644 lua/libmodal/src/deprecated/mode.lua rename lua/libmodal/src/{deprecated => }/prompt.lua (100%) diff --git a/autoload/libmodal.vim b/autoload/libmodal.vim index 129b00f..d79b028 100644 --- a/autoload/libmodal.vim +++ b/autoload/libmodal.vim @@ -17,8 +17,6 @@ let s:winOpenOpts = { " RETURNS: " * Input from `input()`. function! libmodal#_inputWith(indicator, completions) - " TODO: 0.5 — return input(a:indicator, '', 'customlist,v:lua.require("libmodal/src/prompt")…') - " return the closure that was generated using the completions from lua. function! LibmodalCompletionsProvider(argLead, cmdLine, cursorPos) abort closure return luaeval( \ 'require("libmodal/src/Prompt/").createCompletionsProvider(_A[1])(_A[2], _A[3], _A[4])', @@ -30,19 +28,6 @@ function! libmodal#_inputWith(indicator, completions) return input(a:indicator, '', 'customlist,LibmodalCompletionsProvider') endfunction -" SUMMARY: -" * Open a floating window using native vimscript. -" REMARKS: -" * There are bugs with creating floating windows using Lua (mostly they are -" always focused), so it was necessary to create a vimscript method. -" PARAMS: -" * `bufHandle` => the buffer to spawn the window for. -" RETURNS: -" * A window handle. -function! libmodal#_winOpen(bufHandle) abort - return nvim_open_win(a:bufHandle, 0, s:winOpenOpts) -endfunction - " SUMMARY: " * Runs the nvim-libmodal command prompt loop. The function takes an optional " argument specifying how many times to run (runs until exiting by default). @@ -74,7 +59,7 @@ endfunction " * `args` => the arguments to pass to `lib`.enter() function! libmodal#_lua(lib, args) call luaeval( - \ 'require("libmodal/src/' . a:lib . '").enter(_A[1], _A[2], _A[3])', + \ 'require("libmodal").' . a:lib . '.enter(_A[1], _A[2], _A[3])', \ [ \ a:args[0], \ a:args[1], diff --git a/doc/libmodal.txt b/doc/libmodal.txt index 821c909..1650fe4 100644 --- a/doc/libmodal.txt +++ b/doc/libmodal.txt @@ -660,25 +660,29 @@ When submitting an issue, please describe the following: 0.5.0 ~ Additions: ~ - * New class `libmodal.Mode` - * New class `libmodal.Mode.Popup` - * New class `libmodal.Prompt` - * New class `libmodal.Vars` + * New class `libmodal.collections.Stack`. + * New class `libmodal.Mode`. + * New class `libmodal.Mode.Popup`. + * New class `libmodal.Prompt`. + * New class `libmodal.Vars`. Breaking Changes: ~ - * Removed `libmodal.utils.vars` - * Moved `libmodal.utils.Indicator` to `libmodal.Indicator` + * Moved `libmodal.mode.ParseTable` to `libmodal.collections.ParseTable`. + * Removed `libmodal.utils.vars`. + * Moved `libmodal.utils.Indicator` to `libmodal.Indicator`. * Moved `libmodal.utils.Indicator.Entry` to - `libmodal.Indicator.HighlightSegment` + `libmodal.Indicator.HighlightSegment`. Changes: ~ - *Error deprecated `libmodal.mode.enter()` - * Use `libmodal.Mode.new(…):enter()` instead. - *Error deprecated `libmodal.prompt.enter()` - * Use `libmodal.Prompt.new(…):enter()` instead. - * Allowed creation of |libmodal-mode| and |libmodal-prompt| table-objects. - * Call `{mode}:enter()` to enter the mode. - * More efficient recursion. + * Allow creation of |libmodal-mode| and |libmodal-prompt| table-objects. + * Call `libmodal.Mode.new()` or `libmodal.Prompt.new()` to create one. + * Call `{mode}:enter()` to enter the mode. + * More consistent mode recursion with `libmodal.collections.Stack`. + + * Removed program logic from `libmodal.mode.enter()`. + * Now internally calls `libmodal.Mode.new(…):enter()` instead. + * Removed program logic from `libmodal.prompt.enter()`. + * Now internally calls `libmodal.Prompt.new(…):enter()` instead. 0.4.1 ~ diff --git a/examples/lua/submodes.lua b/examples/lua/submodes.lua index f0a6d6b..3d0b04b 100644 --- a/examples/lua/submodes.lua +++ b/examples/lua/submodes.lua @@ -1,5 +1,5 @@ local libmodal = require('libmodal') -local fooModeRecurse = 0 +local fooModeRecurse = 1 function fooMode() local userInput = string.char(vim.api.nvim_get_var( diff --git a/lua/libmodal/src/Indicator/init.lua b/lua/libmodal/src/Indicator/init.lua index 5f943f0..29cd618 100644 --- a/lua/libmodal/src/Indicator/init.lua +++ b/lua/libmodal/src/Indicator/init.lua @@ -9,17 +9,13 @@ local Indicator = {} Indicator.HighlightSegment = require('libmodal/src/Indicator/HighlightSegment') -- highlight group names -local _HL_GROUP_PROMPT = 'LibmodalPrompt' -local _HL_GROUP_STAR = 'LibmodalStar' -local _HL_GROUP_NONE = 'None' +local _HL_GROUP_MODE = 'LibmodalPrompt' +local _HL_GROUP_PROMPT = 'LibmodalStar' --- `libmodal.mode` `HighlightSegment`s. -local _ENTRY_MODE_START = Indicator.HighlightSegment.new(_HL_GROUP_PROMPT, '-- ') -local _ENTRY_MODE_END = Indicator.HighlightSegment.new(_HL_GROUP_PROMPT, ' --') - --- `libmodal.prompt` `HighlightSegment`s. -local _ENTRY_PROMPT_START = Indicator.HighlightSegment.new(_HL_GROUP_STAR, '* ') -local _ENTRY_PROMPT_END = Indicator.HighlightSegment.new(_HL_GROUP_PROMPT, ' > ') +-- predefined segments +local _SEGMENT_MODE_BEGIN = Indicator.HighlightSegment.new(_HL_GROUP_MODE, '-- ') +local _SEGMENT_MODE_END = Indicator.HighlightSegment.new(_HL_GROUP_MODE, ' --') +local _PROMPT_TEMPLATE = {'* ', ' > '} --[[ /* @@ -27,15 +23,6 @@ local _ENTRY_PROMPT_END = Indicator.HighlightSegment.new(_HL_GROUP_PROMPT, ' > */ --]] -local _metaIndicator = { - _ENTRY_MODE_START, nil, _ENTRY_MODE_END -} -_metaIndicator.__index = _metaIndicator - -local _PROMPT_TEMPLATE = { - '* ', ' > ' -} - --[[ /* * CLASS `Indicator` @@ -51,14 +38,13 @@ local _PROMPT_TEMPLATE = { ]] --------------------------------- function Indicator.mode(modeName) - local self = {} - setmetatable(self, _metaIndicator) - - self[2] = Indicator.HighlightSegment.new( - _HL_GROUP_PROMPT, tostring(modeName) - ) - - return self + return { + [1] = _SEGMENT_MODE_BEGIN, + [2] = Indicator.HighlightSegment.new( + _HL_GROUP_MODE, tostring(modeName) + ), + [3] = _SEGMENT_MODE_END, + } end ----------------------------------- @@ -70,7 +56,10 @@ end ]] ----------------------------------- function Indicator.prompt(modeName) - return table.concat(_PROMPT_TEMPLATE, modeName) + return Indicator.HighlightSegment.new( + _HL_GROUP_PROMPT, + table.concat(_PROMPT_TEMPLATE, modeName) + ) end --[[ diff --git a/lua/libmodal/src/Mode/Popup.lua b/lua/libmodal/src/Mode/Popup.lua index 2191ed6..03ebcd2 100644 --- a/lua/libmodal/src/Mode/Popup.lua +++ b/lua/libmodal/src/Mode/Popup.lua @@ -14,62 +14,93 @@ local api = vim.api local Popup = {} +local _winOpenOpts = { + ['anchor'] = 'SW', + ['col'] = api.nvim_get_option('columns') - 1, + ['focusable'] = false, + ['height'] = 1, + ['relative'] = 'editor', + ['row'] = api.nvim_get_option('lines') + - api.nvim_get_option('cmdheight') + - 1, + ['style'] = 'minimal', + ['width'] = 25, +} + --[[ /* - * CLASS `Popup` + * META `Popup` */ --]] -function Popup.new() - local self = {} - - local buffer = api.nvim_create_buf(false, true) - local inputChars = {} - self.window = api.nvim_call_function('libmodal#_winOpen', {buf}) - - --------------------------- - --[[ SUMMARY: - * Close `self.window` - * The `self` is inert after calling this. - ]] - --------------------------- - function self.close() - api.nvim_win_close(__self.window, false) - - buffer = nil - inputChars = nil - self.window = nil - end +local _metaPopup = {} +_metaPopup.__index = _metaPopup - --------------------------------- - --[[ SUMMARY: - * Update `buffer` with the latest user `inputBytes`. - ]] - --------------------------------- - function self.refresh(inputBytes) - -- The user simply typed one more character onto the last one. - if #inputBytes == #inputChars + 1 then - local len = #inputBytes - inputChars[len] = string.char(inputBytes[len]) - elseif #inputBytes == 1 then -- the input was cleared. - inputChars = { - [1] = string.char(inputBytes[1]) - } - else -- other tries to optimize this procedure fellthrough, - -- so do it the hard way. - inputChars = {} - for i, byte in ipairs(inputBytes) do - inputChars[i] = string.char(byte) - end - end +_metaPopup._buffer = nil +_metaPopup._inputChars = nil +_metaPopup.window = nil - api.nvim_buf_set_lines( - buffer, 0, 1, true, - {table.concat(inputChars)} - ) +--------------------------- +--[[ SUMMARY: + * Close `self.window` + * The `self` is inert after calling this. +]] +--------------------------- +function _metaPopup:close() + api.nvim_win_close(self.window, false) + + self._buffer = nil + self._inputChars = nil + self.window = nil +end + +--------------------------------- +--[[ SUMMARY: + * Update `buffer` with the latest user `inputBytes`. +]] +--------------------------------- +function _metaPopup:refresh(inputBytes) + local inputBytesLen = #inputBytes + local inputChars = self._inputChars + + -- The user simply typed one more character onto the last one. + if inputBytesLen == #inputChars + 1 then + inputChars[inputBytesLen] = string.char(inputBytes[inputBytesLen]) + elseif inputBytesLen == 1 then + inputChars = {string.char(inputBytes[1])} + else -- other tries to optimize this procedure fellthrough, so do it the hard way. + local chars = {} + for i, byte in ipairs(inputBytes) do + chars[i] = string.char(byte) + end + self._inputChars = chars end - return self + api.nvim_buf_set_lines( + self._buffer, 0, 1, true, + {table.concat(self._inputChars)} + ) +end + +--[[ + /* + * CLASS `Popup` + */ +--]] + +function Popup.new() + local buf = api.nvim_create_buf(false, true) + + return setmetatable( + { + ['_buffer'] = buf, + ['_inputChars'] = {}, + ['window'] = api.nvim_call_function( + 'nvim_open_win', {buf, false, _winOpenOpts} + ) + }, + _metaPopup + ) end --[[ diff --git a/lua/libmodal/src/Mode/init.lua b/lua/libmodal/src/Mode/init.lua index 7a4563c..d8d2b66 100644 --- a/lua/libmodal/src/Mode/init.lua +++ b/lua/libmodal/src/Mode/init.lua @@ -4,11 +4,11 @@ */ --]] -local globals = require('libmodal/src/globals') -local Indicator = require('libmodal/src/Indicator') -local Stack = require('libmodal/src/collections/Stack') -local utils = require('libmodal/src/utils') -local Vars = require('libmodal/src/Vars') +local globals = require('libmodal/src/globals') +local Indicator = require('libmodal/src/Indicator') +local collections = require('libmodal/src/collections') +local utils = require('libmodal/src/utils') +local Vars = require('libmodal/src/Vars') local api = utils.api @@ -20,7 +20,6 @@ local api = utils.api local Mode = {} -Mode.ParseTable = require('libmodal/src/Mode/ParseTable') Mode.Popup = require('libmodal/src/Mode/Popup') local _HELP = '?' @@ -42,11 +41,15 @@ _TIMEOUT.NR = string.byte(_TIMEOUT.CHAR) local _metaMode = {} _metaMode.__index = _metaMode -_metaMode._exit = Vars.new('exit') -_metaMode._indicator = nil -_metaMode._instruction = nil -_metaMode._name = nil -_metaMode._winState = nil +local _metaInputBytes = { + ['clear'] = function(__self) + for i, _ in ipairs(__self) do + __self[i] = nil + end + end +} +_metaInputBytes.__index = _metaInputBytes + ----------------------------------------------- --[[ SUMMARY: @@ -55,15 +58,15 @@ _metaMode._winState = nil ----------------------------------------------- function _metaMode:_checkInputForMapping() -- Stop any running timers - self._modeEnterData:peek().flushInputTimer:stop() + self._flushInputTimer:stop() -- Append the latest input to the locally stored input history. - local input = self._modeEnterData:peek().input + local inputBytes = self._modeEnterData:peek().inputBytes - input.bytes[#input.bytes + 1] = input:nvimGet(self._name) + inputBytes[#inputBytes + 1] = self._input:nvimGet() -- Get the command based on the users input. - local cmd = self._mappings:get(input.bytes) + local cmd = self._mappings:get(inputBytes) -- Get the type of the command. local commandType = type(cmd) @@ -71,10 +74,10 @@ function _metaMode:_checkInputForMapping() -- if there was no matching command if cmd == false then - if #input.bytes < 2 and input.bytes[1] == string.byte(_HELP) then + if #inputBytes < 2 and inputBytes[1] == string.byte(_HELP) then self._help:show() end - input:clear() + inputBytes:clear() -- The command was a table, meaning that it MIGHT match. elseif commandType == globals.TYPE_TBL and globals.isTrue(self._timeouts.enabled) @@ -82,27 +85,27 @@ function _metaMode:_checkInputForMapping() -- Create a new timer -- start the timer - self._modeEnterData:peek().flushInputTimer:start( + self._flushInputTimer:start( _TIMEOUT.LEN, 0, vim.schedule_wrap(function() -- Send input to interrupt a blocking `getchar` _TIMEOUT:SEND() -- if there is a command, execute it. - if cmd[Mode.ParseTable.CR] then - api.nvim_command(cmd[Mode.ParseTable.CR]) + if cmd[collections.ParseTable.CR] then + api.nvim_command(cmd[collections.ParseTable.CR]) end -- clear input - input:clear() - self._modeEnterData:peek().popup:refresh(input.bytes) + inputBytes:clear() + self._modeEnterData:peek().popup:refresh(inputBytes) end) ) -- The command was an actual vim command. else api.nvim_command(cmd) - input:clear() + inputBytes:clear() end - self._modeEnterData:peek().popup:refresh(input.bytes) + self._modeEnterData:peek().popup:refresh(inputBytes) end -------------------------- @@ -112,34 +115,26 @@ end -------------------------- function _metaMode:enter() -- intialize variables that are needed for each recurse of a function - if self._instruction == globals.TYPE_TBL then + if type(self._instruction) == globals.TYPE_TBL then -- Initialize the input history variable. - local input = Vars.new('input') - input.bytes = {} - function input:clear() - self.bytes = {} - end - self._modeEnterData:push({ - -- Create a timer - ['flushInputTimer'] = vim.loop.new_timer(), - -- assign input - ['input'] = input, + ['inputBytes'] = setmetatable({}, _metaInputBytes), -- create a floating window ['popup'] = Mode.Popup.new() }) end + --[[ MODE LOOP. ]] local continueMode = true - while continueMode == true do + while continueMode do -- Try (using pcall) to use the mode. local noErrors, modeResult = pcall(self._inputLoop, self) -- If there were errors, handle them. - if noErrors == false then + if not noErrors then utils.showError(modeResult) continueMode = false else @@ -156,26 +151,30 @@ end ]] ---------------------------------- function _metaMode:_initMappings() - -- Create a variable for whether or not timeouts are enabled. - self._timeouts = Vars.new('timeouts') - - -- Read the correct timeout variable. - if api.nvim_exists('g', self._timeouts:name(modeName)) then self._timeouts.enabled = - self._timeouts:nvimGet(self._name) - else self._timeouts.enabled = - Vars.libmodalTimeouts - end + -- Create a timer to perform actions with. + self._flushInputTimer = vim.loop.new_timer() -- Determine if a default `Help` should be created. if not self._instruction[_HELP] then self._help = utils.Help.new(self._instruction, 'KEY MAP') end + -- Build the parse tree. + self._mappings = collections.ParseTable.new(self._instruction) + -- Create a table for mode-specific data. - self._modeEnterData = Stack.new() + self._modeEnterData = collections.Stack.new() - -- Build the parse tree. - self._mappings = Mode.ParseTable.new(self._instruction) + -- Create a variable for whether or not timeouts are enabled. + self._timeouts = Vars.new('timeouts', self._name) + + -- Read the correct timeout variable. + if api.nvim_exists('g', self._timeouts:name()) + then self._timeouts.enabled = + self._timeouts:nvimGet() + else self._timeouts.enabled = + Vars.libmodalTimeouts + end end ------------------------------- @@ -189,7 +188,7 @@ end function _metaMode:_inputLoop() -- If the mode is not handling exit events automatically and the global exit var is true. if self._exit.supress - and globals.isTrue(self._exit:nvimGet(self._name)) + and globals.isTrue(self._exit:nvimGet()) then return false end @@ -206,7 +205,7 @@ function _metaMode:_inputLoop() end -- Set the global input variable to the new input. - self._modeEnterData:peek().input:nvimSet(self._name, userInput) + self._input:nvimSet(userInput) -- Make sure that the user doesn't want to exit. if not self._exit.supress @@ -221,28 +220,28 @@ function _metaMode:_inputLoop() return true end ---------------------------------- +------------------------------ --[[ SUMMARY: * Remove variables used for a mode. ]] ---------------------------------- +------------------------------ function _metaMode:_tearDown() - if self._instruction == globals.TYPE_TBL then - local modeEnterData = self._modeEnterData:pop() + if type(self._instruction) == globals.TYPE_TBL then + self._flushInputTimer:stop() - modeEnterData.flushInputTimer:stop() - modeEnterData.flushInputTimer = nil + local modeEnterData = self._modeEnterData:pop() - for k, _ in pairs(modeEnterData.input) do - modeEnterData.input[k] = nil + for k, _ in pairs(modeEnterData.inputBytes) do + modeEnterData.inputBytes[k] = nil end - modeEnterData.input = nil + modeEnterData.inputBytes = nil modeEnterData.popup:close() modeEnterData.popup = nil end self._winState:restore() + api.nvim_redraw() end --[[ @@ -263,12 +262,21 @@ end ----------------------------------------- function Mode.new(name, instruction, ...) -- Inherit the metatable. - self = setmetatable({}, _metaMode) + local self = setmetatable( + { + ['_exit'] = Vars.new('exit', name), + ['_indicator'] = Indicator.mode(name), + ['_input'] = Vars.new('input', name), + ['_instruction'] = instruction, + ['_name'] = name, + ['_winState'] = utils.WindowState.new(), + }, + _metaMode + ) -- Define the exit flag - self._exit = setmetatable({}, _metaMode._exit) self._exit.supress = (function(optionalValue) - if #optionalValue > 0 then + if optionalValue then return globals.isTrue(optionalValue) else return false @@ -276,10 +284,6 @@ function Mode.new(name, instruction, ...) end)(unpack({...})) -- Define other "session" variables. - self._indicator = Indicator.mode(name) - self._instruction = instruction - self._name = name - self._winState = utils.WindowState.new() -- Determine whether a callback was specified, or a combo table. if type(instruction) == globals.TYPE_TBL then @@ -290,7 +294,7 @@ function Mode.new(name, instruction, ...) end --[[ - /* + / * PUBLICIZE MODULE */ --]] diff --git a/lua/libmodal/src/Prompt.lua b/lua/libmodal/src/Prompt.lua index 810d8cd..da71bf8 100644 --- a/lua/libmodal/src/Prompt.lua +++ b/lua/libmodal/src/Prompt.lua @@ -6,7 +6,6 @@ local globals = require('libmodal/src/globals') local Indicator = require('libmodal/src/Indicator') -local Stack = require('libmodal/src/collections/Stack') local utils = require('libmodal/src/utils') local Vars = require('libmodal/src/Vars') @@ -21,11 +20,14 @@ local api = utils.api local Prompt = {} local _HELP = 'help' -local _replacements = { +local _REPLACEMENTS = { '(', ')', '[', ']', '{', '}', '=', '+', '<', '>', '^', ',', '/', ':', '?', '@', '!', '$', '*', '.', '%', '&', '\\', } +for i, replacement in ipairs(_REPLACEMENTS) do + _REPLACEMENTS[i] = vim.pesc(replacement) +end --[[ /* @@ -36,58 +38,72 @@ local _replacements = { local _metaPrompt = {} _metaPrompt.__index = _metaPrompt -_metaPrompt._indicator = nil -_metaPrompt._input = nil -_metaPrompt._instruction = nil -_metaPrompt._name = nil - +--------------------------------- +--[[ SUMMARY: + * Loop to get user input with `input()`. +]] +--------------------------------- function _metaPrompt:_inputLoop() -- clear previous `echo`s. api.nvim_redraw() - -- get user input based on `instruction`. + -- define a placeholder for user input local userInput = '' - if self._completions then userInput = + + -- echo the highlighting + api.nvim_command('echohl ' .. self._indicator.hl) + + -- set the user input variable + if self._completions + then userInput = api.nvim_call_function('libmodal#_inputWith', { - self._indicator, self._completions + self._indicator.str, self._completions }) else userInput = - api.nvim_call_function('input', { - self._indicator - }) + api.nvim_call_function('input', {self._indicator}) end - -- if a:2 is a function then call it. - if string.len(userInput) > 0 then - self._input:nvimSet(self._name, userInput) - if type(self._instruction) == globals.TYPE_TBL then - if self._instruction[userInput] then -- there is a defined command for the input. + -- get the instruction for the mode. + local instruction = self._instruction + + -- determine what to do with the input + if string.len(userInput) > 0 then -- the user actually entered something + self._input:nvimSet(userInput) + 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. api.nvim_command(instruction[userInput]) elseif userInput == _HELP then -- the user did not define a 'help' command, so use the default. self._help:show() else -- show an error. api.nvim_show_err(globals.DEFAULT_ERROR_TITLE, 'Unknown command') end - else instruction() + else -- attempt to call the instruction. + instruction() end - else return false + else -- indicate we want to leave the prompt + return false end return true end +---------------------------- +--[[ SUMMARY: + * Enter a prompt 'mode'. +]] +---------------------------- function _metaPrompt:enter() -- enter the mode using a loop. - local continueMode = true - while continueMode == true do - local noErrors, result = pcall(self._inputLoop, self) + local continueMode = true + while continueMode do + local noErrors, promptResult = pcall(self._inputLoop, self) -- if there were errors. if not noErrors then - utils.showError(err) + utils.showError(promptResult) continueMode = false else - continueMode = result + continueMode = promptResult end end end @@ -121,13 +137,15 @@ function Prompt.createCompletionsProvider(completions) -- replace conjoining characters with spaces. local spacedArgLead = argLead - for _, v in ipairs(_replacements) do - spacedArgLead, _ = string.gsub(spacedArgLead, vim.pesc(v), ' ') + for _, replacement in ipairs(_REPLACEMENTS) do + spacedArgLead, _ = string.gsub(spacedArgLead, replacement, ' ') end -- split the spaced version of `argLead`. local splitArgLead = vim.split(spacedArgLead, ' ', true) + print(spacedArgLead) + -- make sure the user is in a position were this function -- will provide accurate completions. if #splitArgLead > 1 then @@ -139,10 +157,14 @@ function Prompt.createCompletionsProvider(completions) -- get all matches from the completions list. local matches = {} - for _, v in ipairs(completions) do + for _, completion in ipairs(completions) do + print('testing ' .. word .. ' against ' .. string.upper(completion)) -- test if `word` is inside of `completions`:`v`, ignoring case. - if string.match(vim.pesc(string.upper(v)), word) then - matches[#matches + 1] = v -- preserve case when providing completions. + if string.match(vim.pesc(string.upper(completion)), word) then + print('>>match') + matches[#matches + 1] = completion -- preserve case when providing completions. + else + print('>>nomatch') end end return matches @@ -160,12 +182,15 @@ end ]] ------------------------------------------- function Prompt.new(name, instruction, ...) - self = setmetatable({}, _metaPrompt) - - self._indicator = Indicator.prompt(name) - self._input = vars.new('input') - self._instruction = instruction - self._name = name + local self = setmetatable( + { + ['_indicator'] = Indicator.prompt(name), + ['_input'] = Vars.new('input', name), + ['_instruction'] = instruction, + ['_name'] = name + }, + _metaPrompt + ) -- get the arguments local userCompletions = unpack({...}) @@ -176,15 +201,15 @@ function Prompt.new(name, instruction, ...) local completions = {} local containedHelp = false - for k, _ in pairs(instruction) do - completions[#completions + 1] = k - if k == _HELP then containedHelp = true + 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 - vars.help.instances[modeName] = utils.Help.new(instruction, 'COMMAND') + self._help = utils.Help.new(instruction, 'COMMAND') end self._completions = completions @@ -192,6 +217,8 @@ function Prompt.new(name, instruction, ...) -- Use the table that the user gave. self._completions = userCompletions end + + return self end diff --git a/lua/libmodal/src/Vars.lua b/lua/libmodal/src/Vars.lua index 682789c..c9843f1 100644 --- a/lua/libmodal/src/Vars.lua +++ b/lua/libmodal/src/Vars.lua @@ -29,18 +29,19 @@ local _metaVars = {} _metaVars.__index = _metaVars -- Instances of variables pertaining to a certain mode. -_metaVars.varName = nil +_metaVars._varName = nil +_metaVars._modeName = nil -------------------------- +--------------------------------- --[[ SUMMARY: * Get the name of `modeName`s global setting. ]] --[[ PARAMS: * `modeName` => the name of the mode. ]] -------------------------- -function _metaVars:name(modeName) - return string.lower(modeName) .. self._varName +--------------------------------- +function _metaVars:name() + return string.lower(self._modeName) .. self._varName end ------------------------------------ @@ -51,8 +52,8 @@ end * `modeName` => the mode name this value is being retrieved for. ]] ------------------------------------ -function _metaVars:nvimGet(modeName) - return api.nvim_get_var(self:name(modeName)) +function _metaVars:nvimGet() + return api.nvim_get_var(self:name()) end ----------------------------------------- @@ -64,8 +65,8 @@ end * `val` => the value to set `self`'s Vimscript var to. ]] ----------------------------------------- -function _metaVars:nvimSet(modeName, val) - api.nvim_set_var(self:name(modeName), val) +function _metaVars:nvimSet(val) + api.nvim_set_var(self:name(), val) end --[[ @@ -82,11 +83,11 @@ end * `keyName` => the name of the key used to refer to this variable in `Vars`. ]] -------------------------- -function Vars.new(keyName) - self = {} - setmetatable(self, _metaVars) +function Vars.new(keyName, modeName) + local self = setmetatable({}, _metaVars) - self._varName = 'Mode' .. string.upper( + self._modeName = modeName + self._varName = 'Mode' .. string.upper( string.sub(keyName, 0, 1) ) .. string.sub(keyName, 2) diff --git a/lua/libmodal/src/Mode/ParseTable.lua b/lua/libmodal/src/collections/ParseTable.lua similarity index 100% rename from lua/libmodal/src/Mode/ParseTable.lua rename to lua/libmodal/src/collections/ParseTable.lua diff --git a/lua/libmodal/src/collections/Stack.lua b/lua/libmodal/src/collections/Stack.lua index c1414d2..ad54bbd 100644 --- a/lua/libmodal/src/collections/Stack.lua +++ b/lua/libmodal/src/collections/Stack.lua @@ -27,7 +27,7 @@ _metaStack._top = nil ]] -------------------------------- function _metaStack:peek() - return top + return self._top end ------------------------- @@ -39,18 +39,28 @@ end ]] ------------------------- function _metaStack:pop() - if len < 1 then return nil + local previousLen = self._len + + if previousLen < 1 then return nil end -- Store the previous top of the stack. local previousTop = self._top -- Remove the previous top of the stack. - self[_len] = nil + self[previousLen] = nil + + -- Get the new length of the stack + local newLen = previousLen - 1 -- Update the values of the stack. - self._len = self._len - 1 - self._top = self[_len] + if newLen < 1 then -- the stack is empty + self._len = nil + self._top = nil + else -- there is still something in the stack + self._len = newLen + self._top = self[newLen] + end -- Return the previous top of the stack. return previousTop @@ -65,8 +75,15 @@ end ]] ------------------------------- function _metaStack:push(value) - self._len = self._len + 1 - self[_len] = value + -- create placeholder so new values are not put into the table until operations have succeeded. + local newLen = self._len + 1 + + -- Push to the stack + self[newLen] = value + + -- update stack values + self._len = newLen + self._top = value end --[[ @@ -76,8 +93,13 @@ end --]] function Stack.new() - local self = {} - setmetatable(self, _metaStack) - - return self + return setmetatable({}, _metaStack) end + +--[[ + /* + * PUBLICIZE `Stack` + */ +--]] + +return Stack diff --git a/lua/libmodal/src/collections/init.lua b/lua/libmodal/src/collections/init.lua index 8e3ffde..74b82ba 100644 --- a/lua/libmodal/src/collections/init.lua +++ b/lua/libmodal/src/collections/init.lua @@ -1,3 +1,6 @@ local collections = {} -collections.Stack = require('libmodal/src/collections/Stack') + +collections.ParseTable = require('libmodal/src/collections/ParseTable') +collections.Stack = require('libmodal/src/collections/Stack') + return collections diff --git a/lua/libmodal/src/deprecated/mode.lua b/lua/libmodal/src/deprecated/mode.lua deleted file mode 100644 index e0d4aef..0000000 --- a/lua/libmodal/src/deprecated/mode.lua +++ /dev/null @@ -1,46 +0,0 @@ ---[[ - /* - * IMPORTS - */ ---]] - -local Mode = require('libmodal/src/Mode') - ---[[ - /* - * MODULE - */ ---]] - -local mode = {} - -mode.ParseTable = Mode.ParseTable - ---[[ - /* - * LIBRARY `mode` - */ ---]] - ------------------------- ---[[ DEPRECATED. ]] ---[[ SUMMARY: - * Enter a mode. -]] ---[[ PARAMS: - * `args[1]` => the mode name. - * `args[2]` => the mode callback, or mode combo table. - * `args[3]` => optional exit supresion flag. -]] ------------------------- -function mode.enter(name, instruction, ...) - Mode.new(name, instruction, ...):enter() -end - ---[[ - /* - * PUBLICIZE MODULE - */ ---]] - -return mode diff --git a/lua/libmodal/src/init.lua b/lua/libmodal/src/init.lua index 018b663..5721287 100644 --- a/lua/libmodal/src/init.lua +++ b/lua/libmodal/src/init.lua @@ -6,12 +6,26 @@ local libmodal = {} -libmodal.globals = require('libmodal/src/globals') -libmodal.mode = require('libmodal/src/deprecated/mode') -libmodal.Mode = require('libmodal/src/Mode') -libmodal.prompt = require('libmodal/src/deprecated/prompt') -libmodal.Prompt = require('libmodal/src/Prompt') -libmodal.utils = require('libmodal/src/utils') +libmodal.collection = require('libmodal/src/collections') +libmodal.globals = require('libmodal/src/globals') +libmodal.Indicator = require('libmodal/src/Indicator') +libmodal.Mode = require('libmodal/src/Mode') +libmodal.Prompt = require('libmodal/src/Prompt') +libmodal.utils = require('libmodal/src/utils') + +--[[ + /* + * MIRRORS + */ +--]] + +libmodal.mode = {['enter'] = function(name, instruction, ...) + libmodal.Mode.new(name, instruction, ...):enter() +end} + +libmodal.prompt = {['enter'] = function(name, instruction, ...) + libmodal.Prompt.new(name, instruction, ...):enter() +end} --[[ /* diff --git a/lua/libmodal/src/deprecated/prompt.lua b/lua/libmodal/src/prompt.lua similarity index 100% rename from lua/libmodal/src/deprecated/prompt.lua rename to lua/libmodal/src/prompt.lua diff --git a/lua/libmodal/src/utils/Help.lua b/lua/libmodal/src/utils/Help.lua index 0f85ff4..8bbea78 100644 --- a/lua/libmodal/src/utils/Help.lua +++ b/lua/libmodal/src/utils/Help.lua @@ -1,3 +1,24 @@ +--[[ + /* + * META `Help` + */ +--]] + +local _metaHelp = {} +_metaHelp.__index = _metaHelp + +------------------------- +--[[ SUMMARY: + * Show the contents of this `Help`. +]] +------------------------- +function _metaHelp:show() + for _, helpText in ipairs(self) do + print(helpText) + end + vim.api.nvim_call_function('getchar', {}) +end + --[[ /* * CLASS `Help` @@ -68,27 +89,19 @@ function Help.new(commandsOrMaps, title) helpSeparator = table.concat(helpSeparator) -- Create a new `Help`. - return { - [1] = ' ', - [2] = table.concat(tabAlign({ - [title] = 'VIM EXPRESSION', - })), - [3] = table.concat(tabAlign({ - [helpSeparator] = '--------------' - })), - [4] = table.concat(tabAlign(commandsOrMaps)), - ----------------------- - --[[ SUMMARY: - * Show the contents of this `Help`. - ]] - ----------------------- - show = function(__self) - for _, v in ipairs(__self) do - print(v) - end - vim.api.nvim_call_function('getchar', {}) - end - } + return setmetatable( + { + [1] = ' ', + [2] = table.concat(tabAlign({ + [title] = 'VIM EXPRESSION', + })), + [3] = table.concat(tabAlign({ + [helpSeparator] = '--------------' + })), + [4] = table.concat(tabAlign(commandsOrMaps)), + }, + _metaHelp + ) end --[[ diff --git a/lua/libmodal/src/utils/WindowState.lua b/lua/libmodal/src/utils/WindowState.lua index 2c85828..5aef1e7 100644 --- a/lua/libmodal/src/utils/WindowState.lua +++ b/lua/libmodal/src/utils/WindowState.lua @@ -4,7 +4,7 @@ */ --]] -local api = vim.api +local api = require('libmodal/src/utils/api') --[[ /* @@ -17,6 +17,26 @@ local WindowState = {} local height = 'winheight' local width = 'winwidth' +--[[ + /* + * META `WindowState` + */ +--]] + +local _metaWindowState = {} +_metaWindowState.__index = _metaWindowState + +----------------------------------- +--[[ SUMMARY + * Restore the state of `self`. +]] +----------------------------------- +function _metaWindowState:restore() + api.nvim_set_option(height, self.height) + api.nvim_set_option(width, self.width) + api.nvim_redraw() +end + --[[ /* * CLASS `WindowState` @@ -32,22 +52,13 @@ local width = 'winwidth' ]] -------------------------- function WindowState.new() - local winState = { - ['height'] = api.nvim_get_option(height), - ['width'] = api.nvim_get_option(width), - } - - --------------------------- - --[[ SUMMARY - * Restore the state of `self`. - ]] - --------------------------- - function winState:restore() - api.nvim_set_option(height, self['height']) - api.nvim_set_option(width, self['width']) - end - - return winState + return setmetatable( + { + ['height'] = api.nvim_get_option(height), + ['width'] = api.nvim_get_option(width), + }, + _metaWindowState + ) end --[[ diff --git a/lua/libmodal/src/utils/api.lua b/lua/libmodal/src/utils/api.lua index f4f9065..6adb3c2 100644 --- a/lua/libmodal/src/utils/api.lua +++ b/lua/libmodal/src/utils/api.lua @@ -74,14 +74,21 @@ end * `hlTables` => the tables to echo with highlights. ]] --------------------------------- +local lecho_template = { + [1] = "echohl ", + [2] = nil, + [3] = " | echon '", + [4] = nil, + [5] = "'" +} function api.nvim_lecho(hlTables) api.nvim_redraw() for _, hlTable in ipairs(hlTables) do -- `:echohl` the hlgroup and then `:echon` the string - api.nvim_command( - "echohl " .. tostring(hlTable['hl']) - .. " | echon '" .. tostring(hlTable['str']) .. "'" - ) + lecho_template[2] = tostring(hlTable.hl) + lecho_template[4] = tostring(hlTable.str) + + api.nvim_command(table.concat(lecho_template)) end api.nvim_command('echohl None') end