Skip to content

Commit

Permalink
Make the tab width used for whitespace checks configurable
Browse files Browse the repository at this point in the history
A new whitespace "rule" is added that sets the tab width to use for
whitespace checks and fix-ups and replaces the hard-coded constant 8.

Since the setting is part of the rules, it can be set per file using
.gitattributes.

The new configuration is backwards compatible because older git versions
simply ignore unknown whitespace rules.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
j6t authored and gitster committed Dec 1, 2010
1 parent dee40e5 commit f4b05a4
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 21 deletions.
3 changes: 3 additions & 0 deletions Documentation/config.txt
Expand Up @@ -513,6 +513,9 @@ core.whitespace::
part of the line terminator, i.e. with it, `trailing-space` part of the line terminator, i.e. with it, `trailing-space`
does not trigger if the character before such a carriage-return does not trigger if the character before such a carriage-return
is not a whitespace (not enabled by default). is not a whitespace (not enabled by default).
* `tabwidth=<n>` tells how many character positions a tab occupies; this
is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
errors. The default tab width is 8. Allowed values are 1 to 63.


core.fsyncobjectfiles:: core.fsyncobjectfiles::
This boolean will enable 'fsync()' when writing object files. This boolean will enable 'fsync()' when writing object files.
Expand Down
6 changes: 4 additions & 2 deletions Documentation/gitattributes.txt
Expand Up @@ -723,20 +723,22 @@ control per path.
Set:: Set::


Notice all types of potential whitespace errors known to git. Notice all types of potential whitespace errors known to git.
The tab width is taken from the value of the `core.whitespace`
configuration variable.


Unset:: Unset::


Do not notice anything as error. Do not notice anything as error.


Unspecified:: Unspecified::


Use the value of `core.whitespace` configuration variable to Use the value of the `core.whitespace` configuration variable to
decide what to notice as error. decide what to notice as error.


String:: String::


Specify a comma separate list of common whitespace problems to Specify a comma separate list of common whitespace problems to
notice in the same format as `core.whitespace` configuration notice in the same format as the `core.whitespace` configuration
variable. variable.




Expand Down
17 changes: 10 additions & 7 deletions cache.h
Expand Up @@ -1087,15 +1087,17 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *
/* /*
* whitespace rules. * whitespace rules.
* used by both diff and apply * used by both diff and apply
* last two digits are tab width
*/ */
#define WS_BLANK_AT_EOL 01 #define WS_BLANK_AT_EOL 0100
#define WS_SPACE_BEFORE_TAB 02 #define WS_SPACE_BEFORE_TAB 0200
#define WS_INDENT_WITH_NON_TAB 04 #define WS_INDENT_WITH_NON_TAB 0400
#define WS_CR_AT_EOL 010 #define WS_CR_AT_EOL 01000
#define WS_BLANK_AT_EOF 020 #define WS_BLANK_AT_EOF 02000
#define WS_TAB_IN_INDENT 040 #define WS_TAB_IN_INDENT 04000
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF) #define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB) #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
#define WS_TAB_WIDTH_MASK 077
extern unsigned whitespace_rule_cfg; extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *); extern unsigned whitespace_rule(const char *);
extern unsigned parse_whitespace_rule(const char *); extern unsigned parse_whitespace_rule(const char *);
Expand All @@ -1104,6 +1106,7 @@ extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *str
extern char *whitespace_error_string(unsigned ws); extern char *whitespace_error_string(unsigned ws);
extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *); extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
extern int ws_blank_line(const char *line, int len, unsigned ws_rule); extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)


/* ls-files */ /* ls-files */
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset); int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
Expand Down
28 changes: 28 additions & 0 deletions t/t4015-diff-whitespace.sh
Expand Up @@ -344,6 +344,13 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
' '


test_expect_success 'ditto, but tabwidth=9' '
git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
git diff --check
'

test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' ' test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
git config core.whitespace "indent-with-non-tab" && git config core.whitespace "indent-with-non-tab" &&
Expand All @@ -352,6 +359,20 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:
' '


test_expect_success 'ditto, but tabwidth=10' '
git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
test_must_fail git diff --check
'

test_expect_success 'ditto, but tabwidth=20' '
git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
git diff --check
'

test_expect_success 'check tabs as indentation (tab-in-indent: off)' ' test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
git config core.whitespace "-tab-in-indent" && git config core.whitespace "-tab-in-indent" &&
Expand All @@ -376,6 +397,13 @@ test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
' '


