mirror of
https://github.com/neovim/neovim.git
synced 2026-08-02 04:51:38 +08:00
refactor(exmode): Ex-mode as cmdwin + Lua #40991
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.
This commit is contained in:
parent
faaa4d57f1
commit
359459dec6
@ -153,7 +153,7 @@ Overview >vim
|
||||
Methods of the "vim" module
|
||||
|
||||
vim.command(str) *python-command*
|
||||
Executes the vim (ex-mode) command str. Returns None.
|
||||
Executes the Ex command str. Returns None.
|
||||
Examples: >vim
|
||||
:py vim.command("set tw=72")
|
||||
:py vim.command("%s/aaa/bbb/g")
|
||||
|
||||
@ -2026,7 +2026,7 @@ containing only a ".". Watch out for lines starting with a backslash, see
|
||||
|line-continuation|.
|
||||
|
||||
Text typed after a "|" command separator is used first. So the following
|
||||
command in ex mode: >
|
||||
command in Ex mode: >
|
||||
:a|one
|
||||
two
|
||||
.
|
||||
|
||||
@ -276,8 +276,7 @@ Vim has seven BASIC modes:
|
||||
"!". |Cmdline-mode|
|
||||
|
||||
- Ex mode: Like Command-line mode, but after entering a command
|
||||
you remain in Ex mode. Very limited editing of the
|
||||
command line. |Ex-mode|
|
||||
you remain in |Ex-mode|.
|
||||
|
||||
*Terminal-mode*
|
||||
- Terminal mode: In Terminal mode all input (except CTRL-\) is sent to
|
||||
@ -380,19 +379,26 @@ CTRL-O in Insert mode you get a beep but you are still in Insert mode, type
|
||||
*v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N*
|
||||
Additionally the command CTRL-\ CTRL-N or <C-\><C-N> can be used to go to
|
||||
Normal mode from any other mode. This can be used to make sure Vim is in
|
||||
Normal mode, without causing a beep like <Esc> would. However, this does not
|
||||
work in Ex mode. When used after a command that takes an argument, such as
|
||||
|f| or |m|, the timeout set with 'ttimeoutlen' applies.
|
||||
Normal mode, without causing a beep like <Esc> would. When used after
|
||||
a command that takes an argument, such as |f| or |m|, the timeout set with
|
||||
'ttimeoutlen' applies.
|
||||
|
||||
*CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G*
|
||||
CTRL-\ CTRL-G works the same as |CTRL-\_CTRL-N| for backward compatibility.
|
||||
|
||||
*gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501*
|
||||
gQ Switch to Ex mode. This is like typing ":" commands
|
||||
one after another, except:
|
||||
- You don't have to keep pressing ":".
|
||||
- The screen doesn't get updated after each command.
|
||||
Use the `:vi` command (|:visual|) to exit this mode.
|
||||
one after another, except you don't have to keep
|
||||
pressing ":". Use |:visual| to exit this mode.
|
||||
|
||||
Implementation details: a persistent |cmdwin| where
|
||||
<Enter> executes the current line as a command
|
||||
(against the window from which Ex mode was entered)
|
||||
and stays open, in insert-mode. Command output is
|
||||
presented as comment (") lines. After a command that
|
||||
moves the cursor or changes the buffer, the current
|
||||
line is printed. An empty line moves to (and prints)
|
||||
the next line.
|
||||
|
||||
==============================================================================
|
||||
Window contents *window-contents*
|
||||
|
||||
@ -93,6 +93,21 @@ DIAGNOSTICS
|
||||
|
||||
EDITOR
|
||||
|
||||
• Interactive |Ex-mode| (|gQ|, `nvim -e`) is a persistent |cmdwin|. This is much
|
||||
more flexible and makes "Ex mode" more useful as a kind of "cmdline REPL",
|
||||
with the tradeoff of dropping some POSIX Ex-mode quirks.
|
||||
• |mode()| returns "n" or "i"; "cv" indicates |-es| (script mode) only.
|
||||
• ":global/pat/visual" no longer switches to Normal mode per matching line.
|
||||
• Non-interactive Ex-mode (|-es|, "script mode") executes stdin as Ex commands
|
||||
directly. It is a "script host", not "POSIX Ex mode".
|
||||
• Output ends with a final newline.
|
||||
• |:visual| is |:edit|: it does not switch to reading stdin as Normal-mode
|
||||
commands, and ":global/pat/visual" does not enter Normal mode per
|
||||
matching line.
|
||||
• Commands that wait for typed input (|input()|, |getchar()|, …) stop Nvim
|
||||
at end-of-input instead of consuming the next stdin line.
|
||||
• An empty line is a no-op instead of advancing the cursor (or failing
|
||||
with |E501| at end-of-file).
|
||||
• On Windows, the |trust| db now stores paths with "/" slashes. This means the
|
||||
trust store will be reset (one time).
|
||||
• `stdpath("log")` moved to `stdpath("state")/logs`.
|
||||
@ -242,7 +257,7 @@ EDITOR
|
||||
real-time, instead of only on |FocusGained| or |:checktime|.
|
||||
• During |complete()|-triggered completion, CTRL-N and CTRL-P are now subject
|
||||
to insert-mode mappings.
|
||||
• Multi-byte characters, translated by 'langmap', now invoke correct
|
||||
• Multibyte characters, translated by 'langmap', now invoke correct
|
||||
mappings.
|
||||
Example: >vim
|
||||
set langmap=õ]
|
||||
|
||||
@ -1019,7 +1019,7 @@ Short explanation of each option: *option-list*
|
||||
|:version| :ve[rsion] show version information
|
||||
|:normal| :norm[al][!] {commands}
|
||||
execute Normal mode commands
|
||||
|gQ| gQ switch to "Ex" mode
|
||||
|gQ| gQ switch to |Ex-mode|
|
||||
|
||||
|:redir| :redir >{file} redirect messages to {file}
|
||||
|:silent| :silent[!] {command} execute {command} silently
|
||||
@ -1144,7 +1144,7 @@ Context-sensitive completion on the command-line:
|
||||
|-+| +[num] put the cursor at line [num] (default: last line)
|
||||
|-+c| +{command} execute {command} after loading the file
|
||||
|-+/| +/{pat} {file} .. put the cursor at the first occurrence of {pat}
|
||||
|-e| -e Ex mode, start vim in Ex mode
|
||||
|-e| -e |Ex-mode|
|
||||
|-R| -R Read-only mode, implies -n
|
||||
|-m| -m modifications not allowed (resets 'write' option)
|
||||
|-d| -d |diff-mode|
|
||||
|
||||
@ -102,12 +102,6 @@ This replaces all occurrences of "pat" with "PAT". The same can be done with: >
|
||||
:%s/pat/PAT/g
|
||||
Which is two characters shorter!
|
||||
|
||||
When using "global" in Ex mode, a special case is using ":visual" as a
|
||||
command. This will move to a matching line, go to Normal mode to let you
|
||||
execute commands there until you use |gQ| to return to Ex mode. This will be
|
||||
repeated for each matching line. While doing this you cannot use ":global".
|
||||
To abort this type CTRL-C twice.
|
||||
|
||||
==============================================================================
|
||||
Complex repeats *complex-repeat*
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ argument.
|
||||
changes and writing.
|
||||
|
||||
-e *-e* *-E*
|
||||
-E Start Nvim in Ex mode |gQ|, see |Ex-mode|.
|
||||
-E Start Nvim in |Ex-mode|.
|
||||
|
||||
If stdin is not a TTY:
|
||||
• -e reads/executes stdin as Ex commands.
|
||||
@ -209,6 +209,11 @@ argument.
|
||||
With |:verbose| or 'verbose', other commands display on stderr: >
|
||||
nvim -es +"verbose echo 'foo'"
|
||||
nvim -V1 -es +"echo 'foo'"
|
||||
<
|
||||
Commands that wait for typed input (|input()|, |getchar()|, …)
|
||||
stop Nvim on EOF; they do not consume stdin. Lua can read
|
||||
stdin as data: >
|
||||
printf 'lua vim.g.b = io.read()\nfoo\nput =g:b\np' | nvim -es
|
||||
<
|
||||
Skips user |config| unless |-u| was given.
|
||||
Disables |shada| unless |-i| was given.
|
||||
|
||||
@ -232,8 +232,8 @@ gx Opens the current filepath, URL (decided by
|
||||
will be aborted as if <Esc> or <C-C> was typed.
|
||||
This implies that an insert command must be completed
|
||||
(to start Insert mode, see |:startinsert|). A ":"
|
||||
command must be completed as well. And you can't use
|
||||
"gQ" to start Ex mode.
|
||||
command must be completed as well. And you can't
|
||||
start |Ex-mode|.
|
||||
|
||||
The display is not updated while ":normal" is busy.
|
||||
|
||||
|
||||
@ -334,6 +334,7 @@ Commands:
|
||||
- |:uptime|
|
||||
|
||||
Editor:
|
||||
- Interactive |Ex-mode| is implemented as persistent, insert-mode |cmdwin|.
|
||||
- |prompt-buffer| supports multiline input/paste, undo/redo, and o/O normal
|
||||
commands.
|
||||
- |i_CTRL-R| inserts named/clipboard registers (A-Z,a-z,0-9+) literally, like
|
||||
@ -482,10 +483,11 @@ Signs:
|
||||
Startup:
|
||||
- |-e| and |-es| invoke the same "improved Ex mode" as -E and -Es.
|
||||
- |-E| and |-Es| read stdin as text (into buffer 1).
|
||||
- |-es| and |-Es| have improved behavior:
|
||||
- Quits automatically, don't need "-c qa!".
|
||||
- |-es| and |-Es| act as a generic "script mode":
|
||||
- Exits automatically, don't need "-c qa!".
|
||||
- Skips swap-file dialog.
|
||||
- Optimized for non-interactive scripts: disables swapfile, shada.
|
||||
- Disables swapfile, shada.
|
||||
- Various "POSIX Ex" quirks were dropped.
|
||||
- |-l| Executes Lua scripts non-interactively.
|
||||
- |-s| reads Normal commands from stdin if the script name is "-".
|
||||
- Reading text (instead of commands) from stdin |--|:
|
||||
|
||||
@ -4126,7 +4126,7 @@ getpos({expr}) *getpos()*
|
||||
'x Position of mark x (if the mark is not set, 0 is
|
||||
returned for all values).
|
||||
w0 First line visible in current window (one if the
|
||||
display isn't updated, e.g. in silent Ex mode).
|
||||
display isn't updated, e.g. in |silent-mode|).
|
||||
w$ Last line visible in current window (this is one
|
||||
less than "w0" if no lines are visible).
|
||||
v End of the current Visual selection (unlike |'<|
|
||||
@ -7329,8 +7329,7 @@ mode([{expr}]) *mode()*
|
||||
Rvx Virtual Replace mode |i_CTRL-X| completion
|
||||
c Command-line editing
|
||||
cr Command-line editing overstrike mode |c_<Insert>|
|
||||
cv Vim Ex mode |gQ|
|
||||
cvr Vim Ex mode while in overstrike mode |c_<Insert>|
|
||||
cv Non-interactive Ex mode |-es|
|
||||
r Hit-enter prompt
|
||||
rm The -- more -- prompt
|
||||
r? A |:confirm| query of some sort
|
||||
|
||||
@ -302,7 +302,7 @@ do
|
||||
local function cmd(opts)
|
||||
local ok, err = pcall(vim.api.nvim_cmd, opts, {})
|
||||
if not ok then
|
||||
vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true })
|
||||
vim.api.nvim_echo({ { require('vim._core.util').cmd_errmsg(err) } }, true, { err = true })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
223
runtime/lua/vim/_core/exmode.lua
Normal file
223
runtime/lua/vim/_core/exmode.lua
Normal file
@ -0,0 +1,223 @@
|
||||
--- Ex mode.
|
||||
--- - `run()`: non-interactive (`nvim -es`): executes stdin as Ex commands.
|
||||
--- - `open()`: interactive (`gQ`, `nvim -e`), a keep-open "cmdwin" REPL.
|
||||
|
||||
local api = vim.api
|
||||
local N_ = vim.fn.gettext
|
||||
local util = require('vim._core.util')
|
||||
|
||||
local M = {}
|
||||
|
||||
--- Commands that end Ex mode before executing (in the caller window).
|
||||
local exit_cmds = {
|
||||
cquit = true,
|
||||
exit = true,
|
||||
quit = true,
|
||||
quitall = true,
|
||||
restart = true,
|
||||
view = true,
|
||||
visual = true,
|
||||
wq = true,
|
||||
wqall = true,
|
||||
xall = true,
|
||||
xit = true,
|
||||
}
|
||||
|
||||
--- Continuation lines (of a multiline cmd), for the statuscolumn.
|
||||
--- @type table<integer, true>
|
||||
local cont_lines = {}
|
||||
|
||||
--- Non-interactive "script mode" ("nvim -es", fka "Ex-mode", but POSIX behavior was dropped):
|
||||
--- Executes stdin as Ex commands, until EOF.
|
||||
---
|
||||
--- Stdin is not consumed as typeahead: fd 0 is read directly (`io.lines()`), so `:lua` script lines
|
||||
--- can also read stdin as data via `io.read()`.
|
||||
function M.run()
|
||||
local lines = io.lines()
|
||||
local getline = function()
|
||||
local line = lines()
|
||||
-- Strip trailing CR so CRLF input (e.g. Windows pipes) works like LF.
|
||||
return line and (line:gsub('\r$', '')) or nil
|
||||
end
|
||||
-- Instead of per-line `nvim_exec2`, this runs one `do_cmdline` per logical command with
|
||||
-- `getline` as the line-getter, in order to preserve these behaviors:
|
||||
-- - :append/:function/:if/heredocs pull continuation lines from stdin. #7679
|
||||
-- - Errors are emitted, not thrown (nvim_exec2 turns the first error into an exception):
|
||||
-- execution continues with the next command, `ex_exitval` still sets the exit code, and
|
||||
-- "-V1" shows errors on stderr.
|
||||
-- - Output (:print, :set, …) to stdout as each command executes.
|
||||
--
|
||||
-- TODO: could enhance nvim_exec2 instead?
|
||||
while vim._core.ex_docmd(getline) do
|
||||
vim.wait(0) -- Run pending events (vim.schedule() callbacks, RPC).
|
||||
end
|
||||
end
|
||||
|
||||
--- Whether `lines` form a complete command block: no `:append` text, `:function` body, heredoc, or
|
||||
--- :if/:while/:for/:try block is awaiting continuation lines. Feeds the lines to do_cmdline(),
|
||||
--- which groups multiline constructs even when skipping (false `:if`), without executing anything.
|
||||
--- If the construct is still open it consumes the sentinel "endif" and asks the getter for more.
|
||||
--- @param lines string[]
|
||||
--- @return boolean
|
||||
local function block_complete(lines)
|
||||
-- :append/etc. not grouped by the skipped-:if below: collect until the "." terminator.
|
||||
local ok, p = pcall(api.nvim_parse_cmd, lines[1], {})
|
||||
if ok and (p.cmd == 'append' or p.cmd == 'insert' or p.cmd == 'change') then
|
||||
return vim.list_contains({ unpack(lines, 2) }, '.')
|
||||
end
|
||||
|
||||
local feed = { 'if v:false' }
|
||||
vim.list_extend(feed, lines)
|
||||
feed[#feed + 1] = 'endif'
|
||||
local i = 0
|
||||
local complete = true
|
||||
-- Silenced: probing an incomplete block emits e.g. E126/E990 when the reader hits EOF.
|
||||
vim._with({ silent = true, emsg_silent = true }, function()
|
||||
vim._core.ex_docmd(function()
|
||||
i = i + 1
|
||||
if i > #feed then
|
||||
complete = false
|
||||
return nil
|
||||
end
|
||||
return feed[i]
|
||||
end)
|
||||
end)
|
||||
return complete
|
||||
end
|
||||
|
||||
--- Ex-mode 'statuscolumn': ":", or blank for continuation lines (`:func` body, `:append` text, …).
|
||||
function M._statuscolumn()
|
||||
return cont_lines[vim.v.lnum] and ' ' or ':'
|
||||
end
|
||||
|
||||
--- Interactive Ex-mode: keep-open cmdwin REPL. `<CR>` executes the current line against the caller
|
||||
--- window and keeps the cmdwin open; command output is presented as comment ('"') lines. Multiline
|
||||
--- commands (`:append`, etc.) are collected until the block is complete.
|
||||
function M.open()
|
||||
local cmdwin = require('vim._core.cmdwin')
|
||||
local caller = api.nvim_get_current_win()
|
||||
cmdwin.open(':')
|
||||
if api.nvim_get_current_win() == caller then
|
||||
return -- cmdwin failed to open (E1292 already echoed).
|
||||
end
|
||||
local buf = api.nvim_get_current_buf()
|
||||
local block_start = api.nvim_buf_line_count(buf)
|
||||
cont_lines = {}
|
||||
vim.wo[0][0].statuscolumn = '%#NonText#%{v:lua.require("vim._core.exmode")._statuscolumn()}'
|
||||
|
||||
--- Formats `text` (possibly multiline) as transcript comment lines.
|
||||
--- @param out string[]
|
||||
--- @param text string
|
||||
local function put(out, text)
|
||||
for _, l in ipairs(vim.split(text, '\n', { trimempty = true })) do
|
||||
out[#out + 1] = '" ' .. l
|
||||
end
|
||||
end
|
||||
|
||||
api.nvim_echo({ { N_('Entering Ex mode. Type "visual" to go to Normal mode.') } }, true, {})
|
||||
|
||||
local function run()
|
||||
if not api.nvim_win_is_valid(caller) then
|
||||
cmdwin._cleanup() -- Caller window is gone: end Ex mode.
|
||||
return
|
||||
end
|
||||
local line = api.nvim_get_current_line()
|
||||
local lnum = api.nvim_win_get_cursor(0)[1]
|
||||
if lnum >= block_start then
|
||||
-- Multiline commands: collect continuation lines until the block is complete.
|
||||
local block = api.nvim_buf_get_lines(buf, block_start - 1, lnum, false)
|
||||
if not block_complete(block) then
|
||||
cont_lines[lnum + 1] = true -- Mark the continuation line.
|
||||
api.nvim_buf_set_lines(buf, lnum, lnum, false, { '' })
|
||||
api.nvim_win_set_cursor(0, { lnum + 1, 0 })
|
||||
return
|
||||
end
|
||||
line = table.concat(block, '\n')
|
||||
end
|
||||
local parse_ok, parsed = pcall(api.nvim_parse_cmd, line:match('^[^\n]*'), {})
|
||||
if parse_ok and not line:find('\n') and exit_cmds[parsed.cmd] then
|
||||
vim.fn.histadd('cmd', line)
|
||||
cmdwin._cleanup() -- Focuses the caller window.
|
||||
vim.cmd.stopinsert()
|
||||
if parsed.cmd ~= 'visual' and parsed.cmd ~= 'view' or #parsed.args > 0 then
|
||||
--- @type boolean, string?
|
||||
local ok, err = pcall(vim.cmd --[[@as function]], line) -- May exit Nvim.
|
||||
if not ok then
|
||||
util.echo_err(util.cmd_errmsg(tostring(err)))
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local out = {} --- @type string[]
|
||||
local prev_lnum = api.nvim_win_get_cursor(caller)[1]
|
||||
local prev_tick = vim.b[api.nvim_win_get_buf(caller)].changedtick
|
||||
if line == '' then
|
||||
-- Empty line: advance one line (POSIX ex "+"); auto-print below shows it.
|
||||
if prev_lnum >= api.nvim_buf_line_count(api.nvim_win_get_buf(caller)) then
|
||||
put(out, N_('E501: At end-of-file'))
|
||||
else
|
||||
api.nvim_win_set_cursor(caller, { prev_lnum + 1, 0 })
|
||||
end
|
||||
else
|
||||
local ok, res = pcall(api.nvim_win_call, caller, function()
|
||||
return api.nvim_exec2(line, { output = true })
|
||||
end)
|
||||
if ok then
|
||||
put(out, res.output)
|
||||
else
|
||||
put(out, util.cmd_errmsg(tostring(res)))
|
||||
end
|
||||
vim.fn.histadd('cmd', line)
|
||||
end
|
||||
|
||||
if not api.nvim_win_is_valid(caller) then
|
||||
-- The command closed the caller window (e.g. ":close"): continue Ex mode against a
|
||||
-- remaining window, or end it.
|
||||
local wins = vim.tbl_filter(
|
||||
--- @param w integer
|
||||
function(w)
|
||||
return api.nvim_win_get_buf(w) ~= buf
|
||||
end,
|
||||
api.nvim_list_wins()
|
||||
)
|
||||
if #wins == 0 then
|
||||
vim.cmd.quit() -- Only the cmdwin is left: exit.
|
||||
return
|
||||
end
|
||||
caller = wins[1]
|
||||
end
|
||||
|
||||
-- Ex-mode feature: auto-print the current line after a cursor move or buffer change, unless the
|
||||
-- cmd already printed something (e.g. ":5,6print" should not redundantly auto-print).
|
||||
local typed_text = parse_ok
|
||||
and (parsed.cmd == 'append' or parsed.cmd == 'insert' or parsed.cmd == 'change')
|
||||
local cbuf = api.nvim_win_get_buf(caller)
|
||||
local cur = api.nvim_win_get_cursor(caller)[1]
|
||||
if
|
||||
#out == 0
|
||||
and not typed_text
|
||||
-- Bare range (":1") prints even when the cursor is already on line 1.
|
||||
and (
|
||||
(parse_ok and parsed.cmd == '')
|
||||
or cur ~= prev_lnum
|
||||
or vim.b[cbuf].changedtick ~= prev_tick
|
||||
)
|
||||
then
|
||||
put(out, api.nvim_buf_get_lines(cbuf, cur - 1, cur, false)[1] or '')
|
||||
end
|
||||
|
||||
-- Keep-open: record the transcript and park the cursor on a fresh last line.
|
||||
out[#out + 1] = ''
|
||||
api.nvim_buf_set_lines(buf, -1, -1, false, out)
|
||||
block_start = api.nvim_buf_line_count(buf)
|
||||
api.nvim_win_set_cursor(0, { block_start, 0 })
|
||||
end
|
||||
|
||||
vim.keymap.set({ 'n', 'i' }, '<CR>', run, { buffer = buf })
|
||||
vim.keymap.set({ 'n', 'i' }, '<NL>', run, { buffer = buf })
|
||||
-- Enter Insert mode before any already-typed keys, so `gQcmd<CR>` types "cmd" into the REPL.
|
||||
api.nvim_feedkeys('i', 'ni', false)
|
||||
end
|
||||
|
||||
return M
|
||||
@ -210,9 +210,8 @@ function M.ex_session_restart(eap, extra)
|
||||
vim.fs.rm(session, { force = true })
|
||||
|
||||
-- Trim error message to be equivalent to `:restart!`
|
||||
local trimmed_msg = msg:match('Vim:.*$')
|
||||
if trimmed_msg then
|
||||
util.echo_err(trimmed_msg:sub(5))
|
||||
if msg:find('Vim:') then
|
||||
util.echo_err(util.cmd_errmsg(msg))
|
||||
else
|
||||
error(msg)
|
||||
end
|
||||
|
||||
@ -166,6 +166,18 @@ function M.get_forge_url(repo, target, target_type)
|
||||
return ('%s/%s/%s'):format(repo, middle, target)
|
||||
end
|
||||
|
||||
--- Gets a scrubbed message from a pcall'd command error (drops Lua context/traceback):
|
||||
--- "…/editor.lua:123: Vim(put):E484: xx" => "E484: xx"
|
||||
---
|
||||
--- @param err string
|
||||
--- @return string
|
||||
function M.cmd_errmsg(err)
|
||||
err = err:match('^[^\n]*') or err
|
||||
--- @type string
|
||||
err = err:match('Vim%b():%s*(.*)') or err:match('Vim:%s*(.*)') or (err:gsub('^.-:%d+:%s*', ''))
|
||||
return (err:gsub('^Lua:%s*', ''))
|
||||
end
|
||||
|
||||
--- Utility function for displaying vim error codes (EXX)
|
||||
--- @param msg string
|
||||
function M.echo_err(msg)
|
||||
|
||||
@ -202,6 +202,13 @@ function vim._core.ui_flush() end
|
||||
--- @return boolean
|
||||
function vim._core.check_interrupt() end
|
||||
|
||||
--- @nodoc
|
||||
--- Executes one Ex command line obtained from `getline`, which is also called for any
|
||||
--- continuation lines (`:append` text, `:function` body, heredoc, …). See `vim._core.exmode`.
|
||||
--- @param getline fun(): string?
|
||||
--- @return boolean # false if {getline} returned nil before any line was read (EOF).
|
||||
function vim._core.ex_docmd(getline) end
|
||||
|
||||
--- @nodoc
|
||||
--- Parses `keys` (internal representation) into a list of key chords. See |vim.keycode()|.
|
||||
--- @param keys string
|
||||
|
||||
5
runtime/lua/vim/_meta/vimfn.gen.lua
generated
5
runtime/lua/vim/_meta/vimfn.gen.lua
generated
@ -3644,7 +3644,7 @@ function vim.fn.getpid() end
|
||||
--- 'x Position of mark x (if the mark is not set, 0 is
|
||||
--- returned for all values).
|
||||
--- w0 First line visible in current window (one if the
|
||||
--- display isn't updated, e.g. in silent Ex mode).
|
||||
--- display isn't updated, e.g. in |silent-mode|).
|
||||
--- w$ Last line visible in current window (this is one
|
||||
--- less than "w0" if no lines are visible).
|
||||
--- v End of the current Visual selection (unlike |'<|
|
||||
@ -6573,8 +6573,7 @@ function vim.fn.mkdir(name, flags, prot) end
|
||||
--- Rvx Virtual Replace mode |i_CTRL-X| completion
|
||||
--- c Command-line editing
|
||||
--- cr Command-line editing overstrike mode |c_<Insert>|
|
||||
--- cv Vim Ex mode |gQ|
|
||||
--- cvr Vim Ex mode while in overstrike mode |c_<Insert>|
|
||||
--- cv Non-interactive Ex mode |-es|
|
||||
--- r Hit-enter prompt
|
||||
--- rm The -- more -- prompt
|
||||
--- r? A |:confirm| query of some sort
|
||||
|
||||
@ -1717,13 +1717,11 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|
||||
}
|
||||
|
||||
const int save_did_emsg = did_emsg;
|
||||
const bool save_ex_pressedreturn = get_pressedreturn();
|
||||
|
||||
// Execute the autocmd. The `getnextac` callback handles iteration.
|
||||
do_cmdline(NULL, getnextac, &patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT);
|
||||
|
||||
did_emsg += save_did_emsg;
|
||||
set_pressedreturn(save_ex_pressedreturn);
|
||||
|
||||
if (nesting == 1) {
|
||||
// restore cursor and topline, unless they were changed
|
||||
|
||||
@ -1013,10 +1013,6 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
||||
write_info.bw_conv_error_lnum = 0;
|
||||
write_info.bw_iconv_fd = (iconv_t)-1;
|
||||
|
||||
// After writing a file changedtick changes but we don't want to display
|
||||
// the line.
|
||||
ex_no_reprint = true;
|
||||
|
||||
// If there is no file name yet, use the one for the written file.
|
||||
// BF_NOTEDITED is set to reflect this (in case the write fails).
|
||||
// Don't do this when the write is for a filter command.
|
||||
|
||||
@ -370,9 +370,8 @@ void screen_resize(int width, int height)
|
||||
// - While at the more prompt or executing an external command, don't
|
||||
// redraw, but position the cursor.
|
||||
// - While editing the command line, only redraw that. TODO: lies
|
||||
// - in Ex mode, don't redraw anything.
|
||||
// - Otherwise, redraw right now, and position the cursor.
|
||||
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || exmode_active
|
||||
if (State == MODE_ASKMORE || State == MODE_EXTERNCMD
|
||||
|| ((State & MODE_CMDLINE) && get_cmdline_info()->one_key)) {
|
||||
if (State & MODE_CMDLINE) {
|
||||
update_screen();
|
||||
|
||||
@ -5022,7 +5022,6 @@ void timer_due_cb(TimeWatcher *tw, void *data)
|
||||
timer_T *timer = (timer_T *)data;
|
||||
int save_did_emsg = did_emsg;
|
||||
const int called_emsg_before = called_emsg;
|
||||
const bool save_ex_pressedreturn = get_pressedreturn();
|
||||
|
||||
if (timer->stopped || timer->paused) {
|
||||
return;
|
||||
@ -5049,7 +5048,6 @@ void timer_due_cb(TimeWatcher *tw, void *data)
|
||||
}
|
||||
}
|
||||
did_emsg = save_did_emsg;
|
||||
set_pressedreturn(save_ex_pressedreturn);
|
||||
|
||||
if (timer->emsg_count >= 3) {
|
||||
timer_stop(timer);
|
||||
|
||||
@ -4487,7 +4487,7 @@ M.funcs = {
|
||||
'x Position of mark x (if the mark is not set, 0 is
|
||||
returned for all values).
|
||||
w0 First line visible in current window (one if the
|
||||
display isn't updated, e.g. in silent Ex mode).
|
||||
display isn't updated, e.g. in |silent-mode|).
|
||||
w$ Last line visible in current window (this is one
|
||||
less than "w0" if no lines are visible).
|
||||
v End of the current Visual selection (unlike |'<|
|
||||
@ -7943,8 +7943,7 @@ M.funcs = {
|
||||
Rvx Virtual Replace mode |i_CTRL-X| completion
|
||||
c Command-line editing
|
||||
cr Command-line editing overstrike mode |c_<Insert>|
|
||||
cv Vim Ex mode |gQ|
|
||||
cvr Vim Ex mode while in overstrike mode |c_<Insert>|
|
||||
cv Non-interactive Ex mode |-es|
|
||||
r Hit-enter prompt
|
||||
rm The -- more -- prompt
|
||||
r? A |:confirm| query of some sort
|
||||
|
||||
@ -2881,10 +2881,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
|
||||
} else {
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
}
|
||||
} else { // no line number, go to last line in Ex mode
|
||||
if (exmode_active) {
|
||||
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
|
||||
}
|
||||
} else {
|
||||
beginline(BL_WHITE | BL_FIX);
|
||||
}
|
||||
}
|
||||
@ -3001,6 +2998,7 @@ void ex_append(exarg_T *eap)
|
||||
lnum = 0;
|
||||
}
|
||||
|
||||
const int save_State = State;
|
||||
State = MODE_INSERT; // behave like in Insert mode
|
||||
if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
|
||||
State |= MODE_LANGMAP;
|
||||
@ -3039,13 +3037,13 @@ void ex_append(exarg_T *eap)
|
||||
}
|
||||
eap->nextcmd = p;
|
||||
} else {
|
||||
int save_State = State;
|
||||
int getline_State = State;
|
||||
// Set State to avoid the cursor shape to be set to MODE_INSERT
|
||||
// state when getline() returns.
|
||||
State = MODE_CMDLINE;
|
||||
theline = eap->ea_getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL,
|
||||
eap->cookie, indent, true);
|
||||
State = save_State;
|
||||
State = getline_State;
|
||||
}
|
||||
lines_left = Rows - 1;
|
||||
if (theline == NULL) {
|
||||
@ -3092,7 +3090,7 @@ void ex_append(exarg_T *eap)
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
State = MODE_NORMAL;
|
||||
State = save_State;
|
||||
ui_cursor_shape();
|
||||
|
||||
if (eap->forceit) {
|
||||
@ -3117,7 +3115,6 @@ void ex_append(exarg_T *eap)
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
|
||||
need_wait_return = false; // don't use wait_return() now
|
||||
ex_no_reprint = true;
|
||||
}
|
||||
|
||||
/// ":change"
|
||||
@ -3268,7 +3265,6 @@ void ex_z(exarg_T *eap)
|
||||
curwin->w_cursor.lnum = curs;
|
||||
curwin->w_cursor.col = 0;
|
||||
}
|
||||
ex_no_reprint = true;
|
||||
}
|
||||
|
||||
/// @return true if the secure flag is set and also give an error message.
|
||||
@ -3946,111 +3942,72 @@ static int do_sub(exarg_T *eap, proftime_T tm, const int cmdpreview_ns,
|
||||
|
||||
// Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
|
||||
while (subflags.do_ask) {
|
||||
if (exmode_active) {
|
||||
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
|
||||
String orig_line = STRING_INIT;
|
||||
int len_change = 0;
|
||||
const bool save_p_lz = p_lz;
|
||||
int save_p_fen = curwin->w_p_fen;
|
||||
|
||||
colnr_T sc, ec;
|
||||
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL, 0);
|
||||
curwin->w_cursor.col = MAX(regmatch.endpos[0].col - 1, 0);
|
||||
curwin->w_p_fen = false;
|
||||
// Invert the matched string.
|
||||
// Remove the inversion afterwards.
|
||||
int temp = RedrawingDisabled;
|
||||
RedrawingDisabled = 0;
|
||||
|
||||
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec, 0);
|
||||
curwin->w_cursor.col = regmatch.startpos[0].col;
|
||||
if (subflags.do_number || curwin->w_p_nu) {
|
||||
int numw = number_width(curwin) + 1;
|
||||
sc += numw;
|
||||
ec += numw;
|
||||
}
|
||||
// avoid calling update_screen() in vgetorpeek()
|
||||
p_lz = false;
|
||||
|
||||
char *prompt = xmallocz((size_t)ec + 1);
|
||||
memset(prompt, ' ', (size_t)sc);
|
||||
memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
|
||||
char *resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL,
|
||||
CALLBACK_NONE, false, NULL);
|
||||
if (!ui_has(kUIMessages)) {
|
||||
msg_putchar('\n');
|
||||
}
|
||||
xfree(prompt);
|
||||
if (resp != NULL) {
|
||||
typed = (uint8_t)(*resp);
|
||||
xfree(resp);
|
||||
} else {
|
||||
// getcmdline_prompt() returns NULL if there is no command line to return.
|
||||
typed = NUL;
|
||||
}
|
||||
// When ":normal" runs out of characters we get
|
||||
// an empty line. Use "q" to get out of the
|
||||
// loop.
|
||||
if (ex_normal_busy && typed == NUL) {
|
||||
typed = 'q';
|
||||
}
|
||||
} else {
|
||||
String orig_line = STRING_INIT;
|
||||
int len_change = 0;
|
||||
const bool save_p_lz = p_lz;
|
||||
int save_p_fen = curwin->w_p_fen;
|
||||
if (new_start.data != NULL) {
|
||||
// There already was a substitution, we would
|
||||
// like to show this to the user. We cannot
|
||||
// really update the line, it would change
|
||||
// what matches. Temporarily replace the line
|
||||
// and change it back afterwards.
|
||||
orig_line = cbuf_to_string(ml_get(lnum), (size_t)ml_get_len(lnum));
|
||||
String new_line = {
|
||||
.data = concat_str(new_start.data, sub_firstline.data + copycol),
|
||||
.size = new_start.size + (sub_firstline.size - (size_t)copycol),
|
||||
};
|
||||
|
||||
curwin->w_p_fen = false;
|
||||
// Invert the matched string.
|
||||
// Remove the inversion afterwards.
|
||||
int temp = RedrawingDisabled;
|
||||
RedrawingDisabled = 0;
|
||||
// Position the cursor relative to the end of the line, the
|
||||
// previous substitute may have inserted or deleted characters
|
||||
// before the cursor.
|
||||
len_change = (int)new_line.size - (int)orig_line.size;
|
||||
curwin->w_cursor.col += len_change;
|
||||
ml_replace(lnum, new_line.data, false);
|
||||
}
|
||||
|
||||
// avoid calling update_screen() in vgetorpeek()
|
||||
p_lz = false;
|
||||
Search.match_lines = regmatch.endpos[0].lnum - regmatch.startpos[0].lnum;
|
||||
Search.match_endcol = regmatch.endpos[0].col + len_change;
|
||||
if (Search.match_lines == 0 && Search.match_endcol == 0) {
|
||||
// highlight at least one character for /^/
|
||||
Search.match_endcol = 1;
|
||||
}
|
||||
Search.hl_match = true;
|
||||
|
||||
if (new_start.data != NULL) {
|
||||
// There already was a substitution, we would
|
||||
// like to show this to the user. We cannot
|
||||
// really update the line, it would change
|
||||
// what matches. Temporarily replace the line
|
||||
// and change it back afterwards.
|
||||
orig_line = cbuf_to_string(ml_get(lnum), (size_t)ml_get_len(lnum));
|
||||
String new_line = {
|
||||
.data = concat_str(new_start.data, sub_firstline.data + copycol),
|
||||
.size = new_start.size + (sub_firstline.size - (size_t)copycol),
|
||||
};
|
||||
update_topline(curwin);
|
||||
validate_cursor(curwin);
|
||||
redraw_later(curwin, UPD_SOME_VALID);
|
||||
show_cursor_info_later(true);
|
||||
update_screen();
|
||||
redraw_later(curwin, UPD_SOME_VALID);
|
||||
|
||||
// Position the cursor relative to the end of the line, the
|
||||
// previous substitute may have inserted or deleted characters
|
||||
// before the cursor.
|
||||
len_change = (int)new_line.size - (int)orig_line.size;
|
||||
curwin->w_cursor.col += len_change;
|
||||
ml_replace(lnum, new_line.data, false);
|
||||
}
|
||||
curwin->w_p_fen = save_p_fen;
|
||||
|
||||
Search.match_lines = regmatch.endpos[0].lnum - regmatch.startpos[0].lnum;
|
||||
Search.match_endcol = regmatch.endpos[0].col + len_change;
|
||||
if (Search.match_lines == 0 && Search.match_endcol == 0) {
|
||||
// highlight at least one character for /^/
|
||||
Search.match_endcol = 1;
|
||||
}
|
||||
Search.hl_match = true;
|
||||
char *p = _("replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)");
|
||||
snprintf(IObuff, IOSIZE, p, sub);
|
||||
p = xstrdup(IObuff);
|
||||
typed = prompt_for_input(p, HLF_R, true, NULL);
|
||||
Search.hl_match = false;
|
||||
xfree(p);
|
||||
|
||||
update_topline(curwin);
|
||||
validate_cursor(curwin);
|
||||
redraw_later(curwin, UPD_SOME_VALID);
|
||||
show_cursor_info_later(true);
|
||||
update_screen();
|
||||
redraw_later(curwin, UPD_SOME_VALID);
|
||||
msg_didout = false; // don't scroll up
|
||||
gotocmdline(true);
|
||||
p_lz = save_p_lz;
|
||||
RedrawingDisabled = temp;
|
||||
|
||||
curwin->w_p_fen = save_p_fen;
|
||||
|
||||
char *p = _("replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)");
|
||||
snprintf(IObuff, IOSIZE, p, sub);
|
||||
p = xstrdup(IObuff);
|
||||
typed = prompt_for_input(p, HLF_R, true, NULL);
|
||||
Search.hl_match = false;
|
||||
xfree(p);
|
||||
|
||||
msg_didout = false; // don't scroll up
|
||||
gotocmdline(true);
|
||||
p_lz = save_p_lz;
|
||||
RedrawingDisabled = temp;
|
||||
|
||||
// restore the line
|
||||
if (orig_line.data != NULL) {
|
||||
ml_replace(lnum, orig_line.data, false);
|
||||
}
|
||||
// restore the line
|
||||
if (orig_line.data != NULL) {
|
||||
ml_replace(lnum, orig_line.data, false);
|
||||
}
|
||||
|
||||
need_wait_return = false; // no hit-return prompt
|
||||
|
||||
@ -145,7 +145,6 @@ static const char e_no_script_file_name_to_substitute_for_script[]
|
||||
= N_("E1274: No script file name to substitute for \"<script>\"");
|
||||
|
||||
static int quitmore = 0;
|
||||
static bool ex_pressedreturn = false;
|
||||
|
||||
// Struct for storing a line inside a while/for loop
|
||||
typedef struct {
|
||||
@ -263,77 +262,6 @@ static bool is_other_file(int fnum, char *ffname)
|
||||
return otherfile(ffname);
|
||||
}
|
||||
|
||||
/// Repeatedly get commands for Ex mode, until the ":vi" command is given.
|
||||
void do_exmode(void)
|
||||
{
|
||||
exmode_active = true;
|
||||
State = MODE_NORMAL;
|
||||
may_trigger_modechanged();
|
||||
|
||||
// When using ":global /pat/ visual" and then "Q" we return to continue
|
||||
// the :global command.
|
||||
if (global_busy) {
|
||||
return;
|
||||
}
|
||||
|
||||
int save_msg_scroll = msg_scroll;
|
||||
RedrawingDisabled++; // don't redisplay the window
|
||||
no_wait_return++; // don't wait for return
|
||||
|
||||
msg(_("Entering Ex mode. Type \"visual\" to go to Normal mode."), 0);
|
||||
while (exmode_active) {
|
||||
// Check for a ":normal" command and no more characters left.
|
||||
if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
|
||||
exmode_active = false;
|
||||
break;
|
||||
}
|
||||
msg_scroll = true;
|
||||
need_wait_return = false;
|
||||
ex_pressedreturn = false;
|
||||
ex_no_reprint = false;
|
||||
varnumber_T changedtick = buf_get_changedtick(curbuf);
|
||||
int prev_msg_row = msg_row;
|
||||
linenr_T prev_line = curwin->w_cursor.lnum;
|
||||
cmdline_row = msg_row;
|
||||
do_cmdline(NULL, getexline, NULL, 0);
|
||||
lines_left = Rows - 1;
|
||||
|
||||
if ((prev_line != curwin->w_cursor.lnum
|
||||
|| changedtick != buf_get_changedtick(curbuf)) && !ex_no_reprint) {
|
||||
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
|
||||
emsg(_(e_empty_buffer));
|
||||
} else {
|
||||
if (ex_pressedreturn) {
|
||||
// Make sure the message overwrites the right line and isn't throttled.
|
||||
msg_scroll_flush();
|
||||
// go up one line, to overwrite the ":<CR>" line, so the
|
||||
// output doesn't contain empty lines.
|
||||
msg_row = prev_msg_row;
|
||||
if (prev_msg_row == Rows - 1) {
|
||||
msg_row--;
|
||||
}
|
||||
}
|
||||
msg_col = 0;
|
||||
print_line_no_prefix(curwin->w_cursor.lnum, false, false);
|
||||
msg_clr_eos();
|
||||
}
|
||||
} else if (ex_pressedreturn && !ex_no_reprint) { // must be at EOF
|
||||
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
|
||||
emsg(_(e_empty_buffer));
|
||||
} else {
|
||||
emsg(_("E501: At end-of-file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RedrawingDisabled--;
|
||||
no_wait_return--;
|
||||
redraw_all_later(UPD_NOT_VALID);
|
||||
update_screen();
|
||||
need_wait_return = false;
|
||||
msg_scroll = save_msg_scroll;
|
||||
}
|
||||
|
||||
/// Print the executed command for when 'verbose' is set.
|
||||
///
|
||||
/// @param lnum if 0, only print the command.
|
||||
@ -1545,8 +1473,6 @@ bool parse_cmdline(char **cmdline, exarg_T *eap, cmdmod_T *cmod, const char **er
|
||||
{
|
||||
char *after_modifier = NULL;
|
||||
bool retval = false;
|
||||
// parsing the command modifiers may set ex_pressedreturn
|
||||
const bool save_ex_pressedreturn = ex_pressedreturn;
|
||||
// parsing the command range may require moving the cursor
|
||||
const pos_T save_cursor = curwin->w_cursor;
|
||||
// parsing the command range may set the last search pattern
|
||||
@ -1694,7 +1620,6 @@ end:
|
||||
if (!retval) {
|
||||
undo_cmdmod(cmod);
|
||||
}
|
||||
ex_pressedreturn = save_ex_pressedreturn;
|
||||
curwin->w_cursor = save_cursor;
|
||||
restore_last_search_pattern();
|
||||
return retval;
|
||||
@ -2258,7 +2183,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
||||
// When global command is busy, don't ask, will fail below.
|
||||
if (!global_busy && ea.line1 > ea.line2) {
|
||||
if (msg_silent == 0) {
|
||||
if ((flags & DOCMD_VERBOSE) || exmode_active) {
|
||||
if ((flags & DOCMD_VERBOSE) || silent_mode) {
|
||||
errormsg = _("E493: Backwards range given");
|
||||
goto doend;
|
||||
}
|
||||
@ -2480,17 +2405,13 @@ char *ex_errmsg(const char *const msg, ...)
|
||||
return ex_error_buf;
|
||||
}
|
||||
|
||||
/// The "+" string used in place of an empty command in Ex mode.
|
||||
/// This string is used in pointer comparison.
|
||||
static const char exmode_plus[] = "+";
|
||||
|
||||
/// Handle a range without a command.
|
||||
/// Returns an error message on failure.
|
||||
static char *ex_range_without_command(exarg_T *eap)
|
||||
{
|
||||
char *errormsg = NULL;
|
||||
|
||||
if (*eap->cmd == '|' || (exmode_active && eap->cmd != exmode_plus + 1)) {
|
||||
if (*eap->cmd == '|') {
|
||||
eap->cmdidx = CMD_print;
|
||||
eap->argt = EX_RANGE | EX_COUNT | EX_TRLBAR;
|
||||
if ((errormsg = invalid_range(eap)) == NULL) {
|
||||
@ -2517,11 +2438,9 @@ static char *ex_range_without_command(exarg_T *eap)
|
||||
/// Parse and skip over command modifiers:
|
||||
/// - update eap->cmd
|
||||
/// - store flags in "cmod".
|
||||
/// - Set ex_pressedreturn for an empty command line.
|
||||
///
|
||||
/// @param skip_only if false, undo_cmdmod() must be called later to free
|
||||
/// any cmod_filter_pat and cmod_filter_regmatch.regprog,
|
||||
/// and ex_pressedreturn may be set.
|
||||
/// any cmod_filter_pat and cmod_filter_regmatch.regprog.
|
||||
/// @param[out] errormsg potential error message.
|
||||
///
|
||||
/// Call apply_cmdmod() to get the side effects of the modifiers:
|
||||
@ -2535,7 +2454,6 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod,
|
||||
{
|
||||
char *orig_cmd = eap->cmd;
|
||||
char *cmd_start = NULL;
|
||||
bool use_plus_cmd = false;
|
||||
bool has_visual_range = false;
|
||||
CLEAR_POINTER(cmod);
|
||||
|
||||
@ -2561,18 +2479,6 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod,
|
||||
eap->cmd++;
|
||||
}
|
||||
|
||||
// in ex mode, an empty command (after modifiers) works like :+
|
||||
if (*eap->cmd == NUL && exmode_active
|
||||
&& getline_equal(eap->ea_getline, eap->cookie, getexline)
|
||||
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
|
||||
eap->cmd = (char *)exmode_plus;
|
||||
use_plus_cmd = true;
|
||||
if (!skip_only) {
|
||||
ex_pressedreturn = true;
|
||||
}
|
||||
break; // no modifiers following
|
||||
}
|
||||
|
||||
// ignore comment and empty lines
|
||||
if (*eap->cmd == '"') {
|
||||
// a comment ends at a NL
|
||||
@ -2587,9 +2493,6 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod,
|
||||
return FAIL;
|
||||
}
|
||||
if (*eap->cmd == NUL) {
|
||||
if (!skip_only) {
|
||||
ex_pressedreturn = true;
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@ -2798,31 +2701,13 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod,
|
||||
// Since the modifiers have been parsed put the colon on top of the
|
||||
// space: "'<,'>mod cmd" -> "mod:'<,'>cmd
|
||||
// Put eap->cmd after the colon.
|
||||
if (use_plus_cmd) {
|
||||
size_t len = strlen(cmd_start);
|
||||
|
||||
// Special case: empty command uses "+":
|
||||
// "'<,'>mods" -> "mods *+
|
||||
// Use "*" instead of "'<,'>" to avoid the command getting
|
||||
// longer, in case is was allocated.
|
||||
memmove(orig_cmd, cmd_start, len);
|
||||
xmemcpyz(orig_cmd + len, S_LEN(" *+"));
|
||||
} else {
|
||||
memmove(cmd_start - 5, cmd_start, (size_t)(eap->cmd - cmd_start));
|
||||
eap->cmd -= 5;
|
||||
memmove(eap->cmd - 1, ":'<,'>", 6);
|
||||
}
|
||||
memmove(cmd_start - 5, cmd_start, (size_t)(eap->cmd - cmd_start));
|
||||
eap->cmd -= 5;
|
||||
memmove(eap->cmd - 1, ":'<,'>", 6);
|
||||
} else {
|
||||
// No modifiers, move the pointer back.
|
||||
// Special case: change empty command to "+".
|
||||
if (use_plus_cmd) {
|
||||
eap->cmd = "'<,'>+";
|
||||
} else {
|
||||
eap->cmd = orig_cmd;
|
||||
}
|
||||
eap->cmd = orig_cmd;
|
||||
}
|
||||
} else if (use_plus_cmd) {
|
||||
eap->cmd = (char *)exmode_plus;
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -5557,8 +5442,6 @@ static void ex_print(exarg_T *eap)
|
||||
curwin->w_cursor.lnum = eap->line2;
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
}
|
||||
|
||||
ex_no_reprint = true;
|
||||
}
|
||||
|
||||
static void ex_goto(exarg_T *eap)
|
||||
@ -6149,43 +6032,6 @@ static void ex_edit(exarg_T *eap)
|
||||
/// @param old_curwin curwin before doing a split or NULL
|
||||
void do_exedit(exarg_T *eap, win_T *old_curwin)
|
||||
{
|
||||
// ":vi" command ends Ex mode.
|
||||
if (exmode_active && (eap->cmdidx == CMD_visual
|
||||
|| eap->cmdidx == CMD_view)) {
|
||||
exmode_active = false;
|
||||
ex_pressedreturn = false;
|
||||
if (ui_has(kUICmdline)) {
|
||||
ui_ext_cmdline_block_leave();
|
||||
}
|
||||
if (*eap->arg == NUL) {
|
||||
// Special case: ":global/pat/visual\NLvi-commands"
|
||||
if (global_busy) {
|
||||
if (eap->nextcmd != NULL) {
|
||||
stuffReadbuff(eap->nextcmd);
|
||||
eap->nextcmd = NULL;
|
||||
}
|
||||
|
||||
const int save_rd = RedrawingDisabled;
|
||||
RedrawingDisabled = 0;
|
||||
const int save_nwr = no_wait_return;
|
||||
no_wait_return = 0;
|
||||
need_wait_return = false;
|
||||
const int save_ms = msg_scroll;
|
||||
msg_scroll = 0;
|
||||
redraw_all_later(UPD_NOT_VALID);
|
||||
pending_exmode_active = true;
|
||||
|
||||
normal_enter(true);
|
||||
|
||||
pending_exmode_active = false;
|
||||
RedrawingDisabled = save_rd;
|
||||
no_wait_return = save_nwr;
|
||||
msg_scroll = save_ms;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((eap->cmdidx == CMD_new
|
||||
|| eap->cmdidx == CMD_tabnew
|
||||
|| eap->cmdidx == CMD_tabedit
|
||||
@ -6267,8 +6113,6 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
|
||||
&& (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
|
||||
old_curwin->w_alt_fnum = curbuf->b_fnum;
|
||||
}
|
||||
|
||||
ex_no_reprint = true;
|
||||
}
|
||||
|
||||
/// ":gui" and ":gvim" when there is no GUI.
|
||||
@ -6348,8 +6192,6 @@ static void ex_syncbind(exarg_T *eap)
|
||||
|
||||
static void ex_read(exarg_T *eap)
|
||||
{
|
||||
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
|
||||
|
||||
if (eap->usefilter) { // :r!cmd
|
||||
do_bang(1, eap, false, false, true);
|
||||
return;
|
||||
@ -6378,24 +6220,6 @@ static void ex_read(exarg_T *eap)
|
||||
semsg(_(e_notopen), eap->arg);
|
||||
}
|
||||
} else {
|
||||
if (empty && exmode_active) {
|
||||
// Delete the empty line that remains. Historically ex does
|
||||
// this but vi doesn't.
|
||||
linenr_T lnum;
|
||||
if (eap->line2 == 0) {
|
||||
lnum = curbuf->b_ml.ml_line_count;
|
||||
} else {
|
||||
lnum = 1;
|
||||
}
|
||||
if (*ml_get(lnum) == NUL && u_savedel(lnum, 1) == OK) {
|
||||
ml_delete(lnum);
|
||||
if (curwin->w_cursor.lnum > 1
|
||||
&& curwin->w_cursor.lnum >= lnum) {
|
||||
curwin->w_cursor.lnum--;
|
||||
}
|
||||
deleted_lines_mark(lnum, 1);
|
||||
}
|
||||
}
|
||||
redraw_curbuf_later(UPD_VALID);
|
||||
}
|
||||
}
|
||||
@ -6817,7 +6641,6 @@ void ex_may_print(exarg_T *eap)
|
||||
if (eap->flags != 0) {
|
||||
print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR),
|
||||
(eap->flags & EXFLAG_LIST), true);
|
||||
ex_no_reprint = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8237,17 +8060,6 @@ bool is_loclist_cmd(int cmdidx)
|
||||
return cmdnames[cmdidx].cmd_name[0] == 'l';
|
||||
}
|
||||
|
||||
bool get_pressedreturn(void)
|
||||
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
return ex_pressedreturn;
|
||||
}
|
||||
|
||||
void set_pressedreturn(bool val)
|
||||
{
|
||||
ex_pressedreturn = val;
|
||||
}
|
||||
|
||||
/// ":checkhealth [plugins]"
|
||||
static void ex_checkhealth(exarg_T *eap)
|
||||
{
|
||||
|
||||
@ -860,7 +860,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
|
||||
|
||||
// Redraw the statusline in case it uses the current mode using the mode()
|
||||
// function.
|
||||
if (!cmd_silent && !exmode_active) {
|
||||
if (!cmd_silent) {
|
||||
bool found_one = false;
|
||||
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
@ -1000,10 +1000,6 @@ theend:
|
||||
char *p = ccline.cmdbuff;
|
||||
|
||||
if (ui_has(kUICmdline)) {
|
||||
// Emit cmdline_block in Ex mode unless cmdbuff is NULL (happens with <C-\><C-N> #39021).
|
||||
if (exmode_active && p != NULL) {
|
||||
ui_ext_cmdline_block_append(0, p);
|
||||
}
|
||||
ui_ext_cmdline_hide(s->gotesc);
|
||||
}
|
||||
if (!cmd_silent) {
|
||||
@ -1374,8 +1370,7 @@ static int command_line_execute(VimState *state, int key)
|
||||
// Don't ignore it for the input() function.
|
||||
if ((s->c == Ctrl_C)
|
||||
&& s->firstc != '@'
|
||||
// do clear got_int in Ex mode to avoid infinite Ctrl-C loop
|
||||
&& (!s->break_ctrl_c || exmode_active)
|
||||
&& !s->break_ctrl_c
|
||||
&& !global_busy) {
|
||||
got_int = false;
|
||||
}
|
||||
@ -1481,30 +1476,19 @@ static int command_line_execute(VimState *state, int key)
|
||||
|| s->c == K_KENTER
|
||||
|| (s->c == ESC
|
||||
&& (!KeyTyped || vim_strchr(p_cpo, kCpoEsc) != NULL))) {
|
||||
// In Ex mode a backslash escapes a newline.
|
||||
if (exmode_active
|
||||
&& s->c != ESC
|
||||
&& ccline.cmdpos == ccline.cmdlen
|
||||
&& ccline.cmdpos > 0
|
||||
&& ccline.cmdbuff[ccline.cmdpos - 1] == '\\') {
|
||||
if (s->c == K_KENTER) {
|
||||
s->c = '\n';
|
||||
}
|
||||
} else {
|
||||
s->gotesc = false; // Might have typed ESC previously, don't
|
||||
// truncate the cmdline now.
|
||||
if (ccheck_abbr(s->c + ABBR_OFF)) {
|
||||
return command_line_changed(s);
|
||||
}
|
||||
|
||||
if (!cmd_silent) {
|
||||
if (!ui_has(kUICmdline)) {
|
||||
msg_cursor_goto(msg_row, 0);
|
||||
}
|
||||
ui_flush();
|
||||
}
|
||||
return 0;
|
||||
s->gotesc = false; // Might have typed ESC previously, don't
|
||||
// truncate the cmdline now.
|
||||
if (ccheck_abbr(s->c + ABBR_OFF)) {
|
||||
return command_line_changed(s);
|
||||
}
|
||||
|
||||
if (!cmd_silent) {
|
||||
if (!ui_has(kUICmdline)) {
|
||||
msg_cursor_goto(msg_row, 0);
|
||||
}
|
||||
ui_flush();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Completion for 'wildchar', 'wildcharm', and wildtrigger()
|
||||
@ -1752,8 +1736,8 @@ static int command_line_erase_chars(CommandLineState *s)
|
||||
redrawcmd();
|
||||
} else if (ccline.cmdlen == 0 && s->c != Ctrl_W
|
||||
&& ccline.cmdprompt == NULL && s->indent == 0) {
|
||||
// In ex and debug mode it doesn't make sense to return.
|
||||
if (exmode_active || ccline.cmdfirstc == '>') {
|
||||
// In debug mode it doesn't make sense to return.
|
||||
if (ccline.cmdfirstc == '>') {
|
||||
return CMDLINE_NOT_CHANGED;
|
||||
}
|
||||
|
||||
@ -2085,11 +2069,8 @@ static int command_line_handle_key(CommandLineState *s)
|
||||
|
||||
case ESC: // get here if p_wc != ESC or when ESC typed twice
|
||||
case Ctrl_C:
|
||||
// In exmode it doesn't make sense to return. Except when
|
||||
// ":normal" runs out of characters. Also when highlight callback is active
|
||||
// <C-c> should interrupt only it.
|
||||
if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0))
|
||||
|| (getln_interrupted_highlight && s->c == Ctrl_C)) {
|
||||
// When the highlight callback is active <C-c> should interrupt only it.
|
||||
if (getln_interrupted_highlight && s->c == Ctrl_C) {
|
||||
getln_interrupted_highlight = false;
|
||||
return command_line_not_changed(s);
|
||||
}
|
||||
@ -2824,7 +2805,6 @@ static int command_line_changed(CommandLineState *s)
|
||||
if (s->firstc == ':'
|
||||
&& current_sctx.sc_sid == 0 // only if interactive
|
||||
&& *p_icm != NUL // 'inccommand' is set
|
||||
&& !exmode_active // not in ex mode
|
||||
&& cmdline_star == 0 // not typing a password
|
||||
&& !vpeekc_any()
|
||||
&& cmdpreview_may_show(s)) {
|
||||
@ -4013,7 +3993,7 @@ void redrawcmd(void)
|
||||
|
||||
void compute_cmdrow(void)
|
||||
{
|
||||
if (exmode_active || msg_scrolled != 0) {
|
||||
if (msg_scrolled != 0) {
|
||||
cmdline_row = Rows - 1;
|
||||
} else {
|
||||
win_T *wp = lastwin_nofloating(NULL);
|
||||
|
||||
@ -277,10 +277,6 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
|
||||
using_b_ffname = (fname == curbuf->b_ffname) || (sfname == curbuf->b_ffname);
|
||||
using_b_fname = (fname == curbuf->b_fname) || (sfname == curbuf->b_fname);
|
||||
|
||||
// After reading a file the cursor line changes but we don't want to
|
||||
// display the line.
|
||||
ex_no_reprint = true;
|
||||
|
||||
// don't display the file info for another buffer now
|
||||
need_fileinfo = false;
|
||||
|
||||
@ -1855,13 +1851,8 @@ failed:
|
||||
|
||||
u_clearline(curbuf); // cannot use "U" command after adding lines
|
||||
|
||||
// In Ex mode: cursor at last new line.
|
||||
// Otherwise: cursor at first new line.
|
||||
if (exmode_active) {
|
||||
curwin->w_cursor.lnum = from + linecnt;
|
||||
} else {
|
||||
curwin->w_cursor.lnum = from + 1;
|
||||
}
|
||||
// Cursor at first new line.
|
||||
curwin->w_cursor.lnum = from + 1;
|
||||
check_cursor_lnum(curwin);
|
||||
beginline(BL_WHITE | BL_FIX); // on first non-blank
|
||||
|
||||
|
||||
@ -445,6 +445,8 @@ EXTERN int sandbox INIT( = 0);
|
||||
|
||||
/// Batch-mode: "-es", "-Es", "-l" commandline argument was given.
|
||||
EXTERN bool silent_mode INIT( = false);
|
||||
/// Non-interactive Ex mode ("-es"): stdin is executed as Ex commands. mode() returns "cv".
|
||||
EXTERN bool exmode_active INIT( = false);
|
||||
|
||||
/// Per-subsystem state for Visual/Select mode; see normal_defs.h.
|
||||
EXTERN VisualState Visual INIT( = { .mode = 'v' });
|
||||
@ -505,14 +507,6 @@ EXTERN bool finish_op INIT( = false); // true while an operator is pending
|
||||
EXTERN int opcount INIT( = 0); // count for pending operator
|
||||
EXTERN int motion_force INIT( = 0); // motion force for pending operator
|
||||
|
||||
// Ex Mode (Q) state
|
||||
EXTERN bool exmode_active INIT( = false); // true if Ex mode is active
|
||||
|
||||
/// Flag set when normal_check() should return 0 when entering Ex mode.
|
||||
EXTERN bool pending_exmode_active INIT( = false);
|
||||
|
||||
EXTERN bool ex_no_reprint INIT( = false); // No need to print after z or p.
|
||||
|
||||
// 'inccommand' command preview state
|
||||
EXTERN bool cmdpreview INIT( = false);
|
||||
|
||||
|
||||
@ -644,7 +644,6 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int
|
||||
|| linebuf_attr[col] != grid->attrs[off_to]
|
||||
|| (cols > 1 && linebuf_char[col + 1] == 0
|
||||
&& linebuf_char[col + 1] != grid->chars[off_to + 1]))
|
||||
|| exmode_active // TODO(bfredl): what in the actual fuck
|
||||
|| rdb_flags & kOptRdbFlagNodelta));
|
||||
}
|
||||
|
||||
|
||||
@ -2956,11 +2956,6 @@ static int vgetorpeek(bool advance)
|
||||
typebuf_was_empty = true;
|
||||
}
|
||||
|
||||
// return 0 in normal_check()
|
||||
if (pending_exmode_active) {
|
||||
exmode_active = true;
|
||||
}
|
||||
|
||||
// no chars to block abbreviations for
|
||||
typebuf.tb_no_abbr_cnt = 0;
|
||||
|
||||
@ -2987,7 +2982,7 @@ static int vgetorpeek(bool advance)
|
||||
// to the user with showcmd.
|
||||
int showcmd_idx = 0;
|
||||
bool showing_partial = false;
|
||||
if (typebuf.tb_len > 0 && advance && !exmode_active) {
|
||||
if (typebuf.tb_len > 0 && advance) {
|
||||
if (((State & (MODE_NORMAL | MODE_INSERT)) || State == MODE_LANGMAP)
|
||||
&& State != MODE_HITRETURN) {
|
||||
// this looks nice when typing a dead character map
|
||||
|
||||
@ -99,6 +99,11 @@ typedef struct {
|
||||
size_t size;
|
||||
} ModuleDef;
|
||||
|
||||
typedef struct {
|
||||
LuaRef getline; ///< Lua function returning the next input line, or nil at EOF.
|
||||
bool got_line; ///< True if at least one line was read.
|
||||
} ExDocmdCookie;
|
||||
|
||||
#include "lua/executor.c.generated.h"
|
||||
#include "lua/vim_module.generated.h"
|
||||
|
||||
@ -598,6 +603,44 @@ static int nlua_check_interrupt(lua_State *lstate)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// LineGetter for nlua_ex_docmd(): calls the Lua `getline` function.
|
||||
static char *nlua_ex_getline(int c, void *cookie, int indent, bool do_concat)
|
||||
{
|
||||
ExDocmdCookie *ecd = cookie;
|
||||
lua_State *lstate = global_lstate;
|
||||
nlua_pushref(lstate, ecd->getline);
|
||||
if (nlua_pcall(lstate, 0, 1)) {
|
||||
nlua_error(lstate, _("Error executing Ex-mode line getter: %.*s"));
|
||||
return NULL;
|
||||
}
|
||||
if (lua_type(lstate, -1) != LUA_TSTRING) { // nil: EOF.
|
||||
lua_pop(lstate, 1);
|
||||
return NULL;
|
||||
}
|
||||
char *line = xstrdup(lua_tostring(lstate, -1));
|
||||
lua_pop(lstate, 1);
|
||||
ecd->got_line = true;
|
||||
return line;
|
||||
}
|
||||
|
||||
/// For exmode.lua: Executes one cmdline obtained from `getline`, which is also called for any
|
||||
/// continuation lines (`:append` text, `:function` body, heredoc, …). Unlike `nvim_exec2()`, errors
|
||||
/// are emitted (not thrown), so the caller can continue with the next command. #40966
|
||||
static int nlua_ex_docmd(lua_State *lstate)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
luaL_checktype(lstate, 1, LUA_TFUNCTION);
|
||||
ExDocmdCookie cookie = { .getline = nlua_ref_global(lstate, 1) };
|
||||
msg_scroll = true;
|
||||
need_wait_return = false;
|
||||
no_wait_return++; // Don't wait for return, e.g. after "-V1" messages.
|
||||
do_cmdline(NULL, nlua_ex_getline, &cookie, 0);
|
||||
no_wait_return--;
|
||||
nlua_unref_global(lstate, cookie.getline);
|
||||
lua_pushboolean(lstate, cookie.got_line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int nlua_os_exit(lua_State *lstate)
|
||||
{
|
||||
if (in_fast_callback > 0) {
|
||||
@ -900,7 +943,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
|
||||
nlua_common_vim_init(lstate, false);
|
||||
|
||||
// vim._core wait helpers
|
||||
// Bindings for `vim.wait()`.
|
||||
lua_getfield(lstate, -1, "_core");
|
||||
lua_pushcfunction(lstate, &nlua_loop_poll);
|
||||
lua_setfield(lstate, -2, "loop_poll");
|
||||
@ -908,6 +951,10 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
||||
lua_setfield(lstate, -2, "ui_flush");
|
||||
lua_pushcfunction(lstate, &nlua_check_interrupt);
|
||||
lua_setfield(lstate, -2, "check_interrupt");
|
||||
|
||||
// Misc vim._core bindings.
|
||||
lua_pushcfunction(lstate, &nlua_ex_docmd);
|
||||
lua_setfield(lstate, -2, "ex_docmd");
|
||||
lua_pop(lstate, 1);
|
||||
|
||||
// patch require() (only for --startuptime)
|
||||
|
||||
@ -172,13 +172,11 @@ void event_init(void)
|
||||
static bool event_teardown(void)
|
||||
{
|
||||
if (!main_loop.events) {
|
||||
input_stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
multiqueue_process_events(main_loop.events);
|
||||
loop_poll_events(&main_loop, 0); // Drain thread_events, fast_events.
|
||||
input_stop();
|
||||
server_teardown();
|
||||
channel_teardown();
|
||||
proc_teardown(&main_loop);
|
||||
@ -417,11 +415,6 @@ int main(int argc, char **argv)
|
||||
// Set the break level after the terminal is initialized.
|
||||
debug_break_level = params.use_debug_break_level;
|
||||
|
||||
// Read ex-commands if invoked with "-es".
|
||||
if (!stdin_isatty && !params.input_istext && silent_mode && exmode_active) {
|
||||
input_start();
|
||||
}
|
||||
|
||||
// Wait for UIs to set up Nvim or show early messages
|
||||
// and prompts (--cmd, swapfile dialog, …).
|
||||
bool use_remote_ui = (embedded_mode && !headless_mode);
|
||||
@ -546,9 +539,7 @@ int main(int argc, char **argv)
|
||||
//
|
||||
starting = NO_BUFFERS;
|
||||
no_wait_return = false;
|
||||
if (!exmode_active) {
|
||||
msg_scroll = false;
|
||||
}
|
||||
msg_scroll = false;
|
||||
|
||||
// Read file (text, not commands) from stdin if:
|
||||
// - stdin is not a tty
|
||||
@ -577,7 +568,7 @@ int main(int argc, char **argv)
|
||||
set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
|
||||
|
||||
// Ex starts at last line of the file.
|
||||
if (exmode_active) {
|
||||
if (params.exmode) {
|
||||
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
@ -676,13 +667,35 @@ int main(int argc, char **argv)
|
||||
msg_didout = false;
|
||||
}
|
||||
getout(lua_ok ? 0 : 1);
|
||||
} else if (silent_mode) {
|
||||
// Non-interactive Ex mode (-es): vim._core.exmode.run() executes Ex commands from stdin.
|
||||
msg_scroll = true;
|
||||
no_wait_return++; // No hit-enter prompts in batch mode, e.g. after "-V1" messages.
|
||||
// Read stdin as commands iff a pipe/file: "nvim -es +cmd" in a tty executes and exits, it
|
||||
// doesn't wait for input. ("-Es" reads stdin as text; already consumed by read_stdin().)
|
||||
if (!params.input_istext && !stdin_isatty) {
|
||||
DLOG("executing stdin as Ex commands");
|
||||
typval_T args[] = { { .v_type = VAR_UNKNOWN } };
|
||||
nlua_call_typval("vim._core.exmode", "run", args, NULL);
|
||||
}
|
||||
if (msg_didout) {
|
||||
msg_putchar('\n'); // Terminate the last output line.
|
||||
msg_didout = false;
|
||||
}
|
||||
getout(0); // getout() adds `ex_exitval` for "-es".
|
||||
}
|
||||
|
||||
if (params.exmode) {
|
||||
// Interactive Ex mode (-e/-E): cmdwin REPL. #40962
|
||||
typval_T args[] = { { .v_type = VAR_UNKNOWN } };
|
||||
nlua_call_typval("vim._core.exmode", "open", args, NULL);
|
||||
}
|
||||
|
||||
TIME_MSG("before starting main loop");
|
||||
ILOG("starting main loop");
|
||||
|
||||
// Main loop: never returns.
|
||||
normal_enter(false);
|
||||
normal_enter();
|
||||
|
||||
#if defined(MSWIN) && !defined(MAKE_LIB)
|
||||
xfree(argv);
|
||||
@ -723,9 +736,6 @@ void os_exit(int r)
|
||||
} else {
|
||||
ml_close_all(true); // remove all memfiles
|
||||
}
|
||||
if (used_stdin) {
|
||||
stream_set_blocking(STDIN_FILENO, true); // normalize stream (#2598)
|
||||
}
|
||||
|
||||
ILOG("Nvim exit: %d", r);
|
||||
|
||||
@ -746,7 +756,7 @@ void getout(int exitval)
|
||||
// make sure startuptimes have been flushed
|
||||
time_finish();
|
||||
|
||||
// On error during Ex mode, exit with a non-zero code.
|
||||
// On error in "-es" (or explicit ":quit"), exit with a non-zero code.
|
||||
// POSIX requires this, although it's not 100% clear from the standard.
|
||||
if (exmode_active) {
|
||||
exitval += ex_exitval;
|
||||
@ -882,10 +892,6 @@ void preserve_exit(const char *errmsg)
|
||||
|
||||
// Prevent repeated calls into this method.
|
||||
if (really_exiting) {
|
||||
if (used_stdin) {
|
||||
// normalize stream (#2598)
|
||||
stream_set_blocking(STDIN_FILENO, true);
|
||||
}
|
||||
exit(2);
|
||||
}
|
||||
|
||||
@ -1080,7 +1086,7 @@ static bool edit_stdin(mparm_T *parmp)
|
||||
{
|
||||
bool implicit = !headless_mode
|
||||
&& !(embedded_mode && stdin_fd <= 0)
|
||||
&& (!exmode_active || parmp->input_istext)
|
||||
&& (!parmp->exmode || parmp->input_istext)
|
||||
&& !stdin_isatty
|
||||
&& parmp->edit_type <= EDIT_STDIN
|
||||
&& parmp->scriptin == NULL; // `-s -` was not given.
|
||||
@ -1119,9 +1125,10 @@ static void command_line_scan(mparm_T *parmp)
|
||||
char c = argv[0][argv_idx++];
|
||||
switch (c) {
|
||||
case NUL: // "nvim -" read from stdin
|
||||
if (exmode_active) {
|
||||
if (parmp->exmode) {
|
||||
// "nvim -e -" silent mode
|
||||
silent_mode = true;
|
||||
exmode_active = true;
|
||||
parmp->no_swap_file = true;
|
||||
} else {
|
||||
if (parmp->edit_type > EDIT_STDIN) {
|
||||
@ -1214,10 +1221,10 @@ static void command_line_scan(mparm_T *parmp)
|
||||
parmp->diff_mode = true;
|
||||
break;
|
||||
case 'e': // "-e" Ex mode
|
||||
exmode_active = true;
|
||||
parmp->exmode = true;
|
||||
break;
|
||||
case 'E': // "-E" Ex mode
|
||||
exmode_active = true;
|
||||
parmp->exmode = true;
|
||||
parmp->input_istext = true;
|
||||
break;
|
||||
case 'f': // "-f" GUI: run in foreground.
|
||||
@ -1282,8 +1289,9 @@ static void command_line_scan(mparm_T *parmp)
|
||||
recoverymode = 1;
|
||||
break;
|
||||
case 's':
|
||||
if (exmode_active) { // "-es" silent (batch) Ex-mode
|
||||
if (parmp->exmode) { // "-es" silent (batch) mode
|
||||
silent_mode = true;
|
||||
exmode_active = true;
|
||||
parmp->no_swap_file = true;
|
||||
if (p_shadafile == NULL || *p_shadafile == NUL) {
|
||||
set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
|
||||
@ -2025,9 +2033,7 @@ static void exe_commands(mparm_T *parmp)
|
||||
curwin->w_cursor.lnum = 1;
|
||||
}
|
||||
|
||||
if (!exmode_active) {
|
||||
msg_scroll = false;
|
||||
}
|
||||
msg_scroll = false;
|
||||
|
||||
// When started with "-q errorfile" jump to first error again.
|
||||
if (parmp->edit_type == EDIT_QF) {
|
||||
|
||||
@ -29,6 +29,7 @@ typedef struct {
|
||||
char *tagname; // tag from -t argument
|
||||
char *use_ef; // 'errorfile' from -q argument
|
||||
|
||||
bool exmode; // "-e"/"-E": Ex mode
|
||||
bool input_istext; // stdin is text, not executable (-E/-Es)
|
||||
|
||||
int no_swap_file; // "-n" argument used
|
||||
|
||||
@ -502,7 +502,7 @@ char *msg_strtrunc(const char *s, int force)
|
||||
|
||||
// May truncate message to avoid a hit-return prompt
|
||||
if ((!msg_scroll && !need_wait_return && shortmess(kShmTruncall)
|
||||
&& !exmode_active && msg_silent == 0 && !ui_has(kUIMessages))
|
||||
&& msg_silent == 0 && !ui_has(kUIMessages))
|
||||
|| force) {
|
||||
int room;
|
||||
int len = vim_strsize(s);
|
||||
@ -1072,7 +1072,7 @@ char *msg_may_trunc(bool force, char *s)
|
||||
// just in case.
|
||||
int room = (Rows - cmdline_row - 1) * Columns + sc_col - 1;
|
||||
if (room > 0
|
||||
&& (force || (shortmess(kShmTrunc) && !exmode_active))
|
||||
&& (force || shortmess(kShmTrunc))
|
||||
&& (int)strlen(s) - room > 0) {
|
||||
int size = vim_strsize(s);
|
||||
|
||||
@ -1436,9 +1436,7 @@ void wait_return(int redraw)
|
||||
}
|
||||
need_wait_return = true;
|
||||
if (no_wait_return) {
|
||||
if (!exmode_active) {
|
||||
cmdline_row = msg_row;
|
||||
}
|
||||
cmdline_row = msg_row;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1448,10 +1446,6 @@ void wait_return(int redraw)
|
||||
c = CAR; // just pretend CR was hit
|
||||
quit_more = false;
|
||||
got_int = false;
|
||||
} else if (exmode_active) {
|
||||
msg_puts(" "); // make sure the cursor is on the right line
|
||||
c = CAR; // no need for a return in ex mode
|
||||
got_int = false;
|
||||
} else if (!stuff_empty()) {
|
||||
// When there are stuffed characters, the next stuffed character will
|
||||
// dismiss the hit-enter prompt immediately and have to be put back, so
|
||||
@ -1567,9 +1561,7 @@ void wait_return(int redraw)
|
||||
// If the user hits ':', '?' or '/' we get a command line from the next
|
||||
// line.
|
||||
if (c == ':' || c == '?' || c == '/') {
|
||||
if (!exmode_active) {
|
||||
cmdline_row = msg_row;
|
||||
}
|
||||
cmdline_row = msg_row;
|
||||
skip_redraw = true; // skip redraw once
|
||||
do_redraw = false;
|
||||
}
|
||||
@ -2539,7 +2531,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
|
||||
inc_msg_scrolled();
|
||||
need_wait_return = true; // may need wait_return() in main()
|
||||
redraw_cmdline = true;
|
||||
if (cmdline_row > 0 && !exmode_active) {
|
||||
if (cmdline_row > 0) {
|
||||
cmdline_row--;
|
||||
}
|
||||
|
||||
@ -2550,7 +2542,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
|
||||
}
|
||||
|
||||
if (p_more && lines_left == 0 && State != MODE_HITRETURN
|
||||
&& !msg_no_more && !exmode_active) {
|
||||
&& !msg_no_more) {
|
||||
if (do_more_prompt(NUL)) {
|
||||
s = confirm_buttons;
|
||||
}
|
||||
@ -3317,7 +3309,7 @@ static void msg_moremsg(bool full)
|
||||
}
|
||||
|
||||
/// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD,
|
||||
/// confirm() prompt or exmode_active.
|
||||
/// or confirm() prompt.
|
||||
void repeat_message(void)
|
||||
{
|
||||
if (ui_has(kUIMessages)) {
|
||||
|
||||
@ -101,8 +101,6 @@ typedef struct {
|
||||
bool need_flushbuf;
|
||||
bool set_prevcount;
|
||||
bool previous_got_int; // `got_int` was true
|
||||
bool noexmode; // true if the normal mode was pushed from
|
||||
// ex mode (:global or :visual for example)
|
||||
bool toplevel; // top-level normal mode
|
||||
oparg_T oa; // operator arguments
|
||||
cmdarg_T ca; // command arguments
|
||||
@ -502,22 +500,16 @@ bool op_pending(void)
|
||||
&& current_oap->regname == NUL);
|
||||
}
|
||||
|
||||
/// Normal state entry point. This is called on:
|
||||
///
|
||||
/// - Startup, In this case the function never returns.
|
||||
/// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
|
||||
/// for example. Returns when re-entering ex mode (because ex mode recursion is
|
||||
/// not allowed)
|
||||
/// Normal state entry point: the main loop. Called on startup, never returns.
|
||||
///
|
||||
/// This used to be called main_loop() on main.c
|
||||
void normal_enter(bool noexmode)
|
||||
void normal_enter(void)
|
||||
{
|
||||
NormalState state;
|
||||
normal_state_init(&state);
|
||||
oparg_T *prev_oap = current_oap;
|
||||
current_oap = &state.oa;
|
||||
state.noexmode = noexmode;
|
||||
state.toplevel = !noexmode;
|
||||
state.toplevel = true;
|
||||
state_enter(&state.state);
|
||||
current_oap = prev_oap;
|
||||
}
|
||||
@ -1265,25 +1257,13 @@ static void normal_check_stuff_buffer(NormalState *s)
|
||||
|
||||
static void normal_check_interrupt(NormalState *s)
|
||||
{
|
||||
// Reset "got_int" now that we got back to the main loop. Except when
|
||||
// inside a ":g/pat/cmd" command, then the "got_int" needs to abort
|
||||
// the ":g" command.
|
||||
// For ":g/pat/vi" we reset "got_int" when used once. When used
|
||||
// a second time we go back to Ex mode and abort the ":g" command.
|
||||
// Reset "got_int" now that we got back to the main loop.
|
||||
if (got_int) {
|
||||
if (s->noexmode && global_busy && !exmode_active
|
||||
&& s->previous_got_int) {
|
||||
// Typed two CTRL-C in a row: go back to ex mode as if "Q" was
|
||||
// used and keep "got_int" set, so that it aborts ":g".
|
||||
exmode_active = true;
|
||||
State = MODE_NORMAL;
|
||||
} else if (!global_busy || !exmode_active) {
|
||||
if (!quit_more) {
|
||||
// flush all buffers
|
||||
vgetc();
|
||||
}
|
||||
got_int = false;
|
||||
if (!quit_more) {
|
||||
// flush all buffers
|
||||
vgetc();
|
||||
}
|
||||
got_int = false;
|
||||
s->previous_got_int = true;
|
||||
} else {
|
||||
s->previous_got_int = false;
|
||||
@ -1410,9 +1390,7 @@ static int normal_check(VimState *state)
|
||||
discard_current_exception();
|
||||
}
|
||||
|
||||
if (!exmode_active) {
|
||||
msg_scroll = false;
|
||||
}
|
||||
msg_scroll = false;
|
||||
quit_more = false;
|
||||
|
||||
state_no_longer_safe(NULL);
|
||||
@ -1420,7 +1398,7 @@ static int normal_check(VimState *state)
|
||||
// If skip redraw is set (for ":" in wait_return()), don't redraw now.
|
||||
// If there is nothing in the stuff_buffer or do_redraw is true,
|
||||
// update cursor and redraw.
|
||||
if (skip_redraw || exmode_active) {
|
||||
if (skip_redraw) {
|
||||
skip_redraw = false;
|
||||
setcursor();
|
||||
} else if (do_redraw || stuff_empty()) {
|
||||
@ -1470,21 +1448,13 @@ static int normal_check(VimState *state)
|
||||
// only at the very toplevel. Otherwise we may be using a List or
|
||||
// Dict internally somewhere.
|
||||
// "may_garbage_collect" is reset in vgetc() which is invoked through
|
||||
// do_exmode() and normal_cmd().
|
||||
may_garbage_collect = !s->noexmode;
|
||||
// normal_cmd().
|
||||
may_garbage_collect = true;
|
||||
|
||||
// Update w_curswant if w_set_curswant has been set.
|
||||
// Postponed until here to avoid computing w_virtcol too often.
|
||||
update_curswant();
|
||||
|
||||
if (exmode_active) {
|
||||
if (s->noexmode) {
|
||||
return 0;
|
||||
}
|
||||
do_exmode();
|
||||
return -1;
|
||||
}
|
||||
|
||||
normal_prepare(s);
|
||||
return 1;
|
||||
}
|
||||
@ -5706,7 +5676,13 @@ static void nv_g_cmd(cmdarg_T *cap)
|
||||
// "gQ": improved Ex mode
|
||||
case 'Q':
|
||||
if (!check_text_locked(cap->oap) && !checkclearopq(oap)) {
|
||||
do_exmode();
|
||||
if (ex_normal_busy > 0 || global_busy) {
|
||||
// Ex mode cannot run inside ":normal" or ":global"; discard the rest of the command.
|
||||
flush_buffers(FLUSH_TYPEAHEAD);
|
||||
} else if (!silent_mode) {
|
||||
typval_T args[] = { { .v_type = VAR_UNKNOWN } };
|
||||
nlua_call_typval("vim._core.exmode", "open", args, NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@ -17,8 +17,6 @@
|
||||
#include "nvim/eval_defs.h"
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/multiqueue.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/gettext_defs.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/input.h"
|
||||
#include "nvim/insexpand.h"
|
||||
@ -40,39 +38,16 @@
|
||||
#define READ_BUFFER_SIZE 0xfff
|
||||
#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
|
||||
|
||||
static RStream read_stream = { .s.closed = true }; // Input before UI starts.
|
||||
static char input_buffer[INPUT_BUFFER_SIZE];
|
||||
static char *input_read_pos = input_buffer;
|
||||
static char *input_write_pos = input_buffer;
|
||||
|
||||
static bool input_eof = false;
|
||||
static bool blocking = false;
|
||||
static int cursorhold_time = 0; ///< time waiting for CursorHold event
|
||||
static int cursorhold_tb_change_cnt = 0; ///< tb_change_cnt when waiting started
|
||||
|
||||
#include "os/input.c.generated.h"
|
||||
|
||||
void input_start(void)
|
||||
{
|
||||
if (!read_stream.s.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
used_stdin = true;
|
||||
rstream_init_fd(&main_loop, &read_stream, STDIN_FILENO);
|
||||
rstream_start(&read_stream, input_read_cb, NULL);
|
||||
}
|
||||
|
||||
void input_stop(void)
|
||||
{
|
||||
if (read_stream.s.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
rstream_stop(&read_stream);
|
||||
rstream_may_close(&read_stream);
|
||||
}
|
||||
|
||||
static void cursorhold_event(void **argv)
|
||||
{
|
||||
event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;
|
||||
@ -83,7 +58,7 @@ static void cursorhold_event(void **argv)
|
||||
static void create_cursorhold_event(bool events_enabled)
|
||||
{
|
||||
// If events are enabled and the queue has any items, this function should not
|
||||
// have been called (`inbuf_poll` would return `kTrue`).
|
||||
// have been called (`inbuf_poll` would return true).
|
||||
// TODO(tarruda): Cursorhold should be implemented as a timer set during the
|
||||
// `state_check` callback for the states where it can be triggered.
|
||||
assert(!events_enabled || multiqueue_empty(main_loop.events));
|
||||
@ -139,9 +114,8 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
ctrl_c_interrupts = false;
|
||||
}
|
||||
|
||||
TriState result; ///< inbuf_poll result.
|
||||
if (ms >= 0) {
|
||||
if ((result = inbuf_poll(ms, events)) == kFalse) {
|
||||
if (!inbuf_poll(ms, events)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
@ -164,10 +138,10 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
int64_t delay_left = p_acl - ins_compl_autocomplete_elapsed();
|
||||
wait_time = MIN(MAX(delay_left, 0), wait_time);
|
||||
}
|
||||
if ((result = inbuf_poll((int)wait_time, events)) == kFalse) {
|
||||
if (read_stream.s.closed && silent_mode) {
|
||||
// Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
|
||||
read_error_exit();
|
||||
if (!inbuf_poll((int)wait_time, events)) {
|
||||
if (silent_mode) {
|
||||
// Ran out of input: exit silent/batch-mode (-es/-Es).
|
||||
getout(0);
|
||||
}
|
||||
// The 'autocompletedelay' expired: trigger the popup. When
|
||||
// 'updatetime' is shorter, fall through to CursorHold instead.
|
||||
@ -191,7 +165,7 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
create_cursorhold_event(events == main_loop.events);
|
||||
} else {
|
||||
before_blocking();
|
||||
result = inbuf_poll(-1, events);
|
||||
inbuf_poll(-1, events);
|
||||
}
|
||||
} else {
|
||||
cursorhold_time += (int)((os_hrtime() - wait_start) / 1000000);
|
||||
@ -212,10 +186,6 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
return push_event_key(buf, maxlen);
|
||||
}
|
||||
|
||||
if (result == kNone && ms != 0) {
|
||||
read_error_exit();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef TRY_READ
|
||||
@ -224,7 +194,7 @@ int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
// Check if a character is available for reading
|
||||
bool os_char_avail(void)
|
||||
{
|
||||
return inbuf_poll(0, NULL) == kTrue;
|
||||
return inbuf_poll(0, NULL);
|
||||
}
|
||||
|
||||
/// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
|
||||
@ -550,48 +520,31 @@ bool input_blocking(void)
|
||||
///
|
||||
/// @param ms Timeout in milliseconds. -1 for indefinite wait, 0 for no wait.
|
||||
/// @param events (optional) Queue to check for pending events.
|
||||
/// @return TriState:
|
||||
/// - kTrue: Input/events available
|
||||
/// - kFalse: No input/events
|
||||
/// - kNone: EOF reached on the input stream
|
||||
static TriState inbuf_poll(int ms, MultiQueue *events)
|
||||
/// @return true if input or events are available.
|
||||
static bool inbuf_poll(int ms, MultiQueue *events)
|
||||
{
|
||||
if (os_input_ready(events)) {
|
||||
return kTrue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (do_profiling == PROF_YES && ms) {
|
||||
prof_input_start();
|
||||
}
|
||||
|
||||
if ((ms == -1 || ms > 0) && events != main_loop.events && !input_eof) {
|
||||
if ((ms == -1 || ms > 0) && events != main_loop.events) {
|
||||
// The pending input provoked a blocking wait. Do special events now. #6247
|
||||
blocking = true;
|
||||
multiqueue_process_events(ch_before_blocking_events);
|
||||
}
|
||||
DLOG("blocking... events=%s", !!events ? "true" : "false");
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, os_input_ready(events) || input_eof);
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, os_input_ready(events));
|
||||
blocking = false;
|
||||
|
||||
if (do_profiling == PROF_YES && ms) {
|
||||
prof_input_end();
|
||||
}
|
||||
|
||||
if (os_input_ready(events)) {
|
||||
return kTrue;
|
||||
}
|
||||
return input_eof ? kNone : kFalse;
|
||||
}
|
||||
|
||||
static size_t input_read_cb(RStream *stream, const char *buf, size_t c, void *data, bool at_eof)
|
||||
{
|
||||
if (at_eof) {
|
||||
input_eof = true;
|
||||
}
|
||||
|
||||
assert(input_space() >= c);
|
||||
input_enqueue_raw(buf, c);
|
||||
return c;
|
||||
return os_input_ready(events);
|
||||
}
|
||||
|
||||
static void process_ctrl_c(void)
|
||||
@ -645,16 +598,6 @@ bool os_input_ready(MultiQueue *events)
|
||||
|| pending_events(events)); // Events must be processed
|
||||
}
|
||||
|
||||
// Exit because of an input read error.
|
||||
static void read_error_exit(void)
|
||||
FUNC_ATTR_NORETURN
|
||||
{
|
||||
if (silent_mode) { // Normal way to exit for "nvim -es".
|
||||
getout(0);
|
||||
}
|
||||
preserve_exit(_("Nvim: Error reading input, exiting...\n"));
|
||||
}
|
||||
|
||||
static bool pending_events(MultiQueue *events)
|
||||
{
|
||||
return events && !multiqueue_empty(events);
|
||||
|
||||
@ -5,8 +5,5 @@
|
||||
|
||||
#include "nvim/api/private/defs.h" // IWYU pragma: keep
|
||||
#include "nvim/event/defs.h" // IWYU pragma: keep
|
||||
#include "nvim/macros_defs.h"
|
||||
|
||||
EXTERN bool used_stdin INIT( = false);
|
||||
|
||||
#include "os/input.h.generated.h"
|
||||
|
||||
@ -5317,30 +5317,16 @@ describe('API', function()
|
||||
)
|
||||
end)
|
||||
it('does not interfere with printing line in Ex mode #19400', function()
|
||||
local screen = Screen.new(60, 7)
|
||||
local screen = Screen.new(60, 12)
|
||||
insert([[
|
||||
foo
|
||||
bar]])
|
||||
feed('gQ1')
|
||||
screen:expect([[
|
||||
foo |
|
||||
bar |
|
||||
{1:~ }|*2
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:1^ |
|
||||
]])
|
||||
screen:expect({ any = vim.pesc('{1::}1^') })
|
||||
eq('Parsing command-line', pcall_err(api.nvim_parse_cmd, '', {}))
|
||||
-- Executing the line still auto-prints it.
|
||||
feed('<CR>')
|
||||
screen:expect([[
|
||||
foo |
|
||||
bar |
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:1 |
|
||||
foo |
|
||||
:^ |
|
||||
]])
|
||||
screen:expect({ any = vim.pesc('" foo') })
|
||||
end)
|
||||
it('does not move cursor or change search history/pattern #19878 #19890', function()
|
||||
api.nvim_buf_set_lines(0, 0, -1, true, { 'foo', 'bar', 'foo', 'bar' })
|
||||
|
||||
@ -204,6 +204,7 @@ describe('vim._core', function()
|
||||
'vim._core.defaults',
|
||||
'vim._core.editor',
|
||||
'vim._core.ex_cmd',
|
||||
'vim._core.exmode',
|
||||
'vim._core.exrc',
|
||||
'vim._core.help',
|
||||
'vim._core.log',
|
||||
|
||||
@ -681,6 +681,43 @@ describe('startup', function()
|
||||
)
|
||||
-- without `-u`
|
||||
eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, { 'set encoding', '' }))
|
||||
-- CRLF input (e.g. Windows pipes) works like LF.
|
||||
eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, 'set encoding\r\n'))
|
||||
-- Stdin is not consumed as typeahead: `:lua` can read the next stdin line as data.
|
||||
eq(
|
||||
'thedata\n',
|
||||
fn.system(
|
||||
{ nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '-es' },
|
||||
{ 'lua vim.g.d = io.read()', 'thedata', 'put =g:d', 'print', '' }
|
||||
)
|
||||
)
|
||||
|
||||
-- Cursor starts at the last line (POSIX ex): ed-style "a" appends at end-of-file.
|
||||
write_file('Xesfile', 'L1\nL2\n')
|
||||
finally(function()
|
||||
os.remove('Xesfile')
|
||||
end)
|
||||
eq(
|
||||
'L1\nL2\nX\n',
|
||||
fn.system(
|
||||
{ nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '-es', 'Xesfile' },
|
||||
{ 'a', 'X', '.', '%print', '' }
|
||||
)
|
||||
)
|
||||
|
||||
-- In a terminal, "-es" executes +cmds and exits; it doesn't wait for input.
|
||||
local screen = Screen.new(60, 6)
|
||||
fn.jobstart({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '-es', '-V1', '+echo "hello"' }, {
|
||||
term = true,
|
||||
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
|
||||
})
|
||||
-- "nvim -es" output ends with a final newline.
|
||||
screen:expect([[
|
||||
^hello |
|
||||
|
|
||||
[Process exited 0] |
|
||||
|*3
|
||||
]])
|
||||
end)
|
||||
|
||||
it('-es/-Es disables swapfile/shada/config #8540', function()
|
||||
@ -731,14 +768,10 @@ describe('startup', function()
|
||||
'--clean',
|
||||
'-es',
|
||||
'-c',
|
||||
-- 'showcmd' leads to a char_avail() call just after the 'Q' (no more input).
|
||||
[[set showcmd | exe "g/^/vi|Vgg:w>>Xoutput.txt\rgQ"]],
|
||||
[[g/^/.w >>Xoutput.txt]],
|
||||
'Xinput.txt',
|
||||
})
|
||||
eq(
|
||||
'OUT\nline1\nline1\nline2\nline1\nline2\nline3\nline1\nline2\nline3\nline4\n',
|
||||
read_file('Xoutput.txt')
|
||||
)
|
||||
eq('OUT\nline1\nline2\nline3\nline4\n', read_file('Xoutput.txt'))
|
||||
end)
|
||||
|
||||
it('ENTER dismisses early message #7967', function()
|
||||
@ -829,35 +862,43 @@ end)
|
||||
describe('startup', function()
|
||||
it('-e/-E interactive #7679', function()
|
||||
clear('-e')
|
||||
local screen = Screen.new(25, 3)
|
||||
local screen = Screen.new(40, 12)
|
||||
feed("put ='from -e'<CR>")
|
||||
screen:expect([[
|
||||
:put ='from -e' |
|
||||
from -e |
|
||||
:^ |
|
||||
|
|
||||
from -e |
|
||||
{1:~ }|*6
|
||||
{2:[No Name] [+] }|
|
||||
{1::}^ |
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
|
||||
clear('-E')
|
||||
screen = Screen.new(25, 3)
|
||||
screen = Screen.new(40, 12)
|
||||
feed("put ='from -E'<CR>")
|
||||
screen:expect([[
|
||||
:put ='from -E' |
|
||||
from -E |
|
||||
:^ |
|
||||
|
|
||||
from -E |
|
||||
{1:~ }|*6
|
||||
{2:[No Name] [+] }|
|
||||
{1::}^ |
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('-e sets ex mode', function()
|
||||
it('-e enters Ex mode at startup', function()
|
||||
clear('-e')
|
||||
local screen = Screen.new(25, 3)
|
||||
-- Verify we set the proper mode both before and after :vi.
|
||||
feed('put =mode(1)<CR>vi<CR>:put =mode(1)<CR>')
|
||||
screen:expect([[
|
||||
cv |
|
||||
^n |
|
||||
:put =mode(1) |
|
||||
]])
|
||||
local screen = Screen.new(40, 12)
|
||||
-- The keep-open cmdwin REPL is focused, in Insert mode. #40962
|
||||
screen:expect({ any = vim.pesc('[Command Line]') })
|
||||
eq(':', fn.getcmdwintype())
|
||||
feed('visual<CR>')
|
||||
eq('n', fn.mode(1))
|
||||
eq('', fn.getcmdwintype())
|
||||
|
||||
-- Scripts can detect batch mode ("-es"): mode() is "cv".
|
||||
eq('cv\n', fn.system({ nvim_prog, '-n', '-es' }, { 'put =mode(1)', 'print', '' }))
|
||||
end)
|
||||
|
||||
@ -1855,44 +1896,3 @@ describe('user session', function()
|
||||
eq(1, eval('g:lua_session'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('inccommand on ex mode', function()
|
||||
it('should not preview', function()
|
||||
clear()
|
||||
local screen
|
||||
screen = Screen.new(60, 10)
|
||||
local id = fn.jobstart({
|
||||
nvim_prog,
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'-c',
|
||||
'set termguicolors background=dark',
|
||||
'-E',
|
||||
'README.md',
|
||||
}, {
|
||||
term = true,
|
||||
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },
|
||||
})
|
||||
fn.chansend(id, '%s/N')
|
||||
screen:add_extra_attr_ids({
|
||||
[101] = {
|
||||
background = Screen.colors.NvimDarkGrey4,
|
||||
foreground = Screen.colors.NvimLightGray2,
|
||||
},
|
||||
[102] = {
|
||||
background = Screen.colors.NvimDarkGray2,
|
||||
foreground = Screen.colors.NvimLightGray2,
|
||||
},
|
||||
})
|
||||
screen:expect([[
|
||||
{102:^ }|
|
||||
{102: }|*5
|
||||
{101: }|
|
||||
{102:Entering Ex mode. Type "visual" to go to Normal mode. }|
|
||||
{102::%s/N }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -15,6 +15,7 @@ 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)
|
||||
|
||||
|
||||
@ -6,171 +6,124 @@ 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 eval = n.eval
|
||||
local feed = n.feed
|
||||
local api = n.api
|
||||
local poke_eventloop = n.poke_eventloop
|
||||
local fn = n.fn
|
||||
|
||||
before_each(clear)
|
||||
|
||||
-- Interactive Ex mode (gQ) is a keep-open |cmdwin| REPL. #40962
|
||||
describe('Ex mode', function()
|
||||
it('supports command line editing', function()
|
||||
local function test_ex_edit(expected, cmd)
|
||||
feed('gQ' .. cmd .. '<C-b>"<CR>')
|
||||
local ret = eval('@:[1:]') -- Remove leading quote.
|
||||
feed('visual<CR>')
|
||||
eq(api.nvim_replace_termcodes(expected, true, true, true), ret)
|
||||
end
|
||||
command('set sw=2')
|
||||
test_ex_edit('bar', 'foo bar<C-u>bar')
|
||||
test_ex_edit('1<C-u>2', '1<C-v><C-u>2')
|
||||
test_ex_edit('213', '1<C-b>2<C-e>3')
|
||||
test_ex_edit('2013', '01<Home>2<End>3')
|
||||
test_ex_edit('0213', '01<Left>2<Right>3')
|
||||
test_ex_edit('0342', '012<Left><Left><Insert>3<Insert>4')
|
||||
test_ex_edit('foo ', 'foo bar<C-w>')
|
||||
test_ex_edit('foo', 'fooba<Del><Del>')
|
||||
test_ex_edit('foobar', 'foo<Tab>bar')
|
||||
test_ex_edit('abbreviate', 'abbrev<Tab>')
|
||||
test_ex_edit('1<C-t><C-t>', '1<C-t><C-t>')
|
||||
test_ex_edit('1<C-t><C-t>', '1<C-t><C-t><C-d>')
|
||||
test_ex_edit(' foo', ' foo<C-d>')
|
||||
test_ex_edit(' foo0', ' foo0<C-d>')
|
||||
test_ex_edit(' foo^', ' foo^<C-d>')
|
||||
test_ex_edit('foo', '<BS><C-H><Del><kDel>foo')
|
||||
-- default wildchar <Tab> interferes with this test
|
||||
command('set wildchar=<c-e>')
|
||||
test_ex_edit('a\tb', 'a\t\t<C-H>b')
|
||||
test_ex_edit('\tm<C-T>n', '\tm<C-T>n')
|
||||
command('set wildchar&')
|
||||
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('gQ')
|
||||
screen:expect([[
|
||||
line1 |
|
||||
{2:[No Name] [+] }|
|
||||
{1::}^ |
|
||||
{1:~ }|*5
|
||||
{3:[Command Line] }|
|
||||
{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))
|
||||
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('gQ')
|
||||
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('gQ')
|
||||
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()
|
||||
command('set noincsearch nohlsearch inccommand=')
|
||||
local screen = Screen.new(60, 6)
|
||||
command([[call setline(1, repeat(['foo foo'], 4))]])
|
||||
command([[set number]])
|
||||
local screen = Screen.new(60, 8)
|
||||
command('set inccommand=')
|
||||
command([[call setline(1, repeat(['foo foo'], 2))]])
|
||||
feed('gQ')
|
||||
screen:expect([[
|
||||
{8: 1 }foo foo |
|
||||
{8: 2 }foo foo |
|
||||
{8: 3 }foo foo |
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:^ |
|
||||
]])
|
||||
|
||||
feed('%s/foo/bar/gc<CR>')
|
||||
screen:expect([[
|
||||
{8: 1 }foo foo |
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:%s/foo/bar/gc |
|
||||
{8: 1 }foo foo |
|
||||
^^^^ |
|
||||
]])
|
||||
feed('N<CR>')
|
||||
screen:expect([[
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:%s/foo/bar/gc |
|
||||
{8: 1 }foo foo |
|
||||
^^^N |
|
||||
{8: 1 }foo foo |
|
||||
^^^^ |
|
||||
]])
|
||||
feed('n<CR>')
|
||||
screen:expect([[
|
||||
{8: 1 }foo foo |
|
||||
^^^N |
|
||||
{8: 1 }foo foo |
|
||||
^^^n |
|
||||
{8: 1 }foo foo |
|
||||
^^^^ |
|
||||
]])
|
||||
feed('y<CR>')
|
||||
|
||||
feed('q<CR>')
|
||||
screen:expect([[
|
||||
{8: 1 }foo foo |
|
||||
^^^y |
|
||||
{8: 2 }foo foo |
|
||||
^^^q |
|
||||
{8: 2 }foo foo |
|
||||
:^ |
|
||||
]])
|
||||
|
||||
-- Pressing enter in ex mode should print the current line
|
||||
feed('<CR>')
|
||||
screen:expect([[
|
||||
^^^y |
|
||||
{8: 2 }foo foo |
|
||||
^^^q |
|
||||
{8: 2 }foo foo |
|
||||
{8: 3 }foo foo |
|
||||
:^ |
|
||||
]])
|
||||
|
||||
-- The printed line should overwrite the colon
|
||||
feed('<CR>')
|
||||
screen:expect([[
|
||||
{8: 2 }foo foo |
|
||||
^^^q |
|
||||
{8: 2 }foo foo |
|
||||
{8: 3 }foo foo |
|
||||
{8: 4 }foo foo |
|
||||
:^ |
|
||||
]])
|
||||
|
||||
feed(':vi<CR>')
|
||||
screen:expect([[
|
||||
{8: 1 }foo bar |
|
||||
{8: 2 }foo foo |
|
||||
{8: 3 }foo foo |
|
||||
{8: 4 }^foo foo |
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
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('pressing Ctrl-C in :append inside a loop in Ex mode does not hang', function()
|
||||
local screen = Screen.new(60, 6)
|
||||
it('collects multi-line commands until the block is complete', function()
|
||||
local screen = Screen.new(60, 10)
|
||||
feed('gQ')
|
||||
feed('append<CR>')
|
||||
-- Continuation lines should not have ":" prefix in 'statuscolumn'.
|
||||
screen:expect([[
|
||||
|
|
||||
{2:[No Name] }|
|
||||
{1::}append |
|
||||
{1: }^ |
|
||||
{1:~ }|*4
|
||||
{3:[Command Line] }|
|
||||
{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:[Command Line] }|
|
||||
{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('gQ')
|
||||
feed('for i in range(1)<CR>')
|
||||
feed('append<CR>')
|
||||
screen:expect([[
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:for i in range(1) |
|
||||
|
|
||||
: append |
|
||||
^ |
|
||||
]])
|
||||
-- CTRL-C cancels the cmdwin (ends Ex mode): back to the ":" cmdline, like |q:|.
|
||||
feed('<C-C>')
|
||||
poke_eventloop() -- Wait for input to be flushed
|
||||
feed('foo<CR>')
|
||||
screen:expect([[
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:for i in range(1) |
|
||||
|
|
||||
: append |
|
||||
foo |
|
||||
^ |
|
||||
]])
|
||||
feed('.<CR>')
|
||||
screen:expect([[
|
||||
:for i in range(1) |
|
||||
|
|
||||
: append |
|
||||
foo |
|
||||
. |
|
||||
: ^ |
|
||||
]])
|
||||
feed('endfor<CR>')
|
||||
feed('vi<CR>')
|
||||
screen:expect([[
|
||||
^foo |
|
||||
{1:~ }|*4
|
||||
|
|
||||
]])
|
||||
eq('', fn.getcmdwintype())
|
||||
feed('<Esc>')
|
||||
eq('n', fn.mode(1))
|
||||
eq({ '' }, fn.getline(1, '$'))
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -30,17 +30,17 @@ describe(':global', function()
|
||||
{9:Interrupted} |
|
||||
]])
|
||||
|
||||
-- Also test in Ex mode
|
||||
-- Also test in Ex mode (keep-open cmdwin REPL)
|
||||
feed('gQg/foo/norm :<C-V>;<CR>')
|
||||
poke_eventloop() -- Wait for :sleep to start
|
||||
feed('<C-C>')
|
||||
screen:expect([[
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:g/foo/norm :; |
|
||||
|
|
||||
{9:Interrupted} |
|
||||
:^ |
|
||||
foo |
|
||||
{2:[No Name] [+] }|
|
||||
{1::}" Keyboard interrupt |
|
||||
{1::}^ |
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -523,13 +523,13 @@ end)
|
||||
describe('buffer cursor position is correct in terminal without number column', function()
|
||||
local screen
|
||||
|
||||
local function setup_ex_register(str)
|
||||
local function setup_register_and_prompt(str, prompt)
|
||||
prompt = ':' .. (prompt or '')
|
||||
screen = tt.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'-E',
|
||||
'--cmd',
|
||||
string.format('let @r = "%s"', str),
|
||||
-- <Left> and <Right> don't always work
|
||||
@ -539,16 +539,16 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
'cnoremap <C-O> <Right>',
|
||||
'--cmd',
|
||||
'set notermguicolors',
|
||||
-- A blocked cmdline prompt at the last row, as a stable text-entry to test cursor tracking.
|
||||
('+call input("%s")'):format(prompt),
|
||||
}, {
|
||||
cols = 70,
|
||||
})
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:^ |
|
||||
screen:expect(([[
|
||||
|*5
|
||||
%s^%s|
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
]]):format(prompt, (' '):rep(70 - #prompt)))
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
@ -558,24 +558,20 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
|
||||
describe('in a line with no multibyte chars or trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('aaaaaaaa')
|
||||
setup_register_and_prompt('aaaaaaaa')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
feed('<C-R>r')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:aaaaaaaa^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 9 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:aaaaaaa^a |
|
||||
|
|
||||
]])
|
||||
@ -585,18 +581,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the end', function()
|
||||
feed('<C-R>r<C-X><C-X>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:aaaaaa^aa |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 7 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:aaaaa^aaa |
|
||||
|
|
||||
]])
|
||||
@ -606,18 +598,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the start', function()
|
||||
feed('<C-R>r<C-B><C-O>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:a^aaaaaaa |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 2 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:^aaaaaaaa |
|
||||
|
|
||||
]])
|
||||
@ -627,24 +615,20 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
|
||||
describe('in a line with single-cell multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('µµµµµµµµ')
|
||||
setup_register_and_prompt('µµµµµµµµ')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
feed('<C-R>r')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µµµµµµµµ^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 17 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µµµµµµµ^µ |
|
||||
|
|
||||
]])
|
||||
@ -654,18 +638,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the end', function()
|
||||
feed('<C-R>r<C-X><C-X>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µµµµµµ^µµ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 13 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µµµµµ^µµµ |
|
||||
|
|
||||
]])
|
||||
@ -675,18 +655,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the start', function()
|
||||
feed('<C-R>r<C-B><C-O>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ^µµµµµµµ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 3 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:^µµµµµµµµ |
|
||||
|
|
||||
]])
|
||||
@ -696,24 +672,20 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
|
||||
describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
|
||||
setup_register_and_prompt('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
feed('<C-R>r')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 33 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -724,18 +696,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
skip(is_os('win'))
|
||||
feed('<C-R>r<C-X><C-X>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -746,18 +714,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
skip(is_os('win'))
|
||||
feed('<C-R>r<C-B><C-O>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 5 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -767,24 +731,20 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
|
||||
describe('in a line with double-cell multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('哦哦哦哦哦哦哦哦')
|
||||
setup_register_and_prompt('哦哦哦哦哦哦哦哦')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
feed('<C-R>r')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:哦哦哦哦哦哦哦哦^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 25 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:哦哦哦哦哦哦哦^哦 |
|
||||
|
|
||||
]])
|
||||
@ -794,18 +754,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the end', function()
|
||||
feed('<C-R>r<C-X><C-X>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:哦哦哦哦哦哦^哦哦 |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 19 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:哦哦哦哦哦^哦哦哦 |
|
||||
|
|
||||
]])
|
||||
@ -815,18 +771,14 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
it('near the start', function()
|
||||
feed('<C-R>r<C-B><C-O>')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:哦^哦哦哦哦哦哦哦 |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
eq({ 6, 4 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
|*5
|
||||
:^哦哦哦哦哦哦哦哦 |
|
||||
|
|
||||
]])
|
||||
@ -835,39 +787,33 @@ describe('buffer cursor position is correct in terminal without number column',
|
||||
end)
|
||||
|
||||
it('at the end of a line with trailing spaces #16234', function()
|
||||
setup_ex_register('aaaaaaaa ')
|
||||
feed('<C-R>r')
|
||||
-- Use the raw tty-test child: it echoes input verbatim, so the trailing spaces are real
|
||||
-- terminal cells (a cmdline/TUI child never emits trailing blank cells).
|
||||
screen = tt.setup_screen(nil, nil, 70)
|
||||
tt.feed_data('aaaaaaaa ')
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:aaaaaaaa ^ |
|
||||
tty ready |
|
||||
aaaaaaaa ^ |
|
||||
|*4
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
|
||||
eq({ 6, 13 }, eval('nvim_win_get_cursor(0)'))
|
||||
matches('^aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
|
||||
eq({ 2, 12 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
|*3
|
||||
{2: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:aaaaaaaa ^ |
|
||||
|
|
||||
]])
|
||||
eq({ 6, 12 }, eval('nvim_win_get_cursor(0)'))
|
||||
eq({ 2, 11 }, eval('nvim_win_get_cursor(0)'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('buffer cursor position is correct in terminal with number column', function()
|
||||
local screen
|
||||
|
||||
local function setup_ex_register(str)
|
||||
local function setup_register_and_prompt(str, prompt)
|
||||
prompt = ':' .. (prompt or '')
|
||||
screen = tt.setup_child_nvim({
|
||||
'-u',
|
||||
'NONE',
|
||||
'-i',
|
||||
'NONE',
|
||||
'-E',
|
||||
'--cmd',
|
||||
string.format('let @r = "%s"', str),
|
||||
-- <Left> and <Right> don't always work
|
||||
@ -877,31 +823,33 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
'cnoremap <C-O> <Right>',
|
||||
'--cmd',
|
||||
'set notermguicolors',
|
||||
-- A blocked cmdline prompt at the last row, as a stable text-entry to test cursor tracking.
|
||||
('+call input("%s")'):format(prompt),
|
||||
}, {
|
||||
cols = 70,
|
||||
})
|
||||
screen:expect([[
|
||||
screen:expect(([[
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 6 }:^ |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }%s^%s|
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
]]):format(prompt, (' '):rep(66 - #prompt)))
|
||||
end
|
||||
|
||||
before_each(function()
|
||||
clear()
|
||||
command('autocmd! nvim.terminal TermOpen')
|
||||
-- 'number' should be set before the terminal process starts, otherwise the resize
|
||||
-- from setting 'number' may cause a redraw that removes the "Entering Ex mode".
|
||||
-- from setting 'number' may cause a redraw that changes the child screen.
|
||||
command('set number')
|
||||
end)
|
||||
|
||||
describe('in a line with no multibyte chars or trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('aaaaaaaa')
|
||||
setup_register_and_prompt('aaaaaaaa')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
@ -910,8 +858,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:aaaaaaaa^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -921,8 +869,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:aaaaaaa^a |
|
||||
|
|
||||
]])
|
||||
@ -935,8 +883,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:aaaaaa^aa |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -946,8 +894,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:aaaaa^aaa |
|
||||
|
|
||||
]])
|
||||
@ -960,8 +908,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:a^aaaaaaa |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -971,8 +919,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:^aaaaaaaa |
|
||||
|
|
||||
]])
|
||||
@ -982,7 +930,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
|
||||
describe('in a line with single-cell multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('µµµµµµµµ')
|
||||
setup_register_and_prompt('µµµµµµµµ')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
@ -991,8 +939,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µµµµµµµµ^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1002,8 +950,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µµµµµµµ^µ |
|
||||
|
|
||||
]])
|
||||
@ -1016,8 +964,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µµµµµµ^µµ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1027,8 +975,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µµµµµ^µµµ |
|
||||
|
|
||||
]])
|
||||
@ -1041,8 +989,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ^µµµµµµµ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1052,8 +1000,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:^µµµµµµµµ |
|
||||
|
|
||||
]])
|
||||
@ -1063,7 +1011,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
|
||||
describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
|
||||
setup_register_and_prompt('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
@ -1072,8 +1020,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1083,8 +1031,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -1098,8 +1046,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳^µ̳µ̳ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1109,8 +1057,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳µ̳µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -1124,8 +1072,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:µ̳^µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1135,8 +1083,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:^µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳ |
|
||||
|
|
||||
]])
|
||||
@ -1146,7 +1094,7 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
|
||||
describe('in a line with double-cell multibyte chars and no trailing spaces,', function()
|
||||
before_each(function()
|
||||
setup_ex_register('哦哦哦哦哦哦哦哦')
|
||||
setup_register_and_prompt('哦哦哦哦哦哦哦哦')
|
||||
end)
|
||||
|
||||
it('at the end', function()
|
||||
@ -1155,8 +1103,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:哦哦哦哦哦哦哦哦^ |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1166,8 +1114,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:哦哦哦哦哦哦哦^哦 |
|
||||
|
|
||||
]])
|
||||
@ -1180,8 +1128,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:哦哦哦哦哦哦^哦哦 |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1191,8 +1139,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:哦哦哦哦哦^哦哦哦 |
|
||||
|
|
||||
]])
|
||||
@ -1205,8 +1153,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:哦^哦哦哦哦哦哦哦 |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
@ -1216,8 +1164,8 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 }:^哦哦哦哦哦哦哦哦 |
|
||||
|
|
||||
]])
|
||||
@ -1226,29 +1174,25 @@ describe('buffer cursor position is correct in terminal with number column', fun
|
||||
end)
|
||||
|
||||
it('at the end of a line with trailing spaces #16234', function()
|
||||
setup_ex_register('aaaaaaaa ')
|
||||
feed('<C-R>r')
|
||||
-- Raw tty-test child: trailing spaces are real terminal cells. See the no-number variant.
|
||||
-- 'number' is set after the terminal opens: setup_screen() asserts the default layout.
|
||||
command('set nonumber')
|
||||
screen = tt.setup_screen(nil, nil, 70)
|
||||
command('set number')
|
||||
screen:expect({ any = 'rows: 6, cols: 66' })
|
||||
tt.feed_data('aaaaaaaa ')
|
||||
screen:expect([[
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 6 }:aaaaaaaa ^ |
|
||||
{121: 1 }tty ready |
|
||||
{121: 2 }rows: 6, cols: 66 |
|
||||
{121: 3 }aaaaaaaa ^ |
|
||||
{121: 4 } |
|
||||
{121: 5 } |
|
||||
{121: 6 } |
|
||||
{5:-- TERMINAL --} |
|
||||
]])
|
||||
matches('^:aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
|
||||
eq({ 6, 13 }, eval('nvim_win_get_cursor(0)'))
|
||||
matches('^aaaaaaaa [ ]*$', eval('nvim_get_current_line()'))
|
||||
eq({ 3, 12 }, eval('nvim_win_get_cursor(0)'))
|
||||
feed([[<C-\><C-N>]])
|
||||
screen:expect([[
|
||||
{121: 1 } |
|
||||
{121: 2 } |
|
||||
{121: 3 } |
|
||||
{121: 4 }{2: }|
|
||||
{121: 5 }Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{121: 6 }:aaaaaaaa ^ |
|
||||
|
|
||||
]])
|
||||
eq({ 6, 12 }, eval('nvim_win_get_cursor(0)'))
|
||||
eq({ 3, 11 }, eval('nvim_win_get_cursor(0)'))
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -690,7 +690,7 @@ describe('TUI :restart', function()
|
||||
-- Assert the command-line echo so the E37 assertion below doesn't "succeed immediately".
|
||||
screen:expect({ any = vim.pesc(cmd) })
|
||||
tt.feed_data('\013')
|
||||
screen:expect({ any = vim.pesc('Vim(qall):E37: No write since last change') })
|
||||
screen:expect({ any = vim.pesc('E37: No write since last change') })
|
||||
assert_exitreason(('QuitPre:%s\nExitPre:%s\n'):format(exitreason, exitreason))
|
||||
end
|
||||
|
||||
|
||||
@ -782,35 +782,34 @@ local function test_cmdline(linegrid)
|
||||
end)
|
||||
|
||||
it('works with exmode', function()
|
||||
screen:try_resize(60, 12)
|
||||
feed('gQ')
|
||||
screen:expect({
|
||||
grid = [[
|
||||
|
|
||||
{3: }|
|
||||
Entering Ex mode. Type "|
|
||||
visual" to go to Normal m|
|
||||
ode.^ |
|
||||
]],
|
||||
cmdline = { { content = { { '' } }, firstc = ':', pos = 0 } },
|
||||
})
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|
|
||||
{2:[No Name] }|
|
||||
{1::}^ |
|
||||
{1:~ }|*6
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
feed('echo "foo"<CR>')
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{3: }|
|
||||
Entering Ex mode. Type "|
|
||||
visual" to go to Normal m|
|
||||
ode. |
|
||||
foo^ |
|
||||
]],
|
||||
cmdline = { { content = { { '' } }, firstc = ':', pos = 0 } },
|
||||
cmdline_block = { { { 'echo "foo"' } } },
|
||||
})
|
||||
-- Shouldn't crash for NULL cmdline_block event after <C-\><C-N> #39021.
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|
|
||||
{2:[No Name] }|
|
||||
{1::}echo "foo" |
|
||||
{1::}" foo |
|
||||
{1::}^ |
|
||||
{1:~ }|*4
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
feed('<C-\\><C-N>vis<CR>')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*3
|
||||
|
|
||||
^ |
|
||||
{1:~ }|*10
|
||||
|
|
||||
]])
|
||||
assert_alive()
|
||||
end)
|
||||
@ -1218,7 +1217,7 @@ end)
|
||||
|
||||
it('tabline is not redrawn in Ex mode #24122', function()
|
||||
clear()
|
||||
local screen = Screen.new(60, 5)
|
||||
local screen = Screen.new(60, 8)
|
||||
|
||||
exec([[
|
||||
set showtabline=2
|
||||
@ -1234,18 +1233,22 @@ it('tabline is not redrawn in Ex mode #24122', function()
|
||||
screen:expect([[
|
||||
{2:foo }|
|
||||
|
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:^ |
|
||||
{2:[No Name] }|
|
||||
{1::}^ |
|
||||
{1:~ }|*2
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('echo 1<CR>')
|
||||
screen:expect([[
|
||||
{3: }|
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
:echo 1 |
|
||||
1 |
|
||||
:^ |
|
||||
{2:foo }|
|
||||
|
|
||||
{2:[No Name] }|
|
||||
{1::}echo 1 |
|
||||
{1::}" 1 |
|
||||
{1::}^ |
|
||||
{3:[Command Line] }|
|
||||
{5:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
|
||||
@ -2090,23 +2090,15 @@ describe('ui/builtin messages', function()
|
||||
it('prints lines in Ex mode correctly with a burst of carriage returns #19341', function()
|
||||
command('set number')
|
||||
api.nvim_buf_set_lines(0, 0, 0, true, { 'aaa', 'bbb', 'ccc' })
|
||||
feed('gggQ<CR><CR>1<CR><CR>vi')
|
||||
-- Empty lines advance the cursor and print; a bare address moves and prints.
|
||||
feed('gggQ<CR><CR>1<CR><CR>')
|
||||
screen:expect({ any = vim.pesc('" bbb') })
|
||||
feed('vi<CR>')
|
||||
screen:expect([[
|
||||
Entering Ex mode. Type "visual" to go to Normal mode. |
|
||||
{8: 2 }bbb |
|
||||
{8: 3 }ccc |
|
||||
:1 |
|
||||
{8: 1 }aaa |
|
||||
{8: 2 }bbb |
|
||||
:vi^ |
|
||||
]])
|
||||
feed('<CR>')
|
||||
screen:expect([[
|
||||
{8: 1 }aaa |
|
||||
{8: 2 }^bbb |
|
||||
{8: 3 }ccc |
|
||||
{8: 4 } |
|
||||
{1:~ }|*2
|
||||
{1:~ }|*3
|
||||
|
|
||||
]])
|
||||
end)
|
||||
@ -2600,11 +2592,20 @@ describe('ui/msg_puts_printf', function()
|
||||
|
||||
fn.mkdir(locale_dir, 'p')
|
||||
fn.filecopy(build_dir .. '/src/nvim/po/ja.mo', locale_dir .. '/nvim.mo')
|
||||
-- "-Es" doesn't read stdin as commands, and the "Entering Ex mode" banner was removed, so
|
||||
-- ":print" a translated message via "-S" (silent mode suppresses ":echo" output). #40966
|
||||
t.write_file(
|
||||
'Xes_ja.vim',
|
||||
[[call setline(1, gettext("Entering Ex mode. Type \"visual\" to go to Normal mode."))]]
|
||||
.. '\n'
|
||||
.. '.print\n'
|
||||
)
|
||||
finally(function()
|
||||
os.remove('Xes_ja.vim')
|
||||
n.rmdir(vim.fs.dirname(locale_dir))
|
||||
end)
|
||||
|
||||
cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -V1'
|
||||
cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -S Xes_ja.vim'
|
||||
command([[call jobstart(']] .. cmd .. [[',{'term':v:true})]])
|
||||
screen:expect([[
|
||||
^Exモードに入ります。ノー |
|
||||
|
||||
@ -108,6 +108,7 @@ endfunc
|
||||
|
||||
" Test for displaying lines from an empty buffer in Ex mode
|
||||
func Test_Ex_emptybuf()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call assert_fails('call feedkeys("Q\<CR>", "xt")', 'E749:')
|
||||
call setline(1, "abc")
|
||||
@ -168,6 +169,7 @@ endfunc
|
||||
" Test for :g/pat/visual to run vi commands in Ex mode
|
||||
" This used to hang Vim before 8.2.0274.
|
||||
func Test_Ex_global()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo'])
|
||||
call feedkeys("Q\<bs>g/bar/visual\<CR>$rxQ$ryQvisual\<CR>j", "xt")
|
||||
@ -235,13 +237,15 @@ endfunc
|
||||
|
||||
" In Ex-mode, a backslash escapes a newline
|
||||
func Test_Ex_escape_enter()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt')
|
||||
call assert_equal("a\rb", l)
|
||||
endfunc
|
||||
|
||||
" Test for :append! command in Ex mode
|
||||
func Test_Ex_append()
|
||||
throw 'Skipped: Nvim only supports Vim Ex mode'
|
||||
" feedkeys('x') implies ex_normal_busy; Ex mode cannot run inside :normal.
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call setline(1, "\t abc")
|
||||
call feedkeys("Qappend!\npqr\nxyz\n.\nvisual\n", 'xt')
|
||||
@ -370,6 +374,7 @@ endfunc
|
||||
|
||||
" Testing implicit print command
|
||||
func Test_implicit_print()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call setline(1, ['one', 'two', 'three'])
|
||||
call feedkeys('Q:let a=execute(":1,2")', 'xt')
|
||||
@ -381,6 +386,8 @@ endfunc
|
||||
|
||||
" Test inserting text after the trailing bar
|
||||
func Test_insert_after_trailing_bar()
|
||||
" feedkeys('x') implies ex_normal_busy; Ex mode cannot run inside :normal.
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call feedkeys("Qi|\nfoo\n.\na|bar\nbar\n.\nc|baz\n.", "xt")
|
||||
call assert_equal(['', 'foo', 'bar', 'baz'], getline(1, '$'))
|
||||
@ -389,6 +396,7 @@ endfunc
|
||||
|
||||
" Test global insert of a newline without terminating period
|
||||
func Test_global_insert_newline()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
new
|
||||
call setline(1, ['foo'])
|
||||
call feedkeys("Qg/foo/i\\\n", "xt")
|
||||
@ -398,6 +406,7 @@ endfunc
|
||||
|
||||
" An empty command followed by a newline shouldn't cause E749 in Ex mode.
|
||||
func Test_ex_empty_command_newline()
|
||||
throw 'Skipped: Nvim implements interactive Ex mode as a cmdwin script'
|
||||
let g:var = 0
|
||||
call feedkeys("gQexecute \"\\nlet g:var = 1\"\r", 'xt')
|
||||
call assert_equal(1, g:var)
|
||||
|
||||
@ -578,9 +578,11 @@ func Test_read_cmd()
|
||||
new
|
||||
read Xcmdfile
|
||||
call assert_equal(['', 'one'], getline(1, '$'))
|
||||
call deletebufline('', 1, '$')
|
||||
call feedkeys("Qr Xcmdfile\<CR>visual\<CR>", 'xt')
|
||||
call assert_equal(['one'], getline(1, '$'))
|
||||
" Nvim: Ex mode is a cmdwin wrapper; cannot run inside feedkeys('x'), and
|
||||
" ":read" no longer deletes the empty first line in Ex mode.
|
||||
"call deletebufline('', 1, '$')
|
||||
"call feedkeys("Qr Xcmdfile\<CR>visual\<CR>", 'xt')
|
||||
"call assert_equal(['one'], getline(1, '$'))
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
|
||||
@ -814,10 +814,12 @@ func Test_mode()
|
||||
call assert_equal('c-c', g:current_modes)
|
||||
call feedkeys(":\<Insert>\<F2>\<CR>", 'xt')
|
||||
call assert_equal("c-cr", g:current_modes)
|
||||
call feedkeys("gQ\<F2>vi\<CR>", 'xt')
|
||||
call assert_equal('c-cv', g:current_modes)
|
||||
call feedkeys("gQ\<Insert>\<F2>vi\<CR>", 'xt')
|
||||
call assert_equal("c-cvr", g:current_modes)
|
||||
" Nvim: interactive Ex mode is a cmdwin wrapper: mode()=n/i ("cv" means
|
||||
" -es); cannot run inside :normal/feedkeys('x').
|
||||
"call feedkeys("gQ\<F2>vi\<CR>", 'xt')
|
||||
"call assert_equal('c-cv', g:current_modes)
|
||||
"call feedkeys("gQ\<Insert>\<F2>vi\<CR>", 'xt')
|
||||
"call assert_equal("c-cvr", g:current_modes)
|
||||
|
||||
" Commandline mode in Visual mode should return "c-c", never "v-v".
|
||||
call feedkeys("v\<Cmd>call input('')\<CR>\<F2>\<CR>\<Esc>", 'xt')
|
||||
@ -825,10 +827,10 @@ func Test_mode()
|
||||
|
||||
" Executing commands in Vim Ex mode should return "cv", never "cvr",
|
||||
" as Cmdline editing has already ended.
|
||||
call feedkeys("gQcall Save_mode()\<CR>vi\<CR>", 'xt')
|
||||
call assert_equal('c-cv', g:current_modes)
|
||||
call feedkeys("gQ\<Insert>call Save_mode()\<CR>vi\<CR>", 'xt')
|
||||
call assert_equal('c-cv', g:current_modes)
|
||||
"call feedkeys("gQcall Save_mode()\<CR>vi\<CR>", 'xt')
|
||||
"call assert_equal('c-cv', g:current_modes)
|
||||
"call feedkeys("gQ\<Insert>call Save_mode()\<CR>vi\<CR>", 'xt')
|
||||
"call assert_equal('c-cv', g:current_modes)
|
||||
|
||||
" call feedkeys("Qcall Save_mode()\<CR>vi\<CR>", 'xt')
|
||||
" call assert_equal('c-ce', g:current_modes)
|
||||
|
||||
@ -1188,7 +1188,8 @@ endfunc
|
||||
" Test for the "-E" (improved Ex mode) argument
|
||||
func Test_E_arg()
|
||||
let after =<< trim [CODE]
|
||||
call assert_equal('cv', mode(1))
|
||||
" Nvim: "-E" enters the cmdwin REPL after startup scripts; mode()="n".
|
||||
call assert_equal('n', mode(1))
|
||||
call writefile(v:errors, 'Xtestout')
|
||||
qall
|
||||
[CODE]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user