diff --git a/README.md b/README.md index b298ffa..72d28b6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ This is a rewrite of [vim-libmodal][libmodal] using Neovim's Lua API. Unfortunately, during `vim-libmodal`'s development several problems with Vimscript became apparent. Because of this, I have decided to rewrite it using Lua. This project aims to be cross-compatable with `vim-libmodal` version 2.`X`.`Y`, with the only alterations being _additions_ to the source code that have been made under the 2.x.y revision number. -> __NOTE:__ There cannot be cross-compatability until Neovim 0.5. +> `funcref()`s cannot be used in `libmodal#Enter` or `libmodal#Prompt`, so `nvim-libmodal` is _mostly_ compatable, but not completely. +> * See |libmodal-usage| for more details. Note that cross-compatability does not mean that `vim-libmodal` and `nvim-libmodal` can be installed at the same time— as a matter of fact, they are developed specifically to replace each other for specific platforms. If you use Vim, use `vim-libmodal`. If you use Neovim, use `nvim-libmodal`. If you are a plugin creator, all code that works for `vim-libmodal` will work with `nvim-libmodal`, but the reverse is not true. diff --git a/autoload/libmodal.vim b/autoload/libmodal.vim index 91628f6..a534b4c 100644 --- a/autoload/libmodal.vim +++ b/autoload/libmodal.vim @@ -42,3 +42,43 @@ endfunction function! libmodal#_winOpen(bufHandle) abort return nvim_open_win(a:bufHandle, 0, s:winOpenOpts) 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). +" PARAMS: +" * `a:1` => `modeName` +" * `a:2` => `modeCallback` OR `modeCombos` +" * `a:3` => `supressExit` +function! libmodal#Enter(...) abort + call libmodal#_lua('mode', a:000) +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). +" PARAMS: +" * `a:1` => `modeName` +" * `a:2` => `modeCallback` OR `modeCommands` +" * `a:3` => `modeCompletions` +function! libmodal#Prompt(...) abort + call libmodal#_lua('path', a:000) +endfunction + +" SUMMARY: +" * Pass arguments to an nvim-libmodal `enter()` command at the specified +" `lib` path. +" PARAMS: +" * `lib` => the name of the library. +" * 'mode" or 'prompt'. +" * `args` => the arguments to pass to `lib`.enter() +function! libmodal#_lua(lib, args) + call luaeval( + \ 'require("libmodal/src/' . a:lib . '").enter(_A[1], _A[2], _A[3])', + \ [ + \ a:args[0], + \ a:args[1], + \ len(a:args) > 2 ? a:args[2] : v:false + \ ] + \) +endfunction diff --git a/doc/libmodal.txt b/doc/libmodal.txt index a9dbc1f..9e0cbba 100644 --- a/doc/libmodal.txt +++ b/doc/libmodal.txt @@ -88,17 +88,14 @@ See: |libmodal-usage| ============================================================================== 2. Usage *libmodal-usage* -The |libmodal| interface is designed completely in |Lua|. It is incompatable -with Vimscript until Neovim 0.5 releases (as `neovim/neovim#11306` will -merge). Because of this timed incompatability, the following must be done: +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. -2. Use |lua-require| as a |user-command|. +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. - -`nvim-tabmode` is a plugin that was written to specifically display how to do -this. See below for a link to the repository where the source code may be -viewed. + * See `Iron-E/nvim-tabmode` for a complete 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|, @@ -112,8 +109,9 @@ See: |api|, |lua-api|, https://github.com/Iron-E/nvim-tabmode ------------------------------------------------------------------------------ FUNCTIONS *libmodal-usage-functions* - *libmodal-mode* *libmodal.mode.enter()* -`libmodal.mode`.enter({name}, {instruction} [, {handleExit}]) + *libmodal-mode* *libmodal#Enter()* *libmodal.mode.enter()* +`libmodal.mode`.enter({name}, {instruction} [, {supressExit}]) +`libmodal`#Enter({name}, {instruction} [, {supressExit}]) Enter a new |vim-mode| using {instruction} to determine what actions will be taken upon specific user inputs. @@ -127,8 +125,8 @@ FUNCTIONS *libmodal-usage-functions* To take input on a line-by-line basis, see |libmodal-prompt|. - Note: `libmodal.mode.enter()` may be called from inside itself. See - |libmodal-examples-submodes| for an example. + Note: `libmodal.mode.enter()`/`libmodal#Enter()` may be called from inside + itself. See |libmodal-examples-submodes| for an example. Parameters: ~ {name} The name of the mode (e.g. |INSERT|). @@ -137,38 +135,51 @@ FUNCTIONS *libmodal-usage-functions* {instruction} What to do when accepting user input. - - If {instruction} is a `table`, then it is treated as - a map of user key-chord to Vim |command|s. Example: > + - 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', ['zfc'] = 'tabnew' } + + " VIMSCRIPT + let s:modeInstruction = { + 'zf': 'split', + 'zfo': 'vsplit', + 'zfc': 'tabnew' + } < - Note: If no `?` key is defined, one will be created - automatically. + 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). + - 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 function to Lua from Vimscript! +        - If you try to use a |funcref()| for {instruction}, it +        will fail, GUARANTEED. This is because of |E5004|. +        - If you want to use a |funcref()| for {instruction}, you +        must use a |Lua| `function` instead. Note: Some QoL features are available by default when - specifying a `table` value for {instruction} that - would otherwise have to be programmed manually if - a `function` is specified. + 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|. + subjected to the |timeoutlen|. {supressExit} Whether or not to automatically exit the mode upon an press. - - If `false`, then is automatically mapped to + - If |v:false|/`false`, then is automatically mapped to exiting. - - If `true`, then is ignored unless specified by + - 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-supress-exit|. @@ -178,13 +189,14 @@ FUNCTIONS *libmodal-usage-functions* |libmodal-examples-mode| For examples of this function. - *libmodal-prompt* *libmodal.prompt.enter()* + *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.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" @@ -197,18 +209,32 @@ FUNCTIONS *libmodal-usage-functions* {instruction} What to do when accepting user input. - - If {instruction} is a `table`, then it is treated as - a map of user inputs to Vim |command|s. Example: > - local modeInstruction = { - ['new'] = 'tabnew', - ['close'] = 'tabclose', - ['last'] = 'tablast' - } + - 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 function to Lua from Vimscript! +        - If you try to use a |funcref()| for {instruction}, it +        will fail, GUARANTEED. This is because of |E5004|. +        - If you want to use a |funcref()| for {instruction}, you +        must use a |Lua| `function` instead. + Note: If you want to create commands with arguments, you will need to use a `function`. @@ -276,6 +302,16 @@ Using a callback `function`: > libmodal.mode.enter('FOO', fooMode) < +using a |key-mapping| `dict`: > + let s:barModeCombos = { + \ 'zf': 'split', + \ 'zfo': 'vsplit', + \ 'zfc': 'tabnew' + \} + + call libmodal#Enter('BAR', s:barModeCombos) +< + Using a |key-mapping| `table`: > local libmodal = require('libmodal') local fooModeCombos = { @@ -309,6 +345,16 @@ Using a callback `function`: > libmodal.mode.enter('FOO', fooMode, true) < +Using a |key-mapping| `dict`: > + let s:barModeCombos = { + \ '': 'echom "You cant exit using escape."', + \ 'q': 'let g:barModeExit = 1' + \} + + let g:barModeExit = 0 + call libmodal#Enter('BAR', s:barModeCombos, 1) +< + Using a |key-mapping| `table`: > local libmodal = require('libmodal') local fooModeCombos = { @@ -346,6 +392,23 @@ Using a callback `function`: > enter() < +Using a |key-mapping| `table`: > + let s:barModeRecurse = 0 + + let s:barModeCombos = { + \ 'z': 'BarModeEnter', + \} + + function! s:BarMode() + let s:barModeRecurse += 1 + call libmodal#Enter('BAR' . s:barModeRecurse, s:barModeCombos) + let s:barModeRecurse -= 1 + endfunction + + command! BarModeEnter call s:BarMode() + execute 'BarModeEnter' +< + Using a |key-mapping| `table`: > local libmodal = require('libmodal') local fooModeRecurse = 0 @@ -384,6 +447,16 @@ Using a callback `function`: > libmodal.prompt.enter('FOO', fooMode, commandList) < +Using a |command| `dict`: > + let s:commands = { + \ 'new': 'tabnew', + \ 'close': 'tabclose', + \ 'last': 'tablast' + \} + + call libmodal#Prompt('TAB', s:commands) +< + Using a |command| `table`: > local libmodal = require('libmodal') local commands = { @@ -524,10 +597,7 @@ along with this program. If not, see . ============================================================================== 6. Bugs *libmodal-bugs* -* Passing a |funcref| through Vimscript to |Lua| yields a `nil`. - * This bug is fixed in Neovim 0.5, and when it releases, there will be a - patch for this plugin to fix it and introduce compatability with - |vim-libmodal|. +* `libmodal#Enter()` does not work when {instruction} is a |funcref|. ============================================================================== 7. Contributing *libmodal-contributing* @@ -636,4 +706,4 @@ www.lua-users.org |Lua| reference. www.stackoverflow.com Vimscript and |Lua| reference. ============================================================================== - vim:tw=78:ts=4:ft=help:norl: +vim:tw=78:ts=4:ft=help:norl: diff --git a/examples/key-combos-submode.vim b/examples/key-combos-submode.vim new file mode 100644 index 0000000..f596cba --- /dev/null +++ b/examples/key-combos-submode.vim @@ -0,0 +1,14 @@ +let s:barModeRecurse = 0 + +let s:barModeCombos = { +\ 'z': 'BarModeEnter', +\} + +function! s:BarMode() + let s:barModeRecurse += 1 + call libmodal#Enter('BAR' . s:barModeRecurse, s:barModeCombos) + let s:barModeRecurse -= 1 +endfunction + +command! BarModeEnter call s:BarMode() +execute 'BarModeEnter' diff --git a/examples/key-combos-supress-exit.vim b/examples/key-combos-supress-exit.vim new file mode 100644 index 0000000..b5528e0 --- /dev/null +++ b/examples/key-combos-supress-exit.vim @@ -0,0 +1,7 @@ +let s:barModeCombos = { +\ '': 'echom "You cant exit using escape."', +\ 'q': 'let g:barModeExit = 1' +\} + +let g:barModeExit = 0 +call libmodal#Enter('BAR', s:barModeCombos, 1) diff --git a/examples/key-combos.vim b/examples/key-combos.vim new file mode 100644 index 0000000..b89ba8d --- /dev/null +++ b/examples/key-combos.vim @@ -0,0 +1,7 @@ +let s:barModeCombos = { +\ 'zf': 'split', +\ 'zfo': 'vsplit', +\ 'zfc': 'tabnew' +\} + +call libmodal#Enter('BAR', s:barModeCombos) diff --git a/examples/prompt-commands.vim b/examples/prompt-commands.vim new file mode 100644 index 0000000..a8d6c0b --- /dev/null +++ b/examples/prompt-commands.vim @@ -0,0 +1,7 @@ +let s:commands = { +\ 'new': 'tabnew', +\ 'close': 'tabclose', +\ 'last': 'tablast' +\} + +call libmodal#Prompt('TAB', s:commands) diff --git a/lua/libmodal/src/mode/init.lua b/lua/libmodal/src/mode/init.lua index 15cd1ca..eb1075e 100644 --- a/lua/libmodal/src/mode/init.lua +++ b/lua/libmodal/src/mode/init.lua @@ -91,7 +91,7 @@ local function _comboSelect(modeName) -- if there was no matching command if cmd == false then - if vars.help.instances[modeName] then + if #userInputHistory < 2 and userInputHistory[1] == string.byte(_HELP) then vars.help.instances[modeName]:show() end clearUserInput = true @@ -205,6 +205,8 @@ end -------------------------------------------------------------------------------- local function _modeLoop(handleExitEvents, indicator, modeInstruction, modeName) -- If the mode is not handling exit events automatically and the global exit var is true. + print(type(modeInstruction)) + print(modeInstruction) if not handleExitEvents and globals.isTrue( vars.nvim_get(vars.exit, modeName) ) then return false end @@ -247,6 +249,9 @@ end ------------------------ function mode.enter(...) local args = {...} + print(args[1]) + print(args[2]) + print(args[3]) --[[ SETUP. ]]