feat: ignore swapfile for running Nvim processes #25336

Problem:
The swapfile "E325: ATTENTION" dialog is displayed when editing a file
already open in another (running) Nvim. Usually this behavior is
annoying and irrelevant:
- "Recover" and the other options ("Open readonly", "Quit", "Abort") are
  almost never wanted.
- swapfiles are less relevant for "multi-Nvim" since 'autoread' is
  enabled by default.
  - Even less relevant if user enables 'autowrite'.

Solution:
Define a default SwapExists handler which does the following:
1. If the swapfile is owned by a running Nvim process, automatically
   chooses "(E)dit anyway" (caveat: this creates a new, extra swapfile,
   which is mostly harmless and ignored except by `:recover` or `nvim -r`.
2. Shows a 1-line "ignoring swapfile..." message.
3. Users can disable the default SwapExists handler via `autocmd! nvim_swapfile`.
This commit is contained in:
Justin M. Keyes
2023-10-04 06:31:25 -07:00
committed by GitHub
parent 1e7e9ee91f
commit 29fe883aa9
15 changed files with 267 additions and 178 deletions

View File

@ -118,7 +118,7 @@ manually. Mostly the screen will not scroll up, thus there is no hit-enter
prompt. When one command outputs two messages this can happen anyway.
==============================================================================
3. Removing autocommands *autocmd-remove*
3. Removing autocommands *autocmd!* *autocmd-remove*
:au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd}
Remove all autocommands associated with {event} and

View File

@ -7843,8 +7843,8 @@ swapinfo({fname}) *swapinfo()*
user user name
host host name
fname original file name
pid PID of the Vim process that created the swap
file
pid PID of the Nvim process that created the swap
file, or zero if not running.
mtime last modification time in seconds
inode Optional: INODE number of the file
dirty 1 if file was modified, 0 if not

View File

@ -169,33 +169,26 @@ If you want to keep the changed buffer without saving it, switch on the
2. Editing a file *edit-a-file*
*:e* *:edit* *reload*
:e[dit] [++opt] [+cmd] Edit the current file. This is useful to re-edit the
:e[dit][!] [++opt] [+cmd]
Edit the current file. This is useful to re-edit the
current file, when it has been changed outside of Vim.
This fails when changes have been made to the current
buffer and 'autowriteall' isn't set or the file can't
be written.
Also see |++opt| and |+cmd|.
*:edit!* *discard*
:e[dit]! [++opt] [+cmd]
Edit the current file always. Discard any changes to
the current buffer. This is useful if you want to
start all over again.
If [!] is given, unsaved changes in the current buffer
are discarded. Without [!] the command fails if there
are unsaved changes, unless 'autowriteall' is set and
the file can be written.
Also see |++opt| and |+cmd|.
*:edit_f*
:e[dit] [++opt] [+cmd] {file}
:e[dit][!] [++opt] [+cmd] {file}
Edit {file}.
This fails when changes have been made to the current
buffer, unless 'hidden' is set or 'autowriteall' is
set and the file can be written.
*:edit!_f*
If [!] is given, unsaved changes in the current buffer
are discarded. Without [!] the command fails if there
are unsaved changes, unless 'hidden' is set or
'autowriteall' is set and the file can be written.
Also see |++opt| and |+cmd|.
*:edit!_f*
:e[dit]! [++opt] [+cmd] {file}
Edit {file} always. Discard any changes to the
current buffer.
Also see |++opt| and |+cmd|.
*:edit_#* *:e#*
:e[dit] [++opt] [+cmd] #[count]
Edit the [count]th buffer (as shown by |:files|).
@ -1224,10 +1217,10 @@ MULTIPLE WINDOWS AND BUFFERS *window-exit*
*:confirm* *:conf*
:conf[irm] {command} Execute {command}, and use a dialog when an
operation has to be confirmed. Can be used on the
|:q|, |:qa| and |:w| commands (the latter to override
a read-only setting), and any other command that can
fail in such a way, such as |:only|, |:buffer|,
|:bdelete|, etc.
|:edit|, |:q|, |:qa| and |:w| commands (the latter to
override a read-only setting), and any commands that
can fail because of unsaved changes, such as |:only|,
|:buffer|, |:bdelete|, etc.
Examples: >
:confirm w foo

View File

