diff options
Diffstat (limited to 'src/nvim/diff.c')
| -rw-r--r-- | src/nvim/diff.c | 1449 |
1 files changed, 893 insertions, 556 deletions
diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 5485d528f7..032de561b3 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -10,9 +10,15 @@ /// - Use the compiled-in xdiff library. /// - Let 'diffexpr' do the work, using files. +#include <assert.h> +#include <ctype.h> #include <inttypes.h> #include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "auto/config.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" @@ -23,9 +29,15 @@ #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" +#include "nvim/extmark_defs.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/linematch.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -35,10 +47,13 @@ #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/optionstr.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" #include "nvim/os/shell.h" #include "nvim/path.h" +#include "nvim/pos.h" #include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/vim.h" @@ -61,10 +76,12 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm #define DIFF_CLOSE_OFF 0x400 // diffoff when closing window #define DIFF_FOLLOWWRAP 0x800 // follow the wrap option +#define DIFF_LINEMATCH 0x1000 // match most similar lines within diff #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF; static long diff_algorithm = 0; +static int linematch_lines = 0; #define LBUFLEN 50 // length of line in diff file @@ -74,13 +91,13 @@ static TriState diff_a_works = kNone; // used for diff input typedef struct { - char_u *din_fname; // used for external diff + char *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 + char *dout_fname; // used for external diff garray_T dout_ga; // used for internal diff } diffout_T; @@ -100,6 +117,12 @@ typedef struct { int dio_internal; // using internal diff } diffio_T; +typedef enum { + DIFF_ED, + DIFF_UNIFIED, + DIFF_NONE, +} diffstyle_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.c.generated.h" #endif @@ -120,7 +143,7 @@ void diff_buf_delete(buf_T *buf) // don't redraw right away, more might change or buffer state // is invalid right now need_diff_redraw = true; - redraw_later(curwin, VALID); + redraw_later(curwin, UPD_VALID); } } } @@ -204,13 +227,7 @@ static void diff_buf_clear(void) /// @return Its index or DB_COUNT if not found. static int diff_buf_idx(buf_T *buf) { - int idx; - for (idx = 0; idx < DB_COUNT; idx++) { - if (curtab->tp_diffbuf[idx] == buf) { - break; - } - } - return idx; + return diff_buf_idx_tp(buf, curtab); } /// Find buffer "buf" in the list of diff buffers for tab page "tp". @@ -309,8 +326,6 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T diff_T *dp = tp->tp_first_diff; linenr_T lnum_deleted = line1; // lnum of remaining deletion - linenr_T n; - linenr_T off; for (;;) { // If the change is after the previous diff block and before the next // diff block, thus not touching an existing change, create a new diff @@ -363,7 +378,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T if (last >= line1 - 1) { // 6. change below line2: only adjust for amount_after; also when // "deleted" became zero when deleted all lines between two diffs. - if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) { + if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2 - dp->is_linematched) { if (amount_after == 0) { // nothing left to change break; @@ -374,7 +389,8 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { - off = 0; + linenr_T n; + linenr_T off = 0; if (dp->df_lnum[idx] >= line1) { if (last <= line2) { // 4. delete all lines of diff @@ -454,7 +470,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } // check if this block touches the previous one, may merge them. - if ((dprev != NULL) + if ((dprev != NULL) && !dp->is_linematched && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) { int i; for (i = 0; i < DB_COUNT; i++) { @@ -462,9 +478,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T dprev->df_count[i] += dp->df_count[i]; } } - dprev->df_next = dp->df_next; - xfree(dp); - dp = dprev->df_next; + dp = diff_free(tp, dprev, dp); } else { // Advance to next entry. dprev = dp; @@ -485,15 +499,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } if (i == DB_COUNT) { - diff_T *dnext = dp->df_next; - xfree(dp); - dp = dnext; - - if (dprev == NULL) { - tp->tp_first_diff = dnext; - } else { - dprev->df_next = dnext; - } + dp = diff_free(tp, dprev, dp); } else { // Advance to next entry. dprev = dp; @@ -523,6 +529,7 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) { diff_T *dnew = xmalloc(sizeof(*dnew)); + dnew->is_linematched = false; dnew->df_next = dp; if (dprev == NULL) { tp->tp_first_diff = dnew; @@ -533,6 +540,20 @@ static diff_T *diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp) return dnew; } +static diff_T *diff_free(tabpage_T *tp, diff_T *dprev, diff_T *dp) +{ + diff_T *ret = dp->df_next; + xfree(dp); + + if (dprev == NULL) { + tp->tp_first_diff = ret; + } else { + dprev->df_next = ret; + } + + return ret; +} + /// Check if the diff block "dp" can be made smaller for lines at the start and /// end that are equal. Called after inserting lines. /// @@ -573,9 +594,9 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) if (dir == BACKWARD) { off_org = dp->df_count[i_org] - 1; } - char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], - dp->df_lnum[i_org] + off_org, - false)); + char *line_org = xstrdup(ml_get_buf(tp->tp_diffbuf[i_org], + dp->df_lnum[i_org] + off_org, + false)); int i_new; for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) { @@ -659,7 +680,7 @@ void diff_redraw(bool dofold) continue; } - redraw_later(wp, SOME_VALID); + redraw_later(wp, UPD_SOME_VALID); if (wp != curwin) { wp_other = wp; } @@ -704,16 +725,16 @@ static void clear_diffin(diffin_T *din) if (din->din_fname == NULL) { XFREE_CLEAR(din->din_mmfile.ptr); } else { - os_remove((char *)din->din_fname); + os_remove(din->din_fname); } } static void clear_diffout(diffout_T *dout) { if (dout->dout_fname == NULL) { - ga_clear_strings(&dout->dout_ga); + ga_clear(&dout->dout_ga); } else { - os_remove((char *)dout->dout_fname); + os_remove(dout->dout_fname); } } @@ -723,15 +744,19 @@ static void clear_diffout(diffout_T *dout) /// @param din /// /// @return FAIL for failure. -static int diff_write_buffer(buf_T *buf, diffin_T *din) +static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T end) { - long len = 0; + size_t len = 0; + + if (end < 0) { + end = buf->b_ml.ml_line_count; + } // xdiff requires one big block of memory with all the text. - for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1; + for (linenr_T lnum = start; lnum <= end; lnum++) { + len += strlen(ml_get_buf(buf, lnum, false)) + 1; } - char_u *ptr = try_malloc((size_t)len); + char *ptr = try_malloc(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 @@ -745,37 +770,32 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) } return FAIL; } - din->din_mmfile.ptr = (char *)ptr; - din->din_mmfile.size = len; + m->ptr = ptr; + m->size = (long)len; len = 0; - for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) { - if (diff_flags & DIFF_ICASE) { - int c; + for (linenr_T lnum = start; lnum <= end; lnum++) { + char *s = ml_get_buf(buf, lnum, false); + if (diff_flags & DIFF_ICASE) { + while (*s != NUL) { char cbuf[MB_MAXBYTES + 1]; - if (*s == NL) { - c = NUL; - } else { - // xdiff doesn't support ignoring case, fold-case the text. - c = utf_ptr2char((char *)s); - c = utf_fold(c); - } - const int orig_len = utfc_ptr2len((char *)s); - if (utf_char2bytes(c, (char *)cbuf) != orig_len) { - // TODO(Bram): handle byte length difference - memmove(ptr + len, s, (size_t)orig_len); - } else { - memmove(ptr + len, cbuf, (size_t)orig_len); - } + // xdiff doesn't support ignoring case, fold-case the text. + int c = *s == NL ? NUL : utf_fold(utf_ptr2char(s)); + const int orig_len = utfc_ptr2len(s); + // TODO(Bram): handle byte length difference + char *s1 = (utf_char2bytes(c, cbuf) != orig_len) ? s : cbuf; + memmove(ptr + len, s1, (size_t)orig_len); s += orig_len; - len += orig_len; - } else { - ptr[len++] = *s == NL ? NUL : *s; - s++; + len += (size_t)orig_len; } + } else { + size_t slen = strlen(s); + memmove(ptr + len, s, slen); + // NUL is represented as NL; convert + memchrsub(ptr + len, NL, NUL, slen); + len += slen; } ptr[len++] = NL; } @@ -793,17 +813,17 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) static int diff_write(buf_T *buf, diffin_T *din) { if (din->din_fname == NULL) { - return diff_write_buffer(buf, din); + return diff_write_buffer(buf, &din->din_mmfile, 1, -1); } // Always use 'fileformat' set to "unix". - char_u *save_ff = buf->b_p_ff; - buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + char *save_ff = buf->b_p_ff; + buf->b_p_ff = xstrdup(FF_UNIX); const bool save_cmod_flags = cmdmod.cmod_flags; // Writing the buffer is an implementation detail of performing the diff, // so it shouldn't update the '[ and '] marks. cmdmod.cmod_flags |= CMOD_LOCKMARKS; - int r = buf_write(buf, (char *)din->din_fname, NULL, + int r = buf_write(buf, din->din_fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, NULL, false, false, false, true); cmdmod.cmod_flags = save_cmod_flags; @@ -821,7 +841,7 @@ static int diff_write(buf_T *buf, diffin_T *din) static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) { if (dio->dio_internal) { - ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); + ga_init(&dio->dio_diff.dout_ga, sizeof(diffhunk_T), 100); } else { // We need three temp file names. dio->dio_orig.din_fname = vim_tempname(); @@ -992,7 +1012,7 @@ static int check_external_diff(diffio_T *diffio) TriState ok = kFalse; for (;;) { ok = kFalse; - FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w"); + FILE *fd = os_fopen(diffio->dio_orig.din_fname, "w"); if (fd == NULL) { io_error = true; @@ -1001,7 +1021,7 @@ static int check_external_diff(diffio_T *diffio) io_error = true; } fclose(fd); - fd = os_fopen((char *)diffio->dio_new.din_fname, "w"); + fd = os_fopen(diffio->dio_new.din_fname, "w"); if (fd == NULL) { io_error = true; @@ -1012,13 +1032,13 @@ static int check_external_diff(diffio_T *diffio) fclose(fd); fd = NULL; if (diff_file(diffio) == OK) { - fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r"); + fd = os_fopen(diffio->dio_diff.dout_fname, "r"); } if (fd == NULL) { io_error = true; } else { - char_u linebuf[LBUFLEN]; + char linebuf[LBUFLEN]; for (;;) { // For normal diff there must be a line that contains @@ -1027,17 +1047,17 @@ static int check_external_diff(diffio_T *diffio) break; } - if (STRNCMP(linebuf, "1c1", 3) == 0 - || STRNCMP(linebuf, "@@ -1 +1 @@", 11) == 0) { + if (strncmp(linebuf, "1c1", 3) == 0 + || strncmp(linebuf, "@@ -1 +1 @@", 11) == 0) { ok = kTrue; } } fclose(fd); } - os_remove((char *)diffio->dio_diff.dout_fname); - os_remove((char *)diffio->dio_new.din_fname); + os_remove(diffio->dio_diff.dout_fname); + os_remove(diffio->dio_new.din_fname); } - os_remove((char *)diffio->dio_orig.din_fname); + os_remove(diffio->dio_orig.din_fname); } // When using 'diffexpr' break here. @@ -1115,9 +1135,9 @@ static int diff_file_internal(diffio_T *diffio) /// @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; + char *tmp_orig = dio->dio_orig.din_fname; + char *tmp_new = dio->dio_new.din_fname; + char *tmp_diff = dio->dio_diff.dout_fname; if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); @@ -1126,37 +1146,37 @@ static int diff_file(diffio_T *dio) // 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); - - // 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%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); - block_autocmds(); // Avoid ShellCmdPost stuff - (void)call_shell((char_u *)cmd, - kShellOptFilter | kShellOptSilent | kShellOptDoOut, - NULL); - unblock_autocmds(); - xfree(cmd); - return OK; } + + const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + + strlen(p_srr) + 27); + char *const cmd = xmalloc(len); + + // 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%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, p_srr, tmp_diff); + block_autocmds(); // Avoid ShellCmdPost stuff + (void)call_shell(cmd, + kShellOptFilter | kShellOptSilent | kShellOptDoOut, + NULL); + unblock_autocmds(); + xfree(cmd); + return OK; } /// Create a new version of a file from the current buffer and a diff file. @@ -1167,10 +1187,10 @@ static int diff_file(diffio_T *dio) /// @param eap void ex_diffpatch(exarg_T *eap) { - char_u *buf = NULL; + char *buf = NULL; win_T *old_curwin = curwin; char *newname = NULL; // name of patched file buffer - char_u *esc_name = NULL; + char *esc_name = NULL; #ifdef UNIX char *fullname = NULL; @@ -1178,16 +1198,16 @@ void ex_diffpatch(exarg_T *eap) // We need two temp file names. // Name of original temp file. - char_u *tmp_orig = vim_tempname(); + char *tmp_orig = vim_tempname(); // Name of patched temp file. - char_u *tmp_new = vim_tempname(); + char *tmp_new = vim_tempname(); if ((tmp_orig == NULL) || (tmp_new == NULL)) { goto theend; } // Write the current buffer to "tmp_orig". - if (buf_write(curbuf, (char *)tmp_orig, NULL, + if (buf_write(curbuf, tmp_orig, NULL, (linenr_T)1, curbuf->b_ml.ml_line_count, NULL, false, false, false, true) == FAIL) { goto theend; @@ -1196,23 +1216,22 @@ void ex_diffpatch(exarg_T *eap) #ifdef UNIX // Get the absolute path of the patchfile, changing directory below. fullname = FullName_save(eap->arg, false); - esc_name = - vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true); + esc_name = vim_strsave_shellescape(fullname != NULL ? fullname : eap->arg, true, true); #else esc_name = vim_strsave_shellescape(eap->arg, true, true); #endif - size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16; + size_t buflen = strlen(tmp_orig) + strlen(esc_name) + strlen(tmp_new) + 16; buf = xmalloc(buflen); #ifdef UNIX - char_u dirbuf[MAXPATHL]; + char dirbuf[MAXPATHL]; // Temporarily chdir to /tmp, to avoid patching files in the current // directory when the patch file contains more than one patch. When we // have our own temp dir use that instead, it will be cleaned up when we // exit (any .rej files created). Don't change directory if we can't // return to the current. if ((os_dirname(dirbuf, MAXPATHL) != OK) - || (os_chdir((char *)dirbuf) != 0)) { + || (os_chdir(dirbuf) != 0)) { dirbuf[0] = NUL; } else { char *tempdir = vim_gettempdir(); @@ -1227,15 +1246,13 @@ void ex_diffpatch(exarg_T *eap) if (*p_pex != NUL) { // Use 'patchexpr' to generate the new file. #ifdef UNIX - eval_patch((char *)tmp_orig, - (fullname != NULL ? fullname : eap->arg), - (char *)tmp_new); + eval_patch(tmp_orig, (fullname != NULL ? fullname : eap->arg), tmp_new); #else - eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new); + eval_patch(tmp_orig, eap->arg, tmp_new); #endif } else { // Build the patch command and execute it. Ignore errors. - vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", + vim_snprintf(buf, buflen, "patch -o %s %s < %s", tmp_new, tmp_orig, esc_name); block_autocmds(); // Avoid ShellCmdPost stuff (void)call_shell(buf, kShellOptFilter, NULL); @@ -1244,7 +1261,7 @@ void ex_diffpatch(exarg_T *eap) #ifdef UNIX if (dirbuf[0] != NUL) { - if (os_chdir((char *)dirbuf) != 0) { + if (os_chdir(dirbuf) != 0) { emsg(_(e_prev_dir)); } shorten_fnames(true); @@ -1254,20 +1271,20 @@ void ex_diffpatch(exarg_T *eap) // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); STRCAT(buf, ".orig"); - os_remove((char *)buf); + os_remove(buf); STRCPY(buf, tmp_new); STRCAT(buf, ".rej"); - os_remove((char *)buf); + os_remove(buf); // Only continue if the output file was created. FileInfo file_info; - bool info_ok = os_fileinfo((char *)tmp_new, &file_info); + bool info_ok = os_fileinfo(tmp_new, &file_info); uint64_t filesize = os_fileinfo_size(&file_info); if (!info_ok || filesize == 0) { emsg(_("E816: Cannot read patch output")); } else { if (curbuf->b_fname != NULL) { - newname = xstrnsave(curbuf->b_fname, STRLEN(curbuf->b_fname) + 4); + newname = xstrnsave(curbuf->b_fname, strlen(curbuf->b_fname) + 4); STRCAT(newname, ".new"); } @@ -1277,7 +1294,7 @@ void ex_diffpatch(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; - eap->arg = (char *)tmp_new; + eap->arg = tmp_new; do_exedit(eap, old_curwin); // check that split worked and editing tmp_new @@ -1302,12 +1319,12 @@ void ex_diffpatch(exarg_T *eap) theend: if (tmp_orig != NULL) { - os_remove((char *)tmp_orig); + os_remove(tmp_orig); } xfree(tmp_orig); if (tmp_new != NULL) { - os_remove((char *)tmp_new); + os_remove(tmp_new); } xfree(tmp_new); xfree(newname); @@ -1334,30 +1351,33 @@ void ex_diffsplit(exarg_T *eap) // don't use a new tab page, each tab page has its own diffs cmdmod.cmod_tab = 0; - 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; - do_exedit(eap, old_curwin); - - // split must have worked - if (curwin != old_curwin) { - // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, true); - if (win_valid(old_curwin)) { - diff_win_options(old_curwin, true); + if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) == FAIL) { + return; + } - if (bufref_valid(&old_curbuf)) { - // Move the cursor position to that of the old window. - curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf, - old_curwin->w_cursor.lnum); - } - } - // Now that lines are folded scroll to show the cursor at the same - // relative position. - scroll_to_fraction(curwin, curwin->w_height); + // Pretend it was a ":split fname" command + eap->cmdidx = CMD_split; + curwin->w_p_diff = true; + do_exedit(eap, old_curwin); + + if (curwin == old_curwin) { // split didn't work + return; + } + + // Set 'diff', 'scrollbind' on and 'wrap' off. + diff_win_options(curwin, true); + if (win_valid(old_curwin)) { + diff_win_options(old_curwin, true); + + if (bufref_valid(&old_curbuf)) { + // Move the cursor position to that of the old window. + curwin->w_cursor.lnum = diff_get_corresponding_line(old_curbuf.br_buf, + old_curwin->w_cursor.lnum); } } + // Now that lines are folded scroll to show the cursor at the same + // relative position. + scroll_to_fraction(curwin, curwin->w_height); } // Set options to show diffs for the current window. @@ -1374,7 +1394,7 @@ static void set_diff_option(win_T *wp, int value) curwin = wp; curbuf = curwin->w_buffer; curbuf->b_ro_locked++; - set_option_value("diff", (long)value, NULL, OPT_LOCAL); + set_option_value_give_err("diff", (long)value, NULL, OPT_LOCAL); curbuf->b_ro_locked--; curwin = old_curwin; curbuf = curwin->w_buffer; @@ -1413,7 +1433,7 @@ void diff_win_options(win_T *wp, int addbuf) if (wp->w_p_diff_saved) { free_string_option(wp->w_p_fdm_save); } - wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); + wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); } set_string_option_direct_in_win(wp, "fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0); @@ -1424,12 +1444,12 @@ void diff_win_options(win_T *wp, int addbuf) if (wp->w_p_diff_saved) { free_string_option(wp->w_p_fdc_save); } - wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc); + wp->w_p_fdc_save = xstrdup(wp->w_p_fdc); } free_string_option(wp->w_p_fdc); - wp->w_p_fdc = (char_u *)xstrdup("2"); + wp->w_p_fdc = xstrdup("2"); assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9); - snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn); + snprintf(wp->w_p_fdc, strlen(wp->w_p_fdc) + 1, "%d", diff_foldcolumn); wp->w_p_fen = true; wp->w_p_fdl = 0; foldUpdateAll(wp); @@ -1448,7 +1468,7 @@ void diff_win_options(win_T *wp, int addbuf) if (addbuf) { diff_buf_add(wp->w_buffer); } - redraw_later(wp, NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } /// Set options not to show diffs. For the current window or all windows. @@ -1480,11 +1500,9 @@ void ex_diffoff(exarg_T *eap) } } free_string_option(wp->w_p_fdm); - wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save - ? wp->w_p_fdm_save - : (char_u *)"manual"); + wp->w_p_fdm = xstrdup(*wp->w_p_fdm_save ? wp->w_p_fdm_save : "manual"); free_string_option(wp->w_p_fdc); - wp->w_p_fdc = vim_strsave(wp->w_p_fdc_save); + wp->w_p_fdc = xstrdup(*wp->w_p_fdc_save ? wp->w_p_fdc_save : "0"); if (wp->w_p_fdl == 0) { wp->w_p_fdl = wp->w_p_fdl_save; @@ -1528,210 +1546,219 @@ void ex_diffoff(exarg_T *eap) } } -/// Read the diff output and add each entry to the diff list. -/// -/// @param idx_orig idx of original file -/// @param idx_new idx of new file -/// @dout diff output -static void diff_read(int idx_orig, int idx_new, diffio_T *dio) +static bool extract_hunk_internal(diffout_T *dout, diffhunk_T *hunk, int *line_idx) { - FILE *fd = NULL; - int line_idx = 0; - diff_T *dprev = NULL; - diff_T *dp = curtab->tp_first_diff; - diff_T *dn, *dpl; - diffout_T *dout = &dio->dio_diff; - char_u linebuf[LBUFLEN]; // only need to hold the diff line - char_u *line; - linenr_T off; - int i; - int notset = true; // block "*dp" not set yet - diffhunk_T *hunk = NULL; // init to avoid gcc warning - enum { - DIFF_ED, - DIFF_UNIFIED, - DIFF_NONE, - } diffstyle = DIFF_NONE; - - if (dout->dout_fname == NULL) { - diffstyle = DIFF_UNIFIED; - } else { - fd = os_fopen((char *)dout->dout_fname, "r"); - if (fd == NULL) { - emsg(_("E98: Cannot read diff output")); - return; - } - } - - if (!dio->dio_internal) { - hunk = xmalloc(sizeof(*hunk)); + bool eof = *line_idx >= dout->dout_ga.ga_len; + if (!eof) { + *hunk = ((diffhunk_T *)dout->dout_ga.ga_data)[(*line_idx)++]; } + return eof; +} +// Extract hunk by parsing the diff output from file and calculate the diffstyle. +static bool extract_hunk(FILE *fd, diffhunk_T *hunk, diffstyle_T *diffstyle) +{ for (;;) { - if (dio->dio_internal) { - if (line_idx >= dout->dout_ga.ga_len) { - break; // did last line - } - hunk = ((diffhunk_T **)dout->dout_ga.ga_data)[line_idx++]; - } else { - 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++]; + char line[LBUFLEN]; // only need to hold the diff line + if (vim_fgets(line, LBUFLEN, fd)) { + return true; // end of file + } + + 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((uint8_t)(*line))) { + *diffstyle = DIFF_ED; + } else if ((strncmp(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; + } else if ((strncmp(line, "--- ", 4) == 0) // -V501 + && (vim_fgets(line, LBUFLEN, fd) == 0) // -V501 + && (strncmp(line, "+++ ", 4) == 0) + && (vim_fgets(line, LBUFLEN, fd) == 0) // -V501 + && (strncmp(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; } else { - 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) // -V501 - && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501 - && (STRNCMP(line, "+++ ", 4) == 0) - && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501 - && (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; - } + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; } + } - if (diffstyle == DIFF_ED) { - if (!isdigit(*line)) { - continue; // not the start of a diff block - } - if (parse_diff_ed(line, hunk) == FAIL) { - continue; - } - } else { - assert(diffstyle == DIFF_UNIFIED); - if (STRNCMP(line, "@@ ", 3) != 0) { - continue; // not the start of a diff block - } - if (parse_diff_unified(line, hunk) == FAIL) { - continue; - } + if (*diffstyle == DIFF_ED) { + if (!isdigit((uint8_t)(*line))) { + continue; // not the start of a diff block + } + if (parse_diff_ed(line, hunk) == FAIL) { + continue; + } + } else { + assert(*diffstyle == DIFF_UNIFIED); + if (strncmp(line, "@@ ", 3) != 0) { + continue; // not the start of a diff block + } + if (parse_diff_unified(line, hunk) == FAIL) { + continue; } } - // Go over blocks before the change, for which orig and new are equal. - // Copy blocks from orig to new. - while (dp != NULL - && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { - if (notset) { - diff_copy_entry(dprev, dp, idx_orig, idx_new); - } - dprev = dp; - dp = dp->df_next; - notset = true; + // Successfully parsed diff output, can return + return false; + } +} + +static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_new, diffhunk_T *hunk, + bool *notsetp) +{ + diff_T *dp = *dpp; + diff_T *dprev = *dprevp; + + // Go over blocks before the change, for which orig and new are equal. + // Copy blocks from orig to new. + while (dp != NULL + && hunk->lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) { + if (*notsetp) { + diff_copy_entry(dprev, dp, idx_orig, idx_new); } + dprev = dp; + dp = dp->df_next; + *notsetp = true; + } - if ((dp != NULL) - && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) - && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { - // New block overlaps with existing block(s). - // First find last block that overlaps. - for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { - if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { - break; - } + if ((dp != NULL) + && (hunk->lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) + && (hunk->lnum_orig + hunk->count_orig >= dp->df_lnum[idx_orig])) { + // New block overlaps with existing block(s). + // First find last block that overlaps. + diff_T *dpl; + for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) { + if (hunk->lnum_orig + hunk->count_orig < dpl->df_next->df_lnum[idx_orig]) { + break; } + } - // If the newly found block starts before the old one, set the - // start back a number of lines. - off = dp->df_lnum[idx_orig] - hunk->lnum_orig; + // If the newly found block starts before the old one, set the + // start back a number of lines. + linenr_T off = dp->df_lnum[idx_orig] - hunk->lnum_orig; - if (off > 0) { - for (i = idx_orig; i < idx_new; i++) { - if (curtab->tp_diffbuf[i] != NULL) { - dp->df_lnum[i] -= off; - } - } - dp->df_lnum[idx_new] = hunk->lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk->count_new; - } else if (notset) { - // new block inside existing one, adjust new block - dp->df_lnum[idx_new] = hunk->lnum_new + off; - dp->df_count[idx_new] = (linenr_T)hunk->count_new - off; - } else { - // second overlap of new block with existing block - dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig - + dpl->df_lnum[idx_orig] + - dpl->df_count[idx_orig] - - (dp->df_lnum[idx_orig] + - dp->df_count[idx_orig]); - } - - // Adjust the size of the block to include all the lines to the - // end of the existing block or the new diff, whatever ends last. - off = (hunk->lnum_orig + (linenr_T)hunk->count_orig) - - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]); - - if (off < 0) { - // new change ends in existing block, adjust the end if not - // done already - if (notset) { - dp->df_count[idx_new] += -off; + if (off > 0) { + for (int i = idx_orig; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + dp->df_lnum[i] -= off; } - off = 0; } + dp->df_lnum[idx_new] = hunk->lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk->count_new; + } else if (*notsetp) { + // new block inside existing one, adjust new block + dp->df_lnum[idx_new] = hunk->lnum_new + off; + dp->df_count[idx_new] = (linenr_T)hunk->count_new - off; + } else { + // second overlap of new block with existing block + dp->df_count[idx_new] += (linenr_T)hunk->count_new - (linenr_T)hunk->count_orig + + dpl->df_lnum[idx_orig] + + dpl->df_count[idx_orig] + - (dp->df_lnum[idx_orig] + + dp->df_count[idx_orig]); + } + |