Merge branch 'jc/maint-1.6.0-blank-at-eof' (early part) into jc/maint-blank-at-eof
* 'jc/maint-1.6.0-blank-at-eof' (early part):
diff --whitespace: fix blank lines at end
core.whitespace: split trailing-space into blank-at-{eol,eof}
diff --color: color blank-at-eof
diff --whitespace=warn/error: fix blank-at-eof check
diff --whitespace=warn/error: obey blank-at-eof
diff.c: the builtin_diff() deals with only two-file comparison
apply --whitespace: warn blank but not necessarily empty lines at EOF
apply --whitespace=warn/error: diagnose blank at EOF
apply.c: split check_whitespace() into two
apply --whitespace=fix: detect new blank lines at eof correctly
apply --whitespace=fix: fix handling of blank lines at the eof
This commit is contained in:
155
diff.c
155
diff.c
@ -532,8 +532,12 @@ static void diff_words_show(struct diff_words_data *diff_words)
|
||||
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
|
||||
|
||||
struct emit_callback {
|
||||
int nparents, color_diff;
|
||||
int color_diff;
|
||||
unsigned ws_rule;
|
||||
int blank_at_eof_in_preimage;
|
||||
int blank_at_eof_in_postimage;
|
||||
int lno_in_preimage;
|
||||
int lno_in_postimage;
|
||||
sane_truncate_fn truncate;
|
||||
const char **label_path;
|
||||
struct diff_words_data *diff_words;
|
||||
@ -586,6 +590,17 @@ static void emit_line(FILE *file, const char *set, const char *reset, const char
|
||||
fputc('\n', file);
|
||||
}
|
||||
|
||||
static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
|
||||
{
|
||||
if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
|
||||
ecbdata->blank_at_eof_in_preimage &&
|
||||
ecbdata->blank_at_eof_in_postimage &&
|
||||
ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
|
||||
ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
|
||||
return 0;
|
||||
return ws_blank_line(line + 1, len - 1, ecbdata->ws_rule);
|
||||
}
|
||||
|
||||
static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
|
||||
{
|
||||
const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
|
||||
@ -593,11 +608,13 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
|
||||
|
||||
if (!*ws)
|
||||
emit_line(ecbdata->file, set, reset, line, len);
|
||||
else if (new_blank_line_at_eof(ecbdata, line, len))
|
||||
/* Blank line at EOF - paint '+' as well */
|
||||
emit_line(ecbdata->file, ws, reset, line, len);
|
||||
else {
|
||||
/* Emit just the prefix, then the rest. */
|
||||
emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
|
||||
ws_check_emit(line + ecbdata->nparents,
|
||||
len - ecbdata->nparents, ecbdata->ws_rule,
|
||||
emit_line(ecbdata->file, set, reset, line, 1);
|
||||
ws_check_emit(line + 1, len - 1, ecbdata->ws_rule,
|
||||
ecbdata->file, set, reset, ws);
|
||||
}
|
||||
}
|
||||
@ -620,10 +637,23 @@ static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, u
|
||||
return allot - l;
|
||||
}
|
||||
|
||||
static void find_lno(const char *line, struct emit_callback *ecbdata)
|
||||
{
|
||||
const char *p;
|
||||
ecbdata->lno_in_preimage = 0;
|
||||
ecbdata->lno_in_postimage = 0;
|
||||
p = strchr(line, '-');
|
||||
if (!p)
|
||||
return; /* cannot happen */
|
||||
ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
|
||||
p = strchr(p, '+');
|
||||
if (!p)
|
||||
return; /* cannot happen */
|
||||
ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
|
||||
}
|
||||
|
||||
static void fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
{
|
||||
int i;
|
||||
int color;
|
||||
struct emit_callback *ecbdata = priv;
|
||||
const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
|
||||
const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
|
||||
@ -650,14 +680,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
len = 1;
|
||||
}
|
||||
|
||||
/* This is not really necessary for now because
|
||||
* this codepath only deals with two-way diffs.
|
||||
*/
|
||||
for (i = 0; i < len && line[i] == '@'; i++)
|
||||
;
|
||||
if (2 <= i && i < len && line[i] == ' ') {
|
||||
ecbdata->nparents = i - 1;
|
||||
if (line[0] == '@') {
|
||||
len = sane_truncate_line(ecbdata, line, len);
|
||||
find_lno(line, ecbdata);
|
||||
emit_line(ecbdata->file,
|
||||
diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
|
||||
reset, line, len);
|
||||
@ -666,15 +691,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
return;
|
||||
}
|
||||
|
||||
if (len < ecbdata->nparents) {
|
||||
if (len < 1) {
|
||||
emit_line(ecbdata->file, reset, reset, line, len);
|
||||
return;
|
||||
}
|
||||
|
||||
color = DIFF_PLAIN;
|
||||
if (ecbdata->diff_words && ecbdata->nparents != 1)
|
||||
/* fall back to normal diff */
|
||||
free_diff_words_data(ecbdata);
|
||||
if (ecbdata->diff_words) {
|
||||
if (line[0] == '-') {
|
||||
diff_words_append(line, len,
|
||||
@ -693,20 +714,19 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
|
||||
emit_line(ecbdata->file, plain, reset, line, len);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ecbdata->nparents && len; i++) {
|
||||
if (line[i] == '-')
|
||||
color = DIFF_FILE_OLD;
|
||||
else if (line[i] == '+')
|
||||
color = DIFF_FILE_NEW;
|
||||
}
|
||||
|
||||
if (color != DIFF_FILE_NEW) {
|
||||
emit_line(ecbdata->file,
|
||||
diff_get_color(ecbdata->color_diff, color),
|
||||
reset, line, len);
|
||||
return;
|
||||
if (line[0] != '+') {
|
||||
const char *color =
|
||||
diff_get_color(ecbdata->color_diff,
|
||||
line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
|
||||
ecbdata->lno_in_preimage++;
|
||||
if (line[0] == ' ')
|
||||
ecbdata->lno_in_postimage++;
|
||||
emit_line(ecbdata->file, color, reset, line, len);
|
||||
} else {
|
||||
ecbdata->lno_in_postimage++;
|
||||
emit_add_line(reset, ecbdata, line, len);
|
||||
}
|
||||
emit_add_line(reset, ecbdata, line, len);
|
||||
}
|
||||
|
||||
static char *pprint_rename(const char *a, const char *b)
|
||||
@ -1211,7 +1231,6 @@ struct checkdiff_t {
|
||||
struct diff_options *o;
|
||||
unsigned ws_rule;
|
||||
unsigned status;
|
||||
int trailing_blanks_start;
|
||||
};
|
||||
|
||||
static int is_conflict_marker(const char *line, unsigned long len)
|
||||
@ -1255,10 +1274,6 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||
if (line[0] == '+') {
|
||||
unsigned bad;
|
||||
data->lineno++;
|
||||
if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
|
||||
data->trailing_blanks_start = 0;
|
||||
else if (!data->trailing_blanks_start)
|
||||
data->trailing_blanks_start = data->lineno;
|
||||
if (is_conflict_marker(line + 1, len - 1)) {
|
||||
data->status |= 1;
|
||||
fprintf(data->o->file,
|
||||
@ -1278,14 +1293,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
|
||||
data->o->file, set, reset, ws);
|
||||
} else if (line[0] == ' ') {
|
||||
data->lineno++;
|
||||
data->trailing_blanks_start = 0;
|
||||
} else if (line[0] == '@') {
|
||||
char *plus = strchr(line, '+');
|
||||
if (plus)
|
||||
data->lineno = strtol(plus, NULL, 10) - 1;
|
||||
else
|
||||
die("invalid diff");
|
||||
data->trailing_blanks_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1437,6 +1450,51 @@ static const char *get_textconv(struct diff_filespec *one)
|
||||
return one->driver->textconv;
|
||||
}
|
||||
|
||||
static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
|
||||
{
|
||||
char *ptr = mf->ptr;
|
||||
long size = mf->size;
|
||||
int cnt = 0;
|
||||
|
||||
if (!size)
|
||||
return cnt;
|
||||
ptr += size - 1; /* pointing at the very end */
|
||||
if (*ptr != '\n')
|
||||
; /* incomplete line */
|
||||
else
|
||||
ptr--; /* skip the last LF */
|
||||
while (mf->ptr < ptr) {
|
||||
char *prev_eol;
|
||||
for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
|
||||
if (*prev_eol == '\n')
|
||||
break;
|
||||
if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
|
||||
break;
|
||||
cnt++;
|
||||
ptr = prev_eol - 1;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
|
||||
struct emit_callback *ecbdata)
|
||||
{
|
||||
int l1, l2, at;
|
||||
unsigned ws_rule = ecbdata->ws_rule;
|
||||
l1 = count_trailing_blank(mf1, ws_rule);
|
||||
l2 = count_trailing_blank(mf2, ws_rule);
|
||||
if (l2 <= l1) {
|
||||
ecbdata->blank_at_eof_in_preimage = 0;
|
||||
ecbdata->blank_at_eof_in_postimage = 0;
|
||||
return;
|
||||
}
|
||||
at = count_lines(mf1->ptr, mf1->size);
|
||||
ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
|
||||
|
||||
at = count_lines(mf2->ptr, mf2->size);
|
||||
ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
|
||||
}
|
||||
|
||||
static void builtin_diff(const char *name_a,
|
||||
const char *name_b,
|
||||
struct diff_filespec *one,
|
||||
@ -1562,6 +1620,8 @@ static void builtin_diff(const char *name_a,
|
||||
ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
|
||||
ecbdata.found_changesp = &o->found_changes;
|
||||
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
|
||||
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
|
||||
check_blank_at_eof(&mf1, &mf2, &ecbdata);
|
||||
ecbdata.file = o->file;
|
||||
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
|
||||
xecfg.ctxlen = o->context;
|
||||
@ -1704,11 +1764,22 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
|
||||
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
|
||||
&xpp, &xecfg, &ecb);
|
||||
|
||||
if ((data.ws_rule & WS_TRAILING_SPACE) &&
|
||||
data.trailing_blanks_start) {
|
||||
fprintf(o->file, "%s:%d: ends with blank lines.\n",
|
||||
data.filename, data.trailing_blanks_start);
|
||||
data.status = 1; /* report errors */
|
||||
if (data.ws_rule & WS_BLANK_AT_EOF) {
|
||||
struct emit_callback ecbdata;
|
||||
int blank_at_eof;
|
||||
|
||||
ecbdata.ws_rule = data.ws_rule;
|
||||
check_blank_at_eof(&mf1, &mf2, &ecbdata);
|
||||
blank_at_eof = ecbdata.blank_at_eof_in_preimage;
|
||||
|
||||
if (blank_at_eof) {
|
||||
static char *err;
|
||||
if (!err)
|
||||
err = whitespace_error_string(WS_BLANK_AT_EOF);
|
||||
fprintf(o->file, "%s:%d: %s.\n",
|
||||
data.filename, blank_at_eof, err);
|
||||
data.status = 1; /* report errors */
|
||||
}
|
||||
}
|
||||
}
|
||||
free_and_return:
|
||||
|
||||
Reference in New Issue
Block a user