From 67c5bc1ac082236c0a36c785619987b9462aa89a Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Tue, 4 Dec 2018 21:17:00 +0200 Subject: Calm down the clinter --- src/nvim/buffer_defs.h | 56 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index e86eaf010f..d9befee296 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -58,10 +58,10 @@ typedef struct { * functions that set or reset the flags. * * VALID_BOTLINE VALID_BOTLINE_AP - * on on w_botline valid - * off on w_botline approximated - * off off w_botline not valid - * on off not possible + * on on w_botline valid + * off on w_botline approximated + * off off w_botline not valid + * on off not possible */ #define VALID_WROW 0x01 /* w_wrow (window row) is valid */ #define VALID_WCOL 0x02 /* w_wcol (window col) is valid */ @@ -159,7 +159,7 @@ typedef struct { int wo_arab; # define w_p_arab w_onebuf_opt.wo_arab /* 'arabic' */ int wo_bri; -# define w_p_bri w_onebuf_opt.wo_bri /* 'breakindent' */ +# define w_p_bri w_onebuf_opt.wo_bri // 'breakindent' char_u *wo_briopt; # define w_p_briopt w_onebuf_opt.wo_briopt /* 'breakindentopt' */ int wo_diff; @@ -412,13 +412,13 @@ typedef struct { * syntax state too often. * b_sst_array[] is allocated to hold the state for all displayed lines, * and states for 1 out of about 20 other lines. - * b_sst_array pointer to an array of synstate_T - * b_sst_len number of entries in b_sst_array[] - * b_sst_first pointer to first used entry in b_sst_array[] or NULL - * b_sst_firstfree pointer to first free entry in b_sst_array[] or NULL - * b_sst_freecount number of free entries in b_sst_array[] - * b_sst_check_lnum entries after this lnum need to be checked for - * validity (MAXLNUM means no check needed) + * b_sst_array pointer to an array of synstate_T + * b_sst_len number of entries in b_sst_array[] + * b_sst_first pointer to first used entry in b_sst_array[] or NULL + * b_sst_firstfree pointer to first free entry in b_sst_array[] or NULL + * b_sst_freecount number of free entries in b_sst_array[] + * b_sst_check_lnum entries after this lnum need to be checked for + * validity (MAXLNUM means no check needed) */ synstate_T *b_sst_array; int b_sst_len; @@ -479,7 +479,7 @@ struct file_buffer { * b_ffname has the full path of the file (NULL for no name). * b_sfname is the name as the user typed it (or NULL). * b_fname is the same as b_sfname, unless ":cd" has been done, - * then it is the same as b_ffname (NULL for no name). + * then it is the same as b_ffname (NULL for no name). */ char_u *b_ffname; /* full path file name */ char_u *b_sfname; /* short file name */ @@ -587,12 +587,12 @@ struct file_buffer { bool b_scanned; /* ^N/^P have scanned this buffer */ - /* flags for use of ":lmap" and IM control */ - long b_p_iminsert; /* input mode for insert */ - long b_p_imsearch; /* input mode for search */ -#define B_IMODE_USE_INSERT -1 /* Use b_p_iminsert value for search */ -#define B_IMODE_NONE 0 /* Input via none */ -#define B_IMODE_LMAP 1 /* Input via langmap */ + // flags for use of ":lmap" and IM control + long b_p_iminsert; // input mode for insert + long b_p_imsearch; // input mode for search +#define B_IMODE_USE_INSERT -1 // Use b_p_iminsert value for search +#define B_IMODE_NONE 0 // Input via none +#define B_IMODE_LMAP 1 // Input via langmap # define B_IMODE_LAST 1 short b_kmap_state; /* using "lmap" mappings */ @@ -760,7 +760,7 @@ struct file_buffer { /* Two special kinds of buffers: * help buffer - used for help files, won't use a swap file. * spell buffer - used for spell info, never displayed and doesn't have a - * file name. + * file name. */ bool b_help; /* TRUE for help file buffer (when set b_p_bt is "help") */ @@ -916,9 +916,9 @@ typedef struct { /// Same as lpos_T, but with additional field len. typedef struct { - linenr_T lnum; ///< line number - colnr_T col; ///< column number - int len; ///< length: 0 - to the end of line + linenr_T lnum; ///< line number + colnr_T col; ///< column number + int len; ///< length: 0 - to the end of line } llpos_T; /// posmatch_T provides an array for storing match items for matchaddpos() @@ -926,10 +926,10 @@ typedef struct typedef struct posmatch posmatch_T; struct posmatch { - llpos_T pos[MAXPOSMATCH]; ///< array of positions - int cur; ///< internal position counter - linenr_T toplnum; ///< top buffer line - linenr_T botlnum; ///< bottom buffer line + llpos_T pos[MAXPOSMATCH]; ///< array of positions + int cur; ///< internal position counter + linenr_T toplnum; ///< top buffer line + linenr_T botlnum; ///< bottom buffer line }; /* @@ -1036,7 +1036,7 @@ struct window_S { * Recomputing is minimized by storing the result of computations. * Use functions in screen.c to check if they are valid and to update. * w_valid is a bitfield of flags, which indicate if specific values are - * valid or need to be recomputed. + * valid or need to be recomputed. */ int w_valid; pos_T w_valid_cursor; /* last known position of w_cursor, used -- cgit From cf1ffa91667a482c6355f5999b9e97ccc0b4126a Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 2 Dec 2018 22:58:07 +0200 Subject: vim-patch:8.1.0360: using an external diff program is slow and inflexible Problem: Using an external diff program is slow and inflexible. Solution: Include the xdiff library. (Christian Brabandt) Use it by default. https://github.com/vim/vim/commit/e828b7621cf9065a3582be0c4dd1e0e846e335bf vim-patch:8.1.0360 vim-patch:8.1.0364 vim-patch:8.1.0366 vim-patch:8.1.0370 vim-patch:8.1.0377 vim-patch:8.1.0378 vim-patch:8.1.0381 vim-patch:8.1.0396 vim-patch:8.1.0432 --- src/nvim/CMakeLists.txt | 9 +- src/nvim/buffer_defs.h | 2 + src/nvim/diff.c | 831 ++++++++++++++++++++-------- src/nvim/fileio.c | 4 +- src/nvim/globals.h | 3 +- src/nvim/options.lua | 2 +- src/nvim/testdir/test_diffmode.vim | 105 +++- src/nvim/xdiff/COPYING | 504 +++++++++++++++++ src/nvim/xdiff/README.txt | 16 + src/nvim/xdiff/xdiff.h | 143 +++++ src/nvim/xdiff/xdiffi.c | 1043 ++++++++++++++++++++++++++++++++++++ src/nvim/xdiff/xdiffi.h | 64 +++ src/nvim/xdiff/xemit.c | 332 ++++++++++++ src/nvim/xdiff/xemit.h | 36 ++ src/nvim/xdiff/xhistogram.c | 386 +++++++++++++ src/nvim/xdiff/xinclude.h | 61 +++ src/nvim/xdiff/xmacros.h | 54 ++ src/nvim/xdiff/xpatience.c | 393 ++++++++++++++ src/nvim/xdiff/xprepare.c | 483 +++++++++++++++++ src/nvim/xdiff/xprepare.h | 34 ++ src/nvim/xdiff/xtypes.h | 67 +++ src/nvim/xdiff/xutils.c | 425 +++++++++++++++ src/nvim/xdiff/xutils.h | 47 ++ 23 files changed, 4809 insertions(+), 235 deletions(-) create mode 100644 src/nvim/xdiff/COPYING create mode 100644 src/nvim/xdiff/README.txt create mode 100644 src/nvim/xdiff/xdiff.h create mode 100644 src/nvim/xdiff/xdiffi.c create mode 100644 src/nvim/xdiff/xdiffi.h create mode 100644 src/nvim/xdiff/xemit.c create mode 100644 src/nvim/xdiff/xemit.h create mode 100644 src/nvim/xdiff/xhistogram.c create mode 100644 src/nvim/xdiff/xinclude.h create mode 100644 src/nvim/xdiff/xmacros.h create mode 100644 src/nvim/xdiff/xpatience.c create mode 100644 src/nvim/xdiff/xprepare.c create mode 100644 src/nvim/xdiff/xprepare.h create mode 100644 src/nvim/xdiff/xtypes.h create mode 100644 src/nvim/xdiff/xutils.c create mode 100644 src/nvim/xdiff/xutils.h (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 29a4e1e163..e4505f7484 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -79,6 +79,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) +file(GLOB XDIFF_SOURCES xdiff/*.c) +file(GLOB XDIFF_HEADERS xdiff/*.h) foreach(subdir os @@ -155,6 +157,8 @@ foreach(sfile ${CONV_SOURCES}) endif() endforeach() +list(APPEND CONV_SOURCES ${XDIFF_SOURCES}) + if(NOT MSVC) set_source_files_properties( ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") @@ -404,7 +408,8 @@ if(POLICY CMP0069) endif() add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -490,6 +495,7 @@ add_library( EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS} ) set_property(TARGET libnvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) @@ -514,6 +520,7 @@ else() EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS} ${UNIT_TEST_FIXTURES} ) target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES}) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index d9befee296..238893260e 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -787,6 +787,8 @@ struct file_buffer { // array of channelids which have asked to receive updates for this // buffer. kvec_t(uint64_t) update_channels; + + int b_diff_failed; // internal diff failed for this buffer }; /* diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4bfe67c9d3..c3eb2465bd 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -4,11 +4,17 @@ /// @file diff.c /// /// Code for diff'ing two, three or four buffers. +/// +/// There are three ways to diff: +/// - Shell out to an external diff program, using files. +/// - Use the compiled-in xdiff library. +/// - Let 'diffexpr' do the work, using files. #include #include #include "nvim/vim.h" +#include "xdiff/xdiff.h" #include "nvim/ascii.h" #include "nvim/diff.h" #include "nvim/buffer.h" @@ -36,16 +42,19 @@ #include "nvim/os/os.h" #include "nvim/os/shell.h" -static int diff_busy = FALSE; // ex_diffgetput() is busy +static int diff_busy = false; // ex_diffgetput() is busy // Flags obtained from the 'diffopt' option -#define DIFF_FILLER 1 // display filler lines -#define DIFF_ICASE 2 // ignore case -#define DIFF_IWHITE 4 // ignore change in white space -#define DIFF_HORIZONTAL 8 // horizontal splits -#define DIFF_VERTICAL 16 // vertical splits -#define DIFF_HIDDEN_OFF 32 // diffoff when hidden -static int diff_flags = DIFF_FILLER; +#define DIFF_FILLER 1 // display filler lines +#define DIFF_ICASE 2 // ignore case +#define DIFF_IWHITE 4 // ignore change in white space +#define DIFF_HORIZONTAL 8 // horizontal splits +#define DIFF_VERTICAL 16 // vertical splits +#define DIFF_HIDDEN_OFF 32 // diffoff when hidden +#define DIFF_INTERNAL 64 // use internal xdiff algorithm +static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; + +static long diff_algorithm = 0; #define LBUFLEN 50 // length of line in diff file @@ -53,6 +62,25 @@ static int diff_flags = DIFF_FILLER; // kNone when not checked yet static TriState diff_a_works = kNone; +// used for diff input +typedef struct { + char_u *din_fname; // used for external diff + mmfile_t din_mmfile; // used for internal diff +} diffin_T; + +// used for diff result +typedef struct { + char_u *dout_fname; // used for external diff + garray_T dout_ga; // used for internal diff +} diffout_T; + +// two diff inputs and one result +typedef struct { + diffin_T dio_orig; // original file input + diffin_T dio_new; // new file input + diffout_T dio_diff; // diff result + int dio_internal; // using internal diff +} diffio_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.c.generated.h" @@ -68,10 +96,10 @@ void diff_buf_delete(buf_T *buf) if (i != DB_COUNT) { tp->tp_diffbuf[i] = NULL; - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); } } } @@ -98,8 +126,8 @@ void diff_buf_adjust(win_T *win) int i = diff_buf_idx(win->w_buffer); if (i != DB_COUNT) { curtab->tp_diffbuf[i] = NULL; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); + curtab->tp_diff_invalid = true; + diff_redraw(true); } } } else { @@ -127,8 +155,8 @@ void diff_buf_add(buf_T *buf) for (i = 0; i < DB_COUNT; ++i) { if (curtab->tp_diffbuf[i] == NULL) { curtab->tp_diffbuf[i] = buf; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); + curtab->tp_diff_invalid = true; + diff_redraw(true); return; } } @@ -192,9 +220,9 @@ void diff_invalidate(buf_T *buf) FOR_ALL_TABS(tp) { int i = diff_buf_idx_tp(buf, tp); if (i != DB_COUNT) { - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); } } } @@ -293,14 +321,14 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, // // Check for these situations: - // 1 2 3 - // 1 2 3 - // line1 2 3 4 5 - // 2 3 4 5 - // 2 3 4 5 - // line2 2 3 4 5 - // 3 5 6 - // 3 5 6 + // 1 2 3 + // 1 2 3 + // line1 2 3 4 5 + // 2 3 4 5 + // 2 3 4 5 + // line2 2 3 4 5 + // 3 5 6 + // 3 5 6 // compute last line of this change last = dp->df_lnum[idx] + dp->df_count[idx] - 1; @@ -316,7 +344,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } dp->df_lnum[idx] += amount_after; } else { - int check_unchanged = FALSE; + int check_unchanged = false; // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { @@ -341,7 +369,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, // 5. delete lines at or just before top of diff n = off; dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; - check_unchanged = TRUE; + check_unchanged = true; } dp->df_lnum[idx] = line1; } else { @@ -361,7 +389,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } else { n = line2 - last; } - check_unchanged = TRUE; + check_unchanged = true; } else { // 3. delete lines inside the diff n = 0; @@ -380,7 +408,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, if (dp->df_lnum[idx] <= line1) { // inserted lines somewhere in this diff dp->df_count[idx] += inserted; - check_unchanged = TRUE; + check_unchanged = true; } else { // inserted lines somewhere above this diff dp->df_lnum[idx] += inserted; @@ -447,12 +475,12 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); // Need to recompute the scroll binding, may remove or add filler // lines (e.g., when adding lines above w_topline). But it's slow when // making many changes, postpone until redrawing. - diff_need_scrollbind = TRUE; + diff_need_scrollbind = true; } } @@ -519,7 +547,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) } char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], dp->df_lnum[i_org] + off_org, - FALSE)); + false)); int i_new; for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) { @@ -538,7 +566,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], dp->df_lnum[i_new] + off_new, - FALSE)) != 0) { + false)) != 0) { break; } } @@ -612,30 +640,224 @@ static void diff_redraw(int dofold) } else if ((n > 0) && (n > wp->w_topfill)) { wp->w_topfill = n; } - check_topfill(wp, FALSE); + check_topfill(wp, false); } } } -/// Write buffer "buf" to file "name". +static void clear_diffin(diffin_T *din) +{ + if (din->din_fname == NULL) { + xfree(din->din_mmfile.ptr); + din->din_mmfile.ptr = NULL; + } else { + os_remove((char *)din->din_fname); + } +} + +static void clear_diffout(diffout_T *dout) +{ + if (dout->dout_fname == NULL) { + ga_clear_strings(&dout->dout_ga); + } else { + os_remove((char *)dout->dout_fname); + } +} + +/// Write buffer "buf" to a memory buffer. +/// +/// @param buf +/// @param din +/// +/// @return FAIL for failure. +static int diff_write_buffer(buf_T *buf, diffin_T *din) +{ + linenr_T lnum; + char_u *s; + long len = 0; + char_u *ptr; + + // xdiff requires one big block of memory with all the text. + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + len += STRLEN(ml_get_buf(buf, lnum, false)) + 1; + } + ptr = xmalloc(len); + if (ptr == NULL) { + // Allocating memory failed. This can happen, because we try to read + // the whole buffer text into memory. Set the failed flag, the diff + // will be retried with external diff. The flag is never reset. + buf->b_diff_failed = true; + if (p_verbose > 0) { + verbose_enter(); + smsg(_("Not enough memory to use internal diff for buffer \"%s\""), + buf->b_fname); + verbose_leave(); + } + return FAIL; + } + din->din_mmfile.ptr = (char *)ptr; + din->din_mmfile.size = len; + + len = 0; + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (s = ml_get_buf(buf, lnum, false); *s != NUL; ) { + if (diff_flags & DIFF_ICASE) { + int c; + + // xdiff doesn't support ignoring case, fold-case the text. + int orig_len; + char_u cbuf[MB_MAXBYTES + 1]; + + c = PTR2CHAR(s); + c = enc_utf8 ? utf_fold(c) : TOLOWER_LOC(c); + orig_len = MB_PTR2LEN(s); + if (utf_char2bytes(c, cbuf) != orig_len) { + // TODO(Bram): handle byte length difference + memmove(ptr + len, s, orig_len); + } else { + memmove(ptr + len, cbuf, orig_len); + } + + s += orig_len; + len += orig_len; + } else { + ptr[len++] = *s++; + } + } + ptr[len++] = NL; + } + return OK; +} + +/// Write buffer "buf" to file or memory buffer. /// /// Always use 'fileformat' set to "unix". /// /// @param buf -/// @param fname +/// @param din /// /// @return FAIL for failure -static int diff_write(buf_T *buf, char_u *fname) +static int diff_write(buf_T *buf, diffin_T *din) { + if (din->din_fname == NULL) { + return diff_write_buffer(buf, din); + } + + // Always use 'fileformat' set to "unix". char_u *save_ff = buf->b_p_ff; buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); - int r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE); + int r = buf_write(buf, din->din_fname, NULL, + (linenr_T)1, buf->b_ml.ml_line_count, + NULL, false, false, false, true); free_string_option(buf->b_p_ff); buf->b_p_ff = save_ff; return r; } +/// +/// Update the diffs for all buffers involved. +/// +/// @param dio +/// @param idx_orig +/// @param eap can be NULL +static void diff_try_update(diffio_T *dio, + int idx_orig, + exarg_T *eap) +{ + buf_T *buf; + int idx_new; + + if (dio->dio_internal) { + ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); + } else { + // We need three temp file names. + dio->dio_orig.din_fname = vim_tempname(); + dio->dio_new.din_fname = vim_tempname(); + dio->dio_diff.dout_fname = vim_tempname(); + if (dio->dio_orig.din_fname == NULL + || dio->dio_new.din_fname == NULL + || dio->dio_diff.dout_fname == NULL) { + goto theend; + } + } + + // Check external diff is actually working. + if (!dio->dio_internal && check_external_diff(dio) == FAIL) { + goto theend; + } + + // :diffupdate! + if (eap != NULL && eap->forceit) { + for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { + buf = curtab->tp_diffbuf[idx_new]; + if (buf_valid(buf)) { + buf_check_timestamp(buf, false); + } + } + } + + // Write the first buffer to a tempfile or mmfile_t. + buf = curtab->tp_diffbuf[idx_orig]; + if (diff_write(buf, &dio->dio_orig) == FAIL) { + goto theend; + } + + // Make a difference between the first buffer and every other. + for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { + buf = curtab->tp_diffbuf[idx_new]; + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + continue; // skip buffer that isn't loaded + } + + // Write the other buffer and diff with the first one. + if (diff_write(buf, &dio->dio_new) == FAIL) { + continue; + } + if (diff_file(dio) == FAIL) { + continue; + } + + // Read the diff output and add each entry to the diff list. + diff_read(idx_orig, idx_new, &dio->dio_diff); + + clear_diffin(&dio->dio_new); + clear_diffout(&dio->dio_diff); + } + clear_diffin(&dio->dio_orig); + +theend: + xfree(dio->dio_orig.din_fname); + xfree(dio->dio_new.din_fname); + xfree(dio->dio_diff.dout_fname); +} + +/// +/// Return true if the options are set to use the internal diff library. +/// Note that if the internal diff failed for one of the buffers, the external +/// diff will be used anyway. +/// +static int diff_internal(void) +{ + return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; +} + +/// +/// Return true if the internal diff failed for one of the diff buffers. +/// +static int diff_internal_failed(void) +{ + int idx; + + // Only need to do something when there is another buffer. + for (idx = 0; idx < DB_COUNT; idx++) { + if (curtab->tp_diffbuf[idx] != NULL + && curtab->tp_diffbuf[idx]->b_diff_failed) { + return true; + } + } + return false; +} + /// Completely update the diffs for the buffers involved. /// /// This uses the ordinary "diff" command. @@ -647,7 +869,7 @@ void ex_diffupdate(exarg_T *eap) { // Delete all diffblocks. diff_clear(curtab); - curtab->tp_diff_invalid = FALSE; + curtab->tp_diff_invalid = false; // Use the first buffer as the original text. int idx_orig; @@ -673,46 +895,61 @@ void ex_diffupdate(exarg_T *eap) return; } - // We need three temp file names. - char *tmp_orig = (char *) vim_tempname(); - char *tmp_new = (char *) vim_tempname(); - char *tmp_diff = (char *) vim_tempname(); + // Only use the internal method if it did not fail for one of the buffers. + diffio_T diffio; + memset(&diffio, 0, sizeof(diffio)); + diffio.dio_internal = diff_internal() && !diff_internal_failed(); - if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) { - goto theend; + diff_try_update(&diffio, idx_orig, eap); + if (diffio.dio_internal && diff_internal_failed()) { + // Internal diff failed, use external diff instead. + memset(&diffio, 0, sizeof(diffio)); + diff_try_update(&diffio, idx_orig, eap); } - // Do a quick test if "diff" really works. Otherwise it looks like there - // are no differences. Can't use the return value, it's non-zero when - // there are differences. + // force updating cursor position on screen + curwin->w_valid_cursor.lnum = 0; + + diff_redraw(true); +} + +/// +/// Do a quick test if "diff" really works. Otherwise it looks like there +/// are no differences. Can't use the return value, it's non-zero when +/// there are differences. +/// +static int check_external_diff(diffio_T *diffio) +{ // May try twice, first with "-a" and then without. int io_error = false; TriState ok = kFalse; for (;;) { ok = kFalse; - FILE *fd = mch_fopen(tmp_orig, "w"); + FILE *fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w"); if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; + io_error = true; } fclose(fd); - fd = mch_fopen(tmp_new, "w"); + fd = mch_fopen((char *)diffio->dio_new.din_fname, "w"); if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; + io_error = true; } fclose(fd); - diff_file(tmp_orig, tmp_new, tmp_diff); - fd = mch_fopen(tmp_diff, "r"); + fd = NULL; + if (diff_file(diffio) == OK) { + fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r"); + } if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { char_u linebuf[LBUFLEN]; @@ -728,10 +965,10 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); } - os_remove(tmp_diff); - os_remove(tmp_new); + os_remove((char *)diffio->dio_diff.dout_fname); + os_remove((char *)diffio->dio_new.din_fname); } - os_remove(tmp_orig); + os_remove((char *)diffio->dio_orig.din_fname); } // When using 'diffexpr' break here. @@ -757,80 +994,77 @@ void ex_diffupdate(exarg_T *eap) } EMSG(_("E97: Cannot create diffs")); diff_a_works = kNone; - goto theend; + return FAIL; } + return OK; +} - // :diffupdate! - if ((eap != NULL) && eap->forceit) { - for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf_valid(buf)) { - buf_check_timestamp(buf, FALSE); - } - } - } +/// +/// Invoke the xdiff function. +/// +static int diff_file_internal(diffio_T *diffio) +{ + xpparam_t param; + xdemitconf_t emit_cfg; + xdemitcb_t emit_cb; - // Write the first buffer to a tempfile. - buf_T *buf = curtab->tp_diffbuf[idx_orig]; - if (diff_write(buf, (char_u *) tmp_orig) == FAIL) { - goto theend; - } + memset(¶m, 0, sizeof(param)); + memset(&emit_cfg, 0, sizeof(emit_cfg)); + memset(&emit_cb, 0, sizeof(emit_cb)); - // Make a difference between the first buffer and every other. - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf == NULL || buf->b_ml.ml_mfp == NULL) { - continue; // skip buffer that isn't loaded - } + param.flags = diff_algorithm; - if (diff_write(buf, (char_u *) tmp_new) == FAIL) { - continue; - } - diff_file(tmp_orig, tmp_new, tmp_diff); - - // Read the diff output and add each entry to the diff list. - diff_read(idx_orig, idx_new, (char_u *) tmp_diff); - os_remove(tmp_diff); - os_remove(tmp_new); + if (diff_flags & DIFF_IWHITE) { + param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; } - os_remove(tmp_orig); - - // force updating cursor position on screen - curwin->w_valid_cursor.lnum = 0; - - diff_redraw(TRUE); -theend: - xfree(tmp_orig); - xfree(tmp_new); - xfree(tmp_diff); + emit_cfg.ctxlen = 0; // don't need any diff_context here + emit_cb.priv = &diffio->dio_diff; + emit_cb.outf = xdiff_out; + if (xdl_diff(&diffio->dio_orig.din_mmfile, + &diffio->dio_new.din_mmfile, + ¶m, &emit_cfg, &emit_cb) < 0) { + EMSG(_("E960: Problem creating the internal diff")); + return FAIL; + } + return OK; } /// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". /// -/// @param tmp_orig -/// @param tmp_new -/// @param tmp_diff -static void diff_file(const char *const tmp_orig, const char *const tmp_new, - const char *const tmp_diff) +/// @param dio +/// +/// @return OK or FAIL +static int diff_file(diffio_T *dio) { + char *tmp_orig = (char *)dio->dio_orig.din_fname; + char *tmp_new = (char *)dio->dio_new.din_fname; + char *tmp_diff = (char *)dio->dio_diff.dout_fname; if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); + return OK; + } + // Use xdiff for generating the diff. + if (dio->dio_internal) { + return diff_file_internal(dio); } else { const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + STRLEN(p_srr) + 27); char *const cmd = xmalloc(len); + if (cmd == NULL) { + return FAIL; + } - /* We don't want $DIFF_OPTIONS to get in the way. */ + // We don't want $DIFF_OPTIONS to get in the way. if (os_getenv("DIFF_OPTIONS")) { os_unsetenv("DIFF_OPTIONS"); } - /* Build the diff command and execute it. Always use -a, binary - * differences are of no use. Ignore errors, diff returns - * non-zero when differences have been found. */ - vim_snprintf(cmd, len, "diff %s%s%s%s%s %s", + // Build the diff command and execute it. Always use -a, binary + // differences are of no use. Ignore errors, diff returns + // non-zero when differences have been found. + vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s", diff_a_works == kFalse ? "" : "-a ", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", @@ -843,6 +1077,7 @@ static void diff_file(const char *const tmp_orig, const char *const tmp_new, NULL); unblock_autocmds(); xfree(cmd); + return OK; } } @@ -876,7 +1111,7 @@ void ex_diffpatch(exarg_T *eap) // Write the current buffer to "tmp_orig". if (buf_write(curbuf, tmp_orig, NULL, (linenr_T)1, curbuf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) { + NULL, false, false, false, true) == FAIL) { goto theend; } @@ -907,7 +1142,7 @@ void ex_diffpatch(exarg_T *eap) tempdir = "/tmp"; } os_chdir(tempdir); - shorten_fnames(TRUE); + shorten_fnames(true); } #endif @@ -934,7 +1169,7 @@ void ex_diffpatch(exarg_T *eap) if (os_chdir((char *)dirbuf) != 0) { EMSG(_(e_prev_dir)); } - shorten_fnames(TRUE); + shorten_fnames(true); } #endif @@ -971,8 +1206,8 @@ void ex_diffpatch(exarg_T *eap) // check that split worked and editing tmp_new if ((curwin != old_curwin) && win_valid(old_curwin)) { // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); - diff_win_options(old_curwin, TRUE); + diff_win_options(curwin, true); + diff_win_options(old_curwin, true); if (newname != NULL) { // do a ":file filename.new" on the patched buffer @@ -1025,7 +1260,7 @@ void ex_diffsplit(exarg_T *eap) if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { // Pretend it was a ":split fname" command eap->cmdidx = CMD_split; - curwin->w_p_diff = TRUE; + curwin->w_p_diff = true; do_exedit(eap, old_curwin); // split must have worked @@ -1052,7 +1287,7 @@ void ex_diffsplit(exarg_T *eap) void ex_diffthis(exarg_T *eap) { // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); + diff_win_options(curwin, true); } static void set_diff_option(win_T *wp, int value) @@ -1085,12 +1320,12 @@ void diff_win_options(win_T *wp, int addbuf) if (!wp->w_p_diff) { wp->w_p_scb_save = wp->w_p_scb; } - wp->w_p_scb = TRUE; + wp->w_p_scb = true; if (!wp->w_p_diff) { wp->w_p_crb_save = wp->w_p_crb; } - wp->w_p_crb = TRUE; + wp->w_p_crb = true; if (!wp->w_p_diff) { wp->w_p_wrap_save = wp->w_p_wrap; @@ -1116,7 +1351,7 @@ void diff_win_options(win_T *wp, int addbuf) wp->w_p_fdl_save = wp->w_p_fdl; } wp->w_p_fdc = diff_foldcolumn; - wp->w_p_fen = TRUE; + wp->w_p_fen = true; wp->w_p_fdl = 0; foldUpdateAll(wp); @@ -1211,88 +1446,93 @@ void ex_diffoff(exarg_T *eap) /// /// @param idx_orig idx of original file /// @param idx_new idx of new file -/// @param fname name of diff output file -static void diff_read(int idx_orig, int idx_new, char_u *fname) +/// @dout diff output +static void diff_read(int idx_orig, int idx_new, diffout_T *dout) { - FILE *fd; + FILE *fd = NULL; + int line_idx = 0; diff_T *dprev = NULL; diff_T *dp = curtab->tp_first_diff; diff_T *dn, *dpl; - long f1, l1, f2, l2; - char_u linebuf[LBUFLEN]; // only need to hold the diff line - int difftype; - char_u *p; + char_u linebuf[LBUFLEN]; // only need to hold the diff line + char_u *line; long off; int i; linenr_T lnum_orig, lnum_new; long count_orig, count_new; - int notset = TRUE; // block "*dp" not set yet - - fd = mch_fopen((char *)fname, "r"); - - if (fd == NULL) { - EMSG(_("E98: Cannot read diff output")); - return; + int notset = true; // block "*dp" not set yet + enum { + DIFF_ED, + DIFF_UNIFIED, + DIFF_NONE + } diffstyle = DIFF_NONE; + + if (dout->dout_fname == NULL) { + diffstyle = DIFF_UNIFIED; + } else { + fd = mch_fopen((char *)dout->dout_fname, "r"); + if (fd == NULL) { + EMSG(_("E98: Cannot read diff output")); + return; + } } for (;;) { - if (vim_fgets(linebuf, LBUFLEN, fd)) { - // end of file - break; - } - - if (!isdigit(*linebuf)) { - // not the start of a diff block - continue; - } - - // This line must be one of three formats: - // {first}[,{last}]c{first}[,{last}] - // {first}a{first}[,{last}] - // {first}[,{last}]d{first} - p = linebuf; - f1 = getdigits_long(&p); - - if (*p == ',') { - ++p; - l1 = getdigits_long(&p); - } else { - l1 = f1; - } - - if ((*p != 'a') && (*p != 'c') && (*p != 'd')) { - // invalid diff format - continue; - } - difftype = *p++; - f2 = getdigits_long(&p); - - if (*p == ',') { - ++p; - l2 = getdigits_long(&p); - } else { - l2 = f2; - } - - if ((l1 < f1) || (l2 < f2)) { - // invalid line range - continue; - } - - if (difftype == 'a') { - lnum_orig = f1 + 1; - count_orig = 0; + if (fd == NULL) { + if (line_idx >= dout->dout_ga.ga_len) { + break; // did last line + } + line = ((char_u **)dout->dout_ga.ga_data)[line_idx++]; } else { - lnum_orig = f1; - count_orig = l1 - f1 + 1; + if (vim_fgets(linebuf, LBUFLEN, fd)) { + break; // end of file + } + line = linebuf; + } + + if (diffstyle == DIFF_NONE) { + // Determine diff style. + // ed like diff looks like this: + // {first}[,{last}]c{first}[,{last}] + // {first}a{first}[,{last}] + // {first}[,{last}]d{first} + // + // unified diff looks like this: + // --- file1 2018-03-20 13:23:35.783153140 +0100 + // +++ file2 2018-03-20 13:23:41.183156066 +0100 + // @@ -1,3 +1,5 @@ + if (isdigit(*line)) { + diffstyle = DIFF_ED; + } else if ((STRNCMP(line, "@@ ", 3) == 0)) { + diffstyle = DIFF_UNIFIED; + } else if ((STRNCMP(line, "--- ", 4) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "+++ ", 4) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "@@ ", 3) == 0)) { + diffstyle = DIFF_UNIFIED; + } } - if (difftype == 'd') { - lnum_new = f2 + 1; - count_new = 0; + if (diffstyle == DIFF_ED) { + if (!isdigit(*line)) { + continue; // not the start of a diff block + } + if (parse_diff_ed(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) { + continue; + } + } else if (diffstyle == DIFF_UNIFIED) { + if (STRNCMP(line, "@@ ", 3) != 0) { + continue; // not the start of a diff block + } + if (parse_diff_unified(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) { + continue; + } } else { - lnum_new = f2; - count_new = l2 - f2 + 1; + EMSG(_("E959: Invalid diff format.")); + break; } // Go over blocks before the change, for which orig and new are equal. @@ -1304,7 +1544,7 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } dprev = dp; dp = dp->df_next; - notset = TRUE; + notset = true; } if ((dp != NULL) @@ -1391,7 +1631,7 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } } } - notset = FALSE; // "*dp" has been set + notset = false; // "*dp" has been set } // for remaining diff blocks orig and new are equal @@ -1401,10 +1641,12 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } dprev = dp; dp = dp->df_next; - notset = TRUE; + notset = true; } - fclose(fd); + if (fd != NULL) { + fclose(fd); + } } /// Copy an entry at "dp" from "idx_orig" to "idx_new". @@ -1503,23 +1745,23 @@ int diff_check(win_T *wp, linenr_T lnum) } if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { - int zero = FALSE; + int zero = false; // Changed or inserted line. If the other buffers have a count of // zero, the lines were inserted. If the other buffers have the same // count, check if the lines are identical. - cmp = FALSE; + cmp = false; for (i = 0; i < DB_COUNT; ++i) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { if (dp->df_count[i] == 0) { - zero = TRUE; + zero = true; } else { if (dp->df_count[i] != dp->df_count[idx]) { // nr of lines changed. return -1; } - cmp = TRUE; + cmp = true; } } } @@ -1543,7 +1785,7 @@ int diff_check(win_T *wp, linenr_T lnum) // the difference. Can't remove the entry here, we might be halfway // through updating the window. Just report the text as unchanged. // Other windows might still show the change though. - if (zero == FALSE) { + if (zero == false) { return 0; } return -2; @@ -1815,6 +2057,7 @@ int diffopt_changed(void) int diff_context_new = 6; int diff_flags_new = 0; int diff_foldcolumn_new = 2; + long diff_algorithm_new = 0; char_u *p = p_dip; while (*p != NUL) { @@ -1842,6 +2085,27 @@ int diffopt_changed(void) } else if (STRNCMP(p, "hiddenoff", 9) == 0) { p += 9; diff_flags_new |= DIFF_HIDDEN_OFF; + } else if (STRNCMP(p, "indent-heuristic", 16) == 0) { + p += 16; + diff_algorithm_new |= XDF_INDENT_HEURISTIC; + } else if (STRNCMP(p, "internal", 8) == 0) { + p += 8; + diff_flags_new |= DIFF_INTERNAL; + } else if (STRNCMP(p, "algorithm:", 10) == 0) { + p += 10; + if (STRNCMP(p, "myers", 5) == 0) { + p += 5; + diff_algorithm_new = 0; + } else if (STRNCMP(p, "minimal", 7) == 0) { + p += 7; + diff_algorithm_new = XDF_NEED_MINIMAL; + } else if (STRNCMP(p, "patience", 8) == 0) { + p += 8; + diff_algorithm_new = XDF_PATIENCE_DIFF; + } else if (STRNCMP(p, "histogram", 9) == 0) { + p += 9; + diff_algorithm_new = XDF_HISTOGRAM_DIFF; + } } if ((*p != ',') && (*p != NUL)) { @@ -1859,17 +2123,18 @@ int diffopt_changed(void) } // If "icase" or "iwhite" was added or removed, need to update the diff. - if (diff_flags != diff_flags_new) { + if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) { FOR_ALL_TABS(tp) { - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; } } diff_flags = diff_flags_new; diff_context = diff_context_new; diff_foldcolumn = diff_foldcolumn_new; + diff_algorithm = diff_algorithm_new; - diff_redraw(TRUE); + diff_redraw(true); // recompute the scroll binding with the new option value, may // remove or add filler lines @@ -1910,7 +2175,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) int l; // Make a copy of the line, the next ml_get() will invalidate it. - char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); + char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false)); int idx = diff_buf_idx(wp->w_buffer); if (idx == DB_COUNT) { @@ -1942,7 +2207,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) } added = false; line_new = ml_get_buf(curtab->tp_diffbuf[i], - dp->df_lnum[i] + off, FALSE); + dp->df_lnum[i] + off, false); // Search for start of difference si_org = si_new = 0; @@ -2117,7 +2382,7 @@ void ex_diffgetput(exarg_T *eap) int start_skip, end_skip; int new_count; int buf_empty; - int found_not_ma = FALSE; + int found_not_ma = false; int idx_other; int idx_from; int idx_to; @@ -2138,7 +2403,7 @@ void ex_diffgetput(exarg_T *eap) || MODIFIABLE(curtab->tp_diffbuf[idx_other])) { break; } - found_not_ma = TRUE; + found_not_ma = true; } } @@ -2176,7 +2441,7 @@ void ex_diffgetput(exarg_T *eap) // digits only i = atol((char *)eap->arg); } else { - i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE); + i = buflist_findpat(eap->arg, p, false, true, false); if (i < 0) { // error message already given @@ -2202,7 +2467,7 @@ void ex_diffgetput(exarg_T *eap) } } - diff_busy = TRUE; + diff_busy = true; // When no range given include the line above or below the cursor. if (eap->addr_count == 0) { @@ -2308,7 +2573,7 @@ void ex_diffgetput(exarg_T *eap) for (i = 0; i < count; ++i) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; - ml_delete(lnum, FALSE); + ml_delete(lnum, false); added--; } @@ -2317,15 +2582,15 @@ void ex_diffgetput(exarg_T *eap) if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { break; } - p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, FALSE)); - ml_append(lnum + i - 1, p, 0, FALSE); + p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false)); + ml_append(lnum + i - 1, p, 0, false); xfree(p); added++; if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) { // Added the first line into an empty buffer, need to // delete the dummy empty line. - buf_empty = FALSE; - ml_delete((linenr_T)2, FALSE); + buf_empty = false; + ml_delete((linenr_T)2, false); } } new_count = dp->df_count[idx_to] + added; @@ -2399,12 +2664,12 @@ void ex_diffgetput(exarg_T *eap) // another buffer. Sync undo if the command was typed. This isn't // 100% right when ":diffput" is used in a function or mapping. if (KeyTyped) { - u_sync(FALSE); + u_sync(false); } aucmd_restbuf(&aco); } - diff_busy = FALSE; + diff_busy = false; // Check that the cursor is on a valid character and update it's position. // When there were filler lines the topline has become invalid. @@ -2412,7 +2677,7 @@ void ex_diffgetput(exarg_T *eap) changed_line_abv_curs(); // Also need to redraw the other buffers. - diff_redraw(FALSE); + diff_redraw(false); } /// Update folds for all diff buffers for entry "dp". @@ -2637,3 +2902,143 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) } return n; } + +/// +/// Handle an ED style diff line. +/// Return FAIL if the line does not contain diff info. +/// +static int parse_diff_ed(char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long f1, l1, f2, l2; + int difftype; + + // The line must be one of three formats: + // change: {first}[,{last}]c{first}[,{last}] + // append: {first}a{first}[,{last}] + // delete: {first}[,{last}]d{first} + p = line; + f1 = getdigits(&p); + if (*p == ',') { + p++; + l1 = getdigits(&p); + } else { + l1 = f1; + } + if (*p != 'a' && *p != 'c' && *p != 'd') { + return FAIL; // invalid diff format + } + difftype = *p++; + f2 = getdigits(&p); + if (*p == ',') { + p++; + l2 = getdigits(&p); + } else { + l2 = f2; + } + if (l1 < f1 || l2 < f2) { + return FAIL; + } + + if (difftype == 'a') { + *lnum_orig = f1 + 1; + *count_orig = 0; + } else { + *lnum_orig = f1; + *count_orig = l1 - f1 + 1; + } + if (difftype == 'd') { + *lnum_new = f2 + 1; + *count_new = 0; + } else { + *lnum_new = f2; + *count_new = l2 - f2 + 1; + } + return OK; +} + +/// +/// Parses unified diff with zero(!) context lines. +/// Return FAIL if there is no diff information in "line". +/// +static int parse_diff_unified(char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long oldline, oldcount, newline, newcount; + + // Parse unified diff hunk header: + // @@ -oldline,oldcount +newline,newcount @@ + p = line; + if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { + oldline = getdigits(&p); + if (*p == ',') { + p++; + oldcount = getdigits(&p); + } else { + oldcount = 1; + } + if (*p++ == ' ' && *p++ == '+') { + newline = getdigits(&p); + if (*p == ',') { + p++; + newcount = getdigits(&p); + } else { + newcount = 1; + } + } else { + return FAIL; // invalid diff format + } + + if (oldcount == 0) { + oldline += 1; + } + if (newcount == 0) { + newline += 1; + } + if (newline == 0) { + newline = 1; + } + + *lnum_orig = oldline; + *count_orig = oldcount; + *lnum_new = newline; + *count_new = newcount; + + return OK; + } + + return FAIL; +} + +/// +/// Callback function for the xdl_diff() function. +/// Stores the diff output in a grow array. +/// +static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + diffout_T *dout = (diffout_T *)priv; + int i; + char_u *p; + + for (i = 0; i < nbuf; i++) { + // We are only interested in the header lines, skip text lines. + if (STRNCMP(mb[i].ptr, "@@ ", 3) != 0) { + continue; + } + ga_grow(&dout->dout_ga, 1); + p = vim_strnsave((char_u *)mb[i].ptr, mb[i].size); + if (p == NULL) { + return -1; + } + ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; + } + return 0; +} diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 7e8a7d1a35..8e5455bc67 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1739,13 +1739,13 @@ failed: close(0); #ifndef WIN32 // On Unix, use stderr for stdin, makes shell commands work. - ignored = dup(2); + vim_ignored = dup(2); #else // On Windows, use the console input handle for stdin. HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, (HANDLE)NULL); - ignored = _open_osfhandle(conin, _O_RDONLY); + vim_ignored = _open_osfhandle(conin, _O_RDONLY); #endif } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index df74e8b2ff..04ff1320ce 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1125,8 +1125,7 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ * can't do anything useful with the value. Assign to this variable to avoid * the warning. */ -EXTERN int ignored; -EXTERN char *ignoredp; +EXTERN int vim_ignored; // Start a msgpack-rpc channel over stdin/stdout. EXTERN bool embedded_mode INIT(= false); diff --git a/src/nvim/options.lua b/src/nvim/options.lua index b0575df7ec..0cc6f58c5f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -615,7 +615,7 @@ return { alloced=true, redraw={'current_window'}, varname='p_dip', - defaults={if_true={vi="filler"}} + defaults={if_true={vi="internal,filler"}} }, { full_name='digraph', abbreviation='dg', diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index afe289b262..3e47b02fc8 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -33,6 +33,18 @@ func Test_diff_fold_sync() endfunc func Test_vert_split() + set diffopt=filler + call Common_vert_split() + set diffopt& +endfunc + +func Test_vert_split_internal() + set diffopt=internal,filler + call Common_vert_split() + set diffopt& +endfunc + +func Common_vert_split() " Disable the title to avoid xterm keeping the wrong one. set notitle noicon new @@ -275,10 +287,8 @@ func Test_diffoff() bwipe! endfunc -func Test_diffopt_icase() - set diffopt=icase,foldcolumn:0 - - e one +func Common_icase_test() + edit one call setline(1, ['One', 'Two', 'Three', 'Four', 'Fi#ve']) redraw let normattr = screenattr(1, 1) @@ -300,32 +310,54 @@ func Test_diffopt_icase() diffoff! %bwipe! +endfunc + +func Test_diffopt_icase() + set diffopt=icase,foldcolumn:0 + call Common_icase_test() set diffopt& endfunc -func Test_diffopt_iwhite() - set diffopt=iwhite,foldcolumn:0 +func Test_diffopt_icase_internal() + set diffopt=icase,foldcolumn:0,internal + call Common_icase_test() + set diffopt& +endfunc - e one - " Difference in trailing spaces should be ignored, +func Common_iwhite_test() + edit one + " Difference in trailing spaces and amount of spaces should be ignored, " but not other space differences. - call setline(1, ["One \t", 'Two', 'Three', 'Four']) + call setline(1, ["One \t", 'Two', 'Three', 'one two', 'one two', 'Four']) redraw let normattr = screenattr(1, 1) diffthis botright vert new two - call setline(1, ["One\t ", "Two\t ", 'Three', ' Four']) + call setline(1, ["One\t ", "Two\t ", 'Three', 'one two', 'onetwo', ' Four']) diffthis redraw call assert_equal(normattr, screenattr(1, 1)) call assert_equal(normattr, screenattr(2, 1)) call assert_equal(normattr, screenattr(3, 1)) - call assert_notequal(normattr, screenattr(4, 1)) + call assert_equal(normattr, screenattr(4, 1)) + call assert_notequal(normattr, screenattr(5, 1)) + call assert_notequal(normattr, screenattr(6, 1)) diffoff! %bwipe! +endfunc + +func Test_diffopt_iwhite() + set diffopt=iwhite,foldcolumn:0 + call Common_iwhite_test() + set diffopt& +endfunc + +func Test_diffopt_iwhite_internal() + set diffopt=internal,iwhite,foldcolumn:0 + call Common_iwhite_test() set diffopt& endfunc @@ -339,8 +371,13 @@ func Test_diffopt_context() set diffopt=context:2 call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=internal,context:2 + call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=context:1 call assert_equal('+-- 3 lines: 1', foldtextresult(1)) + set diffopt=internal,context:1 + call assert_equal('+-- 3 lines: 1', foldtextresult(1)) diffoff! %bwipe! @@ -348,7 +385,7 @@ func Test_diffopt_context() endfunc func Test_diffopt_horizontal() - set diffopt=horizontal + set diffopt=internal,horizontal diffsplit call assert_equal(&columns, winwidth(1)) @@ -362,7 +399,7 @@ func Test_diffopt_horizontal() endfunc func Test_diffopt_vertical() - set diffopt=vertical + set diffopt=internal,vertical diffsplit call assert_equal(&lines - 2, winheight(1)) @@ -376,7 +413,7 @@ func Test_diffopt_vertical() endfunc func Test_diffopt_hiddenoff() - set diffopt=filler,foldcolumn:0,hiddenoff + set diffopt=internal,filler,foldcolumn:0,hiddenoff e! one call setline(1, ['Two', 'Three']) redraw @@ -399,7 +436,7 @@ func Test_diffopt_hiddenoff() endfunc func Test_diffoff_hidden() - set diffopt=filler,foldcolumn:0 + set diffopt=internal,filler,foldcolumn:0 e! one call setline(1, ['Two', 'Three']) redraw @@ -523,7 +560,7 @@ func Test_diffpatch() 3 + 4 . - saveas Xpatch + saveas! Xpatch bwipe! new call assert_fails('diffpatch Xpatch', 'E816:') @@ -569,6 +606,22 @@ func Test_diff_nomodifiable() %bwipe! endfunc +func Test_diff_filler() + new + call setline(1, [1, 2, 3, 'x', 4]) + diffthis + vnew + call setline(1, [1, 2, 'y', 'y', 3, 4]) + diffthis + redraw + + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) + wincmd w + call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) + + %bwipe! +endfunc + func Test_diff_lastline() enew! only! @@ -615,3 +668,23 @@ func Test_diff_with_cursorline() call StopVimInTerminal(buf) call delete('Xtest_diff_cursorline') endfunc + +func Test_diff_of_diff() + if !CanRunVimInTerminal() + return + endif + + call writefile([ + \ 'call setline(1, ["aa","bb","cc","@@ -3,2 +5,7 @@","dd","ee","ff"])', + \ 'vnew', + \ 'call setline(1, ["aa","bb","cc"])', + \ 'windo diffthis', + \ ], 'Xtest_diff_diff') + let buf = RunVimInTerminal('-S Xtest_diff_diff', {}) + + call VerifyScreenDump(buf, 'Test_diff_of_diff_01', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_diff') +endfunc diff --git a/src/nvim/xdiff/COPYING b/src/nvim/xdiff/COPYING new file mode 100644 index 0000000000..f3f1b3b65e --- /dev/null +++ b/src/nvim/xdiff/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good fait