diff --git a/doc/libmodal-lua.txt b/doc/libmodal-lua.txt index 778a0c4..42d3b55 100644 --- a/doc/libmodal-lua.txt +++ b/doc/libmodal-lua.txt @@ -60,6 +60,46 @@ MODULES *libmodal-lua-modules* │ └──  `.WindowState` └──  `.Vars` +============================================================================== +2. `libmodal.classes` *libmodal-lua-classes* + +`libmodal.classes` is a small library to help shorten the amount of +boilerplate code that it takes to set up a `metatable` and subsequent +subtables. + +There aren't an astounding amount of functions: the intent is not to implement +object orientation into Lua, rather it is to help facilitate the emulation of +object orientation through `metatable`s. + +------------------------------------------------------------------------------ +FUNCTIONS *libmodal-lua-classes-functions* + +`classes`.new({name}, {base}) *libmodal-lua-classes.new()* + + Define a class-metatable. + + Parameters: ~ + {name} The name of the class. + + Note: this value is used for `classes.type()`. + + {base} Optional. The base class to use. + + Return: ~ + A table set up to for use as a `metatable`. + +`classes`.type({value}) *libmodal-lua-classes.type()* + + Get the type of some {value}. + + Parameters: ~ + {value} The value to get the type of. + + Return: ~ + * If {value} is a `table` and it has a `__type` attribute, return + `__type`. + * Else, return `type(`{value}`)` + ============================================================================== 3. `libmodal.collections` *libmodal-lua-collections* @@ -586,6 +626,8 @@ FUNCTIONS *libmodal-lua-Layer-functions* Note: mappings only get replaced for the current buffer. + *Error when the Layer has been entered previously but hasn't been exited. + Example: ~ > local libmodal = require('libmodal') @@ -692,6 +734,8 @@ FUNCTIONS *libmodal-lua-Layer-functions* Exit the `Layer`. + *Error when the Layer has not been entered yet. + Example: ~ > local libmodal = require('libmodal') @@ -900,6 +944,34 @@ opened with the following options: > } < +------------------------------------------------------------------------------ +VARIABLES *libmodal-lua-Popup-variables* + +`Popup`.config *libmodal-lua-Popup.apiOptions* + + The options used when opening a `Popup`. + + Note: this can be overwritten to change the default behavior of `Popup`. + + Type: ~ + |nvim_open_win| {config} `table`. + + Value: ~ +> + { + ['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 + } +< + ------------------------------------------------------------------------------ FUNCTIONS *libmodal-lua-Popup-functions* diff --git a/examples/key-combos.vim b/examples/key-combos.vim index b89ba8d..3009d43 100644 --- a/examples/key-combos.vim +++ b/examples/key-combos.vim @@ -1,7 +1,7 @@ let s:barModeCombos = { \ 'zf': 'split', \ 'zfo': 'vsplit', -\ 'zfc': 'tabnew' +\ 'zfc': 'q' \} call libmodal#Enter('BAR', s:barModeCombos) diff --git a/examples/lua/layer-simple.lua b/examples/lua/layer-simple.lua index a4ba12d..eadb696 100644 --- a/examples/lua/layer-simple.lua +++ b/examples/lua/layer-simple.lua @@ -14,6 +14,7 @@ local exitFunc = libmodal.layer.enter('FOO', { } }) +-- the layer will deactivate in 5 seconds. vim.loop.new_timer():start(5000, 0, vim.schedule_wrap( function() exitFunc(); print('EXITED.') end )) diff --git a/lua/libmodal/init.lua b/lua/libmodal/init.lua index faad8ed..c4834e8 100644 --- a/lua/libmodal/init.lua +++ b/lua/libmodal/init.lua @@ -4,7 +4,7 @@ */ --]] -local libmodal = require('libmodal/src') +local libmodal = require('libmodal/src') --[[ /* diff --git a/lua/libmodal/src/Indicator/init.lua b/lua/libmodal/src/Indicator/init.lua index 29cd618..cabde39 100644 --- a/lua/libmodal/src/Indicator/init.lua +++ b/lua/libmodal/src/Indicator/init.lua @@ -4,9 +4,9 @@ */ --]] -local Indicator = {} - -Indicator.HighlightSegment = require('libmodal/src/Indicator/HighlightSegment') +local Indicator = { + ['HighlightSegment'] = require('libmodal/src/Indicator/HighlightSegment') +} -- highlight group names local _HL_GROUP_MODE = 'LibmodalPrompt' diff --git a/lua/libmodal/src/Layer.lua b/lua/libmodal/src/Layer.lua index 3aaaf19..d9c5f54 100644 --- a/lua/libmodal/src/Layer.lua +++ b/lua/libmodal/src/Layer.lua @@ -47,6 +47,10 @@ local _metaLayer = require('libmodal/src/classes').new(Layer.TYPE) ]] --------------------------- function _metaLayer:enter() + if self._priorKeymap then + error('This layer has already been entered.') + end + -- add local aliases. local layerKeymap = self._keymap local priorKeymap = {} @@ -203,6 +207,10 @@ end ]] -------------------------- function _metaLayer:exit() + if not self._priorKeymap then + error('This layer has not been entered yet.') + end + for mode, mappings in pairs(self._keymap) do for lhs, _ in pairs(mappings) do self:_unmapFromBuffer(mode, lhs) diff --git a/lua/libmodal/src/Mode/init.lua b/lua/libmodal/src/Mode.lua similarity index 90% rename from lua/libmodal/src/Mode/init.lua rename to lua/libmodal/src/Mode.lua index 4f52bce..05e29ae 100644 --- a/lua/libmodal/src/Mode/init.lua +++ b/lua/libmodal/src/Mode.lua @@ -4,13 +4,15 @@ */ --]] -local classes = require('libmodal/src/classes') -local globals = require('libmodal/src/globals') -local collections = require('libmodal/src/collections') -local utils = require('libmodal/src/utils') -local Vars = require('libmodal/src/Vars') +local classes = require('libmodal/src/classes') +local globals = require('libmodal/src/globals') +local ParseTable = require('libmodal/src/collections/ParseTable') +local utils = require('libmodal/src/utils') +local Vars = require('libmodal/src/Vars') -local api = utils.api +local api = utils.api + +local collections = nil --[[ /* @@ -18,10 +20,7 @@ local api = utils.api */ --]] -local Mode = { - ['Popup'] = require('libmodal/src/Mode/Popup'), - ['TYPE'] = 'libmodal-mode' -} +local Mode = {['TYPE'] = 'libmodal-mode'} local _HELP = '?' local _TIMEOUT = { @@ -90,8 +89,8 @@ function _metaMode:_checkInputForMapping() -- Send input to interrupt a blocking `getchar` _TIMEOUT:SEND() -- if there is a command, execute it. - if cmd[collections.ParseTable.CR] then - api.nvim_command(cmd[collections.ParseTable.CR]) + if cmd[ParseTable.CR] then + api.nvim_command(cmd[ParseTable.CR]) end -- clear input inputBytes:clear() @@ -117,7 +116,7 @@ function _metaMode:enter() -- intialize variables that are needed for each recurse of a function if type(self._instruction) == globals.TYPE_TBL then -- Initialize the input history variable. - self._popups:push(Mode.Popup.new()) + self._popups:push(require('libmodal/src/collections/Popup').new()) end @@ -156,10 +155,10 @@ function _metaMode:_initMappings() self.inputBytes = setmetatable({}, _metaInputBytes) -- Build the parse tree. - self.mappings = collections.ParseTable.new(self._instruction) + self.mappings = ParseTable.new(self._instruction) -- Create a table for mode-specific data. - self._popups = collections.Stack.new() + self._popups = require('libmodal/src/collections/Stack').new() -- Create a variable for whether or not timeouts are enabled. self._timeouts = Vars.new('timeouts', self._name) diff --git a/lua/libmodal/src/ModeLayer.lua b/lua/libmodal/src/ModeLayer.lua deleted file mode 100644 index 2676dca..0000000 --- a/lua/libmodal/src/ModeLayer.lua +++ /dev/null @@ -1,127 +0,0 @@ ---[[ - /* - * MODULE - */ ---]] - -local ModeLayer = {['TYPE'] = 'libmodal-mode-layer'} - ---[[ - /* - * META `ModeLayer` - */ ---]] - -local _metaModeLayer = require('libmodal/src/classes').new(ModeLayer.TYPE) - -function _metaModeLayer:map(keys, mapping) - local priorInstruction = self._priorInstruction - local layerInstruction = self._instruction - - if priorInstruction then - local modeInstruction = self._mode._instruction - - -- only save the value from `mode` when adding a new mapping. - if not layerInstruction[keys] then - priorInstruction[keys] = modeInstruction:parseGet(keys) - end - - -- map the keys to mode. - modeInstruction:parsePut(keys, mapping) - end - - -- add the keys to the instruction. - layerInstruction[keys] = mapping -end - -function _metaModeLayer:unmap(keys) - local priorInstruction = self._priorInstruction or {} - local layerInstruction = self._instruction - - if priorInstruction[keys] then - self._mode._instruction:parsePut(keys, priorInstruction[keys]) - priorInstruction[keys] = nil - end - - -- remove `keys` from the instruction. - layerInstruction[keys] = nil -end - - -------------------------------- ---[[ SUMMARY: - * Enter the `ModeLayer`, replacing any conflicting mappings. -]] -------------------------------- -function _metaModeLayer:enter() - -- Create aliases. - local layerMode = self._mode - local layerInstruction = self._instruction - - -- Create a new `priorInstruction`. - local priorInstruction = nil - - if self._isTable then -- the layer is a table - local modeInstruction = mode._instruction - priorInstruction = {} - - for keys, mapping in pairs(layerInstruction) do - priorInstruction[keys] = modeInstruction:parseGet(keys) - modeInstruction:parsePut(keys, mapping) - end - else -- the layer is a function - priorInstruction = layerMode._instruction - mode._instruction = layerInstruction - end - - self._priorInstruction = priorInstruction -end - -------------------------------- ---[[ SUMMARY: - * Exit the `ModeLayer`, and restore any overwritten mappings. -]] -------------------------------- -function _metaModeLayer:exit() - if self._isTable then - local modeInstruction = self._mode._instruction - - for keys, mapping in pairs(self._priorInstruction) do - modeInstruction:parsePut(keys, mapping) - end - else - self._mode._instruction = self._priorInstruction - end - - self._priorInstruction = nil -end - ---[[ - /* - * CLASS `ModeLayer` - */ ---]] - ------------------------------------------ ---[[ SUMMARY: - * Create a new `ModeLayer`. -]] ------------------------------------------ -function ModeLayer.new(mode, instruction) - if require('libmodal/src/classes').type(mode) == require('libmodal/src/Mode').TYPE - and type(mode._instruction) == type(instruction) - then - return setmetatable( - { - ['_isTable'] = type(instruction) == require('libmodal/src/globals').TYPE_TBL, - ['_instruction'] = instruction, - ['_mode'] = mode - }, _metaModeLayer - ) - else - error('Either `mode` is not a `Mode`, ' - .. 'or `instruction` is not the same type ' - .. 'as it was when `mode` was created.' - ) - end -end diff --git a/lua/libmodal/src/PromptLayer.lua b/lua/libmodal/src/PromptLayer.lua deleted file mode 100644 index fdd68dc..0000000 --- a/lua/libmodal/src/PromptLayer.lua +++ /dev/null @@ -1,101 +0,0 @@ ---[[ - /* - * IMPORTS - */ ---]] - -local classes = require('libmodal/src/classes') - ---[[ - /* - * MODULE - */ ---]] - -local PromptLayer = {['TYPE'] = 'libmodal-prompt-layer'} - ---[[ - /* - * META `PromptLayer` - */ ---]] - -local _metaPromptLayer = classes.new(PromptLayer.TYPE) - -------------------------------- ---[[ SUMMARY: - * Enter the `PromptLayer`, replacing any conflicting executes. -]] -------------------------------- -function _metaPromptLayer:enter() - -- Create aliases. - local layerPrompt = self._prompt - local layerInstruction = self._instruction - - -- Create a new `priorInstruction`. - local priorInstruction = nil - - if self._isTable then -- the layer is a table - local promptInstruction = prompt._instruction - priorInstruction = {} - - for command, execute in pairs(layerInstruction) do - priorInstruction[command] = promptInstruction[command] - promptInstruction[command] = execute - end - else -- the layer is a function - priorInstruction = layerPrompt._instruction - prompt._instruction = layerInstruction - end - - self._priorInstruction = priorInstruction -end - -------------------------------- ---[[ SUMMARY: - * Exit the `PromptLayer`, and restore any overwritten executes. -]] -------------------------------- -function _metaPromptLayer:exit() - if self._isTable then - local promptInstruction = self._prompt._instruction - - for command, execute in pairs(self._priorInstruction) do - promptInstruction[command] = execute - end - else - self._prompt._instruction = self._priorInstruction - end - - self._priorInstruction = nil -end - ---[[ - /* - * CLASS `PromptLayer` - */ ---]] - ------------------------------------------ ---[[ SUMMARY: - * Create a new `PromptLayer`. -]] ------------------------------------------ -function PromptLayer.new(prompt, instruction) - if classes.type(prompt) == require('libmodal/src/Prompt').TYPE - and type(prompt._instruction) == type(instruction) - then - return setmetatable( - { - ['_isTable'] = type(instruction) == require('libmodal/src/globals').TYPE_TBL, - ['_instruction'] = instruction, - ['_prompt'] = prompt - }, _metaPromptLayer - ) - else - error('Either `prompt` is not a `Prompt`, ' - .. 'or `instruction` is not the same type ' - .. 'as it was when `prompt` was created.' - ) - end -end diff --git a/lua/libmodal/src/classes.lua b/lua/libmodal/src/classes.lua index 63de69b..c5ede09 100644 --- a/lua/libmodal/src/classes.lua +++ b/lua/libmodal/src/classes.lua @@ -1,44 +1,37 @@ ---[[ - /* - * MODULE - */ ---]] - -local classes = {} - --------------------------- ---[[ SUMMARY: - * Define a class-metatable. -]] ---[[ - * `name` => the name of the class. - * `base` => the base class to use (`{}` by default). -]] --------------------------------- -function classes.new(name, ...) - -- set self to `base`, or `{}` if nil. - local self = unpack({...}) or {} - - -- set `__index`. - if not self.__index then - self.__index = self +return { + ------------------------- + --[[ SUMMARY: + * Define a class-metatable. + ]] + --[[ + * `name` => the name of the class. + * `base` => the base class to use (`{}` by default). + ]] + ------------------------- + new = function(name, ...) + -- set self to `base`, or `{}` if nil. + local self = unpack({...}) or {} + + -- set `__index`. + if not self.__index then + self.__index = self + end + + -- set `__type`. + self.__type = name + + return self + end, + + ------------------ + --[[ SUMMARY: + * Get the type of some value `v`, if it has one. + ]] + --[[ PARAMS: + * `v` => the value to get the type of. + ]] + ------------------ + type = function(v) + return v.__type or type(v) end - - -- set `__type`. - self.__type = name - - return self -end - ------------------------- -function classes.type(v) - return v.__type or type(v) -end - ---[[ - /* - * PUBLICIZE `classes` - */ ---]] - -return classes +} diff --git a/lua/libmodal/src/Mode/Popup.lua b/lua/libmodal/src/collections/Popup.lua similarity index 76% rename from lua/libmodal/src/Mode/Popup.lua rename to lua/libmodal/src/collections/Popup.lua index 5035b2a..013e964 100644 --- a/lua/libmodal/src/Mode/Popup.lua +++ b/lua/libmodal/src/collections/Popup.lua @@ -12,19 +12,20 @@ local api = vim.api */ --]] -local Popup = {['TYPE'] = 'libmodal-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, +local Popup = { + ['TYPE'] = 'libmodal-popup', + ['config'] = { + ['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 + } } --[[ @@ -85,13 +86,14 @@ end function Popup.new() local buf = api.nvim_create_buf(false, true) + local Popup = require('libmodal/src/Mode/Popup') return setmetatable( { ['_buffer'] = buf, ['_inputChars'] = {}, ['_window'] = api.nvim_call_function( - 'nvim_open_win', {buf, false, _winOpenOpts} + 'nvim_open_win', {buf, false, Popup.config} ) }, _metaPopup diff --git a/lua/libmodal/src/collections/init.lua b/lua/libmodal/src/collections/init.lua index 74b82ba..b446561 100644 --- a/lua/libmodal/src/collections/init.lua +++ b/lua/libmodal/src/collections/init.lua @@ -1,6 +1,5 @@ -local collections = {} - -collections.ParseTable = require('libmodal/src/collections/ParseTable') -collections.Stack = require('libmodal/src/collections/Stack') - -return collections +return { + ['ParseTable'] = require('libmodal/src/collections/ParseTable'), + ['Popup'] = require('libmodal/src/collections/Popup'), + ['Stack'] = require('libmodal/src/collections/Stack') +} diff --git a/lua/libmodal/src/globals.lua b/lua/libmodal/src/globals.lua index fedcacb..4254675 100644 --- a/lua/libmodal/src/globals.lua +++ b/lua/libmodal/src/globals.lua @@ -1,41 +1,25 @@ ---[[ - /* - * MODULE - */ ---]] +local _VIM_FALSE = 0 +local _VIM_TRUE = 1 -local globals = {} +return { + ['DEFAULT_ERROR_TITLE'] = 'vim-libmodal error', ---[[ - /* - * TABLE `globals` - */ ---]] + ['ESC_NR'] = 27, -globals.DEFAULT_ERROR_TITLE = 'vim-libmodal error' + ['TYPE_FUNC'] = 'function', + ['TYPE_NUM'] = 'number', + ['TYPE_STR'] = 'string', + ['TYPE_TBL'] = 'table', -globals.ESC_NR = 27 + ['VIM_FALSE'] = _VIM_FALSE, + ['VIM_TRUE'] = _VIM_TRUE, -globals.TYPE_FUNC = 'function' -globals.TYPE_NUM = 'number' -globals.TYPE_STR = 'string' -globals.TYPE_TBL = 'table' + is_false = function(val) + return val == false or val == _VIM_FALSE + end, -globals.VIM_FALSE = 0 -globals.VIM_TRUE = 1 + is_true = function(val) + return val == true or val == _VIM_TRUE + end +} -function globals.is_false(val) - return val == false or val == globals.VIM_FALSE -end - -function globals.is_true(val) - return val == true or val == globals.VIM_TRUE -end - ---[[ - /* - * PUBLICIZE MODULE - */ ---]] - -return globals diff --git a/lua/libmodal/src/init.lua b/lua/libmodal/src/init.lua index ac3f0ab..ac122a2 100644 --- a/lua/libmodal/src/init.lua +++ b/lua/libmodal/src/init.lua @@ -1,24 +1,10 @@ ---[[ - /* - * MODULE - */ ---]] - -local libmodal = {} - -libmodal.classes = require('libmodal/src/classes') -libmodal.collections = require('libmodal/src/collections') -libmodal.globals = require('libmodal/src/globals') -libmodal.Indicator = require('libmodal/src/Indicator') -libmodal.Layer = require('libmodal/src/Layer') -libmodal.Mode = require('libmodal/src/Mode') -libmodal.Prompt = require('libmodal/src/Prompt') -libmodal.utils = require('libmodal/src/utils') - ---[[ - /* - * PUBLICIZE MODULE - */ ---]] - -return libmodal +return { + ['classes'] = require('libmodal/src/classes'), + ['collections'] = require('libmodal/src/collections'), + ['globals'] = require('libmodal/src/globals'), + ['Indicator'] = require('libmodal/src/Indicator'), + ['Layer'] = require('libmodal/src/Layer'), + ['Mode'] = require('libmodal/src/Mode'), + ['Prompt'] = require('libmodal/src/Prompt'), + ['utils'] = require('libmodal/src/utils') +} diff --git a/lua/libmodal/src/utils/init.lua b/lua/libmodal/src/utils/init.lua index 07076d7..dc344a2 100644 --- a/lua/libmodal/src/utils/init.lua +++ b/lua/libmodal/src/utils/init.lua @@ -24,8 +24,11 @@ utils.WindowState = require('libmodal/src/utils/WindowState') ]] ---------------------------------- function utils.show_error(pcall_err) - utils.api.nvim_bell() - utils.api.nvim_show_err( + local api = utils.api + + api.nvim_bell() + + api.nvim_show_err( require('libmodal/src/globals').DEFAULT_ERROR_TITLE, api.nvim_get_vvar('throwpoint') .. '\n' ..