{"id":13898646,"url":"https://github.com/Iron-E/vim-libmodal","last_synced_at":"2025-07-17T16:30:35.144Z","repository":{"id":107020430,"uuid":"257702807","full_name":"Iron-E/vim-libmodal","owner":"Iron-E","description":"A Neo/vim plugin to create modes.","archived":true,"fork":true,"pushed_at":"2020-05-15T22:37:36.000Z","size":6396,"stargazers_count":30,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-25T05:30:28.665Z","etag":null,"topics":["callback-flow-control","callback-functions","free-software","library","mapping-tools","modal","modal-editing","neovim","plugin","vim","vim-libmodal","vim-plugin","vim-win"],"latest_commit_sha":null,"homepage":"","language":"Vim script","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"dstein64/vim-win","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Iron-E.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2020-04-21T20:02:36.000Z","updated_at":"2024-07-01T07:25:54.000Z","dependencies_parsed_at":"2023-10-11T11:54:47.406Z","dependency_job_id":null,"html_url":"https://github.com/Iron-E/vim-libmodal","commit_stats":{"total_commits":207,"total_committers":2,"mean_commits":103.5,"dds":"0.42995169082125606","last_synced_commit":"e842912d4f7f39ff9004effee4ee47717a9ac996"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/Iron-E/vim-libmodal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iron-E%2Fvim-libmodal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iron-E%2Fvim-libmodal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iron-E%2Fvim-libmodal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iron-E%2Fvim-libmodal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Iron-E","download_url":"https://codeload.github.com/Iron-E/vim-libmodal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iron-E%2Fvim-libmodal/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265629769,"owners_count":23801471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["callback-flow-control","callback-functions","free-software","library","mapping-tools","modal","modal-editing","neovim","plugin","vim","vim-libmodal","vim-plugin","vim-win"],"created_at":"2024-08-06T18:04:23.369Z","updated_at":"2025-07-17T16:30:34.846Z","avatar_url":"https://github.com/Iron-E.png","language":"Vim script","readme":"# About\n\n`vim-libmodal`:\n\n- Author: Iron-E\n\t- [GitHub](https://github.com/Iron-E)\n\t- [GitLab](https://gitlab.com/Iron_E)\n\nForked from [`vim-win`](https://github.com/dstein64/vim-win):\n\n- Author: [Daniel Steinberg](https://www.dannyadam.com)\n\n`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 `\u003cEsc\u003e`. The function and name of modes is also user-defined, and is outlined in `libmodal-usage`.\n\nIf you use Neovim, try [nvim-libmodal][libmodal] instead!\n\n# Installation\n\nUse the built-in package manager or one of the various package managers.\n\n| Manager   | Command                                                   |\n|:----------|:----------------------------------------------------------|\n| dein.vim  | `call dein#add('https://github.com/Iron-E/vim-libmodal')` |\n| NeoBundle | `NeoBundle 'https://github.com/Iron-E/vim-libmodal'`      |\n| Vim-Plug  | `Plug 'https://github.com/Iron-E/vim-libmodal'`           |\n| Vundle    | `Plugin 'https://github.com/Iron-E/vim-libmodal'`         |\n\n## Requirements\n\n* [nvim-libmodal][libmodal] is __not__ installed.\n\n# Usage\n\n## Commands\n\n### `libmodal#Enter`\n\n`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`).\n\n| Arg            | Index | Use                                            |\n|:---------------|:-----:|:-----------------------------------------------|\n| `modeName`     | 0     | The name for the mode when prompting the user. |\n| `modeCallback` | 1     | The function used to control the mode.         |\n| `modeCombos`   | 1     | A dictionary of `libmodal-key-combinations`.   |\n| `supressExit`  | 2     | A flag to enable `libmodal-exit-supression`.   |\n\n- Note that _either_ `modeCallback` _or_ `modeCombos` may be specified, __not both__.\n\n### `libmodal#Prompt`\n\n`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`).\n\n| Arg            | Index | Use                                            |\n|:---------------|:-----:|:-----------------------------------------------|\n| `modeName`     | 0     | The name for the mode when prompting the user. |\n| `modeCallback` | 1     | The function used to control the mode.         |\n| `modeCommands` | 1     | A dictionary of commands→strings to execute.   |\n| `commandList`  | 2     | A list of the commands in a `modeCallback`.    |\n\n- Note that _either_ `modeCallback` _or_ `modeCommands` may be specified, __not both__.\n- Note that `commandList` is an optional parameter.\n\t- It is used as a completion source for when `modeCallback` is specified.\n\t- Additionally, `commandList` is __ignored__ when `modeCommands` is specified since completions can be created from the dictionary keys.\n\t- If `commandList` is not specified when `modeCallback` is, no completions will be provided for the prompt.\n\n## Receiving Input\n\nWhen 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:\n\n```viml\nlet g:{tolower(a:modeName)}ModeInput = …\n```\n\nFor example, if `modeName` is 'FOO', then the variable that is created is `g:fooModeInput`.\n\n## Creating Modes\n\nFor an example of a plugin that uses `vim-libmodal`, see [vim-tabmode](https://github.com/Iron-E/vim-tabmode).\n\nTo define a new mode, you must first create a function to pass into `libmodal#Enter`. Example:\n\n```viml\nfunction! s:FooMode()\n\tif g:fooModeInput ==# \"a\"\n\t\texecute 'tabnew'\n\telseif g:fooModeInput ==# \"d\"\n\t\texecute 'tabclose'\n\tendif\nendfunction\n```\n\nAfter defining said function, you can create a mapping to enter the mode. Example:\n\n```viml\ncommand! FooModeEnter call libmodal#Enter('FOO', funcref('s:FooMode'))\nnnoremap \u003cleader\u003en :FooModeEnter\n```\n\n- Note the `funcref()` call. __It must be there__ or else `libmodal#Enter` won't execute properly.\n\n### Key Combinations\n\nWhile 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.\n\nWhen 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.\n\n- Note that `libmodal-exit-supression` is still compatable with defining key combinations.\n\nHere is an example that shows how to create a dictionary that defines the following actions:\n\n| Combo | Action                            |\n|:-----:|:----------------------------------|\n| `zfo` | Echo a message saying \"It works!\" |\n| `zfc` | Create a new tab.                 |\n\n```viml\nlet s:barModeCombos = {\n\\\t'zfo': 'echom \"It works!\"',\n\\\t'zfc': 'tabnew'\n\\}\n```\n\n- NOTE: When defining actions that involve a chorded keypress (e.g. `CTRL-W_s`), mode creators should use `i_CTRL-V` to insert the literal of that character.\n\t- For example, if a mode creator wants a mapping for `\u003cC-s\u003ev`, then it should be specified as `\u0013v`.\n\nAnd then to enter that mode, you can call:\n\n```viml\ncall libmodal#Enter('BAR', s:barModeCombos)\n```\n\n`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:\n\n![Internal Tree Structure](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggVEJcbnp7en0gLS0-IGZ7Zn1cbmYgLS0-IGN7Y31cbmYgLS0-IG97b31cblxuYyAtLT4gZWNob1tcImVjaG9tICZxdW90O0l0IHdvcmtzISZxdW90O1wiXVxubyAtLT4gdGFibmV3IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0 \"Internal Tree Structure\")\n\nThis 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.\n\n- Note that `libmodal#Enter` will only parse a `modeCombos` dict _once_ upon entrance.\n\t- Changes to the mapping dictionary that may occur while in a mode _are not reflected_ until the mode is entered again and the dictionary is re-parsed.\n\n### Libmodal Timeouts\n\nWhen 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.\n\n- Note that if two keybinds share a beginning, and one is shorter than the other, (e.g. `zf` and `zfo`), then the user must press \u003cCR\u003e to execute it.\n\t- This also means that commands ending in `^M` are not permitted.\n\t- Unfortunately, because of the limitations of Vimscript (more specifically `getchar()`) it is not possible to execute a function on `timeout` using `timers` exposed by the API. `getchar()` blocks execution and there is no combination of `sleep` or `wait()` that will allow `getchar()` to be called asynchronously\n\t- If you are reading this and know how to do something like this without using a secondary language, please let me know or open a pull request.\n\nThe reasoning for this is that the use of `timeout`s is primarily chosen by the user of a mode, rather than the creator (whereas other features like exit supression are largely creator-oriented).\n\nTo enable `timeout`s, one may set the following variables:\n\n```viml\n\" Set libmodal modes to turn timeouts on.\nlet g:libmodalTimeouts = 1\n\" Enable timeouts for specific mode.\nlet g:{modeName}ModeTimeout = 1\n```\n\nSimilarly, to disable them, one may set them to `0`.\n\n- Note that If not specified by the user, `g:libmodalTimeouts` automatically references the `timeout` on/off value.\n- Note that the `g:limbodalTimeouts` variable should NOT be defined by plugins.\n\t- Allow users to decide whether or not they want timeouts to be enabled globally by themselves.\n- Note that mode-specific timeout variables will override `g:libmodalTimeouts`.\n\t- This is so a default may be set but overridden.\n\nWhen enabled, `libmodal` will reference the mode user's `timeoutlen` as specified in their config. This way, modes will feel consistent to users by default.\n\nHowever, mode creators may change `timeoutlen` upon entrance of a mode, and then reset it upon exit. Example:\n\n```viml\nfunction! s:BarMode() abort\n\t\" Get the user's preferred timeout length.\n\tlet l:timeoutlen = \u0026timeoutlen\n\t\" Set it to something else, like 1500ms\n\tlet \u0026timeoutlen = 1500\n\t\" Enter a mode\n\tcall libmodal#Enter(…)\n\t\" Reset the timeout\n\tlet \u0026timeoutlen = l:timeoutlen\nendfunction\n```\n\nMode creators who use `modeCallback`s may define timeouts manually using `timers`, which is how `libmodal` implements them internally.\n\n### Exit Supression\n\nWhen the `supressExit` parameter is specified, `libmodal#Enter` will ignore `\u003cEsc\u003e` 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:\n\n```viml\nlet g:{tolower(a:modeName)}ModeExit = 0\n```\n\nWhen this variable becomes set to `1`, the mode will exit the next time that the `modeCallback` function returns.\n\n## Creating Prompts\n\nBesides 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`.\n\nWhen `modeCommands` is specified, completions are provided for every key in the dictionary. See an example of this below:\n\n```viml\nlet s:barModeCommands = {\n\\\t'new': 'tabnew',\n\\\t'close': 'tabclose',\n\\\t'last': 'tablast'\n\\}\n```\n\nWhen `modeCallback` is specified, completions must be provided separately.  An equivalent to the above using a `modeCallback` would be:\n\n```viml\n\" Define callback\nfunction! s:BarMode() abort\n\tif g:barModeInput ==# 'new'\n\t\texecute 'tabnew'\n\telseif g:barModeInput ==# 'close'\n\t\texecute 'tabclose'\n\telseif g:barModeInput ==# 'last'\n\t\texecute 'tablast'\n\tendif\nendfunction\n\n\" Define completion list\nlet s:barModeCommandList = ['new', 'close', 'last']\n```\n\nYou can then enter the mode using one of the following commands (depending on whether or not you used a dictionary or a callback):\n\n```viml\n\" Command dict\ncall libmodal#Prompt('BAR', s:barModeCommands)\n\" Callback + completion list\ncall libmodal#Prompt('BAR', funcref('s:BarMode'), s:barModeCommandList)\n```\n\n- Note that if you want to create commands with arguments, _you will need to use a callback_.\n\n# Submodes\n\n`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 `\u003cEsc\u003e` they will automatically be taken back to the mode that they were previously inside of.\n\nTo display this feature, one view the [submode example](./examples/submodes.vim).\n\n# Configuration\n\nThe following highlight groups can be configured to change a mode's colors:\n\n| Name             | Default      | Description                         |\n|:-----------------|:-------------|:------------------------------------|\n| `LibmodalPrompt` | `ModeMsg`    | Color for the mode text.            |\n| `LibmodalStar`   | `StatusLine` | Color for the `*` at the beginning. |\n\n[libmodal]: https://github.com/Iron-E/nvim-libmodal\n","funding_links":[],"categories":["Vim Script"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIron-E%2Fvim-libmodal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FIron-E%2Fvim-libmodal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIron-E%2Fvim-libmodal/lists"}