diff options
-rw-r--r-- | src/nvim/diff.c | 619 |
1 files changed, 315 insertions, 304 deletions
diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4fa5d0df35..87ead61c36 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -26,6 +26,7 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -74,13 +75,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 +101,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 @@ -204,13 +211,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". @@ -703,16 +704,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); } } @@ -730,7 +731,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { len += (long)strlen(ml_get_buf(buf, lnum, false)) + 1; } - char_u *ptr = try_malloc((size_t)len); + char *ptr = try_malloc((size_t)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 @@ -744,12 +745,12 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) } return FAIL; } - din->din_mmfile.ptr = (char *)ptr; + din->din_mmfile.ptr = ptr; din->din_mmfile.size = len; len = 0; for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char_u *s = (char_u *)ml_get_buf(buf, lnum, false); *s != NUL;) { + for (char *s = ml_get_buf(buf, lnum, false); *s != NUL;) { if (diff_flags & DIFF_ICASE) { int c; char cbuf[MB_MAXBYTES + 1]; @@ -758,11 +759,11 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) c = NUL; } else { // xdiff doesn't support ignoring case, fold-case the text. - c = utf_ptr2char((char *)s); + c = utf_ptr2char(s); c = utf_fold(c); } - const int orig_len = utfc_ptr2len((char *)s); - if (utf_char2bytes(c, (char *)cbuf) != orig_len) { + const int orig_len = utfc_ptr2len(s); + if (utf_char2bytes(c, cbuf) != orig_len) { // TODO(Bram): handle byte length difference memmove(ptr + len, s, (size_t)orig_len); } else { @@ -802,7 +803,7 @@ static int diff_write(buf_T *buf, diffin_T *din) // 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; @@ -820,12 +821,12 @@ 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 = (char_u *)vim_tempname(); - dio->dio_new.din_fname = (char_u *)vim_tempname(); - dio->dio_diff.dout_fname = (char_u *)vim_tempname(); + 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) { @@ -991,7 +992,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; @@ -1000,7 +1001,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; @@ -1011,18 +1012,18 @@ 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 // "1c1". For unified diff "@@ -1 +1 @@". - if (vim_fgets(linebuf, LBUFLEN, fd)) { + if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) { break; } @@ -1033,10 +1034,10 @@ static int check_external_diff(diffio_T *diffio) } 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. @@ -1114,9 +1115,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); @@ -1166,10 +1167,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; @@ -1177,16 +1178,16 @@ void ex_diffpatch(exarg_T *eap) // We need two temp file names. // Name of original temp file. - char_u *tmp_orig = (char_u *)vim_tempname(); + char *tmp_orig = vim_tempname(); // Name of patched temp file. - char_u *tmp_new = (char_u *)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,7 +1197,7 @@ void ex_diffpatch(exarg_T *eap) // 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); + (char *)vim_strsave_shellescape((char_u *)(fullname != NULL ? fullname : eap->arg), true, true); #else esc_name = vim_strsave_shellescape(eap->arg, true, true); #endif @@ -1204,14 +1205,14 @@ void ex_diffpatch(exarg_T *eap) 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)) { + if ((os_dirname((char_u *)dirbuf, MAXPATHL) != OK) + || (os_chdir(dirbuf) != 0)) { dirbuf[0] = NUL; } else { char *tempdir = vim_gettempdir(); @@ -1226,24 +1227,22 @@ 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); + (void)call_shell((char_u *)buf, kShellOptFilter, NULL); unblock_autocmds(); } #ifdef UNIX if (dirbuf[0] != NUL) { - if (os_chdir((char *)dirbuf) != 0) { + if (os_chdir(dirbuf) != 0) { emsg(_(e_prev_dir)); } shorten_fnames(true); @@ -1253,14 +1252,14 @@ 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")); @@ -1276,7 +1275,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 @@ -1301,12 +1300,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); @@ -1525,206 +1524,216 @@ 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; - diffout_T *dout = &dio->dio_diff; - 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 = os_fopen((char *)dout->dout_fname, "r"); - if (fd == NULL) { - emsg(_("E98: Cannot read diff output")); - return; - } - } - - diffhunk_T *hunk = NULL; - - 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 { - char *line; - char linebuf[LBUFLEN]; // only need to hold the diff line - if (fd == NULL) { - if (line_idx >= dout->dout_ga.ga_len) { - break; // did last line - } - line = ((char **)dout->dout_ga.ga_data)[line_idx++]; + char line[LBUFLEN]; // only need to hold the diff line + if (vim_fgets((char_u *)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(*line)) { + *diffstyle = DIFF_ED; + } else if ((STRNCMP(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; + } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501 + && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501 + && (STRNCMP(line, "+++ ", 4) == 0) + && (vim_fgets((char_u *)line, LBUFLEN, fd) == 0) // -V501 + && (STRNCMP(line, "@@ ", 3) == 0)) { + *diffstyle = DIFF_UNIFIED; } else { - if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) { - break; // end of file - } - line = linebuf; + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; } + } - 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((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501 - && (STRNCMP(line, "+++ ", 4) == 0) - && (vim_fgets((char_u *)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; - } + if (*diffstyle == DIFF_ED) { + if (!isdigit(*line)) { + continue; // not the start of a diff block } - - if (diffstyle == DIFF_ED) { - if (!isdigit(*line)) { - continue; // not the start of a diff block - } - if (parse_diff_ed((char_u *)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((char_u *)line, hunk) == FAIL) { - continue; - } + if (parse_diff_ed(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); + } 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; } - dprev = dp; - dp = dp->df_next; - notset = 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. - 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; - } - } + // Successfully parsed diff output, can return + return false; + } +} - // 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; +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 (off > 0) { - for (int 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]); + 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; } + } - // 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; - } - off = 0; - } + // 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 (int i = idx_orig; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { - dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] - - dp->df_lnum[i] + off; + 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 (*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]); + } + + // 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 (*notsetp) { + dp->df_count[idx_new] += -off; + } + off = 0; + } - // Delete the diff blocks that have been merged into one. - diff_T *dn = dp->df_next; - dp->df_next = dpl->df_next; - - while (dn != dp->df_next) { - dpl = dn->df_next; - xfree(dn); - dn = dpl; + for (int i = idx_orig; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] + - dp->df_lnum[i] + off; } - } else { - // Allocate a new diffblock. - dp = diff_alloc_new(curtab, dprev, dp); + } - dp->df_lnum[idx_orig] = hunk->lnum_orig; - dp->df_count[idx_orig] = (linenr_T)hunk->count_orig; - dp->df_lnum[idx_new] = hunk->lnum_new; - dp->df_count[idx_new] = (linenr_T)hunk->count_new; + // Delete the diff blocks that have been merged into one. + diff_T *dn = dp->df_next; + dp->df_next = dpl->df_next; - // Set values for other buffers, these must be equal to the - // original buffer, otherwise there would have been a change - // already. - for (int i = idx_orig + 1; i < idx_new; i++) { - if (curtab->tp_diffbuf[i] != NULL) { - diff_copy_entry(dprev, dp, idx_orig, i); - } + while (dn != dp->df_next) { + dpl = dn->df_next; + xfree(dn); + dn = dpl; + } + } else { + // Allocate a new diffblock. + dp = diff_alloc_new(curtab, dprev, dp); + + dp->df_lnum[idx_orig] = hunk->lnum_orig; + dp->df_count[idx_orig] = (linenr_T)hunk->count_orig; + dp->df_lnum[idx_new] = hunk->lnum_new; + dp->df_count[idx_new] = (linenr_T)hunk->count_new; + + // Set values for other buffers, these must be equal to the + // original buffer, otherwise there would have been a change + // already. + for (int i = idx_orig + 1; i < idx_new; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + diff_copy_entry(dprev, dp, idx_orig, i); } } - notset = false; // "*dp" has been set + } + *notsetp = false; // "*dp" has been set + *dpp = dp; + *dprevp = dprev; +} + +/// 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) +{ + FILE *fd = NULL; + int line_idx = 0; + diff_T *dprev = NULL; + diff_T *dp = curtab->tp_first_diff; + diffout_T *dout = &dio->dio_diff; + bool notset = true; // block "*dp" not set yet + diffstyle_T diffstyle = DIFF_NONE; + + if (!dio->dio_internal) { + fd = os_fopen(dout->dout_fname, "r"); + if (fd == NULL) { + emsg(_("E98: Cannot read diff output")); + return; + } + } + + for (;;) { + diffhunk_T hunk = { 0 }; + bool eof = dio->dio_internal + ? extract_hunk_internal(dout, &hunk, &line_idx) + : extract_hunk(fd, &hunk, &diffstyle); + + if (eof) { + break; + } + + process_hunk(&dp, &dprev, idx_orig, idx_new, &hunk, ¬set); } // for remaining diff blocks orig and new are equal @@ -1737,10 +1746,6 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) notset = true; } - if (!dio->dio_internal) { - xfree(hunk); - } - if (fd != NULL) { fclose(fd); } @@ -1934,24 +1939,24 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2) // Compare the characters at "p1" and "p2". If they are equal (possibly // ignoring case) return true and set "len" to the number of bytes. -static bool diff_equal_char(const char_u *const p1, const char_u *const p2, int *const len) +static bool diff_equal_char(const char *const p1, const char *const p2, int *const len) { - const int l = utfc_ptr2len((char *)p1); + const int l = utfc_ptr2len(p1); - if (l != utfc_ptr2len((char *)p2)) { + if (l != utfc_ptr2len(p2)) { return false; } if (l > 1) { if (STRNCMP(p1, p2, l) != 0 && (!(diff_flags & DIFF_ICASE) - || utf_fold(utf_ptr2char((char *)p1)) != utf_fold(utf_ptr2char((char *)p2)))) { + || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) { return false; } *len = l; } else { if ((*p1 != *p2) && (!(diff_flags & DIFF_ICASE) - || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) { + || TOLOWER_LOC((uint8_t)(*p1)) != TOLOWER_LOC((uint8_t)(*p2)))) { return false; } *len = 1; @@ -1978,7 +1983,7 @@ static int diff_cmp(char *s1, char *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) { - return mb_stricmp((const char *)s1, (const char *)s2); + return mb_stricmp(s1, s2); } char *p1 = s1; @@ -1994,7 +1999,7 @@ static int diff_cmp(char *s1, char *s2) p2 = skipwhite(p2); } else { int l; - if (!diff_equal_char((char_u *)p1, (char_u *)p2, &l)) { + if (!diff_equal_char(p1, p2, &l)) { break; } p1 += l; @@ -2334,7 +2339,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) si_new = (int)(skipwhite(line_new + si_new) - line_new); } else { int l; - if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) { + if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) { break; } si_org += l; @@ -2374,11 +2379,11 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) ei_new--; } } else { - const char_u *p1 = (char_u *)line_org + ei_org; - const char_u *p2 = (char_u *)line_new + ei_new; + const char *p1 = line_org + ei_org; + const char *p2 = line_new + ei_new; - p1 -= utf_head_off(line_org, (char *)p1); - p2 -= utf_head_off(line_new, (char *)p2); + p1 -= utf_head_off(line_org, p1); + p2 -= utf_head_off(line_new, p2); int l; if (!diff_equal_char(p1, p2, &l)) { @@ -2499,8 +2504,6 @@ static bool valid_diff(diff_T *diff) /// @param eap void ex_diffgetput(exarg_T *eap) { - linenr_T off = 0; - aco_save_T aco; int idx_other; // Find the current buffer in the list of diff buffers. @@ -2600,15 +2603,9 @@ void ex_diffgetput(exarg_T *eap) } } - int idx_from; - int idx_to; - if (eap->cmdidx == CMD_diffget) { - idx_from = idx_other; - idx_to = idx_cur; - } else { - idx_from = idx_cur; - idx_to = idx_other; + aco_save_T aco; + if (eap->cmdidx != CMD_diffget) { // Need to make the other buffer the current buffer to be able to make // changes in it. @@ -2616,6 +2613,9 @@ void ex_diffgetput(exarg_T *eap) aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]); } + const int idx_from = eap->cmdidx == CMD_diffget ? idx_other : idx_cur; + const int idx_to = eap->cmdidx == CMD_diffget ? idx_cur : idx_other; + // May give the warning for a changed buffer here, which can trigger the // FileChangedRO autocommand, which may do nasty things and mess // everything up. @@ -2627,10 +2627,55 @@ void ex_diffgetput(exarg_T *eap) } } + diffgetput(eap->addr_count, idx_cur, idx_from, idx_to, eap->line1, eap->line2); + + // restore curwin/curbuf and a few other things + if (eap->cmdidx != CMD_diffget) { + // Syncing undo only works for the current buffer, but we change + // 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); + } + aucmd_restbuf(&aco); + } + +theend: + diff_busy = false; + + if (diff_need_update) { + ex_diffupdate(NULL); + } + + // 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(); + 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); + } +} + +/// Apply diffget/diffput to buffers and diffblocks +/// +/// @param idx_cur index of "curbuf" before aucmd_prepbuf() in the list of diff buffers +/// @param idx_from index of the buffer to read from in the list of diff buffers +/// @param idx_to index of the buffer to modify in the list of diff buffers +static void diffgetput(const int addr_count, const int idx_cur, const int idx_from, + const int idx_to, const linenr_T line1, const linenr_T line2) +{ + linenr_T off = 0; diff_T *dprev = NULL; for (diff_T *dp = curtab->tp_first_diff; dp != NULL;) { - if (dp->df_lnum[idx_cur] > eap->line2 + off) { + if (dp->df_lnum[idx_cur] > line2 + off) { // past the range that was specified break; } @@ -2638,15 +2683,15 @@ void ex_diffgetput(exarg_T *eap) linenr_T lnum = dp->df_lnum[idx_to]; linenr_T count = dp->df_count[idx_to]; - if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off) + if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > line1 + off) && (u_save(lnum - 1, lnum + count) != FAIL)) { // Inside the specified range and saving for undo worked. linenr_T start_skip = 0; linenr_T end_skip = 0; - if (eap->addr_count > 0) { + if (addr_count > 0) { // A range was specified: check if lines need to be skipped. - start_skip = eap->line1 + off - dp->df_lnum[idx_cur]; + start_skip = line1 + off - dp->df_lnum[idx_cur]; if (start_skip > 0) { // range starts below start of current diff block if (start_skip > count) { @@ -2661,7 +2706,7 @@ void ex_diffgetput(exarg_T *eap) } end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1 - - (eap->line2 + off); + - (line2 + off); if (end_skip > 0) { // range ends above end of current/from diff block @@ -2786,38 +2831,6 @@ void ex_diffgetput(exarg_T *eap) dp = dp->df_next; } } - - // restore curwin/curbuf and a few other things - if (eap->cmdidx != CMD_diffget) { - // Syncing undo only works for the current buffer, but we change - // 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); - } - aucmd_restbuf(&aco); - } - -theend: - diff_busy = false; - if (diff_need_update) { - ex_diffupdate(NULL); - } - - // 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(); - 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); - } } /// Update folds for all diff buffers for entry "dp". @@ -3041,7 +3054,7 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) /// Handle an ED style diff line. /// Return FAIL if the line does not contain diff info. /// -static int parse_diff_ed(char_u *line, diffhunk_T *hunk) +static int parse_diff_ed(char *line, diffhunk_T *hunk) { long l1, l2; @@ -3049,7 +3062,7 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) // change: {first}[,{last}]c{first}[,{last}] // append: {first}a{first}[,{last}] // delete: {first}[,{last}]d{first} - char *p = (char *)line; + char *p = line; linenr_T f1 = getdigits_int32(&p, true, 0); if (*p == ',') { p++; @@ -3093,11 +3106,11 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) /// 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, diffhunk_T *hunk) +static int parse_diff_unified(char *line, diffhunk_T *hunk) { // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ - char *p = (char *)line; + char *p = line; if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { long oldcount; long newline; @@ -3149,13 +3162,11 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk) static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv) { diffout_T *dout = (diffout_T *)priv; - diffhunk_T *p = xmalloc(sizeof(*p)); - - ga_grow(&dout->dout_ga, 1); - p->lnum_orig = (linenr_T)start_a + 1; - p->count_orig = count_a; - p->lnum_new = (linenr_T)start_b + 1; - p->count_new = count_b; - ((diffhunk_T **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; + GA_APPEND(diffhunk_T, &(dout->dout_ga), ((diffhunk_T){ + .lnum_orig = (linenr_T)start_a + 1, + .count_orig = count_a, + .lnum_new = (linenr_T)start_b + 1, + .count_new = count_b, + })); return 0; } |