From 41564fa9f35b073b4f553ef0101d8b27c8d59494 Mon Sep 17 00:00:00 2001 From: Iron-E Date: Tue, 19 May 2020 06:10:24 -0400 Subject: [PATCH] Begin conversion to OO-centered programming --- .../src/{mode => Mode}/ParseTable/init.lua | 2 +- lua/libmodal/src/Mode/init.lua | 313 ++++++++++++++++++ lua/libmodal/src/Prompt.lua | 0 lua/libmodal/src/Vars.lua | 81 +++++ lua/libmodal/src/base/init.lua | 17 - lua/libmodal/src/{base => }/globals.lua | 0 lua/libmodal/src/init.lua | 12 +- lua/libmodal/src/{mode/init.lua => mode.lua} | 2 +- .../src/{prompt/init.lua => prompt.lua} | 2 +- lua/libmodal/src/utils/api.lua | 2 +- lua/libmodal/src/utils/init.lua | 2 +- lua/libmodal/src/utils/vars.lua | 3 +- 12 files changed, 409 insertions(+), 27 deletions(-) rename lua/libmodal/src/{mode => Mode}/ParseTable/init.lua (98%) create mode 100644 lua/libmodal/src/Mode/init.lua create mode 100644 lua/libmodal/src/Prompt.lua create mode 100644 lua/libmodal/src/Vars.lua delete mode 100644 lua/libmodal/src/base/init.lua rename lua/libmodal/src/{base => }/globals.lua (100%) rename lua/libmodal/src/{mode/init.lua => mode.lua} (99%) rename lua/libmodal/src/{prompt/init.lua => prompt.lua} (98%) diff --git a/lua/libmodal/src/mode/ParseTable/init.lua b/lua/libmodal/src/Mode/ParseTable/init.lua similarity index 98% rename from lua/libmodal/src/mode/ParseTable/init.lua rename to lua/libmodal/src/Mode/ParseTable/init.lua index 76882d5..575e448 100644 --- a/lua/libmodal/src/mode/ParseTable/init.lua +++ b/lua/libmodal/src/Mode/ParseTable/init.lua @@ -5,7 +5,7 @@ --]] local api = vim.api -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') --[[ /* diff --git a/lua/libmodal/src/Mode/init.lua b/lua/libmodal/src/Mode/init.lua new file mode 100644 index 0000000..80a6eb8 --- /dev/null +++ b/lua/libmodal/src/Mode/init.lua @@ -0,0 +1,313 @@ +--[[ + /* + * IMPORTS + */ +--]] + +local globals = require('libmodal/src/globals') +local utils = require('libmodal/src/utils') +local Vars = require('libmodal/src/Vars') + +local api = utils.api + +--[[ + /* + * MODULE + */ +--]] + +-- Public interface for this module. +local Mode = {} + +-- Private class. +local _modeMetaTable = {} + +_modeMetaTable.ParseTable = require('libmodal/src/mode/ParseTable') + +local _HELP = '?' +local _TIMEOUT = { + CHAR = 'ΓΈ', + NR = string.byte(_TIMEOUT.CHAR), + LEN = api.nvim_get_option('timeoutlen'), + SEND = function(__self) + api.nvim_feedkeys(__self.CHAR, '', false) + end +} + +--[[ + /* + * META `_modeMetaTable` + */ +--]] + +---------------------------------------- +--[[ SUMMARY: + * Reset libmodal's internal counter of user input to default. +]] +---------------------------------------- +function _modeMetaTable:clearInputBytes() + self._inputBytes = {} +end + +----------------------------------------------- +--[[ SUMMARY: + * Update the floating window with the latest user input. +]] +--[[ PARAMS: + * `modeName` => the name of the mode. +]] +----------------------------------------------- +function _modeMetaTable:_updateFloatingWindow() + local inputChars = {} + for _, byte in ipairs(self._inputBytes) do + inputChars[#inputChars + 1] = string.char(byte) + end + api.nvim_buf_set_lines( + self._popupBuffer, + 0, 1, true, {table.concat(inputChars)} + ) +end + +-------------------------------------- +--[[ SUMMARY: + * Parse the `comboDict` and see if there is any command to execute. +]] +--[[ PARAMS: + * `modeName` => the name of the mode that is currently active. +]] +-------------------------------------- +function _modeMetaTable:_comboSelect() + -- Stop any running timers + if self._flushInputTimer then + self._flushInputTimer:stop() + end + + -- Append the latest input to the locally stored input history. + self._inputBytes[#self._inputBytes + 1] = Vars.nvim_get( + self._inputBytes, self._modeName + ) + + -- Get the command based on the users input. + local cmd = self._keybindings:get(self._inputBytes) + + -- Get the type of the command. + local commandType = type(cmd) + local clearInputBytes = false + + -- if there was no matching command + if cmd == false then + if #self._inputBytes < 2 and self._inputBytes[1] == string.byte(_HELP) then + self._help:show() + end + clearInputBytes = true + -- The command was a table, meaning that it MIGHT match. + elseif commandType == globals.TYPE_TBL + and globals.isTrue(self._timeoutsEnabled) + then + -- Create a new timer + + -- start the timer + 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[self.ParseTable.CR] then + api.nvim_command(cmd[self.ParseTable.CR]) + end + -- clear input + _clearInputBytes(modeName) + _updateFloatingWindow(modeName) + end) + ) + -- The command was an actual vim command. + else + api.nvim_command(cmd) + clearInputBytes = true + end + + if clearInputBytes then + self:_clearInputBytes() + end + self:_updateFloatingWindow() +end + +------------------------------------------------ +--[[ SUMMARY: + * Set the initial values used for parsing user input as combos. +]] +--[[ PARAMS: + * `modeName` => the name of the mode being initialized. + * `comboTable` => the table of combos being initialized. +]] +------------------------------------------------ +-- TODO +local function _initCombos(modeName, comboTable) + -- Placeholder for timeout value. + local timeoutsEnabled = nil + + -- Read the correct timeout variable. + if api.nvim_exists('g', vars.timeouts:name(modeName)) then timeoutsEnabled = + vars.nvim_get(vars.timeouts, modeName) + else timeoutsEnabled = + vars.libmodalTimeouts + end + + -- Assign the timeout variable according to `timeoutsEnabled` + self._timeoutsEnabled = timeoutsEnabled + + -- create a floating window + local buf = api.nvim_create_buf(false, true) + vars.buffers.instances[modeName] = buf + self._popupWindow = api.nvim_call_function('libmodal#_winOpen', {buf}) + + -- Determine if a default `Help` should be created. + if not comboTable[_HELP] then + vars.help.instances[modeName] = utils.Help.new(comboTable, 'KEY MAP') + end + + -- Build the parse tree. + vars.combos.instances[modeName] = mode.ParseTable.new(comboTable) + + -- Create a timer + self._flushInputTimer = vim.loop.new_timer() + + -- Initialize the input history variable. + _clearInputBytes(modeName) +end + +----------------------------------------------------- +--[[ SUMMARY: + * Remove variables used for a mode. +]] +--[[ PARAMS: + * `modeName` => the name of the mode. + * `self._winState` => the window state prior to mode activation. +]] +----------------------------------------------------- +function _modeMetaTable:_deconstruct() + if self._flushInputTimer:info()['repeat'] ~= 0 then + self._flushInputTimer:stop() + end + + if self._popupWindow then + api.nvim_win_close(self._popupWindow, false) + end + + self._winState:restore() + + for k, _ in pairs(self) do + self[k] = nil + end + + api.nvim_command("mode | echo '' | call garbagecollect()") +end + +-------------------------------------------------------------------------------- +--[[ SUMMARY: + * Loop an initialized `mode`. +]] +--[[ PARAMS: + * `handleExitEvents` => whether or not to automatically exit on `` press. + * `indicator` => the indicator for the mode. + * `modeInstruction` => the instructions for the mode. + * `modeName` => the name of the `mode`. +]] +--[[ RETURNS: + * `boolean` => whether or not the mode should continue +]] +-------------------------------------------------------------------------------- +-- TODO +local function _modeLoop(handleExitEvents, indicator, modeInstruction, modeName) + -- If the mode is not handling exit events automatically and the global exit var is true. + if not handleExitEvents and globals.isTrue( + vars.nvim_get(vars.exit, modeName) + ) then return false end + + -- Echo the indicator. + api.nvim_lecho(indicator) + + -- Capture input. + local userInput = api.nvim_input() + + -- Return if there was a timeout event. + if userInput == _TIMEOUT.NR then + return true + end + + -- Set the global input variable to the new input. + vars.nvim_set(vars.input, modeName, userInput) + + -- Make sure that the user doesn't want to exit. + if handleExitEvents and userInput == globals.ESC_NR then + return false + -- If the second argument was a dict, parse it. + elseif type(modeInstruction) == globals.TYPE_TBL then + _comboSelect(modeName) + -- If the second argument was a function, execute it. + else modeInstruction() end + + return true +end + +------------------------ +--[[ 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. +]] +------------------------ +-- TODO +function _modeMetaTable:enter(...) + local args = {...} + + --[[ SETUP. ]] + + -- Create the indicator for the mode. + local indicator = utils.Indicator.mode(args[1]) + + -- Grab the state of the window. + self._winState = utils.WindowState.new() + + -- Convert the name into one that can be used for variables. + local modeName = string.lower(args[1]) + + -- Determine whether or not this function should handle exiting automatically. + local handleExitEvents = true + if #args > 2 then + handleExitEvents = globals.isFalse(args[3]) + end + + -- Determine whether a callback was specified, or a combo table. + if type(args[2]) == globals.TYPE_TBL then + _initCombos(modeName, args[2]) + end + + --[[ MODE LOOP. ]] + local continueMode = true + while continueMode == true do + -- Try (using pcall) to use the mode. + local noErrors = true + noErrors, continueMode = pcall(_modeLoop, + handleExitEvents, indicator, args[2], modeName + ) + + -- If there were errors, handle them. + if noErrors == false then + utils.showError(continueMode) + continueMode = false + end + + end +end + +--[[ + /* + * PUBLICIZE MODULE + */ +--]] + +return Mode diff --git a/lua/libmodal/src/Prompt.lua b/lua/libmodal/src/Prompt.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/libmodal/src/Vars.lua b/lua/libmodal/src/Vars.lua new file mode 100644 index 0000000..373de32 --- /dev/null +++ b/lua/libmodal/src/Vars.lua @@ -0,0 +1,81 @@ +--[[ + /* + * IMPORTS + */ +--]] + +local globals = require('libmodal/src/globals') +local api = vim.api + +--[[ + /* + * MODULE + */ +--]] + +local Vars = {} + +Vars.libmodalTimeouts = api.nvim_get_var('libmodalTimeouts') + +--[[ + /* + * HELPERS + */ +--]] + +--------------------------- +--[[ SUMMARY: + * Create a new entry in `Vars` +]] +--[[ PARAMS: + * `keyName` => the name of the key used to refer to this variable in `Vars`. + * `varName` => the name of the variable as it is stored in vim. +]] +--------------------------- +local function new(keyName) + self = {} + + -- Instances of variables pertaining to a certain mode. + local varName = 'Mode' .. string.upper( + string.sub(keyName, 0, 1) + ) .. string.sub(keyName, 2) + + ------------------------- + --[[ SUMMARY: + * Get the name of `modeName`s global setting. + ]] + --[[ PARAMS: + * `modeName` => the name of the mode. + ]] + ------------------------- + name = function(modeName) + return string.lower(modeName) .. self._varName + end + + return self +end + +------------------------------------ +--[[ SUMMARY: + * Retrieve a variable value. +]] +--[[ PARAMS: + * `var` => the `Vars.*` table to retrieve the value of. + * `modeName` => the mode name this value is being retrieved for. +]] +------------------------------------ +function Vars.nvimGet(var, modeName) + return api.nvim_get_var(var.name(modeName)) +end + +function Vars.nvimSet(var, modeName, val) + api.nvim_set_var(var.name(modeName), val) +end + +--[[ + /* + * PUBLICIZE MODULE + */ +--]] + +return Vars diff --git a/lua/libmodal/src/base/init.lua b/lua/libmodal/src/base/init.lua deleted file mode 100644 index 65d4922..0000000 --- a/lua/libmodal/src/base/init.lua +++ /dev/null @@ -1,17 +0,0 @@ ---[[ - /* - * MODULE - */ ---]] - -local base = {} -base.globals = require('libmodal/src/base/globals') - ---[[ - /* - * PUBLICIZE MODULE - */ ---]] - -return base - diff --git a/lua/libmodal/src/base/globals.lua b/lua/libmodal/src/globals.lua similarity index 100% rename from lua/libmodal/src/base/globals.lua rename to lua/libmodal/src/globals.lua diff --git a/lua/libmodal/src/init.lua b/lua/libmodal/src/init.lua index b748434..0ad2bc1 100644 --- a/lua/libmodal/src/init.lua +++ b/lua/libmodal/src/init.lua @@ -4,10 +4,14 @@ */ --]] -local libmodal = require('libmodal/src/base') -libmodal.mode = require('libmodal/src/mode') -libmodal.prompt = require('libmodal/src/prompt') -libmodal.utils = require('libmodal/src/utils') +local libmodal = {} + +libmodal.globals = require('libmodal/src/globals') +libmodal.mode = require('libmodal/src/mode') +libmodal.Mode = require('libmodal/src/Mode') +libmodal.prompt = require('libmodal/src/prompt') +libmodal.Prompt = require('libmodal/src/Prompt') +libmodal.utils = require('libmodal/src/utils') --[[ /* diff --git a/lua/libmodal/src/mode/init.lua b/lua/libmodal/src/mode.lua similarity index 99% rename from lua/libmodal/src/mode/init.lua rename to lua/libmodal/src/mode.lua index 54d03cb..83718cb 100644 --- a/lua/libmodal/src/mode/init.lua +++ b/lua/libmodal/src/mode.lua @@ -4,7 +4,7 @@ */ --]] -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') local utils = require('libmodal/src/utils') local api = utils.api diff --git a/lua/libmodal/src/prompt/init.lua b/lua/libmodal/src/prompt.lua similarity index 98% rename from lua/libmodal/src/prompt/init.lua rename to lua/libmodal/src/prompt.lua index 284035e..f17ed72 100644 --- a/lua/libmodal/src/prompt/init.lua +++ b/lua/libmodal/src/prompt.lua @@ -4,7 +4,7 @@ */ --]] -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') local utils = require('libmodal/src/utils') local api = utils.api diff --git a/lua/libmodal/src/utils/api.lua b/lua/libmodal/src/utils/api.lua index 50e8035..117f708 100644 --- a/lua/libmodal/src/utils/api.lua +++ b/lua/libmodal/src/utils/api.lua @@ -4,7 +4,7 @@ */ --]] -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') local Entry = require('libmodal/src/utils/Indicator/Entry') --[[ diff --git a/lua/libmodal/src/utils/init.lua b/lua/libmodal/src/utils/init.lua index c0751fc..5107493 100644 --- a/lua/libmodal/src/utils/init.lua +++ b/lua/libmodal/src/utils/init.lua @@ -5,7 +5,7 @@ --]] local api = vim.api -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') --[[ /* diff --git a/lua/libmodal/src/utils/vars.lua b/lua/libmodal/src/utils/vars.lua index ea4196c..28877fe 100644 --- a/lua/libmodal/src/utils/vars.lua +++ b/lua/libmodal/src/utils/vars.lua @@ -4,7 +4,7 @@ */ --]] -local globals = require('libmodal/src/base/globals') +local globals = require('libmodal/src/globals') local api = vim.api --[[ @@ -14,6 +14,7 @@ local api = vim.api --]] local vars = {} + vars.libmodalTimeouts = api.nvim_get_var('libmodalTimeouts') --[[