Fossil

Check-in [79267437]
Login

Check-in [79267437]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Implement -p|--prototype option for diff command.

This commit introduces support for displaying in the chunk header which C function each change is in. Tested on OpenBSD, Ubuntu, and macOS. [closed: feature rejected]

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | diff-show-func
Files: files | file ages | folders
SHA3-256: 792674372e7d6a8e70552e991163f61ca3d72ec97273ad8b1e7773dc37f8e40b
User & Date: jamsek 2022-01-16 10:09:35
Original Comment: Implement -p|--prototype option for diff command.

This commit introduces support for displaying in the chunk header which C function each change is in. Tested on OpenBSD, Ubuntu, and macOS.

Context
2022-01-16
10:09
Implement -p|--prototype option for diff command.

This commit introduces support for displaying in the chunk header which C function each change is in. Tested on OpenBSD, Ubuntu, and macOS. [closed: feature rejected] ... (Closed-Leaf check-in: 79267437 user: jamsek tags: diff-show-func)

02:03
When reading POST via TLS, fail fatally on a read error. Add SERVER_SOFTWARE to the environment when running in server mode. ... (check-in: ba95498d user: stephan tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/diff.c.

45
46
47
48
49
50
51

52
53
54
55
56
57
58
#define DIFF_WEBPAGE           0x00004000 /* Complete webpage */
#define DIFF_BROWSER           0x00008000 /* The --browser option */
#define DIFF_JSON              0x00010000 /* JSON output */
#define DIFF_DEBUG             0x00020000 /* Debugging diff output */
#define DIFF_RAW               0x00040000 /* Raw triples - for debugging */
#define DIFF_TCL               0x00080000 /* For the --tk option */
#define DIFF_INCBINARY         0x00100000 /* The --diff-binary option */


/*
** These error messages are shared in multiple locations.  They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
    "cannot compute difference between binary files\n"







>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#define DIFF_WEBPAGE           0x00004000 /* Complete webpage */
#define DIFF_BROWSER           0x00008000 /* The --browser option */
#define DIFF_JSON              0x00010000 /* JSON output */
#define DIFF_DEBUG             0x00020000 /* Debugging diff output */
#define DIFF_RAW               0x00040000 /* Raw triples - for debugging */
#define DIFF_TCL               0x00080000 /* For the --tk option */
#define DIFF_INCBINARY         0x00100000 /* The --diff-binary option */
#define DIFF_PROTOTYPE         0x00200000 /* Function prototype in chunk head */

/*
** These error messages are shared in multiple locations.  They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
    "cannot compute difference between binary files\n"
95
96
97
98
99
100
101











102
103
104
105
106
107
108
  int nContext;            /* Number of lines of context */
  int wColumn;             /* Column width in -y mode */
  u32 nFile;               /* Number of files diffed so far */
  const char *zDiffCmd;    /* External diff command to use instead of builtin */
  const char *zBinGlob;    /* GLOB pattern for binary files */
  ReCompiled *pRe;         /* Show only changes matching this pattern */
  const char *zLeftHash;   /* HASH-id of the left file */











};

#endif /* INTERFACE */

/*
** Initialize memory for a DiffConfig based on just a diffFlags integer.
*/







>
>
>
>
>
>
>
>
>
>
>







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  int nContext;            /* Number of lines of context */
  int wColumn;             /* Column width in -y mode */
  u32 nFile;               /* Number of files diffed so far */
  const char *zDiffCmd;    /* External diff command to use instead of builtin */
  const char *zBinGlob;    /* GLOB pattern for binary files */
  ReCompiled *pRe;         /* Show only changes matching this pattern */
  const char *zLeftHash;   /* HASH-id of the left file */
  struct {
    /*
     * DIFF_PROTOTYPE (fossil diff -p|--prototype) data types for matching
     * each chunk change in the diff to it's enclosing function.
     */
    const Blob *pFileLHS;  /* Pointer to "from" file content */
    char *zSignature;      /* Enclosing function signature */
    uint32_t iLastMatch;   /* Line index of the last function match */
    uint32_t iLastLine;    /* Starting line index of the last chunk scanned */
    size_t iOffset;        /* Byte offset into pFileLHS->aData of iLastMatch */
  } proto;
};

