mirror of
https://github.com/neovim/neovim
synced 2025-07-20 21:32:16 +00:00
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:
@ -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.
|
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}
|
:au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd}
|
||||||
Remove all autocommands associated with {event} and
|
Remove all autocommands associated with {event} and
|
||||||
|
4
runtime/doc/builtin.txt
generated
4
runtime/doc/builtin.txt
generated
@ -7843,8 +7843,8 @@ swapinfo({fname}) *swapinfo()*
|
|||||||
user user name
|
user user name
|
||||||
host host name
|
host host name
|
||||||
fname original file name
|
fname original file name
|
||||||
pid PID of the Vim process that created the swap
|
pid PID of the Nvim process that created the swap
|
||||||
file
|
file, or zero if not running.
|
||||||
mtime last modification time in seconds
|
mtime last modification time in seconds
|
||||||
inode Optional: INODE number of the file
|
inode Optional: INODE number of the file
|
||||||
dirty 1 if file was modified, 0 if not
|
dirty 1 if file was modified, 0 if not
|
||||||
|
@ -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*
|
2. Editing a file *edit-a-file*
|
||||||
|
|
||||||
*:e* *:edit* *reload*
|
*: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.
|
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*
|
*:edit!* *discard*
|
||||||
:e[dit]! [++opt] [+cmd]
|
If [!] is given, unsaved changes in the current buffer
|
||||||
Edit the current file always. Discard any changes to
|
are discarded. Without [!] the command fails if there
|
||||||
the current buffer. This is useful if you want to
|
are unsaved changes, unless 'autowriteall' is set and
|
||||||
start all over again.
|
the file can be written.
|
||||||
Also see |++opt| and |+cmd|.
|
Also see |++opt| and |+cmd|.
|
||||||
|
|
||||||
*:edit_f*
|
*:edit_f*
|
||||||
:e[dit] [++opt] [+cmd] {file}
|
:e[dit][!] [++opt] [+cmd] {file}
|
||||||
Edit {file}.
|
Edit {file}.
|
||||||
This fails when changes have been made to the current
|
*:edit!_f*
|
||||||
buffer, unless 'hidden' is set or 'autowriteall' is
|
If [!] is given, unsaved changes in the current buffer
|
||||||
set and the file can be written.
|
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|.
|
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#*
|
*:edit_#* *:e#*
|
||||||
:e[dit] [++opt] [+cmd] #[count]
|
:e[dit] [++opt] [+cmd] #[count]
|
||||||
Edit the [count]th buffer (as shown by |:files|).
|
Edit the [count]th buffer (as shown by |:files|).
|
||||||
@ -1224,10 +1217,10 @@ MULTIPLE WINDOWS AND BUFFERS *window-exit*
|
|||||||
*:confirm* *:conf*
|
*:confirm* *:conf*
|
||||||
:conf[irm] {command} Execute {command}, and use a dialog when an
|
:conf[irm] {command} Execute {command}, and use a dialog when an
|
||||||
operation has to be confirmed. Can be used on the
|
operation has to be confirmed. Can be used on the
|
||||||
|:q|, |:qa| and |:w| commands (the latter to override
|
|:edit|, |:q|, |:qa| and |:w| commands (the latter to
|
||||||
a read-only setting), and any other command that can
|
override a read-only setting), and any commands that
|
||||||
fail in such a way, such as |:only|, |:buffer|,
|
can fail because of unsaved changes, such as |:only|,
|
||||||
|:bdelete|, etc.
|
|:buffer|, |:bdelete|, etc.
|
||||||
|
|
||||||
Examples: >
|
Examples: >
|
||||||
:confirm w foo
|
:confirm w foo
|
||||||
|
@ -2276,12 +2276,13 @@ v:stderr |channel-id| corresponding to stderr. The value is always 2;
|
|||||||
:call chansend(v:stderr, "error: toaster empty\n")
|
:call chansend(v:stderr, "error: toaster empty\n")
|
||||||
<
|
<
|
||||||
*v:swapname* *swapname-variable*
|
*v:swapname* *swapname-variable*
|
||||||
v:swapname Only valid when executing |SwapExists| autocommands: Name of
|
v:swapname Name of the swapfile found.
|
||||||
the swap file found. Read-only.
|
Only valid during |SwapExists| event.
|
||||||
|
Read-only.
|
||||||
|
|
||||||
*v:swapchoice* *swapchoice-variable*
|
*v:swapchoice* *swapchoice-variable*
|
||||||
v:swapchoice |SwapExists| autocommands can set this to the selected choice
|
v:swapchoice |SwapExists| autocommands can set this to the selected choice
|
||||||
for handling an existing swap file:
|
for handling an existing swapfile:
|
||||||
'o' Open read-only
|
'o' Open read-only
|
||||||
'e' Edit anyway
|
'e' Edit anyway
|
||||||
'r' Recover
|
'r' Recover
|
||||||
|
@ -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
|
• Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers in a
|
||||||
terminal emulator that supports |tui-csiu|.
|
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
|
||||||
• LSP method names are available in |vim.lsp.protocol.Methods|.
|
• LSP method names are available in |vim.lsp.protocol.Methods|.
|
||||||
• Implemented LSP inlay hints: |vim.lsp.inlay_hint()|
|
• Implemented LSP inlay hints: |vim.lsp.inlay_hint()|
|
||||||
|
@ -83,6 +83,15 @@ Detecting an existing swap file ~
|
|||||||
|
|
||||||
You can find this in the user manual, section |11.3|.
|
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 ~
|
Updating the swapfile ~
|
||||||
|
|
||||||
|
@ -139,6 +139,11 @@ nvim_terminal:
|
|||||||
nvim_cmdwin:
|
nvim_cmdwin:
|
||||||
- CmdwinEnter: Limits syntax sync to maxlines=1 in the |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*
|
New Features *nvim-features*
|
||||||
|
|
||||||
|
@ -1147,11 +1147,28 @@ function vim._init_default_autocmds()
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, {
|
vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, {
|
||||||
pattern = '[:>]',
|
pattern = '[:>]',
|
||||||
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
|
group = vim.api.nvim_create_augroup('nvim_cmdwin', {}),
|
||||||
command = 'syntax sync minlines=1 maxlines=1',
|
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
|
end
|
||||||
|
|
||||||
function vim._init_defaults()
|
function vim._init_defaults()
|
||||||
|
4
runtime/lua/vim/_meta/vimfn.lua
generated
4
runtime/lua/vim/_meta/vimfn.lua
generated
@ -9312,8 +9312,8 @@ function vim.fn.swapfilelist() end
|
|||||||
--- user user name
|
--- user user name
|
||||||
--- host host name
|
--- host host name
|
||||||
--- fname original file name
|
--- fname original file name
|
||||||
--- pid PID of the Vim process that created the swap
|
--- pid PID of the Nvim process that created the swap
|
||||||
--- file
|
--- file, or zero if not running.
|
||||||
--- mtime last modification time in seconds
|
--- mtime last modification time in seconds
|
||||||
--- inode Optional: INODE number of the file
|
--- inode Optional: INODE number of the file
|
||||||
--- dirty 1 if file was modified, 0 if not
|
--- dirty 1 if file was modified, 0 if not
|
||||||
|
@ -11123,8 +11123,8 @@ M.funcs = {
|
|||||||
user user name
|
user user name
|
||||||
host host name
|
host host name
|
||||||
fname original file name
|
fname original file name
|
||||||
pid PID of the Vim process that created the swap
|
pid PID of the Nvim process that created the swap
|
||||||
file
|
file, or zero if not running.
|
||||||
mtime last modification time in seconds
|
mtime last modification time in seconds
|
||||||
inode Optional: INODE number of the file
|
inode Optional: INODE number of the file
|
||||||
dirty 1 if file was modified, 0 if not
|
dirty 1 if file was modified, 0 if not
|
||||||
|
@ -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)
|
static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
tv_dict_alloc_ret(rettv);
|
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
|
/// "swapname(expr)" function
|
||||||
|
@ -2491,7 +2491,7 @@ void ex_function(exarg_T *eap)
|
|||||||
} else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
|
} else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
|
||||||
nextcmd = line_arg;
|
nextcmd = line_arg;
|
||||||
} else if (*p != NUL && *p != '"' && p_verbose > 0) {
|
} 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) {
|
if (nextcmd != NULL) {
|
||||||
// Another command follows. If the line came from "eap" we
|
// Another command follows. If the line came from "eap" we
|
||||||
|
@ -168,17 +168,15 @@ enum {
|
|||||||
B0_MAGIC_CHAR = 0x55,
|
B0_MAGIC_CHAR = 0x55,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Block zero holds all info about the swap file. This is the first block in
|
// Block zero holds all info about the swapfile. This is the first block in the file.
|
||||||
// the file.
|
|
||||||
//
|
//
|
||||||
// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing
|
// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing swapfiles unusable!
|
||||||
// swap files unusable!
|
|
||||||
//
|
//
|
||||||
// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
|
// If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!!
|
||||||
//
|
//
|
||||||
// This block is built up of single bytes, to make it portable across
|
// This block is built up of single bytes, to make it portable across
|
||||||
// different machines. b0_magic_* is used to check the byte order and size of
|
// different machines. b0_magic_* is used to check the byte order and size of
|
||||||
// variables, because the rest of the swap file is not portable.
|
// variables, because the rest of the swapfile is not portable.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
|
char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
|
||||||
char b0_version[10]; // Vim version string
|
char b0_version[10]; // Vim version string
|
||||||
@ -210,8 +208,7 @@ typedef struct {
|
|||||||
// EOL_MAC + 1.
|
// EOL_MAC + 1.
|
||||||
#define B0_FF_MASK 3
|
#define B0_FF_MASK 3
|
||||||
|
|
||||||
// Swap file is in directory of edited file. Used to find the file from
|
// Swapfile is in directory of edited file. Used to find the file from different mount points.
|
||||||
// different mount points.
|
|
||||||
#define B0_SAME_DIR 4
|
#define B0_SAME_DIR 4
|
||||||
|
|
||||||
// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
|
// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
|
||||||
@ -289,14 +286,14 @@ int ml_open(buf_T *buf)
|
|||||||
buf->b_p_swf = false;
|
buf->b_p_swf = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When 'updatecount' is non-zero swap file may be opened later.
|
// When 'updatecount' is non-zero swapfile may be opened later.
|
||||||
if (!buf->terminal && p_uc && buf->b_p_swf) {
|
if (!buf->terminal && p_uc && buf->b_p_swf) {
|
||||||
buf->b_may_swap = true;
|
buf->b_may_swap = true;
|
||||||
} else {
|
} else {
|
||||||
buf->b_may_swap = false;
|
buf->b_may_swap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the memfile. No swap file is created yet.
|
// Open the memfile. No swapfile is created yet.
|
||||||
memfile_T *mfp = mf_open(NULL, 0);
|
memfile_T *mfp = mf_open(NULL, 0);
|
||||||
if (mfp == NULL) {
|
if (mfp == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
@ -335,7 +332,7 @@ int ml_open(buf_T *buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Always sync block number 0 to disk, so we can check the file name in
|
// Always sync block number 0 to disk, so we can check the file name in
|
||||||
// the swap file in findswapname(). Don't do this for a help files or
|
// the swapfile in findswapname(). Don't do this for a help files or
|
||||||
// a spell buffer though.
|
// a spell buffer though.
|
||||||
// Only works when there's a swapfile, otherwise it's done when the file
|
// Only works when there's a swapfile, otherwise it's done when the file
|
||||||
// is created.
|
// is created.
|
||||||
@ -386,17 +383,17 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ml_setname() is called when the file name of "buf" has been changed.
|
/// ml_setname() is called when the file name of "buf" has been changed.
|
||||||
/// It may rename the swap file.
|
/// It may rename the swapfile.
|
||||||
void ml_setname(buf_T *buf)
|
void ml_setname(buf_T *buf)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
memfile_T *mfp = buf->b_ml.ml_mfp;
|
memfile_T *mfp = buf->b_ml.ml_mfp;
|
||||||
if (mfp->mf_fd < 0) { // there is no swap file yet
|
if (mfp->mf_fd < 0) { // there is no swapfile yet
|
||||||
// When 'updatecount' is 0 and 'noswapfile' there is no swap file.
|
// When 'updatecount' is 0 and 'noswapfile' there is no swapfile.
|
||||||
// For help files we will make a swap file now.
|
// For help files we will make a swapfile now.
|
||||||
if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) {
|
if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) {
|
||||||
ml_open_file(buf); // create a swap file
|
ml_open_file(buf); // create a swapfile
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -423,13 +420,13 @@ void ml_setname(buf_T *buf)
|
|||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// need to close the swap file before renaming
|
// need to close the swapfile before renaming
|
||||||
if (mfp->mf_fd >= 0) {
|
if (mfp->mf_fd >= 0) {
|
||||||
close(mfp->mf_fd);
|
close(mfp->mf_fd);
|
||||||
mfp->mf_fd = -1;
|
mfp->mf_fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to rename the swap file
|
// try to rename the swapfile
|
||||||
if (vim_rename(mfp->mf_fname, fname) == 0) {
|
if (vim_rename(mfp->mf_fname, fname) == 0) {
|
||||||
success = true;
|
success = true;
|
||||||
mf_free_fnames(mfp);
|
mf_free_fnames(mfp);
|
||||||
@ -440,10 +437,10 @@ void ml_setname(buf_T *buf)
|
|||||||
xfree(fname); // this fname didn't work, try another
|
xfree(fname); // this fname didn't work, try another
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mfp->mf_fd == -1) { // need to (re)open the swap file
|
if (mfp->mf_fd == -1) { // need to (re)open the swapfile
|
||||||
mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0);
|
mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0);
|
||||||
if (mfp->mf_fd < 0) {
|
if (mfp->mf_fd < 0) {
|
||||||
// could not (re)open the swap file, what can we do????
|
// could not (re)open the swapfile, what can we do????
|
||||||
emsg(_("E301: Oops, lost the swap file!!!"));
|
emsg(_("E301: Oops, lost the swap file!!!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -466,7 +463,7 @@ void ml_open_files(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a swap file for an existing memfile, if there is no swap file yet.
|
/// Open a swapfile for an existing memfile, if there is no swapfile yet.
|
||||||
/// If we are unable to find a file name, mf_fname will be NULL
|
/// If we are unable to find a file name, mf_fname will be NULL
|
||||||
/// and the memfile will be in memory only (no recovery possible).
|
/// and the memfile will be in memory only (no recovery possible).
|
||||||
void ml_open_file(buf_T *buf)
|
void ml_open_file(buf_T *buf)
|
||||||
@ -495,7 +492,7 @@ void ml_open_file(buf_T *buf)
|
|||||||
if (*dirp == NUL) {
|
if (*dirp == NUL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// There is a small chance that between choosing the swap file name
|
// There is a small chance that between choosing the swapfile name
|
||||||
// and creating it, another Vim creates the file. In that case the
|
// and creating it, another Vim creates the file. In that case the
|
||||||
// creation will fail and we will use another directory.
|
// creation will fail and we will use another directory.
|
||||||
char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir);
|
char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir);
|
||||||
@ -514,7 +511,7 @@ void ml_open_file(buf_T *buf)
|
|||||||
if (mf_sync(mfp, MFS_ZERO) == OK) {
|
if (mf_sync(mfp, MFS_ZERO) == OK) {
|
||||||
// Mark all blocks that should be in the swapfile as dirty.
|
// Mark all blocks that should be in the swapfile as dirty.
|
||||||
// Needed for when the 'swapfile' option was reset, so that
|
// Needed for when the 'swapfile' option was reset, so that
|
||||||
// the swap file was deleted, and then on again.
|
// the swapfile was deleted, and then on again.
|
||||||
mf_set_dirty(mfp);
|
mf_set_dirty(mfp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -531,12 +528,12 @@ void ml_open_file(buf_T *buf)
|
|||||||
no_wait_return--;
|
no_wait_return--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't try to open a swap file again
|
// don't try to open a swapfile again
|
||||||
buf->b_may_swap = false;
|
buf->b_may_swap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If still need to create a swap file, and starting to edit a not-readonly
|
/// If still need to create a swapfile, and starting to edit a not-readonly
|
||||||
/// file, or reading into an existing buffer, create a swap file now.
|
/// file, or reading into an existing buffer, create a swapfile now.
|
||||||
///
|
///
|
||||||
/// @param newfile reading file into new buffer
|
/// @param newfile reading file into new buffer
|
||||||
void check_need_swap(bool newfile)
|
void check_need_swap(bool newfile)
|
||||||
@ -553,7 +550,7 @@ void check_need_swap(bool newfile)
|
|||||||
|
|
||||||
/// Close memline for buffer 'buf'.
|
/// Close memline for buffer 'buf'.
|
||||||
///
|
///
|
||||||
/// @param del_file if true, delete the swap file
|
/// @param del_file if true, delete the swapfile
|
||||||
void ml_close(buf_T *buf, int del_file)
|
void ml_close(buf_T *buf, int del_file)
|
||||||
{
|
{
|
||||||
if (buf->b_ml.ml_mfp == NULL) { // not open
|
if (buf->b_ml.ml_mfp == NULL) { // not open
|
||||||
@ -643,7 +640,7 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what)
|
|||||||
mf_put(mfp, hp, true, false);
|
mf_put(mfp, hp, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write file name and timestamp into block 0 of a swap file.
|
/// Write file name and timestamp into block 0 of a swapfile.
|
||||||
/// Also set buf->b_mtime.
|
/// Also set buf->b_mtime.
|
||||||
/// Don't use NameBuff[]!!!
|
/// Don't use NameBuff[]!!!
|
||||||
static void set_b0_fname(ZeroBlock *b0p, buf_T *buf)
|
static void set_b0_fname(ZeroBlock *b0p, buf_T *buf)
|
||||||
@ -695,7 +692,7 @@ static void set_b0_fname(ZeroBlock *b0p, buf_T *buf)
|
|||||||
add_b0_fenc(b0p, curbuf);
|
add_b0_fenc(b0p, curbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the
|
/// Update the B0_SAME_DIR flag of the swapfile. It's set if the file and the
|
||||||
/// swapfile for "buf" are in the same directory.
|
/// swapfile for "buf" are in the same directory.
|
||||||
/// This is fail safe: if we are not sure the directories are equal the flag is
|
/// This is fail safe: if we are not sure the directories are equal the flag is
|
||||||
/// not set.
|
/// not set.
|
||||||
@ -724,27 +721,30 @@ static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the process with number "b0p->b0_pid" is still running.
|
/// Returns the PID of the process that owns the swapfile, if it is running.
|
||||||
/// "swap_fname" is the name of the swap file, if it's from before a reboot then
|
///
|
||||||
/// the result is false;
|
/// @param b0p swapfile data
|
||||||
static bool swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname)
|
/// @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;
|
FileInfo st;
|
||||||
double uptime;
|
double uptime;
|
||||||
// If the system rebooted after when the swap file was written then the
|
// If the system rebooted after when the swapfile was written then the
|
||||||
// process can't be running now.
|
// process can't be running now.
|
||||||
if (os_fileinfo(swap_fname, &st)
|
if (os_fileinfo(swap_fname, &st)
|
||||||
&& uv_uptime(&uptime) == 0
|
&& uv_uptime(&uptime) == 0
|
||||||
&& (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) {
|
&& (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.
|
/// Try to recover curbuf from the .swp file.
|
||||||
///
|
///
|
||||||
/// @param checkext if true, check the extension and detect whether it is a
|
/// @param checkext if true, check the extension and detect whether it is a swapfile.
|
||||||
/// swap file.
|
|
||||||
void ml_recover(bool checkext)
|
void ml_recover(bool checkext)
|
||||||
{
|
{
|
||||||
buf_T *buf = NULL;
|
buf_T *buf = NULL;
|
||||||
@ -766,8 +766,8 @@ void ml_recover(bool checkext)
|
|||||||
int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
|
int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
|
||||||
int attr = HL_ATTR(HLF_E);
|
int attr = HL_ATTR(HLF_E);
|
||||||
|
|
||||||
// If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
|
// If the file name ends in ".s[a-w][a-z]" we assume this is the swapfile.
|
||||||
// Otherwise a search is done to find the swap file(s).
|
// Otherwise a search is done to find the swapfile(s).
|
||||||
char *fname = curbuf->b_fname;
|
char *fname = curbuf->b_fname;
|
||||||
if (fname == NULL) { // When there is no file name
|
if (fname == NULL) { // When there is no file name
|
||||||
fname = "";
|
fname = "";
|
||||||
@ -782,17 +782,17 @@ void ml_recover(bool checkext)
|
|||||||
} else {
|
} else {
|
||||||
directly = false;
|
directly = false;
|
||||||
|
|
||||||
// count the number of matching swap files
|
// count the number of matching swapfiles
|
||||||
len = recover_names(fname, false, NULL, 0, NULL);
|
len = recover_names(fname, false, NULL, 0, NULL);
|
||||||
if (len == 0) { // no swap files found
|
if (len == 0) { // no swapfiles found
|
||||||
semsg(_("E305: No swap file found for %s"), fname);
|
semsg(_("E305: No swap file found for %s"), fname);
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
int i;
|
int i;
|
||||||
if (len == 1) { // one swap file found, use it
|
if (len == 1) { // one swapfile found, use it
|
||||||
i = 1;
|
i = 1;
|
||||||
} else { // several swap files found, choose
|
} else { // several swapfiles found, choose
|
||||||
// list the names of the swap files
|
// list the names of the swapfiles
|
||||||
(void)recover_names(fname, true, NULL, 0, NULL);
|
(void)recover_names(fname, true, NULL, 0, NULL);
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
msg_puts(_("Enter number of swap file to use (0 to quit): "));
|
msg_puts(_("Enter number of swap file to use (0 to quit): "));
|
||||||
@ -801,7 +801,7 @@ void ml_recover(bool checkext)
|
|||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// get the swap file name that will be used
|
// get the swapfile name that will be used
|
||||||
(void)recover_names(fname, false, NULL, i, &fname_used);
|
(void)recover_names(fname, false, NULL, i, &fname_used);
|
||||||
}
|
}
|
||||||
if (fname_used == NULL) {
|
if (fname_used == NULL) {
|
||||||
@ -812,7 +812,7 @@ void ml_recover(bool checkext)
|
|||||||
getout(1);
|
getout(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a buffer structure for the swap file that is used for recovery.
|
// Allocate a buffer structure for the swapfile that is used for recovery.
|
||||||
// Only the memline in it is really used.
|
// Only the memline in it is really used.
|
||||||
buf = xmalloc(sizeof(buf_T));
|
buf = xmalloc(sizeof(buf_T));
|
||||||
|
|
||||||
@ -825,7 +825,7 @@ void ml_recover(bool checkext)
|
|||||||
buf->b_ml.ml_locked = NULL; // no locked block
|
buf->b_ml.ml_locked = NULL; // no locked block
|
||||||
buf->b_ml.ml_flags = 0;
|
buf->b_ml.ml_flags = 0;
|
||||||
|
|
||||||
// open the memfile from the old swap file
|
// open the memfile from the old swapfile
|
||||||
p = xstrdup(fname_used); // save "fname_used" for the message:
|
p = xstrdup(fname_used); // save "fname_used" for the message:
|
||||||
// mf_open() will consume "fname_used"!
|
// mf_open() will consume "fname_used"!
|
||||||
mfp = mf_open(fname_used, O_RDONLY);
|
mfp = mf_open(fname_used, O_RDONLY);
|
||||||
@ -837,7 +837,7 @@ void ml_recover(bool checkext)
|
|||||||
buf->b_ml.ml_mfp = mfp;
|
buf->b_ml.ml_mfp = mfp;
|
||||||
|
|
||||||
// The page size set in mf_open() might be different from the page size
|
// The page size set in mf_open() might be different from the page size
|
||||||
// used in the swap file, we must get it from block 0. But to read block
|
// used in the swapfile, we must get it from block 0. But to read block
|
||||||
// 0 we need a page size. Use the minimal size for block 0 here, it will
|
// 0 we need a page size. Use the minimal size for block 0 here, it will
|
||||||
// be set to the real value below.
|
// be set to the real value below.
|
||||||
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
|
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
|
||||||
@ -910,7 +910,7 @@ void ml_recover(bool checkext)
|
|||||||
b0p = hp->bh_data;
|
b0p = hp->bh_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If .swp file name given directly, use name from swap file for buffer.
|
// If .swp file name given directly, use name from swapfile for buffer.
|
||||||
if (directly) {
|
if (directly) {
|
||||||
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
|
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
|
||||||
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
|
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
|
||||||
@ -929,7 +929,7 @@ void ml_recover(bool checkext)
|
|||||||
smsg(0, _("Original file \"%s\""), NameBuff);
|
smsg(0, _("Original file \"%s\""), NameBuff);
|
||||||
msg_putchar('\n');
|
msg_putchar('\n');
|
||||||
|
|
||||||
// check date of swap file and original file
|
// check date of swapfile and original file
|
||||||
FileInfo org_file_info;
|
FileInfo org_file_info;
|
||||||
FileInfo swp_file_info;
|
FileInfo swp_file_info;
|
||||||
long mtime = char_to_long(b0p->b0_mtime);
|
long mtime = char_to_long(b0p->b0_mtime);
|
||||||
@ -968,7 +968,7 @@ void ml_recover(bool checkext)
|
|||||||
0, MAXLNUM, NULL, READ_NEW, false);
|
0, MAXLNUM, NULL, READ_NEW, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the 'fileformat' and 'fileencoding' as stored in the swap file.
|
// Use the 'fileformat' and 'fileencoding' as stored in the swapfile.
|
||||||
if (b0_ff != 0) {
|
if (b0_ff != 0) {
|
||||||
set_fileformat(b0_ff - 1, OPT_LOCAL);
|
set_fileformat(b0_ff - 1, OPT_LOCAL);
|
||||||
}
|
}
|
||||||
@ -1231,7 +1231,7 @@ theend:
|
|||||||
}
|
}
|
||||||
mf_close(mfp, false); // will also xfree(mfp->mf_fname)
|
mf_close(mfp, false); // will also xfree(mfp->mf_fname)
|
||||||
}
|
}
|
||||||
if (buf != NULL) { // may be NULL if swap file not found.
|
if (buf != NULL) { // may be NULL if swapfile not found.
|
||||||
xfree(buf->b_ml.ml_stack);
|
xfree(buf->b_ml.ml_stack);
|
||||||
xfree(buf);
|
xfree(buf);
|
||||||
}
|
}
|
||||||
@ -1243,20 +1243,20 @@ theend:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the names of swap files in current directory and the directory given
|
/// Find the names of swapfiles in current directory and the directory given
|
||||||
/// with the 'directory' option.
|
/// with the 'directory' option.
|
||||||
///
|
///
|
||||||
/// Used to:
|
/// Used to:
|
||||||
/// - list the swap files for "vim -r"
|
/// - list the swapfiles for "vim -r"
|
||||||
/// - count the number of swap files when recovering
|
/// - count the number of swapfiles when recovering
|
||||||
/// - list the swap files when recovering
|
/// - list the swapfiles when recovering
|
||||||
/// - list the swap files for swapfilelist()
|
/// - list the swapfiles for swapfilelist()
|
||||||
/// - find the name of the n'th swap file when recovering
|
/// - find the name of the n'th swapfile when recovering
|
||||||
///
|
///
|
||||||
/// @param fname base for swap file name
|
/// @param fname base for swapfile name
|
||||||
/// @param do_list when true, list the swap file names
|
/// @param do_list when true, list the swapfile names
|
||||||
/// @param ret_list when not NULL add file names to it
|
/// @param ret_list when not NULL add file names to it
|
||||||
/// @param nr when non-zero, return nr'th swap file name
|
/// @param nr when non-zero, return nr'th swapfile name
|
||||||
/// @param fname_out result when "nr" > 0
|
/// @param fname_out result when "nr" > 0
|
||||||
int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fname_out)
|
int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fname_out)
|
||||||
{
|
{
|
||||||
@ -1273,7 +1273,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
|
|||||||
|
|
||||||
if (fname != NULL) {
|
if (fname != NULL) {
|
||||||
#ifdef HAVE_READLINK
|
#ifdef HAVE_READLINK
|
||||||
// Expand symlink in the file name, because the swap file is created
|
// Expand symlink in the file name, because the swapfile is created
|
||||||
// with the actual file instead of with the symlink.
|
// with the actual file instead of with the symlink.
|
||||||
if (resolve_symlink(fname, fname_buf) == OK) {
|
if (resolve_symlink(fname, fname_buf) == OK) {
|
||||||
fname_res = fname_buf;
|
fname_res = fname_buf;
|
||||||
@ -1342,9 +1342,9 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
|
|||||||
num_files = 0;
|
num_files = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When no swap file found, wildcard expansion might have failed (e.g.
|
// When no swapfile found, wildcard expansion might have failed (e.g.
|
||||||
// not able to execute the shell).
|
// not able to execute the shell).
|
||||||
// Try finding a swap file by simply adding ".swp" to the file name.
|
// Try finding a swapfile by simply adding ".swp" to the file name.
|
||||||
if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) {
|
if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) {
|
||||||
char *swapname = modname(fname_res, ".swp", true);
|
char *swapname = modname(fname_res, ".swp", true);
|
||||||
if (swapname != NULL) {
|
if (swapname != NULL) {
|
||||||
@ -1402,7 +1402,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn
|
|||||||
|
|
||||||
if (num_files) {
|
if (num_files) {
|
||||||
for (int i = 0; i < num_files; i++) {
|
for (int i = 0; i < num_files; i++) {
|
||||||
// print the swap file name
|
// print the swapfile name
|
||||||
msg_outnum(++file_count);
|
msg_outnum(++file_count);
|
||||||
msg_puts(". ");
|
msg_puts(". ");
|
||||||
msg_puts(path_tail(files[i]));
|
msg_puts(path_tail(files[i]));
|
||||||
@ -1456,12 +1456,13 @@ char *make_percent_swname(const char *dir, const char *name)
|
|||||||
return d;
|
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".
|
/// @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;
|
int fd;
|
||||||
ZeroBlock b0;
|
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,
|
tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname,
|
||||||
B0_FNAME_SIZE_ORG);
|
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("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("dirty"), b0.b0_dirty ? 1 : 0);
|
||||||
tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino));
|
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).
|
/// @return timestamp (0 when unknown).
|
||||||
static time_t swapfile_info(char *fname)
|
static time_t swapfile_info(char *fname)
|
||||||
@ -1509,7 +1510,7 @@ static time_t swapfile_info(char *fname)
|
|||||||
char uname[B0_UNAME_SIZE];
|
char uname[B0_UNAME_SIZE];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// print the swap file date
|
// print the swapfile date
|
||||||
FileInfo file_info;
|
FileInfo file_info;
|
||||||
if (os_fileinfo(fname, &file_info)) {
|
if (os_fileinfo(fname, &file_info)) {
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
@ -1567,9 +1568,8 @@ static time_t swapfile_info(char *fname)
|
|||||||
if (char_to_long(b0.b0_pid) != 0L) {
|
if (char_to_long(b0.b0_pid) != 0L) {
|
||||||
msg_puts(_("\n process ID: "));
|
msg_puts(_("\n process ID: "));
|
||||||
msg_outnum((int)char_to_long(b0.b0_pid));
|
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)"));
|
msg_puts(_(" (STILL RUNNING)"));
|
||||||
process_still_running = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1589,13 +1589,12 @@ static time_t swapfile_info(char *fname)
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return true if the swap file looks OK and there are no changes, thus it
|
/// @return true if the swapfile looks OK and there are no changes, thus it can be safely deleted.
|
||||||
/// can be safely deleted.
|
|
||||||
static bool swapfile_unchanged(char *fname)
|
static bool swapfile_unchanged(char *fname)
|
||||||
{
|
{
|
||||||
ZeroBlock b0;
|
ZeroBlock b0;
|
||||||
|
|
||||||
// Swap file must exist.
|
// Swapfile must exist.
|
||||||
if (!os_path_exists(fname)) {
|
if (!os_path_exists(fname)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1653,7 +1652,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot)
|
|||||||
{
|
{
|
||||||
int num_names = 0;
|
int num_names = 0;
|
||||||
|
|
||||||
// May also add the file name with a dot prepended, for swap file in same
|
// May also add the file name with a dot prepended, for swapfile in same
|
||||||
// dir as original file.
|
// dir as original file.
|
||||||
if (prepend_dot) {
|
if (prepend_dot) {
|
||||||
names[num_names] = modname(path, ".sw?", true);
|
names[num_names] = modname(path, ".sw?", true);
|
||||||
@ -1663,7 +1662,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot)
|
|||||||
num_names++;
|
num_names++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Form the normal swap file name pattern by appending ".sw?".
|
// Form the normal swapfile name pattern by appending ".sw?".
|
||||||
names[num_names] = concat_fnames(path, ".sw?", false);
|
names[num_names] = concat_fnames(path, ".sw?", false);
|
||||||
if (num_names >= 1) { // check if we have the same name twice
|
if (num_names >= 1) { // check if we have the same name twice
|
||||||
char *p = names[num_names - 1];
|
char *p = names[num_names - 1];
|
||||||
@ -1724,7 +1723,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
|
|||||||
|
|
||||||
/// sync one buffer, including negative blocks
|
/// sync one buffer, including negative blocks
|
||||||
///
|
///
|
||||||
/// after this all the blocks are in the swap file
|
/// after this all the blocks are in the swapfile
|
||||||
///
|
///
|
||||||
/// Used for the :preserve command and when the original file has been
|
/// Used for the :preserve command and when the original file has been
|
||||||
/// changed or deleted.
|
/// changed or deleted.
|
||||||
@ -3132,7 +3131,7 @@ int resolve_symlink(const char *fname, char *buf)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Make swap file name out of the file name and a directory name.
|
/// Make swapfile name out of the file name and a directory name.
|
||||||
///
|
///
|
||||||
/// @return pointer to allocated memory or NULL.
|
/// @return pointer to allocated memory or NULL.
|
||||||
char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
||||||
@ -3141,7 +3140,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
|||||||
#ifdef HAVE_READLINK
|
#ifdef HAVE_READLINK
|
||||||
char fname_buf[MAXPATHL];
|
char fname_buf[MAXPATHL];
|
||||||
|
|
||||||
// Expand symlink in the file name, so that we put the swap file with the
|
// Expand symlink in the file name, so that we put the swapfile with the
|
||||||
// actual file instead of with the symlink.
|
// actual file instead of with the symlink.
|
||||||
if (resolve_symlink(fname, fname_buf) == OK) {
|
if (resolve_symlink(fname, fname_buf) == OK) {
|
||||||
fname_res = fname_buf;
|
fname_res = fname_buf;
|
||||||
@ -3162,7 +3161,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend a '.' to the swap file name for the current directory.
|
// Prepend a '.' to the swapfile name for the current directory.
|
||||||
char *r = modname(fname_res, ".swp",
|
char *r = modname(fname_res, ".swp",
|
||||||
dir_name[0] == '.' && dir_name[1] == NUL);
|
dir_name[0] == '.' && dir_name[1] == NUL);
|
||||||
if (r == NULL) { // out of memory
|
if (r == NULL) { // out of memory
|
||||||
@ -3174,14 +3173,11 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get file name to use for swap file or backup file.
|
/// 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'
|
/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' option "dname".
|
||||||
/// option "dname".
|
/// - If "dname" is ".", return "fname" (swapfile in dir of file).
|
||||||
/// - If "dname" is ".", return "fname" (swap file in dir of file).
|
/// - If "dname" starts with "./", insert "dname" in "fname" (swapfile relative to dir of file).
|
||||||
/// - If "dname" starts with "./", insert "dname" in "fname" (swap file
|
/// - Otherwise, prepend "dname" to the tail of "fname" (swapfile in specific dir).
|
||||||
/// relative to dir of file).
|
|
||||||
/// - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific
|
|
||||||
/// dir).
|
|
||||||
///
|
///
|
||||||
/// The return value is an allocated string and can be NULL.
|
/// The return value is an allocated string and can be NULL.
|
||||||
///
|
///
|
||||||
@ -3212,10 +3208,10 @@ char *get_file_in_dir(char *fname, char *dname)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print the ATTENTION message: info about an existing swap file.
|
/// Print the ATTENTION message: info about an existing swapfile.
|
||||||
///
|
///
|
||||||
/// @param buf buffer being edited
|
/// @param buf buffer being edited
|
||||||
/// @param fname swap file name
|
/// @param fname swapfile name
|
||||||
static void attention_message(buf_T *buf, char *fname)
|
static void attention_message(buf_T *buf, char *fname)
|
||||||
{
|
{
|
||||||
assert(buf->b_fname != NULL);
|
assert(buf->b_fname != NULL);
|
||||||
@ -3299,7 +3295,7 @@ static int do_swapexists(buf_T *buf, char *fname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find out what name to use for the swap file for buffer 'buf'.
|
/// Find out what name to use for the swapfile for buffer 'buf'.
|
||||||
///
|
///
|
||||||
/// Several names are tried to find one that does not exist. Last directory in
|
/// Several names are tried to find one that does not exist. Last directory in
|
||||||
/// option is automatically created.
|
/// option is automatically created.
|
||||||
@ -3308,20 +3304,20 @@ static int do_swapexists(buf_T *buf, char *fname)
|
|||||||
/// not being able to open the swap or undo file.
|
/// not being able to open the swap or undo file.
|
||||||
/// @note May trigger SwapExists autocmd, pointers may change!
|
/// @note May trigger SwapExists autocmd, pointers may change!
|
||||||
///
|
///
|
||||||
/// @param[in] buf Buffer for which swap file names needs to be found.
|
/// @param[in] buf Buffer for which swapfile names needs to be found.
|
||||||
/// @param[in,out] dirp Pointer to a list of directories. When out of memory,
|
/// @param[in,out] dirp Pointer to a list of directories. When out of memory,
|
||||||
/// is set to NULL. Is advanced to the next directory in
|
/// is set to NULL. Is advanced to the next directory in
|
||||||
/// the list otherwise.
|
/// the list otherwise.
|
||||||
/// @param[in] old_fname Allowed existing swap file name. Except for this
|
/// @param[in] old_fname Allowed existing swapfile name. Except for this
|
||||||
/// case, name of the non-existing file is used.
|
/// case, name of the non-existing file is used.
|
||||||
/// @param[in,out] found_existing_dir If points to true, then new directory
|
/// @param[in,out] found_existing_dir If points to true, then new directory
|
||||||
/// for swap file is not created. At first
|
/// for swapfile is not created. At first
|
||||||
/// findswapname() call this argument must
|
/// findswapname() call this argument must
|
||||||
/// point to false. This parameter may only
|
/// point to false. This parameter may only
|
||||||
/// be set to true by this function, it is
|
/// be set to true by this function, it is
|
||||||
/// never set to false.
|
/// never set to false.
|
||||||
///
|
///
|
||||||
/// @return [allocated] Name of the swap file.
|
/// @return [allocated] Name of the swapfile.
|
||||||
static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir)
|
static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir)
|
||||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
|
||||||
{
|
{
|
||||||
@ -3333,7 +3329,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
char *dir_name = xmalloc(dir_len);
|
char *dir_name = xmalloc(dir_len);
|
||||||
(void)copy_option_part(dirp, dir_name, 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);
|
char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -3346,7 +3342,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// check if the swapfile already exists
|
// check if the swapfile already exists
|
||||||
// Extra security check: When a swap file is a symbolic link, this
|
// Extra security check: When a swapfile is a symbolic link, this
|
||||||
// is most likely a symlink attack.
|
// is most likely a symlink attack.
|
||||||
FileInfo file_info;
|
FileInfo file_info;
|
||||||
bool file_or_link_found = os_fileinfo_link(fname, &file_info);
|
bool file_or_link_found = os_fileinfo_link(fname, &file_info);
|
||||||
@ -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
|
// Give an error message, unless recovering, no file name, we are
|
||||||
// viewing a help file or when the path of the file is different
|
// viewing a help file or when the path of the file is different
|
||||||
// (happens when all .swp files are in one directory).
|
// (happens when all .swp files are in one directory).
|
||||||
if (!recoverymode && buf_fname != NULL
|
if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
|
||||||
&& !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
|
|
||||||
int fd;
|
int fd;
|
||||||
ZeroBlock b0;
|
ZeroBlock b0;
|
||||||
int differ = false;
|
int differ = false;
|
||||||
|
|
||||||
// Try to read block 0 from the swap file to get the original
|
// Try to read block 0 from the swapfile to get the original file name (and inode number).
|
||||||
// file name (and inode number).
|
|
||||||
fd = os_open(fname, O_RDONLY, 0);
|
fd = os_open(fname, O_RDONLY, 0);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
|
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
|
// If the swapfile has the same directory as the
|
||||||
// buffer don't compare the directory names, they can
|
// buffer don't compare the directory names, they can
|
||||||
// have a different mountpoint.
|
// have a different mountpoint.
|
||||||
@ -3393,8 +3389,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The name in the swap file may be
|
// The name in the swapfile may be "~user/path/file". Expand it first.
|
||||||
// "~user/path/file". Expand it first.
|
|
||||||
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
|
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
|
||||||
if (fnamecmp_ino(buf->b_ffname, NameBuff,
|
if (fnamecmp_ino(buf->b_ffname, NameBuff,
|
||||||
char_to_long(b0.b0_ino))) {
|
char_to_long(b0.b0_ino))) {
|
||||||
@ -3405,16 +3400,16 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// give the ATTENTION message when there is an old swap file
|
// Show the ATTENTION message when:
|
||||||
// for the current file, and the buffer was not recovered.
|
// - there is an old swapfile for the current file
|
||||||
|
// - the buffer was not recovered
|
||||||
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
|
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
|
||||||
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
|
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
|
||||||
int choice = 0;
|
int choice = 0;
|
||||||
|
|
||||||
process_still_running = false;
|
// It's safe to delete the swapfile if all these are true:
|
||||||
// It's safe to delete the swap file if all these are true:
|
|
||||||
// - the edited file exists
|
// - the edited file exists
|
||||||
// - the swap file has no changes and looks OK
|
// - the swapfile has no changes and looks OK
|
||||||
if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
|
if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) {
|
||||||
choice = 4;
|
choice = 4;
|
||||||
if (p_verbose > 0) {
|
if (p_verbose > 0) {
|
||||||
@ -3430,8 +3425,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
choice = do_swapexists(buf, fname);
|
choice = do_swapexists(buf, fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_running = 0; // Set by attention_message..swapfile_info.
|
||||||
if (choice == 0) {
|
if (choice == 0) {
|
||||||
// Show info about the existing swap file.
|
// Show info about the existing swapfile.
|
||||||
attention_message(buf, fname);
|
attention_message(buf, fname);
|
||||||
|
|
||||||
// We don't want a 'q' typed at the more-prompt
|
// We don't want a 'q' typed at the more-prompt
|
||||||
@ -3459,14 +3455,14 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
xstrlcat(name, sw_msg_2, name_len);
|
xstrlcat(name, sw_msg_2, name_len);
|
||||||
choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"),
|
choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"),
|
||||||
name,
|
name,
|
||||||
process_still_running
|
process_running
|
||||||
? _("&Open Read-Only\n&Edit anyway\n&Recover"
|
? _("&Open Read-Only\n&Edit anyway\n&Recover"
|
||||||
"\n&Quit\n&Abort") :
|
"\n&Quit\n&Abort") :
|
||||||
_("&Open Read-Only\n&Edit anyway\n&Recover"
|
_("&Open Read-Only\n&Edit anyway\n&Recover"
|
||||||
"\n&Delete it\n&Quit\n&Abort"),
|
"\n&Delete it\n&Quit\n&Abort"),
|
||||||
1, NULL, false);
|
1, NULL, false);
|
||||||
|
|
||||||
if (process_still_running && choice >= 4) {
|
if (process_running && choice >= 4) {
|
||||||
choice++; // Skip missing "Delete it" button.
|
choice++; // Skip missing "Delete it" button.
|
||||||
}
|
}
|
||||||
xfree(name);
|
xfree(name);
|
||||||
@ -3477,27 +3473,27 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
|||||||
|
|
||||||
if (choice > 0) {
|
if (choice > 0) {
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case 1:
|
case 1: // "Open Read-Only"
|
||||||
buf->b_p_ro = true;
|
buf->b_p_ro = true;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2: // "Edit anyway"
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3: // "Recover"
|
||||||
swap_exists_action = SEA_RECOVER;
|
swap_exists_action = SEA_RECOVER;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4: // "Delete it"
|
||||||
os_remove(fname);
|
os_remove(fname);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5: // "Quit"
|
||||||
swap_exists_action = SEA_QUIT;
|
swap_exists_action = SEA_QUIT;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6: // "Abort"
|
||||||
swap_exists_action = SEA_QUIT;
|
swap_exists_action = SEA_QUIT;
|
||||||
got_int = true;
|
got_int = true;
|
||||||
break;
|
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)) {
|
if (!os_path_exists(fname)) {
|
||||||
break;
|
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.
|
// First decrement the last char: ".swo", ".swn", etc.
|
||||||
// If that still isn't enough decrement the last but one char: ".svz"
|
// 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 - 1] == 'a') { // ".s?a"
|
||||||
if (fname[n - 2] == 'a') { // ".saa": tried enough, give up
|
if (fname[n - 2] == 'a') { // ".saa": tried enough, give up
|
||||||
emsg(_("E326: Too many swap files found"));
|
emsg(_("E326: Too many swap files found"));
|
||||||
@ -3553,7 +3549,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|
|||||||
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
|
|| b0p->b0_magic_char != B0_MAGIC_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare current file name with file name from swap file.
|
/// Compare current file name with file name from swapfile.
|
||||||
/// Try to use inode numbers when possible.
|
/// Try to use inode numbers when possible.
|
||||||
/// Return non-zero when files are different.
|
/// Return non-zero when files are different.
|
||||||
///
|
///
|
||||||
@ -3563,7 +3559,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|
|||||||
/// because the device number cannot be used over a network.
|
/// because the device number cannot be used over a network.
|
||||||
/// - When a file does not exist yet (editing a new file) there is no inode
|
/// - When a file does not exist yet (editing a new file) there is no inode
|
||||||
/// number.
|
/// number.
|
||||||
/// - The file name in a swap file may not be valid on the current host. The
|
/// - The file name in a swapfile may not be valid on the current host. The
|
||||||
/// "~user" form is used whenever possible to avoid this.
|
/// "~user" form is used whenever possible to avoid this.
|
||||||
///
|
///
|
||||||
/// This is getting complicated, let's make a table:
|
/// This is getting complicated, let's make a table:
|
||||||
@ -3577,7 +3573,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|
|||||||
/// == 0 X OK OK fname_c != fname_s
|
/// == 0 X OK OK fname_c != fname_s
|
||||||
/// X == 0 OK OK fname_c != fname_s
|
/// X == 0 OK OK fname_c != fname_s
|
||||||
///
|
///
|
||||||
/// current file doesn't exist, file for swap file exist, file name(s) not
|
/// current file doesn't exist, file for swapfile exist, file name(s) not
|
||||||
/// available -> probably different
|
/// available -> probably different
|
||||||
/// == 0 != 0 FAIL X true
|
/// == 0 != 0 FAIL X true
|
||||||
/// == 0 != 0 X FAIL true
|
/// == 0 != 0 X FAIL true
|
||||||
@ -3600,11 +3596,11 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|
|||||||
/// without making the block 0 incompatible with 32 bit versions.
|
/// without making the block 0 incompatible with 32 bit versions.
|
||||||
///
|
///
|
||||||
/// @param fname_c current file name
|
/// @param fname_c current file name
|
||||||
/// @param fname_s file name from swap file
|
/// @param fname_s file name from swapfile
|
||||||
static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
|
static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
|
||||||
{
|
{
|
||||||
uint64_t ino_c = 0; // ino of current file
|
uint64_t ino_c = 0; // ino of current file
|
||||||
uint64_t ino_s; // ino of file from swap file
|
uint64_t ino_s; // ino of file from swapfile
|
||||||
char buf_c[MAXPATHL]; // full path of fname_c
|
char buf_c[MAXPATHL]; // full path of fname_c
|
||||||
char buf_s[MAXPATHL]; // full path of fname_s
|
char buf_s[MAXPATHL]; // full path of fname_s
|
||||||
int retval_c; // flag: buf_c valid
|
int retval_c; // flag: buf_c valid
|
||||||
@ -3616,7 +3612,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First we try to get the inode from the file name, because the inode in
|
// First we try to get the inode from the file name, because the inode in
|
||||||
// the swap file may be outdated. If that fails (e.g. this path is not
|
// the swapfile may be outdated. If that fails (e.g. this path is not
|
||||||
// valid on this machine), use the inode from block 0.
|
// valid on this machine), use the inode from block 0.
|
||||||
if (os_fileinfo(fname_s, &file_info)) {
|
if (os_fileinfo(fname_s, &file_info)) {
|
||||||
ino_s = os_fileinfo_inode(&file_info);
|
ino_s = os_fileinfo_inode(&file_info);
|
||||||
@ -3638,7 +3634,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0)
|
|||||||
|
|
||||||
// Can't compare inodes or file names, guess that the files are different,
|
// Can't compare inodes or file names, guess that the files are different,
|
||||||
// unless both appear not to exist at all, then compare with the file name
|
// unless both appear not to exist at all, then compare with the file name
|
||||||
// in the swap file.
|
// in the swapfile.
|
||||||
if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) {
|
if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) {
|
||||||
return strcmp(fname_c, fname_s) != 0;
|
return strcmp(fname_c, fname_s) != 0;
|
||||||
}
|
}
|
||||||
@ -3675,7 +3671,7 @@ static long char_to_long(const char *s_in)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the flags in the first block of the swap file:
|
/// Set the flags in the first block of the swapfile:
|
||||||
/// - file is modified or not: buf->b_changed
|
/// - file is modified or not: buf->b_changed
|
||||||
/// - 'fileformat'
|
/// - 'fileformat'
|
||||||
/// - 'fileencoding'
|
/// - 'fileencoding'
|
||||||
|
@ -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, ...)
|
int smsg(int attr, const char *s, ...)
|
||||||
FUNC_ATTR_PRINTF(2, 3)
|
FUNC_ATTR_PRINTF(2, 3)
|
||||||
{
|
{
|
||||||
@ -757,6 +764,8 @@ void emsg_invreg(int name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Print an error message with unknown number of arguments
|
/// Print an error message with unknown number of arguments
|
||||||
|
///
|
||||||
|
/// @return whether the message was displayed
|
||||||
bool semsg(const char *const fmt, ...)
|
bool semsg(const char *const fmt, ...)
|
||||||
FUNC_ATTR_PRINTF(1, 2)
|
FUNC_ATTR_PRINTF(1, 2)
|
||||||
{
|
{
|
||||||
@ -3337,9 +3346,22 @@ void give_warning(const char *message, bool hl)
|
|||||||
no_wait_return--;
|
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);
|
give_warning(IObuff, hl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +171,7 @@ describe('swapfile detection', function()
|
|||||||
local screen2 = Screen.new(256, 40)
|
local screen2 = Screen.new(256, 40)
|
||||||
screen2:attach()
|
screen2:attach()
|
||||||
exec(init)
|
exec(init)
|
||||||
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
||||||
|
|
||||||
-- With shortmess+=F
|
-- With shortmess+=F
|
||||||
command('set shortmess+=F')
|
command('set shortmess+=F')
|
||||||
@ -219,11 +220,29 @@ describe('swapfile detection', function()
|
|||||||
nvim2:close()
|
nvim2:close()
|
||||||
end)
|
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()
|
-- oldtest: Test_swap_prompt_splitwin()
|
||||||
it('selecting "q" in the attention prompt', function()
|
it('selecting "q" in the attention prompt', function()
|
||||||
exec(init)
|
exec(init)
|
||||||
command('edit Xfile1')
|
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)
|
local screen = Screen.new(75, 18)
|
||||||
screen:set_default_attr_ids({
|
screen:set_default_attr_ids({
|
||||||
@ -235,7 +254,9 @@ describe('swapfile detection', function()
|
|||||||
set_session(nvim1)
|
set_session(nvim1)
|
||||||
screen:attach()
|
screen:attach()
|
||||||
exec(init)
|
exec(init)
|
||||||
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
||||||
feed(':split Xfile1\n')
|
feed(':split Xfile1\n')
|
||||||
|
-- The default SwapExists handler does _not_ skip this prompt.
|
||||||
screen:expect({
|
screen:expect({
|
||||||
any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^')
|
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)
|
set_session(nvim2)
|
||||||
screen:attach()
|
screen:attach()
|
||||||
exec(init)
|
exec(init)
|
||||||
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
||||||
command('set more')
|
command('set more')
|
||||||
command('au bufadd * let foo_w = wincol()')
|
command('au bufadd * let foo_w = wincol()')
|
||||||
feed(':e Xfile1<CR>')
|
feed(':e Xfile1<CR>')
|
||||||
@ -300,8 +322,9 @@ describe('swapfile detection', function()
|
|||||||
nvim2:close()
|
nvim2:close()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- oldtest: Test_nocatch_process_still_running()
|
--- @param swapexists boolean Enable the default SwapExists handler.
|
||||||
it('allows deleting swapfile created before boot vim-patch:8.2.2586', function()
|
--- @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)
|
local screen = Screen.new(75, 30)
|
||||||
screen:set_default_attr_ids({
|
screen:set_default_attr_ids({
|
||||||
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
|
[0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText
|
||||||
@ -311,6 +334,9 @@ describe('swapfile detection', function()
|
|||||||
screen:attach()
|
screen:attach()
|
||||||
|
|
||||||
exec(init)
|
exec(init)
|
||||||
|
if not swapexists then
|
||||||
|
command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog).
|
||||||
|
end
|
||||||
command('set nohidden')
|
command('set nohidden')
|
||||||
|
|
||||||
exec([=[
|
exec([=[
|
||||||
@ -347,12 +373,7 @@ describe('swapfile detection', function()
|
|||||||
os.rename('Xswap', swname)
|
os.rename('Xswap', swname)
|
||||||
|
|
||||||
feed(':edit Xswaptest<CR>')
|
feed(':edit Xswaptest<CR>')
|
||||||
screen:expect({any = table.concat({
|
on_swapfile_running(screen)
|
||||||
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: }^'),
|
|
||||||
}, '.*')})
|
|
||||||
|
|
||||||
feed('e')
|
feed('e')
|
||||||
|
|
||||||
@ -374,7 +395,26 @@ describe('swapfile detection', function()
|
|||||||
}, '.*')})
|
}, '.*')})
|
||||||
|
|
||||||
feed('e')
|
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)
|
||||||
|
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)
|
end)
|
||||||
|
|
||||||
describe('quitting swapfile dialog on startup stops TUI properly', function()
|
describe('quitting swapfile dialog on startup stops TUI properly', function()
|
||||||
|
Reference in New Issue
Block a user