@ -2276,8 +2276,9 @@ v:stderr |channel-id| corresponding to stderr. The value is always 2;
:call chansend(v:stderr, "error: toaster empty\n")
<
*v:swapname* *swapname-variable*
v:swapname Only valid when executing |SwapExists| autocommands: Name of
the swap file found. Read-only.
v:swapname Name of the swapfile found.
Only valid during |SwapExists| event.
Read-only.
*v:swapchoice* *swapchoice-variable*
v:swapchoice |SwapExists| autocommands can set this to the selected choice

View File

@ -114,6 +114,12 @@ The following new APIs and features were added.
• Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers in a
terminal emulator that supports |tui-csiu|.
• Editor
• By default, the swapfile "ATTENTION" |E325| dialog is skipped if the
swapfile is owned by a running Nvim process, instead of prompting. If you
always want the swapfile dialog, delete the default SwapExists handler:
`autocmd! nvim_swapfile`. |default-autocmds|
• LSP
• LSP method names are available in |vim.lsp.protocol.Methods|.
• Implemented LSP inlay hints: |vim.lsp.inlay_hint()|

View File

@ -83,6 +83,15 @@ Detecting an existing swap file ~
You can find this in the user manual, section |11.3|.
*W325*
The default |SwapExists| handler (|default-autocmds|) skips the |E325| prompt
(selects "(E)dit") if the swapfile owner process (1) is still running and (2)
was started by the current user. This presumes that you normally don't want
to be bothered with the |ATTENTION| message just because you happen to edit
the same file from multiple Nvim instances. In the worst case (a system
crash) there will be more than one swapfile for the file; use |:recover| to
inspect all of its swapfiles.
Updating the swapfile ~

View File

@ -139,6 +139,11 @@ nvim_terminal:
nvim_cmdwin:
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|.
nvim_swapfile:
- SwapExists: Skips the swapfile prompt (sets |v:swapchoice| to "e") when the
swapfile is owned by a running Nvim process. Shows |W325| "Ignoring
swapfile…" message.
==============================================================================
New Features *nvim-features*

View File

@ -1147,11 +1147,28 @@ function vim._init_default_autocmds()
end
end,
})
vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, {
pattern = '[:>]',
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
command = 'syntax sync minlines=1 maxlines=1',
})
vim.api.nvim_create_autocmd({ 'SwapExists' }, {
pattern = '*',
group = vim.api.nvim_create_augroup('nvim_swapfile', {}),
callback = function()
local info = vim.fn.swapinfo(vim.v.swapname)
local user = vim.uv.os_get_passwd().username
local iswin = 1 == vim.fn.has('win32')
if info.error or info.pid <= 0 or (not iswin and info.user ~= user) then
vim.v.swapchoice = '' -- Show the prompt.
return
end
vim.v.swapchoice = 'e' -- Choose "(E)dit".
vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid))
end,
})
end
function vim._init_defaults()

View File

@ -9312,8 +9312,8 @@ function vim.fn.swapfilelist() end
--- user user name
--- host host name
--- fname original file name
--- pid PID of the Vim process that created the swap
--- file
--- pid PID of the Nvim process that created the swap
--- file, or zero if not running.
--- mtime last modification time in seconds
--- inode Optional: INODE number of the file
--- dirty 1 if file was modified, 0 if not

View File

