Add initial Layer implementation

pull/3/head
Iron-E 4 years ago
parent 1501358320
commit f84b0288e7
No known key found for this signature in database
GPG Key ID: 19B71B7B7B021D22

@ -1,14 +1,3 @@
let s:winOpenOpts = {
\ 'anchor' : 'SW',
\ 'col' : &columns - 1,
\ 'focusable': v:false,
\ 'height' : 1,
\ 'relative' : 'editor',
\ 'row' : &lines - &cmdheight - 1,
\ 'style' : 'minimal',
\ 'width' : 25,
\}
" SUMMARY:
" * Get user input with some `completions`.
" PARAMS:
@ -38,6 +27,10 @@ function! libmodal#Enter(...) abort
call libmodal#_lua('mode', a:000)
endfunction
function! libmodal#Layer(...) abort
call libmodal#_lua('layer')
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).
@ -62,7 +55,7 @@ function! libmodal#_lua(lib, args)
\ [
\ a:args[0],
\ a:args[1],
\ len(a:args) > 2 ? a:args[2] : v:false
\ get(a:args, 2, v:null)
\ ]
\)
endfunction

@ -657,6 +657,15 @@ When submitting an issue, please describe the following:
==============================================================================
8. Changelog *libmodal-changelog*
0.6.0 ~
Additions: ~
* New module: |libmodal-layer|s.
* Allows for use of built-in modes with overwriting of keymaps.
* New class `libmodal.Layer`.
* New function `libmodal.layer.enter()`.
* New examples for new additions.
0.5.0 ~
Additions: ~

@ -0,0 +1,39 @@
local libmodal = require('libmodal')
-- create a new layer.
local layer = libmodal.Layer.new('FOO', {
['n'] = { -- normal mode mappings
['gg'] = { -- remap `gg`
['rhs'] = 'G', -- map it to `G`
['noremap'] = true, -- don't recursively map.
},
['G'] = { -- remap `G`
['rhs'] = 'gg', -- map it to `gg`
['noremap'] = true -- don't recursively map.
}
}
})
-- enter the `layer`.
layer:enter()
-- add a global function for exiting the mode.
function libmodal_layer_example_exit()
layer:exit()
end
-- Add an additional mapping for `z`.
layer:map('n', 'z', 'gg', {['noremap'] = true})
-- add an additional mapping for `q`.
layer:map(
'n', 'q', ':lua libmodal_layer_example_exit()<CR>',
{['noremap'] = true, ['silent'] = true}
)
--[[ unmap `gg` and `G`. Notice they both return to their defaults,
rather than just not doing anything anymore. ]]
layer:unmap('n', 'gg')
layer:unmap('n', 'G')
-- If you wish to only change the mappings of a layer temporarily, you should use another layer. `map` and `unmap` permanently add and remove from the layer's keymap.

@ -12,6 +12,12 @@ local libmodal = require('libmodal/src')
*/
--]]
libmodal.layer = {['enter'] = function(name, mappings)
local layer = libmodal.Layer.new(name, mappings)
layer:enter()
return layer.exit
end}
libmodal.mode = {['enter'] = function(name, instruction, ...)
libmodal.Mode.new(name, instruction, ...):enter()
end}