test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
git config core.whitespace "tab-in-indent,tabwidth=1" &&
test_must_fail git diff --check
'

test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' ' test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
git config core.whitespace "tab-in-indent,indent-with-non-tab" && git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
Expand Down
85 changes: 85 additions & 0 deletions t/t4019-diff-wserror.sh
Expand Up @@ -51,8 +51,65 @@ test_expect_success default '
' '


test_expect_success 'default (attribute)' '
test_might_fail git config --unset core.whitespace &&
echo "F whitespace" >.gitattributes &&
prepare_output &&
grep Eight error >/dev/null &&
grep HT error >/dev/null &&
grep With error >/dev/null &&
grep Return error >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'default, tabwidth=10 (attribute)' '
git config core.whitespace "tabwidth=10" &&
echo "F whitespace" >.gitattributes &&
prepare_output &&
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
grep With error >/dev/null &&
grep Return error >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'no check (attribute)' '
test_might_fail git config --unset core.whitespace &&
echo "F -whitespace" >.gitattributes &&
prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep Return normal >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '
git config core.whitespace "tabwidth=10" &&
echo "F -whitespace" >.gitattributes &&
prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep Return normal >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'without -trail' ' test_expect_success 'without -trail' '
rm -f .gitattributes &&
git config core.whitespace -trail && git config core.whitespace -trail &&
prepare_output && prepare_output &&
Expand Down Expand Up @@ -134,6 +191,34 @@ test_expect_success 'with indent-non-tab only (attribute)' '
' '


test_expect_success 'with indent-non-tab only, tabwidth=10' '
rm -f .gitattributes &&
git config core.whitespace indent,tabwidth=10,-trailing,-space &&
prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep Return normal >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '
test_might_fail git config --unset core.whitespace &&
echo "F whitespace=indent,-trailing,-space,tabwidth=10" >.gitattributes &&
prepare_output &&
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep Return normal >/dev/null &&
grep No normal >/dev/null
'