#endif /* INTERFACE */

/*
** Initialize memory for a DiffConfig based on just a diffFlags integer.
*/
360
361
362
363
364
365
366











































































































367
368
369
370
371
372
373
  }
  if( lnB>0 ){
    blob_appendf(pOut, "%6d  ", lnB);
  }else{
    blob_append(pOut, "        ", 8);
  }
}












































































































/*
** Output a patch-style text diff.
*/
static void contextDiff(
  DContext *p,      /* The difference */
  Blob *pOut,       /* Output a context diff to here */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  }
  if( lnB>0 ){
    blob_appendf(pOut, "%6d  ", lnB);
  }else{
    blob_append(pOut, "        ", 8);
  }
}

/*
** Starting from iSeek lines, copy n lines from pSrc to pDest.  The seek starts
** from iOffset bytes into pSrc->aData.  This routine does _not_ modify pSrc.
*/
static void blob_copy_lines_from(
  Blob * const pDest,       /* Destination blob */
  const Blob * const pSrc,  /* Source blob from which to copy line(s) */
  size_t *iOffset,          /* Begin seek from this byte offset */
  size_t iSeek,             /* Skip this many lines before copying */
  size_t n                  /* Copy this many lines */
){
  const char *z = (const char *)pSrc->aData;
  size_t idx = *iOffset, ln = 0, start = 0;

  if( n==0 ){
    return;
  }

  while( idx<pSrc->nUsed ){
    if( z[idx]=='\n' ){
      if( ++ln==iSeek ){
        start = idx + 1;  /* skip '\n' */
      }
      if( ln==iSeek + n ){
        ++idx;
        break;
      }
    }
    ++idx;
  }

  if( pDest ){
    blob_append(pDest, &pSrc->aData[start], idx - start - 1);  /* trim '\n' */
  }
  *iOffset = start;
  return;
}

#define starts_with(_str, _pfx) (fossil_strncmp(_str, _pfx, sizeof(_pfx)-1)==0)

/*
 * Scan the diffed file from the line preceding the start of the current chunk
 * for the enclosing function in which the change resides. Return first match.
 */
static char * matchChunkFunction(
  DiffConfig *const pCfg,  /* Diff config options */
  uint32_t iPos            /* Line position in file from which to start scan */
){
  Blob pBuf;               /* Matching function prototype */
  const char *zLine;       /* Text of line being scanned */
  char *zSpec = NULL;      /* Access specifier: private, protected, public */
  size_t iOffset;          /* Byte offset to the last matching line */
  uint32_t iLast;          /* Line index to start of the last chunk matched */

  blob_zero(&pBuf);
  iLast = pCfg->proto.iLastLine;
  pCfg->proto.iLastLine = iPos;
  iOffset = pCfg->proto.iOffset;  /* Begin seeking from last match */

  /* Scan backwards from the line immediately preceding this chunk. */
  while( iPos > 1 && iPos > iLast ){
    blob_copy_lines_from(&pBuf, pCfg->proto.pFileLHS, &iOffset,
     iPos - pCfg->proto.iLastMatch, 1);
    zLine = blob_str(&pBuf);
    if ( zLine ){
      /*
       * GNU C and MSVC allow '$' in identifier names.
       * https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html
       * https://docs.microsoft.com/en-us/cpp/cpp/identifiers-cpp
       */
      if( fossil_isalpha(zLine[0]) || zLine[0] == '_' || zLine[0] == '$' ){
        if( starts_with(zLine, "private:") ){
          if( !zSpec ){
            zSpec = " (private)";
          }
        }else if( starts_with(zLine, "protected:") ){
          if( !zSpec ){
            zSpec = " (protected)";
          }
        }else if( starts_with(zLine, "public:") ){
          if( !zSpec ){
            zSpec = " (public)";
          }
        }else{
          /* Don't exceed 80 cols: chunk header consumes ~25, cap sig at 55. */
          char *zSig = mprintf("%s%s", zLine, zSpec ? zSpec : "");
          fossil_free(pCfg->proto.zSignature);
          pCfg->proto.zSignature = mprintf("%.55s", zSig);
          /*
           * It's expensive to seek from the beginning of the file when diffing
           * large files, so record byte offset and line index of this match.
           */
          pCfg->proto.iLastMatch = iPos;
          pCfg->proto.iOffset = iOffset;
          fossil_free(zSig);
          blob_reset(&pBuf);
          return pCfg->proto.zSignature;
        }
      }
    }
    iOffset = pCfg->proto.iOffset;  /* No match, revert offset to previous */
    blob_reset(&pBuf);
    --iPos;
  }
  return pCfg->proto.iLastMatch > 0 ? pCfg->proto.zSignature : NULL;
}