@ -0,0 +1,254 @@
--[[
/*
* IMPORTS
*/
--]]
local api = vim.api
local classes = require('libmodal/src/classes')
--[[
/*
* MODULE
*/
--]]
local Layer = {}
local _BUFFER_CURRENT = 0
local _RESTORED = nil
local function convertKeymap(keymapEntry)
local lhs = keymapEntry.lhs
keymapEntry.lhs = nil
return {lhs, keymapEntry}
end
local function deconvertKeymap(convertedKeymap)
local rhs = convertedKeymap.rhs
convertedKeymap.rhs = nil
return {rhs, convertedKeymap}
end
--[[
/*
* META `Layer`
*/
--]]
local _metaLayer = classes.new({})
---------------------------
--[[ SUMMARY:
* Enter the `Layer`.
* Only activates for the current buffer.
]]
---------------------------
function _metaLayer:enter()
-- add local aliases.
local layerKeymap = self._keymap
local priorKeymap = {}
--[[ iterate over the new mappings to both:
1. Populate `priorKeymap`
2. Map the `layerKeymap` to the buffer. ]]
for mode, newMappings in pairs(layerKeymap) do
-- if `mode` key has not yet been made for `priorKeymap`.
if not priorKeymap[mode] then
priorKeymap[mode] = {}
end
-- store the previously mapped keys
for _, bufMap in ipairs(api.nvim_buf_get_keymap(_BUFFER_CURRENT, mode)) do
-- if the new mappings would overwrite this one
if newMappings[bufMap.lhs] then
-- remove values so that it is in line with `nvim_set_keymap`.
local lhs, keymap = unpack(convertKeymap(bufMap))
priorKeymap[mode][lhs] = keymap
end
end
-- add the new mappings
for lhs, newMapping in pairs(newMappings) do
local rhs, options = unpack(deconvertKeymap(newMapping))
api.nvim_buf_set_keymap(_BUFFER_CURRENT, mode, lhs, rhs, options)
end
end
print(vim.inspect(priorKeymap))
self._priorKeymap = priorKeymap
end
--------------------------------------------------------
--[[ SUMMARY:
* Add a mapping to the mode.
]]
--[[ PARAMS:
* `mode` => the mode that this mapping for.
* `lhs` => the left hand side of the mapping.
* `rhs` => the right hand side of the mapping.
* `options` => options for the mapping.
]]
--[[ SEE ALSO:
* `nvim_buf_set_keymap()`
]]
--------------------------------------------------------
function _metaLayer:_mapToBuffer(mode, lhs, rhs, options)
local priorKeymap = self._priorKeymap
if not priorKeymap then error(
"You can't map to a buffer without activating the layer first."
) end
if not priorKeymap[mode][lhs] then -- the mapping's state has not been saved.
for _, bufMap in
ipairs(api.nvim_buf_get_keymap(_BUFFER_CURRENT, mode))
do -- check if it exists in the buffer
if bufMap.lhs == lhs then -- add it to the undo list
priorKeymap[mode][lhs] = unpack(convertKeymap(bufMap))
break
end
end
end
-- map the `lhs` to `rhs` in `mode` with `options` for the current buffer.
api.nvim_buf_set_keymap(_BUFFER_CURRENT, mode, lhs, rhs, options)
end
------------------------------------------------
--[[ SUMMARY:
* Add a mapping to the mode.
]]
--[[ PARAMS:
* `mode` => the mode that this mapping for.
* `lhs` => the left hand side of the mapping.
* `rhs` => the right hand side of the mapping.
* `options` => options for the mapping.
]]
--[[ SEE ALSO:
* `nvim_buf_set_keymap()`
]]
------------------------------------------------
function _metaLayer:map(mode, lhs, rhs, options)
if self._priorKeymap then -- the layer has been activated.
self:_mapToBuffer(mode, lhs, rhs, options)
end
-- add the new mapping to the keymap
self._keymap[mode][lhs] = vim.tbl_extend('force',
options, {['rhs'] = rhs}
)
print(vim.inspect(self._priorKeymap))
end
----------------------------------------------
--[[ SUMMARY:
* Undo a mapping after `enter()`.
]]
--[[ PARAMS:
* `mode` => the mode to map (e.g. `n`, `i`).
* `lhs` => the mapping to undo.
]]
----------------------------------------------
function _metaLayer:_unmapFromBuffer(mode, lhs)
local priorKeymap = self._priorKeymap
local priorMapping = self._priorKeymap[mode][lhs]
print('unmapping ' .. mode .. ':' .. lhs)
if not priorKeymap then error(
"You can't undo a map from a buffer without activating the layer first."
) end
if priorMapping then -- there is an older mapping to go back to.
-- undo the mapping
local rhs, deconvertedKeymap = unpack(deconvertKeymap(priorMapping))
api.nvim_buf_set_keymap(_BUFFER_CURRENT, mode, lhs, rhs, deconvertedKeymap)
-- set the prior mapping as restored.
priorKeymap[mode][lhs] = _RESTORED
print('reverted mapping')
else
-- just delete the buffer mapping.
local noErrors, err = pcall(api.nvim_buf_del_keymap, _BUFFER_CURRENT, mode, lhs)
if not noErrors then print(err) end
print('deleted mapping')
end
end
------------------------------------
--[[ SUMMARY:
* Remove a mapping from the mode.
]]
--[[ PARAMS:
* `mode` => the mode that this mapping for.
* `lhs` => the left hand side of the mapping.
]]
--[[ SEE ALSO:
* `nvim_buf_del_keymap()`
]]
------------------------------------
function _metaLayer:unmap(mode, lhs)
-- unmap for the buffer too, if the layer is activated.
if self._priorKeymap then
self:_unmapFromBuffer(mode, lhs)
end
-- remove the mapping from the internal keymap
self._keymap[mode][lhs] = _RESTORED
end
--------------------------
--[[ SUMMARY:
* Exit the layer.
]]
--[[
* This function is regenerated every time that `enter` is called,
because what it must do depends on the state of Vim upon calling `enter()`.
]]
--------------------------
function _metaLayer:exit()
for mode, mappings in pairs(self._keymap) do
for lhs, _ in pairs(mappings) do
self:_unmapFromBuffer(mode, lhs)
end
end
self._priorKeymap = _RESTORED
end
--[[
/*
* CLASS `Layer`
*/
--]]
-----------------------------------------------------
--[[ SUMMARY:
* Create a new `Layer` for `commands`, `mappings`, and `options`.
]]
--[[ PARAMS:
* `name` => the name of the layer.
* `mappings` => the list of user mappings to replace.
]]
--[[ RETURNS:
* A new `Layer`.
]]
-----------------------------------------------------
function Layer.new(name, mappings)
return setmetatable(
{['_keymap'] = mappings, ['name'] = name},
_metaLayer
)
end
--[[
/*
* PUBLICIZE `Layer`
*/
--]]
return Layer

@ -28,7 +28,7 @@ local _TIMEOUT = {
['CHAR'] = 'ø',
['LEN'] = api.nvim_get_option('timeoutlen'),
['SEND'] = function(__self)
api.nvim_feedkeys(__self.CHAR, '', false)
api.nvim_feedkeys(__self.CHAR, 'nt', false)
end
}
_TIMEOUT.NR = string.byte(_TIMEOUT.CHAR)

@ -10,6 +10,7 @@ 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')

Loading…
Cancel
Save