test_expect_success 'with cr-at-eol' ' test_expect_success 'with cr-at-eol' '
rm -f .gitattributes && rm -f .gitattributes &&
Expand Down
23 changes: 17 additions & 6 deletions t/t4124-apply-ws-rule.sh
Expand Up @@ -10,7 +10,8 @@ prepare_test_file () {
# X RULE # X RULE
# ! trailing-space # ! trailing-space
# @ space-before-tab # @ space-before-tab
# # indent-with-non-tab # # indent-with-non-tab (default tab width 8)
# = indent-with-non-tab,tabwidth=16
# % tab-in-indent # % tab-in-indent
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
An_SP in an ordinary line>and a HT. An_SP in an ordinary line>and a HT.
Expand All @@ -25,8 +26,8 @@ prepare_test_file () {
________>_Eight SP, a HT and a SP (@#%). ________>_Eight SP, a HT and a SP (@#%).
_______________Fifteen SP (#). _______________Fifteen SP (#).
_______________>Fifteen SP and a HT (@#%). _______________>Fifteen SP and a HT (@#%).
________________Sixteen SP (#). ________________Sixteen SP (#=).
________________>Sixteen SP and a HT (@#%). ________________>Sixteen SP and a HT (@#%=).
_____a__Five SP, a non WS, two SP. _____a__Five SP, a non WS, two SP.
A line with a (!) trailing SP_ A line with a (!) trailing SP_
A line with a (!) trailing HT> A line with a (!) trailing HT>
Expand Down Expand Up @@ -139,8 +140,8 @@ test_expect_success 'spaces inserted by tab-in-indent' '
_________________Eight SP, a HT and a SP (@#%). _________________Eight SP, a HT and a SP (@#%).
_______________Fifteen SP (#). _______________Fifteen SP (#).
________________Fifteen SP and a HT (@#%). ________________Fifteen SP and a HT (@#%).
________________Sixteen SP (#). ________________Sixteen SP (#=).
________________________Sixteen SP and a HT (@#%). ________________________Sixteen SP and a HT (@#%=).
_____a__Five SP, a non WS, two SP. _____a__Five SP, a non WS, two SP.
A line with a (!) trailing SP_ A line with a (!) trailing SP_
A line with a (!) trailing HT> A line with a (!) trailing HT>
Expand All @@ -157,7 +158,7 @@ do
case "$s" in '') ts='@' ;; *) ts= ;; esac case "$s" in '') ts='@' ;; *) ts= ;; esac
for i in - '' for i in - ''
do do
case "$i" in '') ti='#' ;; *) ti= ;; esac case "$i" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac
for h in - '' for h in - ''
do do
[ -z "$h$i" ] && continue [ -z "$h$i" ] && continue
Expand All @@ -170,12 +171,22 @@ do
test_fix "$tt$ts$ti$th" test_fix "$tt$ts$ti$th"
' '


test_expect_success "rule=$rule,tabwidth=16" '
git config core.whitespace "$rule,tabwidth=16" &&
test_fix "$tt$ts$ti16$th"
'

test_expect_success "rule=$rule (attributes)" ' test_expect_success "rule=$rule (attributes)" '
git config --unset core.whitespace && git config --unset core.whitespace &&
echo "target whitespace=$rule" >.gitattributes && echo "target whitespace=$rule" >.gitattributes &&
test_fix "$tt$ts$ti$th" test_fix "$tt$ts$ti$th"
' '


test_expect_success "rule=$rule,tabwidth=16 (attributes)" '
echo "target whitespace=$rule,tabwidth=16" >.gitattributes &&
test_fix "$tt$ts$ti16$th"
'

done done
done done
done done
Expand Down
22 changes: 16 additions & 6 deletions ws.c
Expand Up @@ -56,6 +56,16 @@ unsigned parse_whitespace_rule(const char *string)
rule |= whitespace_rule_names[i].rule_bits; rule |= whitespace_rule_names[i].rule_bits;
break; break;
} }
if (strncmp(string, "tabwidth=", 9) == 0) {
unsigned tabwidth = atoi(string + 9);
if (0 < tabwidth && tabwidth < 0100) {
rule &= ~WS_TAB_WIDTH_MASK;
rule |= tabwidth;
}
else
warning("tabwidth %.*s out of range",
(int)(len - 9), string + 9);
}
string = ep; string = ep;
} }


Expand Down Expand Up @@ -84,7 +94,7 @@ unsigned whitespace_rule(const char *pathname)
value = attr_whitespace_rule.value; value = attr_whitespace_rule.value;
if (ATTR_TRUE(value)) { if (ATTR_TRUE(value)) {
/* true (whitespace) */ /* true (whitespace) */
unsigned all_rule = 0; unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
int i; int i;
for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
if (!whitespace_rule_names[i].loosens_error && if (!whitespace_rule_names[i].loosens_error &&
Expand All @@ -93,7 +103,7 @@ unsigned whitespace_rule(const char *pathname)
return all_rule; return all_rule;
} else if (ATTR_FALSE(value)) { } else if (ATTR_FALSE(value)) {
/* false (-whitespace) */ /* false (-whitespace) */
return 0; return ws_tab_width(whitespace_rule_cfg);
} else if (ATTR_UNSET(value)) { } else if (ATTR_UNSET(value)) {
/* reset to default (!whitespace) */ /* reset to default (!whitespace) */
return whitespace_rule_cfg; return whitespace_rule_cfg;
Expand Down Expand Up @@ -206,7 +216,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
} }


/* Check for indent using non-tab. */ /* Check for indent using non-tab. */
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) { if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
result |= WS_INDENT_WITH_NON_TAB; result |= WS_INDENT_WITH_NON_TAB;
if (stream) { if (stream) {
fputs(ws, stream); fputs(ws, stream);
Expand Down Expand Up @@ -320,7 +330,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
} else if (ch == ' ') { } else if (ch == ' ') {
last_space_in_indent = i; last_space_in_indent = i;
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
8 <= i - last_tab_in_indent) ws_tab_width(ws_rule) <= i - last_tab_in_indent)
need_fix_leading_space = 1; need_fix_leading_space = 1;
} else } else
break; break;
Expand Down Expand Up @@ -350,7 +360,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
strbuf_addch(dst, ch); strbuf_addch(dst, ch);
} else { } else {
consecutive_spaces++; consecutive_spaces++;
if (consecutive_spaces == 8) { if (consecutive_spaces == ws_tab_width(ws_rule)) {
strbuf_addch(dst, '\t'); strbuf_addch(dst, '\t');
consecutive_spaces = 0; consecutive_spaces = 0;
} }
Expand All @@ -369,7 +379,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
if (src[i] == '\t') if (src[i] == '\t')
do { do {
strbuf_addch(dst, ' '); strbuf_addch(dst, ' ');
} while ((dst->len - start) % 8); } while ((dst->len - start) % ws_tab_width(ws_rule));
else else
strbuf_addch(dst, src[i]); strbuf_addch(dst, src[i]);
} }
Expand Down

0 comments on commit f4b05a4

Please sign in to comment.