*libmodal.txt* Create modes for Neovim *libmodal* *nvim-libmodal* ================================================================================ 0. Table of Contents *libmodal-toc* 1. About ................ |libmodal-about| 2. Usage ................ |libmodal-usage| 3. Examples ............. |libmodal-examples| 4. Configuration ........ |libmodal-configuration| 5. License .............. |libmodal-license| 6. Bugs ................. |libmodal-bugs| 7. Contributing ......... |libmodal-contributing| 8. Credits .............. |libmodal-credits| ================================================================================ 1. About *libmodal-about* |nvim-libmodal|: - Author, Iron-E @ https://github.com/Iron-E & https://gitlab.com/Iron_E - GitHub @ https://github.com/Iron-E/nvim-libmodal Complete rewrite of |vim-libmodal|: - Author, Iron-E @ https://github.com/Iron-E & https://gitlab.com/Iron_E - GitHub @ https://github.com/Iron-E/vim-libmodal |libmodal| is a Neovim library/|plugin| aimed at simplifying the creation of new "modes" (e.g. |Insert|, |Normal|). The entrance of modes is creator-defined, and their exit defaults to . The use and name of modes is also creator-defined, and is outlined in |libmodal-usage|. See: |vim-modes| -------------------------------------------------------------------------------- USE CASE *libmodal-use-case-example* As an |init.vim| configuration grows, it becomes harder to create keybindings that alphabetically represent the action that they perform. To get around this, |libmodal| allows users to create a new "layer" of keybindings contained within a pseudo-|vim-mode|. This layer of keybindings can be bound to a command which executes `libmodal.mode.enter()` or `libmodal.prompt.enter()`, and any settings outside of these commands are preserved. For example, say that a user of Neovim regularly uses |:diffsplit| to merge changes from `git`. They might define a "DIFF" mode that takes input and directly translates it into |:diff|* operations. This would allow them to For instance, perhaps this mode is defined so that `n` goes to the next diff (like `]c`), and `N` goes to the previous diff (like `[c`). This would make going from diff to diff more rememberable, as `n` is commonly used because of `/` searches. Suppose that the mode also numbers each |:buffer|, so that you don't have to remember which |:diffsplit| to |:diffget| from. The numbers would disappear when you leave the mode. Finally, there could be a help key, `?`, which would show exactly which keys have been mapped and what they do. You can see such a mode here: - https://gist.github.com/Iron-E/f36116e8862ea03fd195e4e0a48cb05d Outside of the |libmodal-mode|, `n` still searches for the |last-pattern|, and the buffers are not visibly numbered. Any setup that a |libmodal-mode| does to inderpret keybindings is undone before the mode ends (while any changes to buffers persevere). See: |libmodal-usage| ================================================================================ 2. Usage *libmodal-usage* The |libmodal| interface is designed completely in |Lua|. It is compatable with Vimscript, and so one may either: 1. Define a |Lua| interface for your mode (see |libmodal-examples|). - Use |lua-require| as a |user-command|. - See |lua-require-example| for information about how to do this. - See `Iron-E/nvim-tabmode` for a complete example. - See `Iron-E/mode-fugidiff.lua` on GitHub Gists for another example. 2. |call| `libmodal#Enter()` or `libmodal#Prompt()` from Vimscript. The following is a reference for high-level functions meant to be used by mode creators. For those who wish to see a low-level specification of |libmodal|, see |libmodal-lua|. Note: Examples for all topics covered here can be found in the "examples" folder at the root of the repository. See: |api|, |lua-api|, https://github.com/Iron-E/nvim-tabmode, https://gist.github.com/Iron-E/f36116e8862ea03fd195e4e0a48cb05d -------------------------------------------------------------------------------- VARIABLES *libmodal-usage-variables* `vim.g`.libmodalActiveModeName *g:libmodalActiveModeName* The name of the currently active |libmodal-mode|. Type: ~ `string` Default Value: ~ `nil` See also: ~ |g:| For more information about global variables. |vim.g| For info about accessing |g:| from lua. -------------------------------------------------------------------------------- FUNCTIONS *libmodal-usage-functions* *libmodal-mode* *libmodal#Enter()* *libmodal.mode.enter()* `libmodal.mode`.enter({name}, {instruction} [, {supress_exit}]) `libmodal`#Enter({name}, {instruction} [, {supress_exit}]) Enter a new |vim-mode| using {instruction} to determine what actions will be taken upon specific user inputs. User input is taken one character at a time using |getchar()|. It is passed through a |g:var| determined by the {name} of the mode. For example, if {name} is "FOO" then the |g:var| is `g:fooModeInput`. Additionally, this input is reported as a |char2nr| number, and as such should be decoded with `string.char()` (|nr2char| in |Lua|) if working with raw characters is desired. To take input on a line-by-line basis, see |libmodal-prompt|. Note: `libmodal.mode.enter()`/`libmodal#Enter()` may be called from inside itself. See |libmodal-examples| for an example. Parameters: ~ {name} The name of the mode (e.g. |INSERT|). - Case-sensitive. Caps are recommended. {instruction} What to do when accepting user input. - If {instruction} is a `dict`/`table`, then it is treated as a map of user key-chord to Vim |command|s. Example: > -- LUA local modeInstruction = { zf = 'split', zfo = 'vsplit', -- You can also use lua functions zfc = function() return 'tabnew' end } " VIMSCRIPT let s:modeInstruction = { 'zf': 'split', 'zfo': 'vsplit', 'zfc': 'tabnew' } < Note: If no `?` key is defined, one will be created automatically. - If {instruction} is a `function`, then it is called every time that |getchar()| completes. The user input is received through `g:{name}ModeInput` (see above). *Error you cannot pass a funcref to Lua from Vimscript!        - If you want to use a |funcref()| for {instruction}, it        must be the name of the function as a `string`. >        " VIMSCRIPT        function! s:foo() abort        echo 'It works'        call getchar()        endfunction        lua require('libmodal').mode.enter('FOO', 's:foo') < Note: Some QoL features are available by default when specifying a `dict`/`table` value for {instruction} that would otherwise have to be programmed manually if a `function` is specified. - A user's typed characters will show in the lower right corner when {instruction} is a table. - If `g:libmodalTimeouts` is enabled, then user input will be subjected to the |timeoutlen|. {supress_exit} Whether or not to automatically exit the mode upon an press. - If |v:false|/`false`, then is automatically mapped to exiting. - If |v:true|/`true`, then is ignored unless specified by the user. In such cases, the user should set the `g:`{name}`ModeExit` variable to `true` when exiting is desired. See |libmodal-examples|. See also: ~ |lua-eval| For type conversions between Vimscript to |Lua|. |libmodal-examples| For examples of this function. *libmodal-layer* *libmodal.layer* `libmodal.layer`.enter({keymap} [, {exit_char}]) *libmodal.layer.enter()* While a |libmodal-mode| ignores behavior that has not been explicitly defined, a |libmodal-layer| allows unrecognized |input| to be passed back into Neovim for analysis. So, if you have only defined a few keybindings, all of the remaining ones from a user's configuration would still work— only new keymaps will be overwritten. Parameters: ~ {keymap} The keymap for the layer. General template is this: > { [] = { [] = { rhs = , }, … }, … } < Where {mode}, {lhs}, {rhs}, and {opts} are the same as in |vim.keymap.set()| (except you should not use multiple `` at one time, despite |vim.keymap.set()| supporting it). {exit_char} The character used to exit the layer. Return: ~ - The `function` used to undo changes made by the layer, or `nil` if {exit_char} is provided. See also: ~ |libmodal-examples| For an example. |vim.keymap.set()| For more information about `opts`. `libmodal.layer`.new({keymap}) *libmodal.layer.new()* See |libmodal.layer.enter()| for more information. This function only differs from |libmodal.layer.enter()| in that instead of entering the layer immediately, it returns a |libmodal.Layer| object for you to manipulate. Parameters: ~ {keymap} The keymap for the layer. General template is this: > { [] = { [] = { rhs = , }, … }, … } < Where {mode}, {lhs}, {rhs}, and {opts} are the same as in |vim.keymap.set()| (except you should not use multiple `` at one time, despite |vim.keymap.set()| supporting it). Return: ~ - A |libmodal.Layer| object. See also: ~ |libmodal-layer| For more information about layers. |libmodal-examples| For an example. |vim.keymap.set()| For more information about `opts`. *libmodal-Layer* *libmodal.Layer* `libmodal.Layer`:enter() *libmodal.Layer:enter()* Applies the {keymap} which was provided by |libmodal.layer.new|. These two code snippets are equivalent: > local libmodal = require 'libmodal' -- enter a layer directly libmodal.layer.enter({n = gg = {rhs = 'G'}}) -- enter a layer manually local layer = libmodal.layer.new({n = gg = {rhs = 'G'}}) layer:enter() < See also: ~ |libmodal.layer.enter| A shortcut to access this function. |libmodal.layer.new| How to create a |libmodal.Layer| `libmodal.Layer`:is_active() *libmodal.Layer:is_active()* Return: ~ - `true` if the |Layer:enter()| has been called, but not |Layer:exit()|. `false` otherwise. See also: ~ |libmodal.Layer:enter()| A shortcut to access this function. |libmodal.Layer.exit()| How to create a |libmodal.Layer| `libmodal.Layer`:map({mode}, {lhs}, {rhs}, {opts}) *libmodal.Layer:map()* {mode}, {lhs}, {rhs}, and {opts} are the same as in |vim.keymap.set()| except that a {mode} table is not supported. See also: ~ |libmodal-examples| For an example. |vim.keymap.set()| For information about the args. `libmodal.Layer`:unmap({mode}, {lhs}) *libmodal.Layer:unmap()* {mode} and {lhs} are the same as in |vim.keymap.del()| except that a {mode} table is not supported. Note: this function cannot be called until after |libmodal.Layer:enter()| See also: ~ |libmodal-examples| For an example. |vim.keymap.del()| For information about the args. *libmodal-prompt* *libmodal#Prompt()* *libmodal.prompt.enter()* `libmodal.prompt`.enter({name}, {instruction} [, {completions}]) `libmodal`#Prompt({name}, {instruction} [, {completions}]) 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 this function rather than `libmodal.mode.enter()`/`libmodal#Enter()`. User input is taken using |input()|. It is passed through a |g:var| determined by the {name} of the mode. For example, if {name} is "FOO" then the |g:var| is `g:fooModeInput`. Parameters: ~ {name} The name of the mode (e.g. |INSERT|). - Case-sensitive. Caps are recommended. {instruction} What to do when accepting user input. - If {instruction} is a `dict`/`table`, then it is treated as a map of user inputs to Vim |command|s. Example: > -- LUA local modeInstruction = { new = 'tabnew', close = 'tabclose', last = 'tablast' } " VIMSCRIPT let s:modeInstruction = { 'new': 'tabnew', 'close': 'tabclose', 'last': 'tablast' } < - If {instruction} is a `function`, then it is called every time that |input()| completes. The user input is received through `g:{name}ModeInput` (see above). *Error you cannot pass a funcref to Lua from Vimscript!        - If you want to use a |funcref()| for {instruction}, it        must be the name of the function as a `string`.        " VIMSCRIPT        function! s:foo() abort        echo 'It works'        call getchar()        endfunction        lua require('libmodal').prompt.enter('FOO', 's:foo') < Note: If you want to create commands with arguments, you will need to use a `function`. {completions} An array-like `table` of commands that are offered by the prompt. - Automatically generated when {instruction} is a `table`. - Used to provide auto-completion when the user is typing. - If unspecified, and {instruction} is not a `table`, then no completions will be provided. Note: If no `help` command is defined, one will be created automatically. Note: The user may set the `g:`{name}`ModeExit` variable to `true` at any time to prematurely exit. See also: ~ |lua-eval| For type conversions between Vimscript to |Lua|. |libmodal-examples| For examples of this function. -------------------------------------------------------------------------------- EVENTS *libmodal-usage-events* |libmodal| fires the |ModeChanged| |autocommand| |{event}| when entering and exiting |libmodal-mode|s. ================================================================================ 3. Examples *libmodal-examples* See the official examples at the link below: https://github.com/Iron-E/nvim-libmodal/tree/master/examples ================================================================================ 4. Configuration *libmodal-configuration* The following specifies what settings may be used to configure |libmodal-mode|s and |libmodal-prompt|s. -------------------------------------------------------------------------------- HIGHLIGHT GROUPS *libmodal-highlight-groups* The following |highlight-groups| can be |config|ured to change a mode's |color|s: Name Default Description ---------------- ------------ -------------------------- `LibmodalPrompt` `ModeMsg` Color for the mode text. `LibmodalStar` `StatusLine` Color for the prompt text. Note: `LibmodalStar`'s name — while not indicative of its use — is used for the sake of backwards compatability. -------------------------------------------------------------------------------- TIMEOUTS *libmodal-timeouts* *g:libmodalTimeouts* When `libmodal.mode.enter()`'s {instruction} argument is a `table`, mode creators may also enable the use of Vim's built-in 'timeout' feature. To enable 'timeout's, one may set the following |variables|: Lua: ~ > " Set libmodal modes to turn timeouts on. vim.g.libmodalTimeouts = true " Enable timeouts for specific mode. vim.g[name..'ModeTimeouts'] = true < Vimscript: ~ > " Set libmodal modes to turn timeouts on. let g:libmodalTimeouts = v:true " Enable timeouts for specific mode. let g:{name}ModeTimeouts = v:true < Similarly, to disable them, one may set them to `0`. When `g:libmodalTimeouts` or `g:{name}ModeTimeouts` is set to `1`, |libmodal| will automatically execute commands that have mappings that might also be longer mappings. For example: If a mode specifies `zf` and `zfo` as mappings, - Turning 'timeout's on will cause `zf` to be executed if the user waits for 'timeoutlen' without typing another character. - If 'timeout' were to be off in this case, then the user would either have to hit to execute `zf` or hit `o` to execute `zfo`. NOTE: `g:libmodalTimeouts` defaults to the 'timeout' value. NOTE: The `g:limbodalTimeouts` variable should NOT be defined by plugins. - Allow users to decide whether or not they want timeouts to be enabled globally themselves. NOTE: Mode-specific timeout variables will override `g:libmodalTimeouts`. When enabled, |libmodal-timeouts| 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: Vimscript: ~ > function! s:FooMode() 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 < Lua: ~ > local libmodal = require('libmodal') function fooMode() -- Get the user's preferred timeout length. local prevTimeoutLen = vim.go.timeoutlen -- Set it to something else, like 1500ms. vim.go.timeoutlen = 1500 -- Enter a mode. libmodal.mode.enter(…) -- Restore the `timeoutlen` vim.go.timeoutlen = prevTimeoutLen end < Mode creators who use `function` {instruction}s may define timeouts manually using |timers|, which is how |libmodal| implements them internally. ================================================================================ 5. License *libmodal-license* `nvim-libmodal` – Create new "modes" for Neovim. Copyright © 2020 Iron-E This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ================================================================================ 6. Bugs *libmodal-bugs* - `libmodal#Enter()` does not work when {instruction} is a |funcref|. - See |E5004|. - `libmodal#Prompt()` does not work when {instruction} is a |funcref|. - See |E5004|. ================================================================================ 7. Contributing *libmodal-contributing* The following describes what should be done if an individual wishes to contribute something to the `Iron-E/nvim-libmodal` repository. -------------------------------------------------------------------------------- CODE *libmodal-contributing-code* Bugfixes ~ If you discover a bug and believe you know the solution to fixing it, then submit a bug report and state that you are working on a fix (and what that fix might be), and what general timeframe the fix may be completed in (months, weeks, days, etc.). When the fix is complete, submit a PR that references the issue you submitted. Features ~ If there is a feature you would like to be a part of |libmodal|, the best thing you can do is submit a feature request, and then state that you are working on a pull request (PR) so others don't attempt to do the same work at the same time. When you believe your feature is complete, write some examples for it in the `examples/lua` folder, and add them to |libmodal-examples| as appropriate. Assure that all existing |libmodal-examples| continue to work with your feature, unless a breaking change was discussed on the feature request. If you need help getting them to pass, you can ask for help on the PR. Reference the issue you submitted on the PR so that the two show up together when looking back at the history. Contributing documentation is not necessary but appreciated, since the person who knows the most about the feature being implemented is most likely the one implementing it. -------------------------------------------------------------------------------- DOCUMENTATION *libmodal-contributing-documentation* If there is a problem with the documentation, or you see an area where it could be improved, don't hesitate to submit an issue and a PR. At the very least it will exist in history if such an issue comes up again, and likely it will serve to help yourself and others with more clear and concise wording, or with more helpful and practical examples. -------------------------------------------------------------------------------- ISSUES *libmodal-contributing-issues* Issues are greatly welcomed on the GitHub repository, whether they are bug reports, feature requests, documentation improvements, or misunderstandings: it's all good to have in the archive. When submitting an issue, please describe the following: 1. Context regarding the issue (how you discovered it, pertinent information, etc.) 2. Detailed description of the issue. 3. Steps to reproduce (if applicable). 4. Expected behavior (if applicable). 5. Attached media (screenshots, logs, etc.) (if applicable). ================================================================================ 8. Credits *libmodal-credits* Credit Reason --------------------- ---------------------------------- Daniel Steinberg |vim-win| creator and inspiration. Iron-E Primary contibuter/maintainer. neoclide/|coc-nvim| Development environment provider. r/Neovim |Lua| and Neovim reference. Roberto Ierusalimschy "Programming In Lua: 5.1". Steve Losh "Learn Vimscript The Hard Way". tbastos/vim-lua Syntax highlighting for |Lua|. u/Mambu38 |Lua| reference. u/oryiesis Inspiration. www.lua-users.org |Lua| reference. www.stackoverflow.com Vimscript and |Lua| reference. ================================================================================ vim:tw=80:ts=4:ft=help:norl: