mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +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,15 +3455,15 @@ 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