{"thread":{"id":"25865","subject":"[PATCH] Make the tab width used for whitespace checks configurable","startedAt":"2010-11-29T08:39:13Z","lastAt":"2010-11-30T23:19:10Z","messageCount":6,"participants":["Johannes Sixt","Jonathan Nieder","Junio C Hamano","Chris Webb"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"156790","messageId":"4CF366B1.8070400@viscovery.net","threadId":"25865","inReplyTo":null,"subject":"[PATCH] Make the tab width used for whitespace checks configurable","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-11-29T08:39:13Z","receivedAt":"2010-11-29T08:39:13Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nA new whitespace \"rule\" is added that sets the tab width to use for\nwhitespace checks and fix-ups and replaces the hard-coded constant 8.\n\nSince the setting is part of the rules, it can be set per file using\n.gitattributes.\n\nThe new configuration is backwards compatible because older git versions\nsimply ignore unknown whitespace rules.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n I have a project that uses tab width 4, and which has a lot of whitespace\n errors. This is very helpful to spot them when lines are changed that\n are indented with 4 spaces.\n\n -- Hannes\n\n Documentation/config.txt        |    3 +\n Documentation/gitattributes.txt |    6 ++-\n cache.h                         |   17 +++++---\n t/t4015-diff-whitespace.sh      |   28 +++++++++++++\n t/t4019-diff-wserror.sh         |   85 +++++++++++++++++++++++++++++++++++++++\n t/t4124-apply-ws-rule.sh        |   19 +++++++--\n ws.c                            |   17 +++++---\n 7 files changed, 156 insertions(+), 19 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6a6c0b5..d88db1c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -513,6 +513,9 @@ core.whitespace::\n   part of the line terminator, i.e. with it, `trailing-space`\n   does not trigger if the character before such a carriage-return\n   is not a whitespace (not enabled by default).\n+* `tabwidth=<n>` tells how many character positions a tab occupies; this\n+  is relevant for `indent-with-non-tab` and when git fixes whitespace\n+  errors. The default tab width is 8. Allowed values are 1 to 63.\n \n core.fsyncobjectfiles::\n \tThis boolean will enable 'fsync()' when writing object files.\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex c80ca5d..5a7f936 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -723,6 +723,8 @@ control per path.\n Set::\n \n \tNotice all types of potential whitespace errors known to git.\n+\tThe tab width is taken from the value of the `core.whitespace`\n+\tconfiguration variable.\n \n Unset::\n \n@@ -730,13 +732,13 @@ Unset::\n \n Unspecified::\n \n-\tUse the value of `core.whitespace` configuration variable to\n+\tUse the value of the `core.whitespace` configuration variable to\n \tdecide what to notice as error.\n \n String::\n \n \tSpecify a comma separate list of common whitespace problems to\n-\tnotice in the same format as `core.whitespace` configuration\n+\tnotice in the same format as the `core.whitespace` configuration\n \tvariable.\n \n \ndiff --git a/cache.h b/cache.h\nindex 33decd9..8e8c9d4 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1087,15 +1087,17 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *\n /*\n  * whitespace rules.\n  * used by both diff and apply\n+ * last two digits are tab width\n  */\n-#define WS_BLANK_AT_EOL         01\n-#define WS_SPACE_BEFORE_TAB\t02\n-#define WS_INDENT_WITH_NON_TAB\t04\n-#define WS_CR_AT_EOL           010\n-#define WS_BLANK_AT_EOF        020\n-#define WS_TAB_IN_INDENT       040\n+#define WS_BLANK_AT_EOL         0100\n+#define WS_SPACE_BEFORE_TAB     0200\n+#define WS_INDENT_WITH_NON_TAB  0400\n+#define WS_CR_AT_EOL           01000\n+#define WS_BLANK_AT_EOF        02000\n+#define WS_TAB_IN_INDENT       04000\n #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)\n-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)\n+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)\n+#define WS_TAB_WIDTH_MASK        077\n extern unsigned whitespace_rule_cfg;\n extern unsigned whitespace_rule(const char *);\n extern unsigned parse_whitespace_rule(const char *);\n@@ -1104,6 +1106,7 @@ extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *str\n extern char *whitespace_error_string(unsigned ws);\n extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);\n extern int ws_blank_line(const char *line, int len, unsigned ws_rule);\n+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)\n \n /* ls-files */\n int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 8622eb5..9059bcd 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -344,6 +344,13 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n '\n \n+test_expect_success 'ditto, but tabwidth=9' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=9\" &&\n+\tgit diff --check\n+\n+'\n+\n test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '\n \n \tgit config core.whitespace \"indent-with-non-tab\" &&\n@@ -352,6 +359,20 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:\n \n '\n \n+test_expect_success 'ditto, but tabwidth=10' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=10\" &&\n+\ttest_must_fail git diff --check\n+\n+'\n+\n+test_expect_success 'ditto, but tabwidth=20' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=20\" &&\n+\tgit diff --check\n+\n+'\n+\n test_expect_success 'check tabs as indentation (tab-in-indent: off)' '\n \n \tgit config core.whitespace \"-tab-in-indent\" &&\n@@ -376,6 +397,13 @@ test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '\n \n '\n \n+test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '\n+\n+\tgit config core.whitespace \"tab-in-indent,tabwidth=1\" &&\n+\ttest_must_fail git diff --check\n+\n+'\n+\n test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '\n \n \tgit config core.whitespace \"tab-in-indent,indent-with-non-tab\" &&\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex f7c85ec..6f992c8 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -51,8 +51,65 @@ test_expect_success default '\n \n '\n \n+test_expect_success 'default (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight error >/dev/null &&\n+\tgrep HT error >/dev/null &&\n+\tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'default, tabwidth=10 (attribute)' '\n+\n+\tgit config core.whitespace \"tabwidth=10\" &&\n+\techo \"F whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT error >/dev/null &&\n+\tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'no check (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F -whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '\n+\n+\tgit config core.whitespace \"tabwidth=10\" &&\n+\techo \"F -whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'without -trail' '\n \n+\trm -f .gitattributes &&\n \tgit config core.whitespace -trail &&\n \tprepare_output &&\n \n@@ -134,6 +191,34 @@ test_expect_success 'with indent-non-tab only (attribute)' '\n \n '\n \n+test_expect_success 'with indent-non-tab only, tabwidth=10' '\n+\n+\trm -f .gitattributes &&\n+\tgit config core.whitespace indent,tabwidth=10,-trailing,-space &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F whitespace=indent,-trailing,-space,tabwidth=10\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'with cr-at-eol' '\n \n \trm -f .gitattributes &&\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex 61bfc56..27f0c47 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -10,7 +10,8 @@ prepare_test_file () {\n \t#       X  RULE\n \t#   \t!  trailing-space\n \t#   \t@  space-before-tab\n-\t#   \t#  indent-with-non-tab\n+\t#   \t#  indent-with-non-tab (default tab width 8)\n+\t#\t=  indent-with-non-tab,tabwidth=16\n \t#   \t%  tab-in-indent\n \tsed -e \"s/_/ /g\" -e \"s/>/\t/\" <<-\\EOF\n \t\tAn_SP in an ordinary line>and a HT.\n@@ -25,8 +26,8 @@ prepare_test_file () {\n \t\t________>_Eight SP, a HT and a SP (@#%).\n \t\t_______________Fifteen SP (#).\n \t\t_______________>Fifteen SP and a HT (@#%).\n-\t\t________________Sixteen SP (#).\n-\t\t________________>Sixteen SP and a HT (@#%).\n+\t\t________________Sixteen SP (#=).\n+\t\t________________>Sixteen SP and a HT (@#%=).\n \t\t_____a__Five SP, a non WS, two SP.\n \t\tA line with a (!) trailing SP_\n \t\tA line with a (!) trailing HT>\n@@ -129,7 +130,7 @@ do\n \t\tcase \"$s\" in '') ts='@' ;; *) ts= ;; esac\n \t\tfor i in - ''\n \t\tdo\n-\t\t\tcase \"$i\" in '') ti='#' ;; *) ti= ;; esac\n+\t\t\tcase \"$i\" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac\n \t\t\tfor h in - ''\n \t\t\tdo\n \t\t\t\t[ -z \"$h$i\" ] && continue\n@@ -142,12 +143,22 @@ do\n \t\t\t\t\ttest_fix \"$tt$ts$ti$th\"\n \t\t\t\t'\n \n+\t\t\t\ttest_expect_success \"rule=$rule,tabwidth=16\" '\n+\t\t\t\t\tgit config core.whitespace \"$rule,tabwidth=16\" &&\n+\t\t\t\t\ttest_fix \"$tt$ts$ti16$th\"\n+\t\t\t\t'\n+\n \t\t\t\ttest_expect_success \"rule=$rule (attributes)\" '\n \t\t\t\t\tgit config --unset core.whitespace &&\n \t\t\t\t\techo \"target whitespace=$rule\" >.gitattributes &&\n \t\t\t\t\ttest_fix \"$tt$ts$ti$th\"\n \t\t\t\t'\n \n+\t\t\t\ttest_expect_success \"rule=$rule,tabwidth=16 (attributes)\" '\n+\t\t\t\t\techo \"target whitespace=$rule,tabwidth=16\" >.gitattributes &&\n+\t\t\t\t\ttest_fix \"$tt$ts$ti16$th\"\n+\t\t\t\t'\n+\n \t\t\tdone\n \t\tdone\n \tdone\ndiff --git a/ws.c b/ws.c\nindex 7302f8f..0a2914c 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -56,6 +56,11 @@ unsigned parse_whitespace_rule(const char *string)\n \t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n \t\t\tbreak;\n \t\t}\n+\t\tif (strncmp(string, \"tabwidth=\", 9) == 0) {\n+\t\t\tunsigned tabwidth = atoi(string + 9);\n+\t\t\tif (0 < tabwidth && tabwidth < 0100)\n+\t\t\t\trule &= ~WS_TAB_WIDTH_MASK, rule |= tabwidth;\n+\t\t}\n \t\tstring = ep;\n \t}\n \n@@ -84,7 +89,7 @@ unsigned whitespace_rule(const char *pathname)\n \t\tvalue = attr_whitespace_rule.value;\n \t\tif (ATTR_TRUE(value)) {\n \t\t\t/* true (whitespace) */\n-\t\t\tunsigned all_rule = 0;\n+\t\t\tunsigned all_rule = ws_tab_width(whitespace_rule_cfg);\n \t\t\tint i;\n \t\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)\n \t\t\t\tif (!whitespace_rule_names[i].loosens_error &&\n@@ -93,7 +98,7 @@ unsigned whitespace_rule(const char *pathname)\n \t\t\treturn all_rule;\n \t\t} else if (ATTR_FALSE(value)) {\n \t\t\t/* false (-whitespace) */\n-\t\t\treturn 0;\n+\t\t\treturn ws_tab_width(whitespace_rule_cfg);\n \t\t} else if (ATTR_UNSET(value)) {\n \t\t\t/* reset to default (!whitespace) */\n \t\t\treturn whitespace_rule_cfg;\n@@ -206,7 +211,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \t\tif (stream) {\n \t\t\tfputs(ws, stream);\n@@ -320,7 +325,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t} else if (ch == ' ') {\n \t\t\tlast_space_in_indent = i;\n \t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n-\t\t\t    8 <= i - last_tab_in_indent)\n+\t\t\t    ws_tab_width(ws_rule) <= i - last_tab_in_indent)\n \t\t\t\tneed_fix_leading_space = 1;\n \t\t} else\n \t\t\tbreak;\n@@ -350,7 +355,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t\t\tstrbuf_addch(dst, ch);\n \t\t\t} else {\n \t\t\t\tconsecutive_spaces++;\n-\t\t\t\tif (consecutive_spaces == 8) {\n+\t\t\t\tif (consecutive_spaces == ws_tab_width(ws_rule)) {\n \t\t\t\t\tstrbuf_addch(dst, '\\t');\n \t\t\t\t\tconsecutive_spaces = 0;\n \t\t\t\t}\n@@ -368,7 +373,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t\tif (src[i] == '\\t')\n \t\t\t\tdo {\n \t\t\t\t\tstrbuf_addch(dst, ' ');\n-\t\t\t\t} while (dst->len % 8);\n+\t\t\t\t} while (dst->len % ws_tab_width(ws_rule));\n \t\t\telse\n \t\t\t\tstrbuf_addch(dst, src[i]);\n \t\t}\n-- \n1.7.3.2.1463.g5fe2\n"},{"id":"156818","messageId":"20101129171629.GM8037@burratino","threadId":"25865","inReplyTo":"4CF366B1.8070400@viscovery.net","subject":"Re: [PATCH] Make the tab width used for whitespace checks configurable","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-11-29T17:16:29Z","receivedAt":"2010-11-29T17:16:29Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Johannes Sixt wrote:\n\n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  I have a project that uses tab width 4, and which has a lot of whitespace\n>  errors. This is very helpful to spot them when lines are changed that\n>  are indented with 4 spaces.\n\nThanks.\n\nFor what it's worth, I like the idea and the patch looks good.\n(I haven't checked for missed spots or read the test in detail,\nthough.)\n\n> +                       if (0 < tabwidth && tabwidth < 0100)\n> +                               rule &= ~WS_TAB_WIDTH_MASK, rule |= tabwidth;\n\nTab widths are recognized with atoi and out-of-range tab widths are\nsilently ignored.  Could we help the user spot typos somehow?\n"},{"id":"156871","messageId":"7vhbezwqan.fsf@alter.siamese.dyndns.org","threadId":"25865","inReplyTo":"4CF366B1.8070400@viscovery.net","subject":"Re: [PATCH] Make the tab width used for whitespace checks configurable","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-11-30T01:31:12Z","receivedAt":"2010-11-30T01:31:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> From: Johannes Sixt <j6t@kdbg.org>\n>\n> A new whitespace \"rule\" is added that sets the tab width to use for\n> whitespace checks and fix-ups and replaces the hard-coded constant 8.\n>\n> Since the setting is part of the rules, it can be set per file using\n> .gitattributes.\n>\n> The new configuration is backwards compatible because older git versions\n> simply ignore unknown whitespace rules.\n>\n> Signed-off-by: Johannes Sixt <j6t@kdbg.org>\n> ---\n>  I have a project that uses tab width 4, and which has a lot of whitespace\n>  errors. This is very helpful to spot them when lines are changed that\n>  are indented with 4 spaces.\n\nHeh, I like it.  This will be the second killer feature to upgrade to the\nupcoming release.  We really should start calming things down.\n\nI am sort of surprised how the patch does not have to touch neither diff.c\nnor apply.c at all---it demonstrates how solidly and cleanly the ws.c\ninfrastructure was done in the first place, I guess ;-).\n\nThanks.\n"},{"id":"156890","messageId":"4CF4B42C.4080104@viscovery.net","threadId":"25865","inReplyTo":"7vhbezwqan.fsf@alter.siamese.dyndns.org","subject":"[PATCH 1/2 maint] apply --whitespace=fix: fix tab-in-indent","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-11-30T08:22:04Z","receivedAt":"2010-11-30T08:22:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nWhen the whitespace rule tab-in-indent is enabled, apply --whitespace=fix\nreplaces tabs by the appropriate amount of blanks. The code used\n\"dst->len % 8\" as the criterion to stop adding blanks. But it forgot that\ndst holds more than just the current line. Consequently, the modulus was\ncomputed correctly only for the first added line, but not for the second\nand subsequent lines. Fix it.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n This can go on top of a347b17f or its ancestor.\n\n t/t4124-apply-ws-rule.sh |   28 ++++++++++++++++++++++++++++\n ws.c                     |    3 ++-\n 2 files changed, 30 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex 61bfc56..40e5842 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -121,6 +121,34 @@ test_expect_success 'whitespace=error-all, no rule (attribute)' '\n \n '\n \n+test_expect_success 'spaces inserted by tab-in-indent' '\n+\n+\tgit config core.whitespace -trailing,-space,-indent,tab &&\n+\trm -f .gitattributes &&\n+\ttest_fix % &&\n+\tsed -e \"s/_/ /g\" -e \"s/>/\t/\" <<-\\EOF >expect &&\n+\t\tAn_SP in an ordinary line>and a HT.\n+\t\t________A HT (%).\n+\t\t________A SP and a HT (@%).\n+\t\t_________A SP, a HT and a SP (@%).\n+\t\t_______Seven SP.\n+\t\t________Eight SP (#).\n+\t\t________Seven SP and a HT (@%).\n+\t\t________________Eight SP and a HT (@#%).\n+\t\t_________Seven SP, a HT and a SP (@%).\n+\t\t_________________Eight SP, a HT and a SP (@#%).\n+\t\t_______________Fifteen SP (#).\n+\t\t________________Fifteen SP and a HT (@#%).\n+\t\t________________Sixteen SP (#).\n+\t\t________________________Sixteen SP and a HT (@#%).\n+\t\t_____a__Five SP, a non WS, two SP.\n+\t\tA line with a (!) trailing SP_\n+\t\tA line with a (!) trailing HT>\n+\tEOF\n+\ttest_cmp expect target\n+\n+'\n+\n for t in - ''\n do\n \tcase \"$t\" in '') tt='!' ;; *) tt= ;; esac\ndiff --git a/ws.c b/ws.c\nindex 7302f8f..e350436 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -363,12 +363,13 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\tfixed = 1;\n \t} else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {\n \t\t/* Expand tabs into spaces */\n+\t\tint start = dst->len;\n \t\tint last = last_tab_in_indent + 1;\n \t\tfor (i = 0; i < last; i++) {\n \t\t\tif (src[i] == '\\t')\n \t\t\t\tdo {\n \t\t\t\t\tstrbuf_addch(dst, ' ');\n-\t\t\t\t} while (dst->len % 8);\n+\t\t\t\t} while ((dst->len - start) % 8);\n \t\t\telse\n \t\t\t\tstrbuf_addch(dst, src[i]);\n \t\t}\n-- \n1.7.3.67.gcc234\n"},{"id":"156892","messageId":"4CF4B5D7.2090309@viscovery.net","threadId":"25865","inReplyTo":"4CF4B42C.4080104@viscovery.net","subject":"[PATCH 2/2 v2] Make the tab width used for whitespace checks configurable","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-11-30T08:29:11Z","receivedAt":"2010-11-30T08:29:11Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"From: Johannes Sixt <j6t@kdbg.org>\n\nA new whitespace \"rule\" is added that sets the tab width to use for\nwhitespace checks and fix-ups and replaces the hard-coded constant 8.\n\nSince the setting is part of the rules, it can be set per file using\n.gitattributes.\n\nThe new configuration is backwards compatible because older git versions\nsimply ignore unknown whitespace rules.\n\nSigned-off-by: Johannes Sixt <j6t@kdbg.org>\n---\n Compared to the first submission, I added a warning() when tabwidth=foo\n is junk or out of range and tweaked the documentation. The interdiff is:\n\n\tdiff --git a/Documentation/config.txt b/Documentation/config.txt\n\tindex d88db1c..7aff5ce 100644\n\t--- a/Documentation/config.txt\n\t+++ b/Documentation/config.txt\n\t@@ -514,7 +514,7 @@ core.whitespace::\n\t   does not trigger if the character before such a carriage-return\n\t   is not a whitespace (not enabled by default).\n\t * `tabwidth=<n>` tells how many character positions a tab occupies; this\n\t-  is relevant for `indent-with-non-tab` and when git fixes whitespace\n\t+  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`\n\t   errors. The default tab width is 8. Allowed values are 1 to 63.\n\t \n\t core.fsyncobjectfiles::\n\tdiff --git a/ws.c b/ws.c\n\tindex 0a2914c..e80e156 100644\n\t--- a/ws.c\n\t+++ b/ws.c\n\t@@ -60,6 +60,9 @@ unsigned parse_whitespace_rule(const char *string)\n\t\t\t\tunsigned tabwidth = atoi(string + 9);\n\t\t\t\tif (0 < tabwidth && tabwidth < 0100)\n\t\t\t\t\trule &= ~WS_TAB_WIDTH_MASK, rule |= tabwidth;\n\t+\t\t\telse\n\t+\t\t\t\twarning(\"tabwidth %.*s out of range\",\n\t+\t\t\t\t\tlen - 9, string + 9);\n\t\t\t}\n\t\t\tstring = ep;\n\t\t}\n\t@@ -368,12 +371,13 @@ void ws_fix_copy(struct strbuf *dst, \n\t\t\tfixed = 1;\n\t\t} else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {\n\t\t\t/* Expand tabs into spaces */\n\t+\t\tint start = dst->len;\n\t\t\tint last = last_tab_in_indent + 1;\n\t\t\tfor (i = 0; i < last; i++) {\n\t\t\t\tif (src[i] == '\\t')\n\t\t\t\t\tdo {\n\t\t\t\t\t\tstrbuf_addch(dst, ' ');\n\t-\t\t\t\t} while (dst->len % ws_tab_width(ws_rule));\n\t+\t\t\t\t} while ((dst->len - start) % ws_tab_width(ws_rule));\n\t\t\t\telse\n\t\t\t\t\tstrbuf_addch(dst, src[i]);\n\t\t\t}\n\n Documentation/config.txt        |    3 +\n Documentation/gitattributes.txt |    6 ++-\n cache.h                         |   17 +++++---\n t/t4015-diff-whitespace.sh      |   28 +++++++++++++\n t/t4019-diff-wserror.sh         |   85 +++++++++++++++++++++++++++++++++++++++\n t/t4124-apply-ws-rule.sh        |   23 ++++++++---\n ws.c                            |   20 ++++++---\n 7 files changed, 161 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6a6c0b5..7aff5ce 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -513,6 +513,9 @@ core.whitespace::\n   part of the line terminator, i.e. with it, `trailing-space`\n   does not trigger if the character before such a carriage-return\n   is not a whitespace (not enabled by default).\n+* `tabwidth=<n>` tells how many character positions a tab occupies; this\n+  is relevant for `indent-with-non-tab` and when git fixes `tab-in-indent`\n+  errors. The default tab width is 8. Allowed values are 1 to 63.\n \n core.fsyncobjectfiles::\n \tThis boolean will enable 'fsync()' when writing object files.\ndiff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt\nindex f7e4523..a79a06b 100644\n--- a/Documentation/gitattributes.txt\n+++ b/Documentation/gitattributes.txt\n@@ -722,6 +722,8 @@ control per path.\n Set::\n \n \tNotice all types of potential whitespace errors known to git.\n+\tThe tab width is taken from the value of the `core.whitespace`\n+\tconfiguration variable.\n \n Unset::\n \n@@ -729,13 +731,13 @@ Unset::\n \n Unspecified::\n \n-\tUse the value of `core.whitespace` configuration variable to\n+\tUse the value of the `core.whitespace` configuration variable to\n \tdecide what to notice as error.\n \n String::\n \n \tSpecify a comma separate list of common whitespace problems to\n-\tnotice in the same format as `core.whitespace` configuration\n+\tnotice in the same format as the `core.whitespace` configuration\n \tvariable.\n \n \ndiff --git a/cache.h b/cache.h\nindex 33decd9..8e8c9d4 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1087,15 +1087,17 @@ void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *\n /*\n  * whitespace rules.\n  * used by both diff and apply\n+ * last two digits are tab width\n  */\n-#define WS_BLANK_AT_EOL         01\n-#define WS_SPACE_BEFORE_TAB\t02\n-#define WS_INDENT_WITH_NON_TAB\t04\n-#define WS_CR_AT_EOL           010\n-#define WS_BLANK_AT_EOF        020\n-#define WS_TAB_IN_INDENT       040\n+#define WS_BLANK_AT_EOL         0100\n+#define WS_SPACE_BEFORE_TAB     0200\n+#define WS_INDENT_WITH_NON_TAB  0400\n+#define WS_CR_AT_EOL           01000\n+#define WS_BLANK_AT_EOF        02000\n+#define WS_TAB_IN_INDENT       04000\n #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)\n-#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)\n+#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)\n+#define WS_TAB_WIDTH_MASK        077\n extern unsigned whitespace_rule_cfg;\n extern unsigned whitespace_rule(const char *);\n extern unsigned parse_whitespace_rule(const char *);\n@@ -1104,6 +1106,7 @@ extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *str\n extern char *whitespace_error_string(unsigned ws);\n extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);\n extern int ws_blank_line(const char *line, int len, unsigned ws_rule);\n+#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)\n \n /* ls-files */\n int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 8622eb5..9059bcd 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -344,6 +344,13 @@ test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '\n \n '\n \n+test_expect_success 'ditto, but tabwidth=9' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=9\" &&\n+\tgit diff --check\n+\n+'\n+\n test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '\n \n \tgit config core.whitespace \"indent-with-non-tab\" &&\n@@ -352,6 +359,20 @@ test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab:\n \n '\n \n+test_expect_success 'ditto, but tabwidth=10' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=10\" &&\n+\ttest_must_fail git diff --check\n+\n+'\n+\n+test_expect_success 'ditto, but tabwidth=20' '\n+\n+\tgit config core.whitespace \"indent-with-non-tab,tabwidth=20\" &&\n+\tgit diff --check\n+\n+'\n+\n test_expect_success 'check tabs as indentation (tab-in-indent: off)' '\n \n \tgit config core.whitespace \"-tab-in-indent\" &&\n@@ -376,6 +397,13 @@ test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '\n \n '\n \n+test_expect_success 'ditto, but tabwidth=1 (must be irrelevant)' '\n+\n+\tgit config core.whitespace \"tab-in-indent,tabwidth=1\" &&\n+\ttest_must_fail git diff --check\n+\n+'\n+\n test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '\n \n \tgit config core.whitespace \"tab-in-indent,indent-with-non-tab\" &&\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex f7c85ec..6f992c8 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -51,8 +51,65 @@ test_expect_success default '\n \n '\n \n+test_expect_success 'default (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight error >/dev/null &&\n+\tgrep HT error >/dev/null &&\n+\tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'default, tabwidth=10 (attribute)' '\n+\n+\tgit config core.whitespace \"tabwidth=10\" &&\n+\techo \"F whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT error >/dev/null &&\n+\tgrep With error >/dev/null &&\n+\tgrep Return error >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'no check (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F -whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'no check, tabwidth=10 (attribute), must be irrelevant' '\n+\n+\tgit config core.whitespace \"tabwidth=10\" &&\n+\techo \"F -whitespace\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'without -trail' '\n \n+\trm -f .gitattributes &&\n \tgit config core.whitespace -trail &&\n \tprepare_output &&\n \n@@ -134,6 +191,34 @@ test_expect_success 'with indent-non-tab only (attribute)' '\n \n '\n \n+test_expect_success 'with indent-non-tab only, tabwidth=10' '\n+\n+\trm -f .gitattributes &&\n+\tgit config core.whitespace indent,tabwidth=10,-trailing,-space &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n+test_expect_success 'with indent-non-tab only, tabwidth=10 (attribute)' '\n+\n+\ttest_might_fail git config --unset core.whitespace &&\n+\techo \"F whitespace=indent,-trailing,-space,tabwidth=10\" >.gitattributes &&\n+\tprepare_output &&\n+\n+\tgrep Eight normal >/dev/null &&\n+\tgrep HT normal >/dev/null &&\n+\tgrep With normal >/dev/null &&\n+\tgrep Return normal >/dev/null &&\n+\tgrep No normal >/dev/null\n+\n+'\n+\n test_expect_success 'with cr-at-eol' '\n \n \trm -f .gitattributes &&\ndiff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh\nindex 40e5842..6f6ee88 100755\n--- a/t/t4124-apply-ws-rule.sh\n+++ b/t/t4124-apply-ws-rule.sh\n@@ -10,7 +10,8 @@ prepare_test_file () {\n \t#       X  RULE\n \t#   \t!  trailing-space\n \t#   \t@  space-before-tab\n-\t#   \t#  indent-with-non-tab\n+\t#   \t#  indent-with-non-tab (default tab width 8)\n+\t#\t=  indent-with-non-tab,tabwidth=16\n \t#   \t%  tab-in-indent\n \tsed -e \"s/_/ /g\" -e \"s/>/\t/\" <<-\\EOF\n \t\tAn_SP in an ordinary line>and a HT.\n@@ -25,8 +26,8 @@ prepare_test_file () {\n \t\t________>_Eight SP, a HT and a SP (@#%).\n \t\t_______________Fifteen SP (#).\n \t\t_______________>Fifteen SP and a HT (@#%).\n-\t\t________________Sixteen SP (#).\n-\t\t________________>Sixteen SP and a HT (@#%).\n+\t\t________________Sixteen SP (#=).\n+\t\t________________>Sixteen SP and a HT (@#%=).\n \t\t_____a__Five SP, a non WS, two SP.\n \t\tA line with a (!) trailing SP_\n \t\tA line with a (!) trailing HT>\n@@ -139,8 +140,8 @@ test_expect_success 'spaces inserted by tab-in-indent' '\n \t\t_________________Eight SP, a HT and a SP (@#%).\n \t\t_______________Fifteen SP (#).\n \t\t________________Fifteen SP and a HT (@#%).\n-\t\t________________Sixteen SP (#).\n-\t\t________________________Sixteen SP and a HT (@#%).\n+\t\t________________Sixteen SP (#=).\n+\t\t________________________Sixteen SP and a HT (@#%=).\n \t\t_____a__Five SP, a non WS, two SP.\n \t\tA line with a (!) trailing SP_\n \t\tA line with a (!) trailing HT>\n@@ -157,7 +158,7 @@ do\n \t\tcase \"$s\" in '') ts='@' ;; *) ts= ;; esac\n \t\tfor i in - ''\n \t\tdo\n-\t\t\tcase \"$i\" in '') ti='#' ;; *) ti= ;; esac\n+\t\t\tcase \"$i\" in '') ti='#' ti16='=';; *) ti= ti16= ;; esac\n \t\t\tfor h in - ''\n \t\t\tdo\n \t\t\t\t[ -z \"$h$i\" ] && continue\n@@ -170,12 +171,22 @@ do\n \t\t\t\t\ttest_fix \"$tt$ts$ti$th\"\n \t\t\t\t'\n \n+\t\t\t\ttest_expect_success \"rule=$rule,tabwidth=16\" '\n+\t\t\t\t\tgit config core.whitespace \"$rule,tabwidth=16\" &&\n+\t\t\t\t\ttest_fix \"$tt$ts$ti16$th\"\n+\t\t\t\t'\n+\n \t\t\t\ttest_expect_success \"rule=$rule (attributes)\" '\n \t\t\t\t\tgit config --unset core.whitespace &&\n \t\t\t\t\techo \"target whitespace=$rule\" >.gitattributes &&\n \t\t\t\t\ttest_fix \"$tt$ts$ti$th\"\n \t\t\t\t'\n \n+\t\t\t\ttest_expect_success \"rule=$rule,tabwidth=16 (attributes)\" '\n+\t\t\t\t\techo \"target whitespace=$rule,tabwidth=16\" >.gitattributes &&\n+\t\t\t\t\ttest_fix \"$tt$ts$ti16$th\"\n+\t\t\t\t'\n+\n \t\t\tdone\n \t\tdone\n \tdone\ndiff --git a/ws.c b/ws.c\nindex e350436..e80e156 100644\n--- a/ws.c\n+++ b/ws.c\n@@ -56,6 +56,14 @@ unsigned parse_whitespace_rule(const char *string)\n \t\t\t\trule |= whitespace_rule_names[i].rule_bits;\n \t\t\tbreak;\n \t\t}\n+\t\tif (strncmp(string, \"tabwidth=\", 9) == 0) {\n+\t\t\tunsigned tabwidth = atoi(string + 9);\n+\t\t\tif (0 < tabwidth && tabwidth < 0100)\n+\t\t\t\trule &= ~WS_TAB_WIDTH_MASK, rule |= tabwidth;\n+\t\t\telse\n+\t\t\t\twarning(\"tabwidth %.*s out of range\",\n+\t\t\t\t\tlen - 9, string + 9);\n+\t\t}\n \t\tstring = ep;\n \t}\n \n@@ -84,7 +92,7 @@ unsigned whitespace_rule(const char *pathname)\n \t\tvalue = attr_whitespace_rule.value;\n \t\tif (ATTR_TRUE(value)) {\n \t\t\t/* true (whitespace) */\n-\t\t\tunsigned all_rule = 0;\n+\t\t\tunsigned all_rule = ws_tab_width(whitespace_rule_cfg);\n \t\t\tint i;\n \t\t\tfor (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)\n \t\t\t\tif (!whitespace_rule_names[i].loosens_error &&\n@@ -93,7 +101,7 @@ unsigned whitespace_rule(const char *pathname)\n \t\t\treturn all_rule;\n \t\t} else if (ATTR_FALSE(value)) {\n \t\t\t/* false (-whitespace) */\n-\t\t\treturn 0;\n+\t\t\treturn ws_tab_width(whitespace_rule_cfg);\n \t\t} else if (ATTR_UNSET(value)) {\n \t\t\t/* reset to default (!whitespace) */\n \t\t\treturn whitespace_rule_cfg;\n@@ -206,7 +214,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,\n \t}\n \n \t/* Check for indent using non-tab. */\n-\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= 8) {\n+\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) && i - written >= ws_tab_width(ws_rule)) {\n \t\tresult |= WS_INDENT_WITH_NON_TAB;\n \t\tif (stream) {\n \t\t\tfputs(ws, stream);\n@@ -320,7 +328,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t} else if (ch == ' ') {\n \t\t\tlast_space_in_indent = i;\n \t\t\tif ((ws_rule & WS_INDENT_WITH_NON_TAB) &&\n-\t\t\t    8 <= i - last_tab_in_indent)\n+\t\t\t    ws_tab_width(ws_rule) <= i - last_tab_in_indent)\n \t\t\t\tneed_fix_leading_space = 1;\n \t\t} else\n \t\t\tbreak;\n@@ -350,7 +358,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t\t\tstrbuf_addch(dst, ch);\n \t\t\t} else {\n \t\t\t\tconsecutive_spaces++;\n-\t\t\t\tif (consecutive_spaces == 8) {\n+\t\t\t\tif (consecutive_spaces == ws_tab_width(ws_rule)) {\n \t\t\t\t\tstrbuf_addch(dst, '\\t');\n \t\t\t\t\tconsecutive_spaces = 0;\n \t\t\t\t}\n@@ -369,7 +377,7 @@ void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule,\n \t\t\tif (src[i] == '\\t')\n \t\t\t\tdo {\n \t\t\t\t\tstrbuf_addch(dst, ' ');\n-\t\t\t\t} while ((dst->len - start) % 8);\n+\t\t\t\t} while ((dst->len - start) % ws_tab_width(ws_rule));\n \t\t\telse\n \t\t\t\tstrbuf_addch(dst, src[i]);\n \t\t}\n-- \n1.7.3.67.gcc234\n"},{"id":"156904","messageId":"20101130231909.GA2425@arachsys.com","threadId":"25865","inReplyTo":"4CF4B42C.4080104@viscovery.net","subject":"Re: [PATCH 1/2 maint] apply --whitespace=fix: fix tab-in-indent","fromName":"Chris Webb","fromEmail":"chris@arachsys.com","sentAt":"2010-11-30T23:19:10Z","receivedAt":"2010-11-30T23:19:10Z","isPatch":true,"sender":{"key":"chris@arachsys.com","avatar":"https://avatars.githubusercontent.com/u/299056?v=4"},"body":"Johannes Sixt <j.sixt@viscovery.net> writes:\n\n> When the whitespace rule tab-in-indent is enabled, apply --whitespace=fix\n> replaces tabs by the appropriate amount of blanks. The code used\n> \"dst->len % 8\" as the criterion to stop adding blanks. But it forgot that\n> dst holds more than just the current line. Consequently, the modulus was\n> computed correctly only for the first added line, but not for the second\n> and subsequent lines. Fix it.\n\nYou're quite right! My mistake, so if you like, please feel free to add\n\nAcked-by: Chris Webb <chris@arachsys.com>\n\nCheers,\n\nChris.\n"}]}