Skip to content

Commit

Permalink
patch 8.1.0228: dropping files is ignored while Vim is busy
Browse files Browse the repository at this point in the history
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 92d147b
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 115 deletions.
126 changes: 89 additions & 37 deletions src/ex_docmd.c
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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

Expand Down
70 changes: 39 additions & 31 deletions src/gui.c
Expand Up @@ -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);

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 3 additions & 2 deletions src/gui.h
Expand Up @@ -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

Expand Down
95 changes: 53 additions & 42 deletions src/gui_mac.c
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
10 changes: 8 additions & 2 deletions src/main.c
Expand Up @@ -911,7 +911,7 @@ vim_main2(void)

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

#endif /* NO_VIM_MAIN */
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 92d147b

Please sign in to comment.