mirror of
https://github.com/neovim/neovim.git
synced 2026-08-01 05:09:08 +00:00
Problem:
POSIX-compatible Ex-mode requires special-cases all over the codebase to
match various quirks that don't actually matter to users.
- The main utility of *interactive* Ex-mode is its REPL behavior, and
that can be achieved with `cmdwin`, which also gains extra UX
benefits.
- The main utility of *non-interactive* `nvim -es` is for shell
scripting, where Ex-mode quirks are mostly unhelpful (e.g. the
"Entering Ex mode" message).
Solution:
- Reimplement *interactive* Ex-mode as a "persistent, insert-mode
cmdwin" in Lua.
- "nvim -e/-E" is simply an alias to "gQ".
- Reframe *non-interactive* Ex-mode (`nvim -es`) as "script mode".
- Drop POSIX Ex-mode quirks.
Improvements:
- "nvim -V1 -es" output ends with a final newline!
- "nvim -V1 -es" no longer shows the "Entering Ex mode" msg. (This was
pointless noise, unwanted for scripting purposes.)
- stdin is no longer typeahead. Scripts (":lua io.read()") can read
stdin as data.
- Empty line is a no-op: a stray blank line no longer moves the cursor
(deviates from POSIX ex "+1"), no longer exits 1 at EOF (E501).
Preserved behavior:
- cursor starts at "$"
- mode()=="cv" (for non-interactive)
- multiline commands (:append/:function/heredoc pull continuation lines)
- bare-range print
- :print=>stdout
- -V1=>stderr
- CRLF input
- continue-after-error and exit codes
Dropped (regressed) POSIX behavior (non-interactive):
- Event loop only ticks while/between commands, not while blocked
waiting for a stdin line.
- ":g/pat/visual...Q"
- input()/getchar()/":s/x/y/c" no longer consume stdin lines as
answers: Nvim stops at end-of-input, skipping the rest of the script,
exit 0. Use ":lua io.read()" instead.
- If users care about this they should use interactive Ex-mode (`gQ`).
- ":@r" stops at end of the register instead of continuing to read
cmdline input from stdin.
43 lines
1.0 KiB
Lua
43 lines
1.0 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
|
|
local describe, it, before_each = t.describe, t.it, t.before_each
|
|
local clear = n.clear
|
|
local command = n.command
|
|
local fn = n.fn
|
|
local feed = n.feed
|
|
local expect = n.expect
|
|
local eq = t.eq
|
|
local eval = n.eval
|
|
|
|
before_each(clear)
|
|
|
|
describe(':normal!', function()
|
|
it('can get out of Insert mode if called from Ex mode #17924', function()
|
|
feed('gQnormal! Ifoo<CR>')
|
|
feed('visual<CR>')
|
|
expect('foo')
|
|
end)
|
|
|
|
it('does not execute command in Ex mode when running out of characters', function()
|
|
command('let g:var = 0')
|
|
command('normal! gQlet g:var = 1')
|
|
eq(0, eval('g:var'))
|
|
end)
|
|
|
|
it('gQinsert does not hang #17980', function()
|
|
command('normal! gQinsert')
|
|
expect('')
|
|
end)
|
|
|
|
it('can stop Visual mode without closing cmdwin vim-patch:9.0.0234', function()
|
|
feed('q:')
|
|
feed('v')
|
|
eq('v', fn.mode(1))
|
|
eq(':', fn.getcmdwintype())
|
|
command('normal! \027')
|
|
eq('n', fn.mode(1))
|
|
eq(':', fn.getcmdwintype())
|
|
end)
|
|
end)
|