/*
** Output a patch-style text diff.
*/
static void contextDiff(
  DContext *p,      /* The difference */
  Blob *pOut,       /* Output a context diff to here */
445
446
447
448
449
450
451






452
453
454
455
456
457
458
       * If the patch changes an empty file or results in an empty file,
       * the block header must use 0,0 as position indicator and not 1,0.
       * Otherwise, patch would be confused and may reject the diff.
       */
      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
        na ? a+skip+1 : a+skip, na,
        nb ? b+skip+1 : b+skip, nb);






      blob_append(pOut, "\n", 1);
    }

    /* Show the initial common area */
    a += skip;
    b += skip;
    m = R[r] - skip;







>
>
>
>
>
>







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
       * If the patch changes an empty file or results in an empty file,
       * the block header must use 0,0 as position indicator and not 1,0.
       * Otherwise, patch would be confused and may reject the diff.
       */
      blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
        na ? a+skip+1 : a+skip, na,
        nb ? b+skip+1 : b+skip, nb);
      if((DIFF_PROTOTYPE & pCfg->diffFlags) && a+skip > 1) {
        char *f = matchChunkFunction(pCfg, (a+skip) - 1);
        if( f != NULL ){
          blob_appendf(pOut, " %s", f);
        }
      }
      blob_append(pOut, "\n", 1);
    }

    /* Show the initial common area */
    a += skip;
    b += skip;
    m = R[r] - skip;
493
494
495
496
497
498
499

500
501
502
503
504
505
506
    m = R[r+nr*3];
    if( m>nContext ) m = nContext;
    for(j=0; j<m; j++){
      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
      appendDiffLine(pOut, ' ', &A[a+j]);
    }
  }

}

#define MX_CSN  8  /* Maximum number of change spans across a change region */

/*
** A description of zero or more (up to MX_CSN) areas of difference
** between two lines of text.







>







618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    m = R[r+nr*3];
    if( m>nContext ) m = nContext;
    for(j=0; j<m; j++){
      if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1);
      appendDiffLine(pOut, ' ', &A[a+j]);
    }
  }
  fossil_free(pCfg->proto.zSignature);
}

#define MX_CSN  8  /* Maximum number of change spans across a change region */

