1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local describe, it, before_each = t.describe, t.it, t.before_each
local clear = n.clear
local command = n.command
local eq = t.eq
local feed = n.feed
local fn = n.fn
before_each(clear)
-- Interactive Ex mode ([count]q:) is a keep-open |cmdwin| REPL. #40962
describe('Ex mode', function()
it('executes commands against the caller window, auto-prints, records history', function()
local screen = Screen.new(60, 10)
command([[call setline(1, ['line1', 'line2', 'line3'])]])
feed('1q:')
screen:expect([[
line1 |
{2:[No Name] [+] }|
{1::}^ |
{1:~ }|*5
{3:[Ex mode] }|
{5:-- INSERT --} |
]])
t.matches('Entering Ex mode%.', n.exec_capture('messages'))
feed("put ='inserted'<CR>")
feed('visual<CR>')
eq('n', fn.mode(1))
eq({ 'line1', 'inserted', 'line2', 'line3' }, fn.getline(1, '$'))
eq('visual', fn.histget('cmd', -1))
eq("put ='inserted'", fn.histget('cmd', -2))
-- Ex mode can be re-entered (the "[Ex mode]" buffer name is not duplicated).
feed('1q:')
screen:expect({ any = vim.pesc('{3:[Ex mode]') })
feed('visual<CR>')
eq('n', fn.mode(1))
-- ":exmode" (but not ":ex", which is ":edit") also enters Ex mode.
eq('ex', fn.fullcommand('ex'))
eq('exmode', fn.fullcommand('exm'))
command('exmode')
screen:expect({ any = vim.pesc('{3:[Ex mode]') })
feed('visual<CR>')
eq('n', fn.mode(1))
end)
it('bare address and empty line move the cursor and auto-print', function()
local screen = Screen.new(60, 8)
command([[call setline(1, ['line1', 'line2'])]])
feed('1q:')
feed('1<CR>') -- Bare address: move and auto-print.
screen:expect({ any = vim.pesc('" line1') })
feed('<CR>') -- Empty line: advance and auto-print (POSIX ex "+").
screen:expect({ any = vim.pesc('" line2') })
feed('<CR>') -- At end-of-file.
screen:expect({ any = 'E501' })
feed('visual<CR>')
eq(2, fn.line('.'))
end)
it('explicit :print is not doubled by auto-print', function()
command([[call setline(1, ['line1', 'line2'])]])
feed('1q:')
feed('1,2print<CR>')
-- Transcript records each printed line once, not the cursor line again.
eq({ '1,2print', '" line1', '" line2', '' }, n.api.nvim_buf_get_lines(0, 0, -1, false))
feed('visual<CR>')
end)
it('substitute confirmation prompt', function()
local screen = Screen.new(60, 8)
command('set inccommand=')
command([[call setline(1, repeat(['foo foo'], 2))]])
feed('1q:')
feed('%s/foo/bar/gc<CR>')
screen:expect({ any = vim.pesc('replace with bar? (y)es/(n)o/(a)ll') })
feed('y') -- Replace the first match ...
feed('q') -- ... then quit at the second prompt: back to the REPL.
screen:expect({ any = vim.pesc('{5:-- INSERT --}') })
feed('visual<CR>')
eq({ 'bar foo', 'foo foo' }, fn.getline(1, '$'))
end)
it('collects multi-line commands until the block is complete', function()
local screen = Screen.new(60, 10)
feed('1q:')
feed('append<CR>')
-- Continuation lines should not have ":" prefix in 'statuscolumn'.
screen:expect([[
|
{2:[No Name] }|
{1::}append |
{1: }^ |
{1:~ }|*4
{3:[Ex mode] }|
{5:-- INSERT --} |
]])
feed('x1<CR>x2<CR>.<CR>')
-- Executing :append does not end the REPL's Insert mode (ex_append restores "State").
eq('i', fn.mode(1))
-- Ending :append with "." does not auto-print the last inserted line.
eq({ 'append', 'x1', 'x2', '.', '' }, n.api.nvim_buf_get_lines(0, 0, -1, false))
feed('let g:x = 0<CR>')
feed('if 1<CR>')
-- Continuation lines should not have ":" prefix in 'statuscolumn'.
screen:expect([[
x1 |
{2:[No Name] [+] }|
{1: }x1 |
{1: }x2 |
{1: }. |
{1::}let g:x = 0 |
{1::}if 1 |
{1: }^ |
{3:[Ex mode] }|
{5:-- INSERT --} |
]])
feed('let g:x = 1<CR>endif<CR>')
feed('function! F()<CR>return 42<CR>endfunction<CR>')
feed('let g:h =<lt><lt> trim EOS<CR> hey<CR>EOS<CR>')
feed('visual<CR>')
eq({ 'x1', 'x2' }, fn.getline(1, '$'))
eq(1, n.eval('g:x'))
eq(42, n.eval('F()'))
eq({ 'hey' }, n.eval('g:h'))
end)
it('pressing Ctrl-C in :append inside a loop does not hang', function()
feed('1q:')
feed('for i in range(1)<CR>')
feed('append<CR>')
-- CTRL-C cancels the cmdwin (ends Ex mode): back to the ":" cmdline, like |q:|.
feed('<C-C>')
eq('', fn.getcmdwintype())
feed('<Esc>')
eq('n', fn.mode(1))
eq({ '' }, fn.getline(1, '$'))
end)
end)
|