@ -11123,8 +11123,8 @@ M.funcs = {
user user name
host host name
fname original file name
pid PID of the Vim process that created the swap
file
pid PID of the Nvim process that created the swap
file, or zero if not running.
mtime last modification time in seconds
inode Optional: INODE number of the file
dirty 1 if file was modified, 0 if not

View File

@ -8236,7 +8236,7 @@ static void f_swapfilelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr
static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
swapfile_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/// "swapname(expr)" function

View File

@ -2491,7 +2491,7 @@ void ex_function(exarg_T *eap)
} else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
nextcmd = line_arg;
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
give_warning2(_("W22: Text found after :endfunction: %s"), p, true);
swmsg(true, _("W22: Text found after :endfunction: %s"), p);
}
if (nextcmd != NULL) {
// Another command follows. If the line came from "eap" we

View File

@ -168,11 +168,9 @@ enum {
B0_MAGIC_CHAR = 0x55,
};
// Block zero holds all info about the swap file. This is the first block in
// the file.
// Block zero holds all info about the swapfile. This is the first block in the file.
//
// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
// swap files unusable!
// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing swapfiles unusable!
//
// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
//
@ -210,8 +208,7 @@ typedef struct {
// EOL_MAC + 1.
#define B0_FF_MASK 3
// Swap file is in directory of edited file. Used to find the file from
// different mount points.
// Swapfile is in directory of edited file. Used to find the file from different mount points.
#define B0_SAME_DIR 4
// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
@ -724,10 +721,13 @@ static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf)
}
}
/// Return true if the process with number "b0p->b0_pid" is still running.
/// "swap_fname" is the name of the swap file, if it's from before a reboot then
/// the result is false;
static bool swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
/// Returns the PID of the process that owns the swapfile, if it is running.
///
/// @param b0p swapfile data
/// @param swap_fname Name of the swapfile. If it's from before a reboot, the result is 0.
///
/// @return PID, or 0 if process is not running or the swapfile is from before a reboot.
static int swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
{
FileInfo st;
double uptime;
@ -736,15 +736,15 @@ static bool swapfile_process_running(const ZeroBlock *b0p, const char *swap_fnam
if (os_fileinfo(swap_fname, &st)
&& uv_uptime(&uptime) == 0
&& (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) {
return false;
return 0;
}
return os_proc_running((int)char_to_long(b0p->b0_pid));
int pid = (int)char_to_long(b0p->b0_pid);
return os_proc_running(pid) ? pid : 0;
}
/// Try to recover curbuf from the .swp file.
///
/// @param checkext if true, check the extension and detect whether it is a
/// swap file.
/// @param checkext if true, check the extension and detect whether it is a swapfile.
void ml_recover(bool checkext)
{
buf_T *buf = NULL;
@ -1456,12 +1456,13 @@ char *make_percent_swname(const char *dir, const char *name)
return d;
}
static bool process_still_running;
// PID of swapfile owner, or zero if not running.
static int process_running;
/// This is used by the swapinfo() function.
/// For Vimscript "swapinfo()".
///
/// @return information found in swapfile "fname" in dictionary "d".
void get_b0_dict(const char *fname, dict_T *d)
void swapfile_dict(const char *fname, dict_T *d)
{
int fd;
ZeroBlock b0;
@ -1482,7 +1483,7 @@ void get_b0_dict(const char *fname, dict_T *d)
tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname,
B0_FNAME_SIZE_ORG);
tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid));
tv_dict_add_nr(d, S_LEN("pid"), swapfile_process_running(&b0, fname));
tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime));
tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0);
tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
@ -1496,7 +1497,7 @@ void get_b0_dict(const char *fname, dict_T *d)
}
}
/// Give information about an existing swap file.
/// Loads info from swapfile `fname`, and displays it to the user.
///
/// @return timestamp (0 when unknown).
static time_t swapfile_info(char *fname)
@ -1567,9 +1568,8 @@ static time_t swapfile_info(char *fname)
if (char_to_long(b0.b0_pid) != 0L) {
msg_puts(_("\n process ID: "));
msg_outnum((int)char_to_long(b0.b0_pid));
if (swapfile_process_running(&b0, fname)) {
if ((process_running = swapfile_process_running(&b0, fname))) {
msg_puts(_(" (STILL RUNNING)"));
process_still_running = true;
}
}
@ -1589,8 +1589,7 @@ static time_t swapfile_info(char *fname)
return x;
}
/// @return true if the swap file looks OK and there are no changes, thus it
/// can be safely deleted.
/// @return true if the swapfile looks OK and there are no changes, thus it can be safely deleted.
static bool swapfile_unchanged(char *fname)
{
ZeroBlock b0;
@ -3175,13 +3174,10 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
}
/// Get file name to use for swapfile or backup file.
/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
/// option "dname".
/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' option "dname".
/// - If "dname" is ".", return "fname" (swapfile in dir of file).
/// - If "dname" starts with "./", insert "dname" in "fname" (swap file
/// relative to dir of file).
/// - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
/// dir).
/// - If "dname" starts with "./", insert "dname" in "fname" (swapfile relative to dir of file).
/// - Otherwise, prepend "dname" to the tail of "fname" (swapfile in specific dir).
///
/// The return value is an allocated string and can be NULL.
///
@ -3333,7 +3329,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
char *dir_name = xmalloc(dir_len);
(void)copy_option_part(dirp, dir_name, dir_len, ",");
// we try different names until we find one that does not exist yet
// We try different swapfile names until we find one that does not exist yet.
char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
while (true) {
@ -3365,17 +3361,17 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// Give an error message, unless recovering, no file name, we are
// viewing a help file or when the path of the file is different
// (happens when all .swp files are in one directory).
if (!recoverymode && buf_fname != NULL
&& !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
int fd;
ZeroBlock b0;
int differ = false;
// Try to read block 0 from the swap file to get the original
// file name (and inode number).
// Try to read block 0 from the swapfile to get the original file name (and inode number).
fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
process_running = swapfile_process_running(&b0, fname);
// If the swapfile has the same directory as the
// buffer don't compare the directory names, they can
// have a different mountpoint.
@ -3393,8 +3389,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
}
} else {
// The name in the swap file may be
// "~user/path/file". Expand it first.
// The name in the swapfile may be "~user/path/file". Expand it first.
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino))) {
@ -3405,13 +3400,13 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
close(fd);
}
// give the ATTENTION message when there is an old swap file
// for the current file, and the buffer was not recovered.
// Show the ATTENTION message when:
// - there is an old swapfile for the current file
// - the buffer was not recovered
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
process_still_running = false;
// It's safe to delete the swapfile if all these are true:
// - the edited file exists
// - the swapfile has no changes and looks OK
@ -3430,6 +3425,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
choice = do_swapexists(buf, fname);
}
process_running = 0; // Set by attention_message..swapfile_info.
if (choice == 0) {
// Show info about the existing swapfile.
attention_message(buf, fname);
@ -3459,14 +3455,14 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
xstrlcat(name, sw_msg_2, name_len);
choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"),
name,
process_still_running
process_running
? _("&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Quit\n&Abort") :
_("&Open Read-Only\n&Edit anyway\n&Recover"
"\n&Delete it\n&Quit\n&Abort"),
1, NULL, false);
if (process_still_running && choice >= 4) {
if (process_running && choice >= 4) {
choice++; // Skip missing "Delete it" button.
}
xfree(name);
@ -3477,27 +3473,27 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (choice > 0) {
switch (choice) {
case 1:
case 1: // "Open Read-Only"
buf->b_p_ro = true;
break;
case 2:
case 2: // "Edit anyway"
break;
case 3:
case 3: // "Recover"
swap_exists_action = SEA_RECOVER;
break;
case 4:
case 4: // "Delete it"
os_remove(fname);
break;
case 5:
case 5: // "Quit"
swap_exists_action = SEA_QUIT;
break;
case 6:
case 6: // "Abort"
swap_exists_action = SEA_QUIT;
got_int = true;
break;
}
// If the file was deleted this fname can be used.
// If the swapfile was deleted this `fname` can be used.
if (!os_path_exists(fname)) {
break;
}
@ -3512,10 +3508,10 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
}
}
// Change the ".swp" extension to find another file that can be used.
// Permute the ".swp" extension to find a unique swapfile name.
// First decrement the last char: ".swo", ".swn", etc.
// If that still isn't enough decrement the last but one char: ".svz"
// Can happen when editing many "No Name" buffers.
// Can happen when many Nvim instances are editing the same file (including "No Name" buffers).
if (fname[n - 1] == 'a') { // ".s?a"
if (fname[n - 2] == 'a') { // ".saa": tried enough, give up
emsg(_("E326: Too many swap files found"));

View File

@ -475,7 +475,14 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen)
}
}
// Note: Caller of smsg() must check the resulting string is shorter than IOSIZE!!!
/// Shows a printf-style message with attributes.
///
/// Note: Caller must check the resulting string is shorter than IOSIZE!!!
///
/// @see semsg
/// @see swmsg
///
/// @param s printf-style format message
int smsg(int attr, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
@ -757,6 +764,8 @@ void emsg_invreg(int name)
}
/// Print an error message with unknown number of arguments
///
/// @return whether the message was displayed
bool semsg(const char *const fmt, ...)
FUNC_ATTR_PRINTF(1, 2)
{
@ -3337,9 +3346,22 @@ void give_warning(const char *message, bool hl)
no_wait_return--;
}
void give_warning2(const char *const message, const char *const a1, bool hl)
/// Shows a warning, with optional highlighting.
///
/// @param hl enable highlighting
/// @param fmt printf-style format message
///
/// @see smsg
/// @see semsg
void swmsg(bool hl, const char *const fmt, ...)
FUNC_ATTR_PRINTF(2, 3)
{
vim_snprintf(IObuff, IOSIZE, message, a1);
va_list args;
va_start(args, fmt);
vim_vsnprintf(IObuff, IOSIZE, fmt, args);
va_end(args);
give_warning(IObuff, hl);
}

