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/diff.c | 831 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 618 insertions(+), 213 deletions(-) (limited to 'src/nvim/diff.c') 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; +} -- cgit From 72c5a9db70f263eaac12aee140da03190e2fa605 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 13:50:14 +0200 Subject: vim-patch:8.1.0375: cannot use diff mode with Cygwin diff.exe Problem: Cannot use diff mode with Cygwin diff.exe. (Igor Forca) Solution: Skip over unrecognized lines in the diff output. https://github.com/vim/vim/commit/3b8defd0a52fc1276816608e7bb24b628ab14c2e --- src/nvim/diff.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index c3eb2465bd..2ad9471046 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1511,6 +1511,10 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout) && (vim_fgets(linebuf, LBUFLEN, fd) == 0) && (STRNCMP(line, "@@ ", 3) == 0)) { diffstyle = DIFF_UNIFIED; + } else { + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; } } -- cgit From 972ad1119557a0f72f1907c6758d6ed56438e5b3 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 15:00:19 +0200 Subject: vim-patch:8.1.0393: not all white space difference options available Problem: Not all white space difference options available. Solution: Add "iblank", "iwhiteall" and "iwhiteeol" to 'diffopt'. https://github.com/vim/vim/commit/785fc6567f572b8caefbc89ec29bbd8b801464ae --- src/nvim/diff.c | 75 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 18 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2ad9471046..6e034485d9 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -45,13 +45,17 @@ 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 -#define DIFF_INTERNAL 64 // use internal xdiff algorithm +#define DIFF_FILLER 0x001 // display filler lines +#define DIFF_IBLANK 0x002 // ignore empty lines +#define DIFF_ICASE 0x004 // ignore case +#define DIFF_IWHITE 0x008 // ignore change in white space +#define DIFF_IWHITEALL 0x010 // ignore all white space changes +#define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL +#define DIFF_HORIZONTAL 0x040 // horizontal splits +#define DIFF_VERTICAL 0x080 // vertical splits +#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden +#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm +#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; static long diff_algorithm = 0; @@ -1017,6 +1021,15 @@ static int diff_file_internal(diffio_T *diffio) if (diff_flags & DIFF_IWHITE) { param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; } + if (diff_flags & DIFF_IWHITEALL) { + param.flags |= XDF_IGNORE_WHITESPACE; + } + if (diff_flags & DIFF_IWHITEEOL) { + param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; + } + if (diff_flags & DIFF_IBLANK) { + param.flags |= XDF_IGNORE_BLANK_LINES; + } emit_cfg.ctxlen = 0; // don't need any diff_context here emit_cb.priv = &diffio->dio_diff; @@ -1064,10 +1077,13 @@ static int diff_file(diffio_T *dio) // 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", + vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s", diff_a_works == kFalse ? "" : "-a ", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", + (diff_flags & DIFF_IWHITEALL) ? "-w " : "", + (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", + (diff_flags & DIFF_IBLANK) ? "-B " : "", (diff_flags & DIFF_ICASE) ? "-i " : "", tmp_orig, tmp_new); append_redir(cmd, len, (char *) p_srr, tmp_diff); @@ -1881,20 +1897,28 @@ static bool diff_equal_char(const char_u *const p1, const char_u *const p2, /// @return on-zero if the two strings are different. static int diff_cmp(char_u *s1, char_u *s2) { - if ((diff_flags & (DIFF_ICASE | DIFF_IWHITE)) == 0) { + if ((diff_flags & DIFF_IBLANK) + && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) { + return 0; + } + + if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) { return STRCMP(s1, s2); } - if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { + if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) { return mb_stricmp((const char *)s1, (const char *)s2); } - // Ignore white space changes and possibly ignore case. char_u *p1 = s1; char_u *p2 = s2; + // Ignore white space changes and possibly ignore case. while (*p1 != NUL && *p2 != NUL) { - if (ascii_iswhite(*p1) && ascii_iswhite(*p2)) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(*p1) && ascii_iswhite(*p2)) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(*p1) || ascii_iswhite(*p2)))) { p1 = skipwhite(p1); p2 = skipwhite(p2); } else { @@ -2071,9 +2095,18 @@ int diffopt_changed(void) } else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) { p += 8; diff_context_new = getdigits_int(&p); + } else if (STRNCMP(p, "iblank", 6) == 0) { + p += 6; + diff_flags_new |= DIFF_IBLANK; } else if (STRNCMP(p, "icase", 5) == 0) { p += 5; diff_flags_new |= DIFF_ICASE; + } else if (STRNCMP(p, "iwhiteall", 9) == 0) { + p += 9; + diff_flags_new |= DIFF_IWHITEALL; + } else if (STRNCMP(p, "iwhiteeol", 9) == 0) { + p += 9; + diff_flags_new |= DIFF_IWHITEEOL; } else if (STRNCMP(p, "iwhite", 6) == 0) { p += 6; diff_flags_new |= DIFF_IWHITE; @@ -2217,9 +2250,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) si_org = si_new = 0; while (line_org[si_org] != NUL) { - if ((diff_flags & DIFF_IWHITE) - && ascii_iswhite(line_org[si_org]) - && ascii_iswhite(line_new[si_new])) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(line_org[si_org]) + && ascii_iswhite(line_new[si_new])) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(line_org[si_org]) + || ascii_iswhite(line_new[si_new])))) { si_org = (int)(skipwhite(line_org + si_org) - line_org); si_new = (int)(skipwhite(line_new + si_new) - line_new); } else { @@ -2249,9 +2285,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) && ei_new >= si_new && ei_org >= 0 && ei_new >= 0) { - if ((diff_flags & DIFF_IWHITE) - && ascii_iswhite(line_org[ei_org]) - && ascii_iswhite(line_new[ei_new])) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(line_org[ei_org]) + && ascii_iswhite(line_new[ei_new])) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(line_org[ei_org]) + || ascii_iswhite(line_new[ei_new])))) { while (ei_org >= *startp && ascii_iswhite(line_org[ei_org])) { ei_org--; } -- cgit From 7b6c92eac1b7aa702f734120bdd18b5a4f6cfe04 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 15:31:22 +0200 Subject: vim-patch:8.1.0394: diffs are not always updated correctly Problem: Diffs are not always updated correctly. Solution: When using internal diff update for any changes properly. https://github.com/vim/vim/commit/e3521d9cbb786806eaff106707851d37d2c0ecef --- src/nvim/diff.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 6e034485d9..a927983374 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -266,6 +266,15 @@ void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after) { + if (diff_internal()) { + // Will udpate diffs before redrawing. Set _invalid to update the + // diffs themselves, set _update to also update folds properly just + // before redrawing. + tp->tp_diff_invalid = true; + tp->tp_diff_update = true; + return; + } + int inserted; int deleted; if (line2 == MAXLNUM) { @@ -840,7 +849,7 @@ theend: /// Note that if the internal diff failed for one of the buffers, the external /// diff will be used anyway. /// -static int diff_internal(void) +int diff_internal(void) { return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; } @@ -864,9 +873,9 @@ static int diff_internal_failed(void) /// Completely update the diffs for the buffers involved. /// -/// This uses the ordinary "diff" command. -/// The buffers are written to a file, also for unmodified buffers (the file -/// could have been produced by autocommands, e.g. the netrw plugin). +/// When using the external "diff" command the buffers are written to a file, +/// also for unmodified buffers (the file could have been produced by +/// autocommands, e.g. the netrw plugin). /// /// @param eap can be NULL void ex_diffupdate(exarg_T *eap) -- cgit From b7f2c7dd1d2a5753962c9276df3db8bc5d358b3e Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 19:13:48 +0200 Subject: vim-patch:8.1.0395: compiler warning on 64-bit MS-Windows Problem: Compiler warning on 64-bit MS-Windows. Solution: Add type cast. (Mike Williams) https://github.com/vim/vim/commit/6e272acc82af900318017061f923e7f66dc7ee7a --- src/nvim/diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a927983374..bd66b32213 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -692,7 +692,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) // 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; + len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1; } ptr = xmalloc(len); if (ptr == NULL) { -- cgit From 2c92a4d0c8a398c8a7dfd1666fdcfd2ab89cc887 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 19:26:12 +0200 Subject: vim-patch:8.1.0397: no event triggered after updating diffs Problem: No event triggered after updating diffs. Solution: Add the DiffUpdated event. https://github.com/vim/vim/commit/e8fa05b5bc2d6d76bf5af50176a63655d00d1110 --- src/nvim/diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index bd66b32213..0fb8324546 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -924,6 +924,8 @@ void ex_diffupdate(exarg_T *eap) curwin->w_valid_cursor.lnum = 0; diff_redraw(true); + + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); } /// -- cgit From 271249a48a4847929410b9055bdb560e52271919 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 19:34:20 +0200 Subject: vim-patch:8.1.0400: using freed memory with :diffget Problem: Using freed memory with :diffget. Solution: Skip ex_diffupdate() while updating diffs. (closes #3442) https://github.com/vim/vim/commit/d2b58c0a2c665075a8cfef57db6e1b37d4523e02 --- src/nvim/diff.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0fb8324546..9ac9b6f6db 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -42,7 +42,8 @@ #include "nvim/os/os.h" #include "nvim/os/shell.h" -static int diff_busy = false; // ex_diffgetput() is busy +static int diff_busy = false; // using diff structs, don't change them +static int diff_need_update = false; // ex_diffupdate needs to be called // Flags obtained from the 'diffopt' option #define DIFF_FILLER 0x001 // display filler lines @@ -880,6 +881,11 @@ static int diff_internal_failed(void) /// @param eap can be NULL void ex_diffupdate(exarg_T *eap) { + if (diff_busy) { + diff_need_update = true; + return; + } + // Delete all diffblocks. diff_clear(curtab); curtab->tp_diff_invalid = false; @@ -2558,7 +2564,7 @@ void ex_diffgetput(exarg_T *eap) change_warning(0); if (diff_buf_idx(curbuf) != idx_to) { EMSG(_("E787: Buffer changed unexpectedly")); - return; + goto theend; } } @@ -2723,7 +2729,12 @@ void ex_diffgetput(exarg_T *eap) aucmd_restbuf(&aco); } +theend: diff_busy = false; + if (diff_need_update) { + diff_need_update = false; + ex_diffupdate(NULL); + } // Check that the cursor is on a valid character and update it's position. // When there were filler lines the topline has become invalid. -- cgit From fe0114ec414108d544da73c3147fd67079f6cc2e Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 19:44:58 +0200 Subject: vim-patch:8.1.0402: the DiffUpdate event isn't triggered for :diffput Problem: The DiffUpdate event isn't triggered for :diffput. Solution: Also trigger DiffUpdate for :diffget and :diffput. https://github.com/vim/vim/commit/198fa066b2ec011e91012c1a3d85a73df7b93f31 --- src/nvim/diff.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 9ac9b6f6db..b9376c311f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -268,7 +268,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after) { if (diff_internal()) { - // Will udpate diffs before redrawing. Set _invalid to update the + // Will update diffs before redrawing. Set _invalid to update the // diffs themselves, set _update to also update folds properly just // before redrawing. tp->tp_diff_invalid = true; @@ -886,6 +886,8 @@ void ex_diffupdate(exarg_T *eap) return; } + int had_diffs = curtab->tp_first_diff != NULL; + // Delete all diffblocks. diff_clear(curtab); curtab->tp_diff_invalid = false; @@ -899,7 +901,7 @@ void ex_diffupdate(exarg_T *eap) } if (idx_orig == DB_COUNT) { - return; + goto theend; } // Only need to do something when there is another buffer. @@ -911,7 +913,7 @@ void ex_diffupdate(exarg_T *eap) } if (idx_new == DB_COUNT) { - return; + goto theend; } // Only use the internal method if it did not fail for one of the buffers. @@ -929,9 +931,13 @@ void ex_diffupdate(exarg_T *eap) // force updating cursor position on screen curwin->w_valid_cursor.lnum = 0; - diff_redraw(true); - - apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); +theend: + // A redraw is needed if there were diffs and they were cleared, or there + // are diffs now, which means they got updated. + if (had_diffs || curtab->tp_first_diff != NULL) { + diff_redraw(true); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); + } } /// @@ -2176,7 +2182,8 @@ int diffopt_changed(void) return FAIL; } - // If "icase" or "iwhite" was added or removed, need to update the diff. + // If flags were added or removed, or the algorithm was changed, need to + // update the diff. if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) { FOR_ALL_TABS(tp) { tp->tp_diff_invalid = true; @@ -2734,15 +2741,17 @@ theend: if (diff_need_update) { diff_need_update = false; ex_diffupdate(NULL); - } - - // Check that the cursor is on a valid character and update it's position. - // When there were filler lines the topline has become invalid. - check_cursor(); - changed_line_abv_curs(); + } else { + // Check that the cursor is on a valid character and update it's + // position. When there were filler lines the topline has become + // invalid. + check_cursor(); + changed_line_abv_curs(); - // Also need to redraw the other buffers. - diff_redraw(false); + // Also need to redraw the other buffers. + diff_redraw(false); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); + } } /// Update folds for all diff buffers for entry "dp". -- cgit From e104228b1c89022f0b284753ff92deb2e9374c5b Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 19:55:08 +0200 Subject: vim-patch:8.1.0458: ml_get error and crash when using "do" Problem: Ml_get error and crash when using "do". Solution: Adjust cursor position also when diffupdate is not needed. (Hirohito Higashi) https://github.com/vim/vim/commit/df77cef92ec034796723ffa3adb12e8b46daa98e --- src/nvim/diff.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index b9376c311f..57bfc8db69 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2739,15 +2739,19 @@ void ex_diffgetput(exarg_T *eap) theend: diff_busy = false; if (diff_need_update) { - diff_need_update = false; ex_diffupdate(NULL); - } else { - // Check that the cursor is on a valid character and update it's - // position. When there were filler lines the topline has become - // invalid. - check_cursor(); - changed_line_abv_curs(); + } + + // Check that the cursor is on a valid character and update it's + // position. When there were filler lines the topline has become + // invalid. + check_cursor(); + changed_line_abv_curs(); + if (diff_need_update) { + // redraw already done by ex_diffupdate() + diff_need_update = false; + } else { // Also need to redraw the other buffers. diff_redraw(false); apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); -- cgit From 8f20c22e10c35075afd97bf6af50fb6d9a2dc710 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 20:05:53 +0200 Subject: vim-patch:8.1.0497: :%diffput changes order of lines Problem: :%diffput changes order of lines. (Markus Braun) Solution: Do adjust marks when using internal diff. https://github.com/vim/vim/commit/5f57bdcab77bc417ae0357fe8ad6c7259b6d25df --- src/nvim/diff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 57bfc8db69..2bb73f63ba 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -271,9 +271,9 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, // Will update diffs before redrawing. Set _invalid to update the // diffs themselves, set _update to also update folds properly just // before redrawing. + // Do update marks here, it is needed for :%diffput. tp->tp_diff_invalid = true; tp->tp_diff_update = true; - return; } int inserted; @@ -2742,7 +2742,7 @@ theend: ex_diffupdate(NULL); } - // Check that the cursor is on a valid character and update it's + // Check that the cursor is on a valid character and update its // position. When there were filler lines the topline has become // invalid. check_cursor(); -- cgit From 89eba72792fb9e36ede7e9d58f5673ebfd553178 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 20:13:34 +0200 Subject: vim-patch:8.1.0502: internal diff fails when diffing a context diff Problem: Internal diff fails when diffing a context diff. (Hirohito Higashi) Solution: Only use callback calls with one line. (closes #3581) https://github.com/vim/vim/commit/f080d70a82f3a4477f346d9efcdfaec1bc1e1d58 --- src/nvim/diff.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2bb73f63ba..6dbebd5e29 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -3103,20 +3103,22 @@ static int parse_diff_unified(char_u *line, 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; + // The header line always comes by itself, text lines in at least two + // parts. We drop the text part. + if (nbuf > 1) { + return 0; } + + // sanity check + if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) { + return 0; + } + + ga_grow(&dout->dout_ga, 1); + + p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size); + ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; return 0; } -- cgit From f273e43cb83c1617969ebd4eaa51aa5bc3f0b855 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 20:17:04 +0200 Subject: vim-patch:8.1.0513: no error for set diffopt+=algorithm: Problem: No error for set diffopt+=algorithm:. Solution: Check for missing argument. (Hirohito Higashi, closes #3598) https://github.com/vim/vim/commit/d0721058f494143186f66a60151c9634031a8c96 --- src/nvim/diff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 6dbebd5e29..19751cb73a 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2165,6 +2165,8 @@ int diffopt_changed(void) } else if (STRNCMP(p, "histogram", 9) == 0) { p += 9; diff_algorithm_new = XDF_HISTOGRAM_DIFF; + } else { + return FAIL; } } -- cgit From 4e2981081796c8618caa4bed30d12e1e8fc8ae08 Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Sun, 9 Dec 2018 20:24:38 +0200 Subject: vim-patch:8.1.0562: parsing of 'diffopt' is slightly wrong Problem: Parsing of 'diffopt' is slightly wrong. Solution: Fix the parsing and add a test. (Jason Franklin, Christian Brabandt) https://github.com/vim/vim/commit/b6fc72851c45a36a370f9516c68508e47b41c4c1 --- src/nvim/diff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/diff.c') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 19751cb73a..2e0b198c13 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2109,6 +2109,7 @@ int diffopt_changed(void) int diff_flags_new = 0; int diff_foldcolumn_new = 2; long diff_algorithm_new = 0; + long diff_indent_heuristic = 0; char_u *p = p_dip; while (*p != NUL) { @@ -2147,7 +2148,7 @@ int diffopt_changed(void) diff_flags_new |= DIFF_HIDDEN_OFF; } else if (STRNCMP(p, "indent-heuristic", 16) == 0) { p += 16; - diff_algorithm_new |= XDF_INDENT_HEURISTIC; + diff_indent_heuristic = XDF_INDENT_HEURISTIC; } else if (STRNCMP(p, "internal", 8) == 0) { p += 8; diff_flags_new |= DIFF_INTERNAL; @@ -2179,6 +2180,8 @@ int diffopt_changed(void) } } + diff_algorithm_new |= diff_indent_heuristic; + // Can't have both "horizontal" and "vertical". if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) { return FAIL; -- cgit