mirror of
https://github.com/neovim/neovim.git
synced 2024-12-23 20:55:18 -07: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:
parent
1e7e9ee91f
commit
29fe883aa9
@ -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
|
||||
|
4
runtime/doc/builtin.txt
generated
4
runtime/doc/builtin.txt
generated
@ -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
|
||||
|
@ -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
|
||||
|
@ -2276,12 +2276,13 @@ 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
|
||||
for handling an existing swap file:
|
||||
for handling an existing swapfile:
|
||||
'o' Open read-only
|
||||
'e' Edit anyway
|
||||
'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
|
||||
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()|
|
||||
|
@ -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 ~
|
||||
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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()
|
||||
|
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
|
||||
--- 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -168,17 +168,15 @@ 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!!
|
||||
//
|
||||
// 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
|
||||
// variables, because the rest of the swap file is not portable.
|
||||
// variables, because the rest of the swapfile is not portable.
|
||||
typedef struct {
|
||||
char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.
|
||||
char b0_version[10]; // Vim version string
|
||||
@ -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.
|
||||
@ -289,14 +286,14 @@ int ml_open(buf_T *buf)
|
||||
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) {
|
||||
buf->b_may_swap = true;
|
||||
} else {
|
||||
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);
|
||||
if (mfp == NULL) {
|
||||
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
|
||||
// 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.
|
||||
// Only works when there's a swapfile, otherwise it's done when the file
|
||||
// is created.
|
||||
@ -386,17 +383,17 @@ error:
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
memfile_T *mfp = buf->b_ml.ml_mfp;
|
||||
if (mfp->mf_fd < 0) { // there is no swap file yet
|
||||
// When 'updatecount' is 0 and 'noswapfile' there is no swap file.
|
||||
// For help files we will make a swap file now.
|
||||
if (mfp->mf_fd < 0) { // there is no swapfile yet
|
||||
// When 'updatecount' is 0 and 'noswapfile' there is no swapfile.
|
||||
// For help files we will make a swapfile now.
|
||||
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;
|
||||
}
|
||||
@ -423,13 +420,13 @@ void ml_setname(buf_T *buf)
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
// need to close the swap file before renaming
|
||||
// need to close the swapfile before renaming
|
||||
if (mfp->mf_fd >= 0) {
|
||||
close(mfp->mf_fd);
|
||||
mfp->mf_fd = -1;
|
||||
}
|
||||
|
||||
// try to rename the swap file
|
||||
// try to rename the swapfile
|
||||
if (vim_rename(mfp->mf_fname, fname) == 0) {
|
||||
success = true;
|
||||
mf_free_fnames(mfp);
|
||||
@ -440,10 +437,10 @@ void ml_setname(buf_T *buf)
|
||||
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);
|
||||
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!!!"));
|
||||
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
|
||||
/// and the memfile will be in memory only (no recovery possible).
|
||||
void ml_open_file(buf_T *buf)
|
||||
@ -495,7 +492,7 @@ void ml_open_file(buf_T *buf)
|
||||
if (*dirp == NUL) {
|
||||
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
|
||||
// creation will fail and we will use another directory.
|
||||
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) {
|
||||
// Mark all blocks that should be in the swapfile as dirty.
|
||||
// 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);
|
||||
break;
|
||||
}
|
||||
@ -531,12 +528,12 @@ void ml_open_file(buf_T *buf)
|
||||
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;
|
||||
}
|
||||
|
||||
/// If still need to create a swap file, and starting to edit a not-readonly
|
||||
/// file, or reading into an existing buffer, create a swap file now.
|
||||
/// If still need to create a swapfile, and starting to edit a not-readonly
|
||||
/// file, or reading into an existing buffer, create a swapfile now.
|
||||
///
|
||||
/// @param newfile reading file into new buffer
|
||||
void check_need_swap(bool newfile)
|
||||
@ -553,7 +550,7 @@ void check_need_swap(bool newfile)
|
||||
|
||||
/// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Don't use NameBuff[]!!!
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// This is fail safe: if we are not sure the directories are equal the flag is
|
||||
/// 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.
|
||||
/// "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;
|
||||
// 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.
|
||||
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;
|
||||
@ -766,8 +766,8 @@ void ml_recover(bool checkext)
|
||||
int called_from_main = (curbuf->b_ml.ml_mfp == NULL);
|
||||
int attr = HL_ATTR(HLF_E);
|
||||
|
||||
// If the file name ends in ".s[a-w][a-z]" we assume this is the swap file.
|
||||
// Otherwise a search is done to find the swap file(s).
|
||||
// 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 swapfile(s).
|
||||
char *fname = curbuf->b_fname;
|
||||
if (fname == NULL) { // When there is no file name
|
||||
fname = "";
|
||||
@ -782,17 +782,17 @@ void ml_recover(bool checkext)
|
||||
} else {
|
||||
directly = false;
|
||||
|
||||
// count the number of matching swap files
|
||||
// count the number of matching swapfiles
|
||||
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);
|
||||
goto theend;
|
||||
}
|
||||
int i;
|
||||
if (len == 1) { // one swap file found, use it
|
||||
if (len == 1) { // one swapfile found, use it
|
||||
i = 1;
|
||||
} else { // several swap files found, choose
|
||||
// list the names of the swap files
|
||||
} else { // several swapfiles found, choose
|
||||
// list the names of the swapfiles
|
||||
(void)recover_names(fname, true, NULL, 0, NULL);
|
||||
msg_putchar('\n');
|
||||
msg_puts(_("Enter number of swap file to use (0 to quit): "));
|
||||
@ -801,7 +801,7 @@ void ml_recover(bool checkext)
|
||||
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);
|
||||
}
|
||||
if (fname_used == NULL) {
|
||||
@ -812,7 +812,7 @@ void ml_recover(bool checkext)
|
||||
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.
|
||||
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_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:
|
||||
// mf_open() will consume "fname_used"!
|
||||
mfp = mf_open(fname_used, O_RDONLY);
|
||||
@ -837,7 +837,7 @@ void ml_recover(bool checkext)
|
||||
buf->b_ml.ml_mfp = mfp;
|
||||
|
||||
// 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
|
||||
// be set to the real value below.
|
||||
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
|
||||
@ -910,7 +910,7 @@ void ml_recover(bool checkext)
|
||||
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) {
|
||||
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
|
||||
if (setfname(curbuf, NameBuff, NULL, true) == FAIL) {
|
||||
@ -929,7 +929,7 @@ void ml_recover(bool checkext)
|
||||
smsg(0, _("Original file \"%s\""), NameBuff);
|
||||
msg_putchar('\n');
|
||||
|
||||
// check date of swap file and original file
|
||||
// check date of swapfile and original file
|
||||
FileInfo org_file_info;
|
||||
FileInfo swp_file_info;
|
||||
long mtime = char_to_long(b0p->b0_mtime);
|
||||
@ -968,7 +968,7 @@ void ml_recover(bool checkext)
|
||||
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) {
|
||||
set_fileformat(b0_ff - 1, OPT_LOCAL);
|
||||
}
|
||||
@ -1231,7 +1231,7 @@ theend:
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -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.
|
||||
///
|
||||
/// Used to:
|
||||
/// - list the swap files for "vim -r"
|
||||
/// - count the number of swap files when recovering
|
||||
/// - list the swap files when recovering
|
||||
/// - list the swap files for swapfilelist()
|
||||
/// - find the name of the n'th swap file when recovering
|
||||
/// - list the swapfiles for "vim -r"
|
||||
/// - count the number of swapfiles when recovering
|
||||
/// - list the swapfiles when recovering
|
||||
/// - list the swapfiles for swapfilelist()
|
||||
/// - find the name of the n'th swapfile when recovering
|
||||
///
|
||||
/// @param fname base for swap file name
|
||||
/// @param do_list when true, list the swap file names
|
||||
/// @param fname base for swapfile name
|
||||
/// @param do_list when true, list the swapfile names
|
||||
/// @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
|
||||
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) {
|
||||
#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.
|
||||
if (resolve_symlink(fname, fname_buf) == OK) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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).
|
||||
// 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) {
|
||||
char *swapname = modname(fname_res, ".swp", true);
|
||||
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) {
|
||||
for (int i = 0; i < num_files; i++) {
|
||||
// print the swap file name
|
||||
// print the swapfile name
|
||||
msg_outnum(++file_count);
|
||||
msg_puts(". ");
|
||||
msg_puts(path_tail(files[i]));
|
||||
@ -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)
|
||||
@ -1509,7 +1510,7 @@ static time_t swapfile_info(char *fname)
|
||||
char uname[B0_UNAME_SIZE];
|
||||
#endif
|
||||
|
||||
// print the swap file date
|
||||
// print the swapfile date
|
||||
FileInfo file_info;
|
||||
if (os_fileinfo(fname, &file_info)) {
|
||||
#ifdef UNIX
|
||||
@ -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,13 +1589,12 @@ 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;
|
||||
|
||||
// Swap file must exist.
|
||||
// Swapfile must exist.
|
||||
if (!os_path_exists(fname)) {
|
||||
return false;
|
||||
}
|
||||
@ -1653,7 +1652,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot)
|
||||
{
|
||||
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.
|
||||
if (prepend_dot) {
|
||||
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++;
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (num_names >= 1) { // check if we have the same name twice
|
||||
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
|
||||
///
|
||||
/// 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
|
||||
/// changed or deleted.
|
||||
@ -3132,7 +3131,7 @@ int resolve_symlink(const char *fname, char *buf)
|
||||
}
|
||||
#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.
|
||||
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
|
||||
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.
|
||||
if (resolve_symlink(fname, fname_buf) == OK) {
|
||||
fname_res = fname_buf;
|
||||
@ -3162,7 +3161,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
||||
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",
|
||||
dir_name[0] == '.' && dir_name[1] == NUL);
|
||||
if (r == NULL) { // out of memory
|
||||
@ -3174,14 +3173,11 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name)
|
||||
return s;
|
||||
}
|
||||
|
||||
/// Get file name to use for swap file or backup file.
|
||||
/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
|
||||
/// option "dname".
|
||||
/// - If "dname" is ".", return "fname" (swap file 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).
|
||||
/// 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".
|
||||
/// - If "dname" is ".", return "fname" (swapfile in dir of file).
|
||||
/// - 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.
|
||||
///
|
||||
@ -3212,10 +3208,10 @@ char *get_file_in_dir(char *fname, char *dname)
|
||||
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 fname swap file name
|
||||
/// @param fname swapfile name
|
||||
static void attention_message(buf_T *buf, char *fname)
|
||||
{
|
||||
assert(buf->b_fname != NULL);
|
||||
@ -3299,7 +3295,7 @@ static int do_swapexists(buf_T *buf, char *fname)
|
||||
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
|
||||
/// 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.
|
||||
/// @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,
|
||||
/// is set to NULL. Is advanced to the next directory in
|
||||
/// 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.
|
||||
/// @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
|
||||
/// point to false. This parameter may only
|
||||
/// be set to true by this function, it is
|
||||
/// 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)
|
||||
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);
|
||||
(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) {
|
||||
@ -3346,7 +3342,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
|
||||
break;
|
||||
}
|
||||
// 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.
|
||||
FileInfo 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
|
||||
// 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,16 +3400,16 @@ 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 swap file if all these are true:
|
||||
// It's safe to delete the swapfile if all these are true:
|
||||
// - 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)) {
|
||||
choice = 4;
|
||||
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);
|
||||
}
|
||||
|
||||
process_running = 0; // Set by attention_message..swapfile_info.
|
||||
if (choice == 0) {
|
||||
// Show info about the existing swap file.
|
||||
// Show info about the existing swapfile.
|
||||
attention_message(buf, fname);
|
||||
|
||||
// We don't want a 'q' typed at the more-prompt
|
||||
@ -3459,15 +3455,15 @@ 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) {
|
||||
choice++; // Skip missing "Delete it" button.
|
||||
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"));
|
||||
@ -3553,7 +3549,7 @@ static int b0_magic_wrong(ZeroBlock *b0p)
|
||||
|| 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.
|
||||
/// 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.
|
||||
/// - When a file does not exist yet (editing a new file) there is no inode
|
||||
/// 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.
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
/// == 0 != 0 FAIL X 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.
|
||||
///
|
||||
/// @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)
|
||||
{
|
||||
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_s[MAXPATHL]; // full path of fname_s
|
||||
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
|
||||
// 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.
|
||||
if (os_fileinfo(fname_s, &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,
|
||||
// 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) {
|
||||
return strcmp(fname_c, fname_s) != 0;
|
||||
}
|
||||
@ -3675,7 +3671,7 @@ static long char_to_long(const char *s_in)
|
||||
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
|
||||
/// - 'fileformat'
|
||||
/// - '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, ...)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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,7 +395,26 @@ 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()
|
||||
|
Loading…
Reference in New Issue
Block a user