Home

Awesome

About

vim-libmodal:

Forked from vim-win:

libmodal is a Neo/vim library/plugin aimed at simplifying the creation of new "modes" (e.g. Insert, Normal). The entrance of modes is user-defined, and their exit defaults to <Esc>. The function and name of modes is also user-defined, and is outlined in libmodal-usage.

If you use Neovim, try nvim-libmodal instead!

Installation

Use the built-in package manager or one of the various package managers.

ManagerCommand
dein.vimcall dein#add('https://github.com/Iron-E/vim-libmodal')
NeoBundleNeoBundle 'https://github.com/Iron-E/vim-libmodal'
Vim-PlugPlug 'https://github.com/Iron-E/vim-libmodal'
VundlePlugin 'https://github.com/Iron-E/vim-libmodal'

Requirements

Usage

Commands

libmodal#Enter

libmodal#Enter takes three parameters. These parameters are not formally named by the editor (as libmodal#Enter is declared libmodal#Enter(...) ). However, the names of these parameters will be used throughout the document to describe the index of the parameter (see E740).

ArgIndexUse
modeName0The name for the mode when prompting the user.
modeCallback1The function used to control the mode.
modeCombos1A dictionary of libmodal-key-combinations.
supressExit2A flag to enable libmodal-exit-supression.

libmodal#Prompt

libmodal#Prompt takes two parameters. These parameters are not formally named by the editor (as libmodal#Prompt is declared libmodal#Prompt(...) ). However, the names of these parameters will be used throughout the document to describe the index of the parameter (see E740).

ArgIndexUse
modeName0The name for the mode when prompting the user.
modeCallback1The function used to control the mode.
modeCommands1A dictionary of commands→strings to execute.
commandList2A list of the commands in a modeCallback.

Receiving Input

When a user of libmodal calls libmodal#Enter or libmodal#Prompt, the modeName parameter is used to generate a unique global variable for the specific purpose of receiving said input. The variable is generated as follows:

let g:{tolower(a:modeName)}ModeInput = …

For example, if modeName is 'FOO', then the variable that is created is g:fooModeInput.

Creating Modes

For an example of a plugin that uses vim-libmodal, see vim-tabmode.

To define a new mode, you must first create a function to pass into libmodal#Enter. Example:

function! s:FooMode()
	if g:fooModeInput ==# "a"
		execute 'tabnew'
	elseif g:fooModeInput ==# "d"
		execute 'tabclose'
	endif
endfunction

After defining said function, you can create a mapping to enter the mode. Example:

command! FooModeEnter call libmodal#Enter('FOO', funcref('s:FooMode'))
nnoremap <leader>n :FooModeEnter

Key Combinations

While normally libmodal dictates that a user should define their own function for controlling a mode, there is a way to specify key combinations. If the second argument is set to a modeCombos dictionary, libmodal#Enter will automatically detect the caller's intent and pass control over to an auxilliary function built to handle pre-defined combos.

When providing modeCombos, it is important to note that one no longer has to receive input for themselves. Despite this, the unique variable (see libmodal-receiving-input) is still updated, and you can create a listener for it just like for any other variable.

Here is an example that shows how to create a dictionary that defines the following actions:

ComboAction
zfoEcho a message saying "It works!"
zfcCreate a new tab.
let s:barModeCombos = {
\	'zfo': 'echom "It works!"',
\	'zfc': 'tabnew'
\}

And then to enter that mode, you can call:

call libmodal#Enter('BAR', s:barModeCombos)

libmodal's internal processing of that dictionary becomes more useful the larger the dictionary is. Internally, s:barModeCombos is rendered into a dictionary that looks like this:

Internal Tree Structure

This allows libmodal to quickly determine which mappings are and are not part of the mode. Because of this method, modes with mappings that have similar beginnings are more efficient, and modes with more mappings get more benefit from the quick tree-like traversal.

Libmodal Timeouts

When key combinations are being used, mode creators may also enable the use of Vim's built-in timeout feature. Unlike other options which are specified by passing arguments to libmodal#Enter, this feature is enabled through a variable.

The reasoning for this is that the use of timeouts is primarily chosen by the user of a mode, rather than the creator (whereas other features like exit supression are largely creator-oriented).

To enable timeouts, one may set the following variables:

" Set libmodal modes to turn timeouts on.
let g:libmodalTimeouts = 1
" Enable timeouts for specific mode.
let g:{modeName}ModeTimeout = 1

Similarly, to disable them, one may set them to 0.

When enabled, libmodal will reference the mode user's timeoutlen as specified in their config. This way, modes will feel consistent to users by default.

However, mode creators may change timeoutlen upon entrance of a mode, and then reset it upon exit. Example:

function! s:BarMode() abort
	" Get the user's preferred timeout length.
	let l:timeoutlen = &timeoutlen
	" Set it to something else, like 1500ms
	let &timeoutlen = 1500
	" Enter a mode
	call libmodal#Enter(…)
	" Reset the timeout
	let &timeoutlen = l:timeoutlen
endfunction

Mode creators who use modeCallbacks may define timeouts manually using timers, which is how libmodal implements them internally.

Exit Supression

When the supressExit parameter is specified, libmodal#Enter will ignore <Esc> presses and instead listen for changes to a unique variable created for the specific purpose of exiting the mode. The variable is generated as follows:

let g:{tolower(a:modeName)}ModeExit = 0

When this variable becomes set to 1, the mode will exit the next time that the modeCallback function returns.

Creating Prompts

Besides accepting user input like keys in Normal-mode, libmodal is also capable of prompting the user for input like Cmdline-mode. To define a Cmdline-mode-like prompt, use libmodal#Prompt rather than libmodal#Enter.

When modeCommands is specified, completions are provided for every key in the dictionary. See an example of this below:

let s:barModeCommands = {
\	'new': 'tabnew',
\	'close': 'tabclose',
\	'last': 'tablast'
\}

When modeCallback is specified, completions must be provided separately. An equivalent to the above using a modeCallback would be:

" Define callback
function! s:BarMode() abort
	if g:barModeInput ==# 'new'
		execute 'tabnew'
	elseif g:barModeInput ==# 'close'
		execute 'tabclose'
	elseif g:barModeInput ==# 'last'
		execute 'tablast'
	endif
endfunction

" Define completion list
let s:barModeCommandList = ['new', 'close', 'last']

You can then enter the mode using one of the following commands (depending on whether or not you used a dictionary or a callback):

" Command dict
call libmodal#Prompt('BAR', s:barModeCommands)
" Callback + completion list
call libmodal#Prompt('BAR', funcref('s:BarMode'), s:barModeCommandList)

Submodes

libmodal has built-in support for entering additional modes while already in a libmodal mode. To enter another mode, one must only call libmodal#Enter from within a modeCallback. Additionally, when a user presses <Esc> they will automatically be taken back to the mode that they were previously inside of.

To display this feature, one view the submode example.

Configuration

The following highlight groups can be configured to change a mode's colors:

NameDefaultDescription
LibmodalPromptModeMsgColor for the mode text.
LibmodalStarStatusLineColor for the * at the beginning.