diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d29d9c2676..e64ff1b12c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1875,7 +1875,7 @@ A jump table for the options with a short description can be found at |Q_op|. security reasons. *'dip'* *'diffopt'* -'diffopt' 'dip' string (default "internal,filler") +'diffopt' 'dip' string (default "internal,filler,closeoff") global Option settings for diff mode. It can consist of the following items. All are optional. Items must be separated by a comma. @@ -1932,6 +1932,12 @@ A jump table for the options with a short description can be found at |Q_op|. vertical Start diff mode with vertical splits (unless explicitly specified otherwise). + closeoff When a window is closed where 'diff' is set + and there is only one window remaining in the + same tab page with 'diff' set, execute + `:diffoff` in that window. This undoes a + `:diffsplit` command. + hiddenoff Do not use diff mode for a buffer when it becomes hidden. diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0f916106a2..dccde01d29 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -44,7 +44,7 @@ #include "nvim/os/shell.h" static int diff_busy = false; // using diff structs, don't change them -static int diff_need_update = false; // ex_diffupdate needs to be called +static bool diff_need_update = false; // ex_diffupdate needs to be called // Flags obtained from the 'diffopt' option #define DIFF_FILLER 0x001 // display filler lines @@ -57,8 +57,9 @@ static int diff_need_update = false; // ex_diffupdate needs to be called #define DIFF_VERTICAL 0x080 // vertical splits #define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm +#define DIFF_CLOSE_OFF 0x400 // diffoff when closing window #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) -static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; +static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF; static long diff_algorithm = 0; @@ -1474,6 +1475,13 @@ void ex_diffoff(exarg_T *eap) diff_buf_clear(); } + if (!diffwin) { + diff_need_update = false; + curtab->tp_diff_invalid = false; + curtab->tp_diff_update = false; + diff_clear(curtab); + } + // Remove "hor" from from 'scrollopt' if there are no diff windows left. if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) { do_cmdline_cmd("set sbo-=hor"); @@ -1714,6 +1722,7 @@ static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, /// /// @param tp void diff_clear(tabpage_T *tp) + FUNC_ATTR_NONNULL_ALL { diff_T *p; diff_T *next_p; @@ -2143,6 +2152,9 @@ int diffopt_changed(void) } else if (STRNCMP(p, "hiddenoff", 9) == 0) { p += 9; diff_flags_new |= DIFF_HIDDEN_OFF; + } else if (STRNCMP(p, "closeoff", 8) == 0) { + p += 8; + diff_flags_new |= DIFF_CLOSE_OFF; } else if (STRNCMP(p, "indent-heuristic", 16) == 0) { p += 16; diff_indent_heuristic = XDF_INDENT_HEURISTIC; @@ -2218,6 +2230,13 @@ bool diffopt_hiddenoff(void) return (diff_flags & DIFF_HIDDEN_OFF) != 0; } +// Return true if 'diffopt' contains "closeoff". +bool diffopt_closeoff(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (diff_flags & DIFF_CLOSE_OFF) != 0; +} + /// Find the difference within a changed line. /// /// @param wp window whose current buffer to check diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e96b3f8e02..d20174466d 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -612,7 +612,7 @@ return { alloced=true, redraw={'current_window'}, varname='p_dip', - defaults={if_true={vi="internal,filler"}} + defaults={if_true={vi="internal,filler,closeoff"}} }, { full_name='digraph', abbreviation='dg', diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 57b19aa817..21e0271bda 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -773,3 +773,28 @@ func Test_diff_of_diff() call StopVimInTerminal(buf) call delete('Xtest_diff_diff') endfunc + +func CloseoffSetup() + enew + call setline(1, ['one', 'two', 'three']) + diffthis + new + call setline(1, ['one', 'tow', 'three']) + diffthis + call assert_equal(1, &diff) + only! +endfunc + +func Test_diff_closeoff() + " "closeoff" included by default: last diff win gets 'diff' reset' + call CloseoffSetup() + call assert_equal(0, &diff) + enew! + + " "closeoff" excluded: last diff win keeps 'diff' set' + set diffopt-=closeoff + call CloseoffSetup() + call assert_equal(1, &diff) + diffoff! + enew! +endfunc diff --git a/src/nvim/window.c b/src/nvim/window.c index 0531ad1938..2a7578e33c 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2418,6 +2418,7 @@ int win_close(win_T *win, bool free_buf) bool help_window = false; tabpage_T *prev_curtab = curtab; frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; + const bool had_diffmode = win->w_p_diff; if (last_window() && !win->w_floating) { EMSG(_("E444: Cannot close last window")); @@ -2642,6 +2643,22 @@ int win_close(win_T *win, bool free_buf) if (help_window) restore_snapshot(SNAP_HELP_IDX, close_curwin); + // If the window had 'diff' set and now there is only one window left in + // the tab page with 'diff' set, and "closeoff" is in 'diffopt', then + // execute ":diffoff!". + if (diffopt_closeoff() && had_diffmode && curtab == prev_curtab) { + int diffcount = 0; + + FOR_ALL_WINDOWS_IN_TAB(dwin, curtab) { + if (dwin->w_p_diff) { + diffcount++; + } + } + if (diffcount == 1) { + do_cmdline_cmd("diffoff!"); + } + } + curwin->w_pos_changed = true; redraw_all_later(NOT_VALID); return OK;