Unify whitespace checking

This commit unifies three separate places where whitespace checking was
performed:

 - the whitespace checking previously done in builtin-apply.c is
extracted into a function in ws.c

 - the equivalent logic in "git diff" is removed

 - the emit_line_with_ws() function is also removed because that also
rechecks the whitespace, and its functionality is rolled into ws.c

The new function is called check_and_emit_line() and it does two things:
checks a line for whitespace errors and optionally emits it. The checking
is based on lines of content rather than patch lines (in other words, the
caller must strip the leading "+" or "-"); this was suggested by Junio on
the mailing list to allow for a future extension to "git show" to display
whitespace errors in blobs.

At the same time we teach it to report all classes of whitespace errors
found for a given line rather than reporting only the first found error.

Signed-off-by: Wincent Colaiuta <win@wincent.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Wincent Colaiuta
2007-12-13 14:32:29 +01:00
committed by Junio C Hamano
parent da31b358fb
commit c1795bb08a
5 changed files with 139 additions and 164 deletions

105
ws.c
View File

@ -94,3 +94,108 @@ unsigned whitespace_rule(const char *pathname)
return whitespace_rule_cfg;
}
}
/* The returned string should be freed by the caller. */
char *whitespace_error_string(unsigned ws)
{
struct strbuf err;
strbuf_init(&err, 0);
if (ws & WS_TRAILING_SPACE)
strbuf_addstr(&err, "Adds trailing whitespace");
if (ws & WS_SPACE_BEFORE_TAB) {
if (err.len)
strbuf_addstr(&err, ", ");
strbuf_addstr(&err, "Space in indent is followed by a tab");
}
if (ws & WS_INDENT_WITH_NON_TAB) {
if (err.len)
strbuf_addstr(&err, ", ");
strbuf_addstr(&err, "Indent more than 8 places with spaces");
}
return strbuf_detach(&err, NULL);
}
/* If stream is non-NULL, emits the line after checking. */
unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
FILE *stream, const char *set,
const char *reset, const char *ws)
{
unsigned result = 0;
int leading_space = -1;
int trailing_whitespace = -1;
int trailing_newline = 0;
int i;
/* Logic is simpler if we temporarily ignore the trailing newline. */
if (len > 0 && line[len - 1] == '\n') {
trailing_newline = 1;
len--;
}
/* Check for trailing whitespace. */
if (ws_rule & WS_TRAILING_SPACE) {
for (i = len - 1; i >= 0; i--) {
if (isspace(line[i])) {
trailing_whitespace = i;
result |= WS_TRAILING_SPACE;
}
else
break;
}
}
/* Check for space before tab in initial indent. */
for (i = 0; i < len; i++) {
if (line[i] == '\t') {
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
(leading_space != -1))
result |= WS_SPACE_BEFORE_TAB;
break;
}
else if (line[i] == ' ')
leading_space = i;
else
break;
}
/* Check for indent using non-tab. */
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && leading_space >= 8)
result |= WS_INDENT_WITH_NON_TAB;
if (stream) {
/* Highlight errors in leading whitespace. */
if ((result & WS_SPACE_BEFORE_TAB) ||
(result & WS_INDENT_WITH_NON_TAB)) {
fputs(ws, stream);
fwrite(line, leading_space + 1, 1, stream);
fputs(reset, stream);
leading_space++;
}
else
leading_space = 0;
/* Now the rest of the line starts at leading_space.
* The non-highlighted part ends at trailing_whitespace. */
if (trailing_whitespace == -1)
trailing_whitespace = len;
/* Emit non-highlighted (middle) segment. */
if (trailing_whitespace - leading_space > 0) {
fputs(set, stream);
fwrite(line + leading_space,
trailing_whitespace - leading_space, 1, stream);
fputs(reset, stream);
}
/* Highlight errors in trailing whitespace. */
if (trailing_whitespace != len) {
fputs(ws, stream);
fwrite(line + trailing_whitespace,
len - trailing_whitespace, 1, stream);
fputs(reset, stream);
}
if (trailing_newline)
fputc('\n', stream);
}
return result;
}