View File

@ -171,6 +171,7 @@ describe('swapfile detection', function()
local screen2 = Screen.new(256, 40)
screen2:attach()
exec(init)
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
-- With shortmess+=F
command('set shortmess+=F')
@ -219,11 +220,29 @@ describe('swapfile detection', function()
nvim2:close()
end)
it('default SwapExists handler selects "(E)dit" and skips prompt', function()
exec(init)
command('edit Xfile1')
command("put ='some text...'")
command('preserve') -- Make sure the swap file exists.
local nvimpid = funcs.getpid()
local nvim1 = spawn(new_argv(), true, nil, true)
set_session(nvim1)
local screen = Screen.new(75, 18)
screen:attach()
exec(init)
feed(':edit Xfile1\n')
screen:expect({ any = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid) })
nvim1:close()
end)
-- oldtest: Test_swap_prompt_splitwin()
it('selecting "q" in the attention prompt', function()
exec(init)
command('edit Xfile1')
command('preserve') -- should help to make sure the swap file exists
command('preserve') -- Make sure the swap file exists.
local screen = Screen.new(75, 18)
screen:set_default_attr_ids({
@ -235,7 +254,9 @@ describe('swapfile detection', function()
set_session(nvim1)
screen:attach()
exec(init)
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
feed(':split Xfile1\n')
-- The default SwapExists handler does _not_ skip this prompt.
screen:expect({
any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
})
@ -267,6 +288,7 @@ describe('swapfile detection', function()
set_session(nvim2)
screen:attach()
exec(init)
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
command('set more')
command('au bufadd * let foo_w = wincol()')
feed(':e Xfile1<CR>')
@ -300,8 +322,9 @@ describe('swapfile detection', function()
nvim2:close()
end)
-- oldtest: Test_nocatch_process_still_running()
it('allows deleting swapfile created before boot vim-patch:8.2.2586', function()
--- @param swapexists boolean Enable the default SwapExists handler.
--- @param on_swapfile_running fun(screen: any) Called after swapfile ("STILL RUNNING") prompt.
local function test_swapfile_after_reboot(swapexists, on_swapfile_running)
local screen = Screen.new(75, 30)
screen:set_default_attr_ids({
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
@ -311,6 +334,9 @@ describe('swapfile detection', function()
screen:attach()
exec(init)
if not swapexists then
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
end
command('set nohidden')
exec([=[
@ -347,12 +373,7 @@ describe('swapfile detection', function()
os.rename('Xswap', swname)
feed(':edit Xswaptest<CR>')
screen:expect({any = table.concat({
pesc('{2:E325: ATTENTION}'),
'file name: .*Xswaptest',
'process ID: %d* %(STILL RUNNING%)',
pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
}, '.*')})
on_swapfile_running(screen)
feed('e')
@ -374,9 +395,28 @@ describe('swapfile detection', function()
}, '.*')})
feed('e')
end
-- oldtest: Test_nocatch_process_still_running()
it('swapfile created before boot vim-patch:8.2.2586', function()
test_swapfile_after_reboot(false, function(screen)
screen:expect({any = table.concat({
pesc('{2:E325: ATTENTION}'),
'file name: .*Xswaptest',
'process ID: %d* %(STILL RUNNING%)',
pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'),
}, '.*')})
end)
end)
it('swapfile created before boot + default SwapExists handler', function()
test_swapfile_after_reboot(true, function(screen)
screen:expect({ any = 'W325: Ignoring swapfile from Nvim process' })
end)
end)
end)
describe('quitting swapfile dialog on startup stops TUI properly', function()
local swapdir = luv.cwd()..'/Xtest_swapquit_dir'
local testfile = 'Xtest_swapquit_file1'