/*
** A description of zero or more (up to MX_CSN) areas of difference
** between two lines of text.
2697
2698
2699
2700
2701
2702
2703




2704
2705
2706
2707
2708
2709
2710
  Blob *pB_Blob,   /* TO file */
  Blob *pOut,      /* Write diff here if not NULL */
  DiffConfig *pCfg /* Configuration options */
){
  int ignoreWs; /* Ignore whitespace */
  DContext c;





  if( pCfg->diffFlags & DIFF_INVERT ){
    Blob *pTemp = pA_Blob;
    pA_Blob = pB_Blob;
    pB_Blob = pTemp;
  }
  ignoreWs = (pCfg->diffFlags & DIFF_IGNORE_ALLWS)!=0;
  blob_to_utf8_no_bom(pA_Blob, 0);







>
>
>
>







2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
  Blob *pB_Blob,   /* TO file */
  Blob *pOut,      /* Write diff here if not NULL */
  DiffConfig *pCfg /* Configuration options */
){
  int ignoreWs; /* Ignore whitespace */
  DContext c;

  if( pCfg->diffFlags & DIFF_PROTOTYPE ){
    memset(&pCfg->proto, 0, sizeof(pCfg->proto));
    pCfg->proto.pFileLHS = pA_Blob;
  }
  if( pCfg->diffFlags & DIFF_INVERT ){
    Blob *pTemp = pA_Blob;
    pA_Blob = pB_Blob;
    pB_Blob = pTemp;
  }
  ignoreWs = (pCfg->diffFlags & DIFF_IGNORE_ALLWS)!=0;
  blob_to_utf8_no_bom(pA_Blob, 0);
2823
2824
2825
2826
2827
2828
2829

2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847



2848
2849
2850
2851
2852
2853
2854
**   --brief                      Show filenames only        DIFF_BRIEF
**   -c|--context N               N lines of context.        nContext
**   --html                       Format for HTML            DIFF_HTML
**   --invert                     Invert the diff            DIFF_INVERT
**   -n|--linenum                 Show line numbers          DIFF_LINENO
**   --noopt                      Disable optimization       DIFF_NOOPT
**   --numstat                    Show change counts         DIFF_NUMSTAT

**   --strip-trailing-cr          Strip trailing CR          DIFF_STRIP_EOLCR
**   --unified                    Unified diff.              ~DIFF_SIDEBYSIDE
**   -w|--ignore-all-space        Ignore all whitespaces     DIFF_IGNORE_ALLWS
**   -W|--width N                 N character lines.         wColumn
**   -y|--side-by-side            Side-by-side diff.         DIFF_SIDEBYSIDE
**   -Z|--ignore-trailing-space   Ignore eol-whitespaces     DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
  u64 diffFlags = 0;
  const char *z;
  int f;

  memset(pCfg, 0, sizeof(*pCfg));
  if( find_option("ignore-trailing-space","Z",0)!=0 ){
    diffFlags = DIFF_IGNORE_EOLWS;
  }
  if( find_option("ignore-all-space","w",0)!=0 ){
    diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */



  }
  if( find_option("strip-trailing-cr",0,0)!=0 ){
    diffFlags |= DIFF_STRIP_EOLCR;
  }
  if( !bUnifiedTextOnly ){
    if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
    if( find_option("yy",0,0)!=0 ){







>


















>
>
>







2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
**   --brief                      Show filenames only        DIFF_BRIEF
**   -c|--context N               N lines of context.        nContext
**   --html                       Format for HTML            DIFF_HTML
**   --invert                     Invert the diff            DIFF_INVERT
**   -n|--linenum                 Show line numbers          DIFF_LINENO
**   --noopt                      Disable optimization       DIFF_NOOPT
**   --numstat                    Show change counts         DIFF_NUMSTAT
**   -p|--prototype               Show enclosing function    DIFF_PROTOTYPE
**   --strip-trailing-cr          Strip trailing CR          DIFF_STRIP_EOLCR
**   --unified                    Unified diff.              ~DIFF_SIDEBYSIDE
**   -w|--ignore-all-space        Ignore all whitespaces     DIFF_IGNORE_ALLWS
**   -W|--width N                 N character lines.         wColumn
**   -y|--side-by-side            Side-by-side diff.         DIFF_SIDEBYSIDE
**   -Z|--ignore-trailing-space   Ignore eol-whitespaces     DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
  u64 diffFlags = 0;
  const char *z;
  int f;

  memset(pCfg, 0, sizeof(*pCfg));
  if( find_option("ignore-trailing-space","Z",0)!=0 ){
    diffFlags = DIFF_IGNORE_EOLWS;
  }
  if( find_option("ignore-all-space","w",0)!=0 ){
    diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */
  }
  if( find_option("prototype","p",0)!=0 ){
    diffFlags = DIFF_PROTOTYPE;
  }
  if( find_option("strip-trailing-cr",0,0)!=0 ){
    diffFlags |= DIFF_STRIP_EOLCR;
  }
  if( !bUnifiedTextOnly ){
    if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
    if( find_option("yy",0,0)!=0 ){

Changes to src/diffcmd.c.

1024
1025
1026
1027
1028
1029
1030






1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047

1048
1049
1050
1051
1052
1053
1054
**
** The "--diff-binary" option enables or disables the inclusion of binary files
** when using an external diff program.
**
** The "--binary" option causes files matching the glob PATTERN to be treated
** as binary when considering if they should be used with external diff program.
** This option overrides the "binary-glob" setting.






**
** These command show differences between managed files. Use the "fossil xdiff"
** command to see differences in unmanaged files.
**
** Options:
**   --binary PATTERN            Treat files that match the glob PATTERN
**                               as binary
**   --branch BRANCH             Show diff of all changes on BRANCH
**   --brief                     Show filenames only
**   -b|--browser                Show the diff output in a web-browser
**   --by                        Shorthand for "--browser -y"
**   --checkin VERSION           Show diff of all changes in VERSION
**   --command PROG              External diff program. Overrides "diff-command"
**   -c|--context N              Show N lines of context around each change
**   --diff-binary BOOL          Include binary files with external commands
**   --exec-abs-paths            Force absolute path names on external commands
**   --exec-rel-paths            Force relative path names on external commands

**   -r|--from VERSION           Select VERSION as source for the diff
**   -w|--ignore-all-space       Ignore white space when comparing lines
**   -i|--internal               Use internal diff logic
**   --json                      Output formatted as JSON
**   -N|--new-file               Alias for --verbose
**   --numstat                   Show only the number of lines delete and added
**   -y|--side-by-side           Side-by-side diff







>
>
>
>
>
>

















>







1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
**
** The "--diff-binary" option enables or disables the inclusion of binary files
** when using an external diff program.
**
** The "--binary" option causes files matching the glob PATTERN to be treated
** as binary when considering if they should be used with external diff program.
** This option overrides the "binary-glob" setting.
**
** The "--prototype" option is specific to C/C++ source code, and only applies
** to the standard unified diff, where the enclosing function of each change
** will be shown in the chunk header; for example:
**
**   @@ -2360,10 +2361,11 @@ draw_commits(struct fnc_view *view)
**
** These command show differences between managed files. Use the "fossil xdiff"
** command to see differences in unmanaged files.
**
** Options:
**   --binary PATTERN            Treat files that match the glob PATTERN
**                               as binary
**   --branch BRANCH             Show diff of all changes on BRANCH
**   --brief                     Show filenames only
**   -b|--browser                Show the diff output in a web-browser
**   --by                        Shorthand for "--browser -y"
**   --checkin VERSION           Show diff of all changes in VERSION
**   --command PROG              External diff program. Overrides "diff-command"
**   -c|--context N              Show N lines of context around each change
**   --diff-binary BOOL          Include binary files with external commands
**   --exec-abs-paths            Force absolute path names on external commands
**   --exec-rel-paths            Force relative path names on external commands
**   -p|--prototype              Show which C function each change is in
**   -r|--from VERSION           Select VERSION as source for the diff
**   -w|--ignore-all-space       Ignore white space when comparing lines
**   -i|--internal               Use internal diff logic
**   --json                      Output formatted as JSON
**   -N|--new-file               Alias for --verbose
**   --numstat                   Show only the number of lines delete and added
**   -y|--side-by-side           Side-by-side diff