# HG changeset patch # User Bram Moolenaar # Date 1649088004 -7200 # Node ID 427600f3b1c5a5d21558ca3d928ad2cf584f2df1 # Parent 0a9769423ce4e22179d627c62864ef39d238c721 patch 8.2.4685: when a swap file is found for a popup there is no dialog Commit: https://github.com/vim/vim/commit/188639d75c363dffaf813e8e2209f7350ad1e871 Author: Bram Moolenaar Date: Mon Apr 4 16:57:21 2022 +0100 patch 8.2.4685: when a swap file is found for a popup there is no dialog Problem: When a swap file is found for a popup there is no dialog and the buffer is loaded anyway. Solution: Silently load the buffer read-only. (closes #10073) diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -271,6 +271,11 @@ popup_create({what}, {options}) *popu 'buftype' set to "popup". That buffer will be wiped out once the popup closes. + if {what} is a buffer number and loading the buffer runs into + an existing swap file, it is silently opened read-only, as if + a |SwapExists| autocommand had set |v:swapchoice| to 'o'. + This is because we assume the buffer is only used for viewing. + {options} is a dictionary with many possible entries. See |popup_create-arguments| for details. diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -150,7 +150,8 @@ buffer_ensure_loaded(buf_T *buf) aco_save_T aco; aucmd_prepbuf(&aco, buf); - swap_exists_action = SEA_NONE; + if (swap_exists_action != SEA_READONLY) + swap_exists_action = SEA_NONE; open_buffer(FALSE, NULL, 0); aucmd_restbuf(&aco); } @@ -1053,10 +1054,12 @@ goto_buffer( int count) { bufref_T old_curbuf; + int save_sea = swap_exists_action; set_bufref(&old_curbuf, curbuf); - swap_exists_action = SEA_DIALOG; + if (swap_exists_action == SEA_NONE) + swap_exists_action = SEA_DIALOG; (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, start, dir, count, eap->forceit); if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') @@ -1071,7 +1074,7 @@ goto_buffer( // Quitting means closing the split window, nothing else. win_close(curwin, TRUE); - swap_exists_action = SEA_NONE; + swap_exists_action = save_sea; swap_exists_did_quit = TRUE; #if defined(FEAT_EVAL) diff --git a/src/memline.c b/src/memline.c --- a/src/memline.c +++ b/src/memline.c @@ -4631,19 +4631,22 @@ attention_message( --no_wait_return; } +typedef enum { + SEA_CHOICE_NONE = 0, + SEA_CHOICE_READONLY = 1, + SEA_CHOICE_EDIT = 2, + SEA_CHOICE_RECOVER = 3, + SEA_CHOICE_DELETE = 4, + SEA_CHOICE_QUIT = 5, + SEA_CHOICE_ABORT = 6 +} sea_choice_T; + #if defined(FEAT_EVAL) /* * Trigger the SwapExists autocommands. - * Returns a value for equivalent to do_dialog() (see below): - * 0: still need to ask for a choice - * 1: open read-only - * 2: edit anyway - * 3: recover - * 4: delete it - * 5: quit - * 6: abort + * Returns a value for equivalent to do_dialog(). */ - static int + static sea_choice_T do_swapexists(buf_T *buf, char_u *fname) { set_vim_var_string(VV_SWAPNAME, fname, -1); @@ -4659,15 +4662,15 @@ do_swapexists(buf_T *buf, char_u *fname) switch (*get_vim_var_str(VV_SWAPCHOICE)) { - case 'o': return 1; - case 'e': return 2; - case 'r': return 3; - case 'd': return 4; - case 'q': return 5; - case 'a': return 6; + case 'o': return SEA_CHOICE_READONLY; + case 'e': return SEA_CHOICE_EDIT; + case 'r': return SEA_CHOICE_RECOVER; + case 'd': return SEA_CHOICE_DELETE; + case 'q': return SEA_CHOICE_QUIT; + case 'a': return SEA_CHOICE_ABORT; } - return 0; + return SEA_CHOICE_NONE; } #endif @@ -4986,10 +4989,10 @@ findswapname( if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED) && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { - int choice = 0; - stat_T st; + sea_choice_T choice = SEA_CHOICE_NONE; + stat_T st; #ifdef CREATE_DUMMY_FILE - int did_use_dummy = FALSE; + int did_use_dummy = FALSE; // Avoid getting a warning for the file being created // outside of Vim, it was created at the start of this @@ -5013,7 +5016,7 @@ findswapname( if (mch_stat((char *)buf->b_fname, &st) == 0 && swapfile_unchanged(fname)) { - choice = 4; + choice = SEA_CHOICE_DELETE; if (p_verbose > 0) verb_msg(_("Found a swap file that is not useful, deleting it")); } @@ -5024,13 +5027,20 @@ findswapname( * the response, trigger it. It may return 0 to ask the * user anyway. */ - if (choice == 0 + if (choice == SEA_CHOICE_NONE && swap_exists_action != SEA_NONE && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) choice = do_swapexists(buf, fname); - - if (choice == 0) #endif + + if (choice == SEA_CHOICE_NONE + && swap_exists_action == SEA_READONLY) + { + // always open readonly. + choice = SEA_CHOICE_READONLY; + } + + if (choice == SEA_CHOICE_NONE) { #ifdef FEAT_GUI // If we are supposed to start the GUI but it wasn't @@ -5053,9 +5063,11 @@ findswapname( } #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) - if (swap_exists_action != SEA_NONE && choice == 0) + if (swap_exists_action != SEA_NONE + && choice == SEA_CHOICE_NONE) { char_u *name; + int dialog_result; name = alloc(STRLEN(fname) + STRLEN(_("Swap file \"")) @@ -5067,7 +5079,7 @@ findswapname( 1000, TRUE); STRCAT(name, _("\" already exists!")); } - choice = do_dialog(VIM_WARNING, + dialog_result = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"), name == NULL ? (char_u *)_("Swap file already exists!") @@ -5079,9 +5091,11 @@ findswapname( (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), 1, NULL, FALSE); # ifdef HAVE_PROCESS_STILL_RUNNING - if (process_still_running && choice >= 4) - choice++; // Skip missing "Delete it" button + if (process_still_running && dialog_result >= 4) + // compensate for missing "Delete it" button + dialog_result++; # endif + choice = dialog_result; vim_free(name); // pretend screen didn't scroll, need redraw anyway @@ -5090,41 +5104,37 @@ findswapname( } #endif - if (choice > 0) + switch (choice) { - switch (choice) - { - case 1: - buf->b_p_ro = TRUE; - break; - case 2: - break; - case 3: - swap_exists_action = SEA_RECOVER; - break; - case 4: - mch_remove(fname); - break; - case 5: - swap_exists_action = SEA_QUIT; - break; - case 6: - swap_exists_action = SEA_QUIT; - got_int = TRUE; - break; - } - - // If the file was deleted this fname can be used. - if (mch_getperm(fname) < 0) + case SEA_CHOICE_READONLY: + buf->b_p_ro = TRUE; + break; + case SEA_CHOICE_EDIT: + break; + case SEA_CHOICE_RECOVER: + swap_exists_action = SEA_RECOVER; + break; + case SEA_CHOICE_DELETE: + mch_remove(fname); + break; + case SEA_CHOICE_QUIT: + swap_exists_action = SEA_QUIT; + break; + case SEA_CHOICE_ABORT: + swap_exists_action = SEA_QUIT; + got_int = TRUE; + break; + case SEA_CHOICE_NONE: + msg_puts("\n"); + if (msg_silent == 0) + // call wait_return() later + need_wait_return = TRUE; break; } - else - { - msg_puts("\n"); - if (msg_silent == 0) - // call wait_return() later - need_wait_return = TRUE; - } + + // If the file was deleted this fname can be used. + if (choice != SEA_CHOICE_NONE && mch_getperm(fname) < 0) + break; #ifdef CREATE_DUMMY_FILE // Going to try another name, need the dummy file again. diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1989,7 +1989,9 @@ popup_create(typval_T *argvars, typval_T new_buffer = FALSE; win_init_popup_win(wp, buf); set_local_options_default(wp, FALSE); + swap_exists_action = SEA_READONLY; buffer_ensure_loaded(buf); + swap_exists_action = SEA_NONE; } else { diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2775,6 +2775,26 @@ func Test_popupwin_with_buffer() call delete('XsomeFile') endfunc +func Test_popupwin_buffer_with_swapfile() + call writefile(['some text', 'in a buffer'], 'XopenFile') + call writefile([''], '.XopenFile.swp') + let g:ignoreSwapExists = 1 + + let bufnr = bufadd('XopenFile') + call assert_equal(0, bufloaded(bufnr)) + let winid = popup_create(bufnr, {'hidden': 1}) + call assert_equal(1, bufloaded(bufnr)) + call popup_close(winid) + + exe 'buffer ' .. bufnr + call assert_equal(1, &readonly) + bwipe! + + call delete('XopenFile') + call delete('.XopenFile.swp') + unlet g:ignoreSwapExists +endfunc + func Test_popupwin_terminal_buffer() CheckFeature terminal CheckUnix diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4685, +/**/ 4684, /**/ 4683, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -1250,6 +1250,7 @@ extern int (*dyn_libintl_wputenv)(const #define SEA_DIALOG 1 // use dialog when possible #define SEA_QUIT 2 // quit editing the file #define SEA_RECOVER 3 // recover the file +#define SEA_READONLY 4 // no dialog, mark buffer as read-only /* * Minimal size for block 0 of a swap file.