Permalink
Browse files

patch 8.1.0228: dropping files is ignored while Vim is busy

Problem:    Dropping files is ignored while Vim is busy.
Solution:   Postpone the effect of dropping files until it's safe.
  • Loading branch information...
brammool committed Jul 29, 2018
1 parent fda95e7 commit 92d147be959e689f8f58fd5d138a31835e160289
Showing with 202 additions and 115 deletions.
  1. +89 −37 src/ex_docmd.c
  2. +39 −31 src/gui.c
  3. +3 −2 src/gui.h
  4. +53 −42 src/gui_mac.c
  5. +8 −2 src/main.c
  6. +2 −1 src/proto/ex_docmd.pro
  7. +6 −0 src/screen.c
  8. +2 −0 src/version.c
@@ -7859,57 +7859,37 @@ ex_shell(exarg_T *eap UNUSED)
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)
#if defined(HAVE_DROP_FILE) || 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 */
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;

/* 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;
// 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) && !split)
if (!buf_hide(curbuf) && !drop_split)
{
++emsg_off;
split = check_changed(curbuf, CCGD_AW);
drop_split = check_changed(curbuf, CCGD_AW);
--emsg_off;
}
if (split)
if (drop_split)
{
if (win_split(0, 0) == FAIL)
return;
@@ -7924,7 +7904,7 @@ handle_drop(
/*
* Set up the new argument list.
*/
alist_set(ALIST(curwin), filec, filev, FALSE, NULL, 0);
alist_set(ALIST(curwin), drop_filec, drop_filev, FALSE, NULL, 0);

/*
* Move to the first file.
@@ -7942,6 +7922,78 @@ handle_drop(
* 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

@@ -5383,10 +5383,7 @@ gui_do_findrepl(

#endif

#if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \
|| defined(FEAT_GUI_MSWIN) \
|| defined(FEAT_GUI_MAC) \
|| defined(PROTO)
#if defined(HAVE_DROP_FILE) || defined(PROTO)

static void gui_wingoto_xy(int x, int y);

@@ -5408,6 +5405,42 @@ gui_wingoto_xy(int x, int y)
}
}

/*
* 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
@@ -5488,33 +5521,8 @@ gui_handle_drop(
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);
handle_drop(count, fnames, (modifiers & MOUSE_CTRL) != 0,
drop_callback, (void *)p);
}

entered = FALSE;
@@ -65,8 +65,9 @@
/*
* GUIs that support dropping files on a running Vim.
*/
#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_MAC) \
|| defined(FEAT_GUI_GTK)
#if (defined(FEAT_DND) && defined(FEAT_GUI_GTK)) \
|| defined(FEAT_GUI_MSWIN) \
|| defined(FEAT_GUI_MAC)
# define HAVE_DROP_FILE
#endif

@@ -1007,6 +1007,55 @@ struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPositi
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,48 +1162,10 @@ HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
}

/* 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;
}
}
drop_numFiles = numFiles;
drop_gotPosition = gotPosition;
drop_thePosition = thePosition;
handle_drop(numFiles, fnames, FALSE, drop_callback, NULL);

setcursor();
out_flush();
@@ -911,7 +911,7 @@ vim_main2(void)

/*
* Call the main command loop. This never returns.
*/
*/
main_loop(FALSE, FALSE);

#endif /* NO_VIM_MAIN */
@@ -1155,9 +1155,15 @@ main_loop(
else if (do_redraw || stuff_empty())
{
#ifdef FEAT_GUI
/* If ui_breakcheck() was used a resize may have been postponed. */
// 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()
Oops, something went wrong.

0 comments on commit 92d147b

Please sign in to comment.