To: vim_dev@googlegroups.com Subject: Patch 8.1.0228 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0228 Problem: Dropping files is ignored while Vim is busy. Solution: Postpone the effect of dropping files until it's safe. Files: src/ex_docmd.c, src/proto/ex_docmd.pro, src/gui.c, src/gui.h, src/screen.c, src/main.c, src/gui_mac.c *** ../vim-8.1.0227/src/ex_docmd.c 2018-07-28 17:07:48.608154066 +0200 --- src/ex_docmd.c 2018-07-29 17:22:20.940320176 +0200 *************** *** 7859,7915 **** do_shell(NULL, 0); } ! #if defined(HAVE_DROP_FILE) \ ! || (defined(FEAT_GUI_GTK) && defined(FEAT_DND)) \ ! || defined(FEAT_GUI_MSWIN) \ ! || defined(FEAT_GUI_MAC) \ ! || defined(PROTO) ! /* ! * Handle a file drop. The code is here because a drop is *nearly* like an ! * :args command, but not quite (we have a list of exact filenames, so we ! * don't want to (a) parse a command line, or (b) expand wildcards. So the ! * code is very similar to :args and hence needs access to a lot of the static ! * functions in this file. ! * ! * The list should be allocated using alloc(), as should each item in the ! * list. This function takes over responsibility for freeing the list. ! * ! * XXX The list is made into the argument list. This is freed using ! * FreeWild(), which does a series of vim_free() calls. ! */ ! void ! handle_drop( ! int filec, /* the number of files dropped */ ! char_u **filev, /* the list of files dropped */ ! int split) /* force splitting the window */ { exarg_T ea; int save_msg_scroll = msg_scroll; ! /* Postpone this while editing the command line. */ ! if (text_locked()) ! return; ! if (curbuf_locked()) ! return; ! ! /* When the screen is being updated we should not change buffers and ! * windows structures, it may cause freed memory to be used. */ ! if (updating_screen) ! return; /* Check whether the current buffer is changed. If so, we will need * to split the current window or data could be lost. * We don't need to check if the 'hidden' option is set, as in this * case the buffer won't be lost. */ ! if (!buf_hide(curbuf) && !split) { ++emsg_off; ! split = check_changed(curbuf, CCGD_AW); --emsg_off; } ! if (split) { if (win_split(0, 0) == FAIL) return; --- 7859,7895 ---- do_shell(NULL, 0); } ! #if defined(HAVE_DROP_FILE) || defined(PROTO) ! static int drop_busy = FALSE; ! static int drop_filec; ! static char_u **drop_filev = NULL; ! static int drop_split; ! static void (*drop_callback)(void *); ! static void *drop_cookie; ! ! static void ! handle_drop_internal(void) { exarg_T ea; int save_msg_scroll = msg_scroll; ! // Setting the argument list may cause screen updates and being called ! // recursively. Avoid that by setting drop_busy. ! drop_busy = TRUE; /* Check whether the current buffer is changed. If so, we will need * to split the current window or data could be lost. * We don't need to check if the 'hidden' option is set, as in this * case the buffer won't be lost. */ ! if (!buf_hide(curbuf) && !drop_split) { ++emsg_off; ! drop_split = check_changed(curbuf, CCGD_AW); --emsg_off; } ! if (drop_split) { if (win_split(0, 0) == FAIL) return; *************** *** 7924,7930 **** /* * Set up the new argument list. */ ! alist_set(ALIST(curwin), filec, filev, FALSE, NULL, 0); /* * Move to the first file. --- 7904,7910 ---- /* * Set up the new argument list. */ ! alist_set(ALIST(curwin), drop_filec, drop_filev, FALSE, NULL, 0); /* * Move to the first file. *************** *** 7942,7947 **** --- 7922,7999 ---- * unexpectedly. The screen will be redrawn by the caller, thus * msg_scroll being set by displaying a message is irrelevant. */ msg_scroll = save_msg_scroll; + + if (drop_callback != NULL) + drop_callback(drop_cookie); + + drop_filev = NULL; + drop_busy = FALSE; + } + + /* + * Handle a file drop. The code is here because a drop is *nearly* like an + * :args command, but not quite (we have a list of exact filenames, so we + * don't want to (a) parse a command line, or (b) expand wildcards. So the + * code is very similar to :args and hence needs access to a lot of the static + * functions in this file. + * + * The "filev" list must have been allocated using alloc(), as should each item + * in the list. This function takes over responsibility for freeing the "filev" + * list. + */ + void + handle_drop( + int filec, // the number of files dropped + char_u **filev, // the list of files dropped + int split, // force splitting the window + void (*callback)(void *), // to be called after setting the argument + // list + void *cookie) // argument for "callback" (allocated) + { + // Cannot handle recursive drops, finish the pending one. + if (drop_busy) + { + FreeWild(filec, filev); + vim_free(cookie); + return; + } + + // When calling handle_drop() more than once in a row we only use the last + // one. + if (drop_filev != NULL) + { + FreeWild(drop_filec, drop_filev); + vim_free(drop_cookie); + } + + drop_filec = filec; + drop_filev = filev; + drop_split = split; + drop_callback = callback; + drop_cookie = cookie; + + // Postpone this when: + // - editing the command line + // - not possible to change the current buffer + // - updating the screen + // As it may change buffers and window structures that are in use and cause + // freed memory to be used. + if (text_locked() || curbuf_locked() || updating_screen) + return; + + handle_drop_internal(); + } + + /* + * To be called when text is unlocked, curbuf is unlocked or updating_screen is + * reset: Handle a postponed drop. + */ + void + handle_any_postponed_drop(void) + { + if (!drop_busy && drop_filev != NULL + && !text_locked() && !curbuf_locked() && !updating_screen) + handle_drop_internal(); } #endif *** ../vim-8.1.0227/src/proto/ex_docmd.pro 2018-05-17 13:52:34.000000000 +0200 --- src/proto/ex_docmd.pro 2018-07-29 16:58:19.612440949 +0200 *************** *** 31,37 **** void tabpage_close(int forceit); void tabpage_close_other(tabpage_T *tp, int forceit); void ex_all(exarg_T *eap); ! void handle_drop(int filec, char_u **filev, int split); void alist_clear(alist_T *al); void alist_init(alist_T *al); void alist_unlink(alist_T *al); --- 31,38 ---- void tabpage_close(int forceit); void tabpage_close_other(tabpage_T *tp, int forceit); void ex_all(exarg_T *eap); ! void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie); ! void handle_any_postponed_drop(void); void alist_clear(alist_T *al); void alist_init(alist_T *al); void alist_unlink(alist_T *al); *** ../vim-8.1.0227/src/gui.c 2018-07-29 16:09:14.636945607 +0200 --- src/gui.c 2018-07-29 16:52:45.218249227 +0200 *************** *** 5383,5392 **** #endif ! #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ ! || defined(FEAT_GUI_MSWIN) \ ! || defined(FEAT_GUI_MAC) \ ! || defined(PROTO) static void gui_wingoto_xy(int x, int y); --- 5383,5389 ---- #endif ! #if defined(HAVE_DROP_FILE) || defined(PROTO) static void gui_wingoto_xy(int x, int y); *************** *** 5409,5414 **** --- 5406,5447 ---- } /* + * Function passed to handle_drop() for the actions to be done after the + * argument list has been updated. + */ + static void + drop_callback(void *cookie) + { + char_u *p = cookie; + + /* If Shift held down, change to first file's directory. If the first + * item is a directory, change to that directory (and let the explorer + * plugin show the contents). */ + if (p != NULL) + { + if (mch_isdir(p)) + { + if (mch_chdir((char *)p) == 0) + shorten_fnames(TRUE); + } + else if (vim_chdirfile(p, "drop") == OK) + shorten_fnames(TRUE); + vim_free(p); + } + + /* Update the screen display */ + update_screen(NOT_VALID); + # ifdef FEAT_MENU + gui_update_menus(0); + # endif + #ifdef FEAT_TITLE + maketitle(); + #endif + setcursor(); + out_flush_cursor(FALSE, FALSE); + } + + /* * Process file drop. Mouse cursor position, key modifiers, name of files * and count of files are given. Argument "fnames[count]" has full pathnames * of dropped files, they will be freed in this function, and caller can't use *************** *** 5488,5520 **** vim_free(fnames); } else ! handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0); ! ! /* If Shift held down, change to first file's directory. If the first ! * item is a directory, change to that directory (and let the explorer ! * plugin show the contents). */ ! if (p != NULL) ! { ! if (mch_isdir(p)) ! { ! if (mch_chdir((char *)p) == 0) ! shorten_fnames(TRUE); ! } ! else if (vim_chdirfile(p, "drop") == OK) ! shorten_fnames(TRUE); ! vim_free(p); ! } ! ! /* Update the screen display */ ! update_screen(NOT_VALID); ! # ifdef FEAT_MENU ! gui_update_menus(0); ! # endif ! #ifdef FEAT_TITLE ! maketitle(); ! #endif ! setcursor(); ! out_flush_cursor(FALSE, FALSE); } entered = FALSE; --- 5521,5528 ---- vim_free(fnames); } else ! handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0, ! drop_callback, (void *)p); } entered = FALSE; *** ../vim-8.1.0227/src/gui.h 2018-05-05 18:42:37.000000000 +0200 --- src/gui.h 2018-07-29 16:52:19.418385701 +0200 *************** *** 65,72 **** /* * GUIs that support dropping files on a running Vim. */ ! #if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MAC) \ ! || defined(FEAT_GUI_GTK) # define HAVE_DROP_FILE #endif --- 65,73 ---- /* * GUIs that support dropping files on a running Vim. */ ! #if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \ ! || defined(FEAT_GUI_MSWIN) \ ! || defined(FEAT_GUI_MAC) # define HAVE_DROP_FILE #endif *** ../vim-8.1.0227/src/screen.c 2018-07-29 16:09:14.644945560 +0200 --- src/screen.c 2018-07-29 16:51:41.606584708 +0200 *************** *** 526,531 **** --- 526,537 ---- #ifdef FEAT_TERMINAL term_check_channel_closed_recently(); #endif + + #ifdef HAVE_DROP_FILE + // If handle_drop() was called while updating_screen was TRUE need to + // handle the drop now. + handle_any_postponed_drop(); + #endif } /* *** ../vim-8.1.0227/src/main.c 2018-06-23 16:12:15.784258242 +0200 --- src/main.c 2018-07-29 17:02:36.091020303 +0200 *************** *** 911,917 **** /* * Call the main command loop. This never returns. ! */ main_loop(FALSE, FALSE); #endif /* NO_VIM_MAIN */ --- 911,917 ---- /* * Call the main command loop. This never returns. ! */ main_loop(FALSE, FALSE); #endif /* NO_VIM_MAIN */ *************** *** 1155,1163 **** else if (do_redraw || stuff_empty()) { #ifdef FEAT_GUI ! /* If ui_breakcheck() was used a resize may have been postponed. */ gui_may_resize_shell(); #endif /* Trigger CursorMoved if the cursor moved. */ if (!finish_op && ( has_cursormoved() --- 1155,1169 ---- else if (do_redraw || stuff_empty()) { #ifdef FEAT_GUI ! // If ui_breakcheck() was used a resize may have been postponed. gui_may_resize_shell(); #endif + #ifdef HAVE_DROP_FILE + // If files were dropped while text was locked or the curbuf was + // locked, this would be a good time to handle the drop. + handle_any_postponed_drop(); + #endif + /* Trigger CursorMoved if the cursor moved. */ if (!finish_op && ( has_cursormoved() *** ../vim-8.1.0227/src/gui_mac.c 2018-03-06 18:53:06.000000000 +0100 --- src/gui_mac.c 2018-07-29 17:33:07.670760672 +0200 *************** *** 1007,1012 **** --- 1007,1061 ---- long theDate; // modification date/time }; + static long drop_numFiles; + static short drop_gotPosition; + static SelectionRange drop_thePosition; + + static void + drop_callback(void *cookie UNUSED) + { + /* TODO: Handle the goto/select line more cleanly */ + if ((drop_numFiles == 1) & (drop_gotPosition)) + { + if (drop_thePosition.lineNum >= 0) + { + lnum = drop_thePosition.lineNum + 1; + /* oap->motion_type = MLINE; + setpcmark();*/ + if (lnum < 1L) + lnum = 1L; + else if (lnum > curbuf->b_ml.ml_line_count) + lnum = curbuf->b_ml.ml_line_count; + curwin->w_cursor.lnum = lnum; + curwin->w_cursor.col = 0; + /* beginline(BL_SOL | BL_FIX);*/ + } + else + goto_byte(drop_thePosition.startRange + 1); + } + + /* Update the screen display */ + update_screen(NOT_VALID); + + /* Select the text if possible */ + if (drop_gotPosition) + { + VIsual_active = TRUE; + VIsual_select = FALSE; + VIsual = curwin->w_cursor; + if (drop_thePosition.lineNum < 0) + { + VIsual_mode = 'v'; + goto_byte(drop_thePosition.endRange); + } + else + { + VIsual_mode = 'V'; + VIsual.col = 0; + } + } + } + /* The IDE uses the optional keyAEPosition parameter to tell the ed- itor the selection range. If lineNum is zero or greater, scroll the text to the specified line. If lineNum is less than zero, use the values in *************** *** 1113,1160 **** } /* Handle the drop, :edit to get to the file */ ! handle_drop(numFiles, fnames, FALSE); ! ! /* TODO: Handle the goto/select line more cleanly */ ! if ((numFiles == 1) & (gotPosition)) ! { ! if (thePosition.lineNum >= 0) ! { ! lnum = thePosition.lineNum + 1; ! /* oap->motion_type = MLINE; ! setpcmark();*/ ! if (lnum < 1L) ! lnum = 1L; ! else if (lnum > curbuf->b_ml.ml_line_count) ! lnum = curbuf->b_ml.ml_line_count; ! curwin->w_cursor.lnum = lnum; ! curwin->w_cursor.col = 0; ! /* beginline(BL_SOL | BL_FIX);*/ ! } ! else ! goto_byte(thePosition.startRange + 1); ! } ! ! /* Update the screen display */ ! update_screen(NOT_VALID); ! ! /* Select the text if possible */ ! if (gotPosition) ! { ! VIsual_active = TRUE; ! VIsual_select = FALSE; ! VIsual = curwin->w_cursor; ! if (thePosition.lineNum < 0) ! { ! VIsual_mode = 'v'; ! goto_byte(thePosition.endRange); ! } ! else ! { ! VIsual_mode = 'V'; ! VIsual.col = 0; ! } ! } setcursor(); out_flush(); --- 1162,1171 ---- } /* Handle the drop, :edit to get to the file */ ! drop_numFiles = numFiles; ! drop_gotPosition = gotPosition; ! drop_thePosition = thePosition; ! handle_drop(numFiles, fnames, FALSE, drop_callback, NULL); setcursor(); out_flush(); *** ../vim-8.1.0227/src/version.c 2018-07-29 16:13:13.431551849 +0200 --- src/version.c 2018-07-29 16:44:55.344870301 +0200 *************** *** 796,797 **** --- 796,799 ---- { /* Add new patch number below this line */ + /**/ + 228, /**/ -- We do not stumble over mountains, but over molehills. Confucius /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///