Skip to content

Commit 47bfb3d

Browse files
committed
Merge branch 'js/configurable-tab'
* js/configurable-tab: Make the tab width used for whitespace checks configurable apply --whitespace=fix: fix tab-in-indent
2 parents 6a79be3 + f4b05a4 commit 47bfb3d

File tree

7 files changed

+190
-19
lines changed

7 files changed

+190
-19
lines changed

Documentation/config.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,9 @@ core.whitespace::
522522
part of the line terminator, i.e. with it, `trailing-space`
523523
does not trigger if the character before such a carriage-return
524524
is not a whitespace (not enabled by default).
525+
* `tabwidth=<n>` tells how many character positions a tab occupies; this
526+
is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`
527+
errors. The default tab width is 8. Allowed values are 1 to 63.
525528

526529
core.fsyncobjectfiles::
527530
This boolean will enable 'fsync()' when writing object files.

Documentation/gitattributes.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,20 +723,22 @@ control per path.
723723
Set::
724724

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

727729
Unset::
728730

729731
Do not notice anything as error.
730732

731733
Unspecified::
732734

733-
Use the value of `core.whitespace` configuration variable to
735+
Use the value of the `core.whitespace` configuration variable to
734736
decide what to notice as error.
735737

736738
String::
737739

738740
Specify a comma separate list of common whitespace problems to
739-
notice in the same format as `core.whitespace` configuration
741+
notice in the same format as the `core.whitespace` configuration
740742
variable.
741743

742744

cache.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,15 +1091,17 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *
10911091
/*
10921092
* whitespace rules.
10931093
* used by both diff and apply
1094+
* last two digits are tab width
10941095
*/
1095-
#define WS_BLANK_AT_EOL 01
1096-
#define WS_SPACE_BEFORE_TAB 02
1097-
#define WS_INDENT_WITH_NON_TAB 04
1098-
#define WS_CR_AT_EOL 010
1099-
#define WS_BLANK_AT_EOF 020
1100-
#define WS_TAB_IN_INDENT 040
1096+
#define WS_BLANK_AT_EOL 0100
1097+
#define WS_SPACE_BEFORE_TAB 0200
1098+
#define WS_INDENT_WITH_NON_TAB 0400
1099+
#define WS_CR_AT_EOL 01000
1100+
#define WS_BLANK_AT_EOF 02000
1101+
#define WS_TAB_IN_INDENT 04000
11011102
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
1102-
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
1103+
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
1104+
#define WS_TAB_WIDTH_MASK 077
11031105
extern unsigned whitespace_rule_cfg;
11041106
extern unsigned whitespace_rule(const char *);
11051107
extern unsigned parse_whitespace_rule(const char *);
@@ -1108,6 +1110,7 @@ extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *str
11081110
extern char *whitespace_error_string(unsigned ws);
11091111
extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
11101112
extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
1113+
#define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK)
11111114

11121115
/* ls-files */
11131116
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);

t/t4015-diff-whitespace.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,13 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
344344
345345
'
346346

347+
test_expect_success 'ditto, but tabwidth=9' '
348+
349+
git config core.whitespace "indent-with-non-tab,tabwidth=9" &&
350+
git diff --check
351+
352+
'
353+
347354
test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
348355
349356
git config core.whitespace "indent-with-non-tab" &&
@@ -352,6 +359,20 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:
352359
353360
'
354361

362+
test_expect_success 'ditto, but tabwidth=10' '
363+
364+
git config core.whitespace "indent-with-non-tab,tabwidth=10" &&
365+
test_must_fail git diff --check
366+
367+
'
368+
369+
test_expect_success 'ditto, but tabwidth=20' '
370+
371+
git config core.whitespace "indent-with-non-tab,tabwidth=20" &&
372+
git diff --check
373+
374+
'
375+
355376
test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
356377
357378
git config core.whitespace "-tab-in-indent" &&
@@ -376,6 +397,13 @@ test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
376397
377398
'
378399

400+
test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '
401+
402+
git config core.whitespace "tab-in-indent,tabwidth=1" &&
403+
test_must_fail git diff --check
404+
405+
'
406+
379407
test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
380408
381409
git config core.whitespace "tab-in-indent,indent-with-non-tab" &&

t/t4019-diff-wserror.sh

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,65 @@ test_expect_success default '
5151
5252
'
5353

54+
test_expect_success 'default (attribute)' '
55+
56+
test_might_fail git config --unset core.whitespace &&
57+
echo "F whitespace" >.gitattributes &&
58+
prepare_output &&
59+
60+
grep Eight error >/dev/null &&
61+
grep HT error >/dev/null &&
62+
grep With error >/dev/null &&
63+
grep Return error >/dev/null &&
64+
grep No normal >/dev/null
65+
66+
'
67+
68+
test_expect_success 'default, tabwidth=10 (attribute)' '
69+
70+
git config core.whitespace "tabwidth=10" &&
71+
echo "F whitespace" >.gitattributes &&
72+
prepare_output &&
73+
74+
grep Eight normal >/dev/null &&
75+
grep HT error >/dev/null &&
76+
grep With error >/dev/null &&
77+
grep Return error >/dev/null &&
78+
grep No normal >/dev/null
79+
80+
'
81+
82+
test_expect_success 'no check (attribute)' '
83+
84+
test_might_fail git config --unset core.whitespace &&
85+
echo "F -whitespace" >.gitattributes &&
86+
prepare_output &&
87+
88+
grep Eight normal >/dev/null &&
89+
grep HT normal >/dev/null &&
90+
grep With normal >/dev/null &&
91+
grep Return normal >/dev/null &&
92+
grep No normal >/dev/null
93+
94+
'
95+
96+
test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '
97+
98+
git config core.whitespace "tabwidth=10" &&
99+
echo "F -whitespace" >.gitattributes &&
100+
prepare_output &&
101+
102+
grep Eight normal >/dev/null &&
103+
grep HT normal >/dev/null &&
104+
grep With normal >/dev/null &&
105+
grep Return normal >/dev/null &&
106+
grep No normal >/dev/null
107+
108+
'
109+
54110
test_expect_success 'without -trail' '
55111
112+
rm -f .gitattributes &&
56113
git config core.whitespace -trail &&
57114
prepare_output &&
58115
@@ -134,6 +191,34 @@ test_expect_success 'with indent-non-tab only (attribute)' '
134191
135192
'
136193

194+
test_expect_success 'with indent-non-tab only, tabwidth=10' '
195+
196+
rm -f .gitattributes &&
197+
git config core.whitespace indent,tabwidth=10,-trailing,-space &&
198+
prepare_output &&
199+
200+
grep Eight normal >/dev/null &&
201+
grep HT normal >/dev/null &&
202+
grep With normal >/dev/null &&
203+
grep Return normal >/dev/null &&
204+
grep No normal >/dev/null
205+
206+
'
207+
208+
test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '
209+
210+
test_might_fail git config --unset core.whitespace &&
211+
echo "F whitespace=indent,-trailing,-space,tabwidth=10" >.gitattributes &&
212+
prepare_output &&
213+
214+
grep Eight normal >/dev/null &&
215+
grep HT normal >/dev/null &&
216+
grep With normal >/dev/null &&
217+
grep Return normal >/dev/null &&
218+
grep No normal >/dev/null
219+
220+
'
221+
137222
test_expect_success 'with cr-at-eol' '
138223
139224
rm -f .gitattributes &&

t/t4124-apply-ws-rule.sh

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ prepare_test_file () {
1010
# X RULE
1111
# ! trailing-space
1212
# @ space-before-tab
13-
# # indent-with-non-tab
13+
# # indent-with-non-tab (default tab width 8)
14+
# = indent-with-non-tab,tabwidth=16
1415
# % tab-in-indent
1516
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
1617
An_SP in an ordinary line>and a HT.
@@ -25,8 +26,8 @@ prepare_test_file () {
2526
________>_Eight SP, a HT and a SP (@#%).
2627
_______________Fifteen SP (#).
2728
_______________>Fifteen SP and a HT (@#%).
28-
________________Sixteen SP (#).
29-
________________>Sixteen SP and a HT (@#%).
29+
________________Sixteen SP (#=).
30+
________________>Sixteen SP and a HT (@#%=).
3031
_____a__Five SP, a non WS, two SP.
3132
A line with a (!) trailing SP_
3233
A line with a (!) trailing HT>
@@ -121,6 +122,34 @@ test_expect_success 'whitespace=error-all, no rule (attribute)' '
121122
122123
'
123124

125+
test_expect_success 'spaces inserted by tab-in-indent' '
126+
127+
git config core.whitespace -trailing,-space,-indent,tab &&
128+
rm -f .gitattributes &&
129+
test_fix % &&
130+
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF >expect &&
131+
An_SP in an ordinary line>and a HT.
132+
________A HT (%).
133+
________A SP and a HT (@%).
134+
_________A SP, a HT and a SP (@%).
135+
_______Seven SP.
136+
________Eight SP (#).
137+
________Seven SP and a HT (@%).
138+
________________Eight SP and a HT (@#%).
139+
_________Seven SP, a HT and a SP (@%).
140+
_________________Eight SP, a HT and a SP (@#%).
141+
_______________Fifteen SP (#).
142+
________________Fifteen SP and a HT (@#%).
143+
________________Sixteen SP (#=).
144+
________________________Sixteen SP and a HT (@#%=).
145+
_____a__Five SP, a non WS, two SP.
146+
A line with a (!) trailing SP_
147+
A line with a (!) trailing HT>
148+
EOF
149+
test_cmp expect target
150+
151+
'
152+
124153
for t in - ''
125154
do
126155
case "$t" in '') tt='!' ;; *) tt= ;; esac
@@ -129,7 +158,7 @@ do
129158
case "$s" in '') ts='@' ;; *) ts= ;; esac
130159
for i in - ''
131160
do
132-
case "$i" in '') ti='#' ;; *) ti= ;; esac
161+
case "$i" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac
133162
for h in - ''
134163
do
135164
[ -z "$h$i" ] && continue
@@ -142,12 +171,22 @@ do
142171
test_fix "$tt$ts$ti$th"
143172
'
144173

174+
test_expect_success "rule=$rule,tabwidth=16" '
175+
git config core.whitespace "$rule,tabwidth=16" &&
176+
test_fix "$tt$ts$ti16$th"
177+
'
178+
145179
test_expect_success "rule=$rule (attributes)" '
146180
git config --unset core.whitespace &&
147181
echo "target whitespace=$rule" >.gitattributes &&
148182
test_fix "$tt$ts$ti$th"
149183
'
150184

185+
test_expect_success "rule=$rule,tabwidth=16 (attributes)" '
186+
echo "target whitespace=$rule,tabwidth=16" >.gitattributes &&
187+
test_fix "$tt$ts$ti16$th"
188+
'
189+
151190
done
152191
done
153192
done

ws.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,16 @@ unsigned parse_whitespace_rule(const char *string)
5656
rule |= whitespace_rule_names[i].rule_bits;
5757
break;
5858
}
59+
if (strncmp(string, "tabwidth=", 9) == 0) {
60+
unsigned tabwidth = atoi(string + 9);
61+
if (0 < tabwidth && tabwidth < 0100) {
62+
rule &= ~WS_TAB_WIDTH_MASK;
63+
rule |= tabwidth;
64+
}
65+
else
66+
warning("tabwidth %.*s out of range",
67+
(int)(len - 9), string + 9);
68+
}
5969
string = ep;
6070
}
6171

@@ -84,7 +94,7 @@ unsigned whitespace_rule(const char *pathname)
8494
value = attr_whitespace_rule.value;
8595
if (ATTR_TRUE(value)) {
8696
/* true (whitespace) */
87-
unsigned all_rule = 0;
97+
unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
8898
int i;
8999
for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
90100
if (!whitespace_rule_names[i].loosens_error &&
@@ -93,7 +103,7 @@ unsigned whitespace_rule(const char *pathname)
93103
return all_rule;
94104
} else if (ATTR_FALSE(value)) {
95105
/* false (-whitespace) */
96-
return 0;
106+
return ws_tab_width(whitespace_rule_cfg);
97107
} else if (ATTR_UNSET(value)) {
98108
/* reset to default (!whitespace) */
99109
return whitespace_rule_cfg;
@@ -206,7 +216,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
206216
}
207217

208218
/* Check for indent using non-tab. */
209-
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {
219+
if ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {
210220
result |= WS_INDENT_WITH_NON_TAB;
211221
if (stream) {
212222
fputs(ws, stream);
@@ -320,7 +330,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
320330
} else if (ch == ' ') {
321331
last_space_in_indent = i;
322332
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
323-
8 <= i - last_tab_in_indent)
333+
ws_tab_width(ws_rule) <= i - last_tab_in_indent)
324334
need_fix_leading_space = 1;
325335
} else
326336
break;
@@ -350,7 +360,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
350360
strbuf_addch(dst, ch);
351361
} else {
352362
consecutive_spaces++;
353-
if (consecutive_spaces == 8) {
363+
if (consecutive_spaces == ws_tab_width(ws_rule)) {
354364
strbuf_addch(dst, '\t');
355365
consecutive_spaces = 0;
356366
}
@@ -363,12 +373,13 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,
363373
fixed = 1;
364374
} else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
365375
/* Expand tabs into spaces */
376+
int start = dst->len;
366377
int last = last_tab_in_indent + 1;
367378
for (i = 0; i < last; i++) {
368379
if (src[i] == '\t')
369380
do {
370381
strbuf_addch(dst, ' ');
371-
} while (dst->len % 8);
382+
} while ((dst->len - start) % ws_tab_width(ws_rule));
372383
else
373384
strbuf_addch(dst, src[i]);
374385
}

0 commit comments

Comments
 (0)