{"thread":{"id":"55616","subject":"[PATCH] word diff: handle zero length matches","startedAt":"2021-05-04T09:27:40Z","lastAt":"2021-05-04T09:27:40Z","messageCount":1,"participants":["Phillip Wood via GitGitGadget"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"423567","messageId":"pull.947.git.1620120455364.gitgitgadget@gmail.com","threadId":"55616","inReplyTo":null,"subject":"[PATCH] word diff: handle zero length matches","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-05-04T09:27:34Z","receivedAt":"2021-05-04T09:27:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\nIf find_word_boundaries() encounters a zero length match (which can be\ncaused by matching a newline or using '*' instead of '+' in the regex)\nwe stop splitting the input into words which generates an inaccurate\ndiff. To fix this increment the start point when there is a zero\nlength match and try a new match. This is safe as posix regular\nexpressions always return the longest available match so a zero length\nmatch means there are no longer matches available from the current\nposition.\n\nCommit bf82940dbf1 (color-words: enable REG_NEWLINE to help user,\n2009-01-17) prevented matching newlines in negated character classes\nbut it is still possible for the user to have an explicit newline\nmatch in the regex which could cause a zero length match.\n\nOne could argue that having explicit newline matches or using '*'\nrather than '+' are user errors but it seems to be better to work\nround them than produce inaccurate diffs.\n\nSigned-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n---\n    word diff: handle zero length matches\n    \n    If find_word_boundaries() encounters a zero length match (which can be\n    caused by matching a newline or using '*' instead of '+' in the regex)\n    we stop splitting the input into words which generates an inaccurate\n    diff. To fix this increment the start point when there is a zero length\n    match and try a new match.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-947%2Fphillipwood%2Fwip%2Fword-diff-zero-length-matches-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-947/phillipwood/wip/word-diff-zero-length-matches-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/947\n\n diff.c                | 10 +++++++---\n t/t4034-diff-words.sh |  5 +++++\n 2 files changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 4acccd9d7edb..c8b1d724349c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2053,7 +2053,7 @@ static void fn_out_diff_words_aux(void *priv,\n static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,\n \t\tint *begin, int *end)\n {\n-\tif (word_regex && *begin < buffer->size) {\n+\twhile (word_regex && *begin < buffer->size) {\n \t\tregmatch_t match[1];\n \t\tif (!regexec_buf(word_regex, buffer->ptr + *begin,\n \t\t\t\t buffer->size - *begin, 1, match, 0)) {\n@@ -2061,9 +2061,13 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,\n \t\t\t\t\t'\\n', match[0].rm_eo - match[0].rm_so);\n \t\t\t*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;\n \t\t\t*begin += match[0].rm_so;\n-\t\t\treturn *begin >= *end;\n+\t\t\tif (*begin == *end)\n+\t\t\t\t(*begin)++;\n+\t\t\telse\n+\t\t\t\treturn *begin > *end;\n+\t\t} else {\n+\t\t\treturn -1;\n \t\t}\n-\t\treturn -1;\n \t}\n \n \t/* find the next word */\ndiff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh\nindex ee7721ab9135..561c582d1615 100755\n--- a/t/t4034-diff-words.sh\n+++ b/t/t4034-diff-words.sh\n@@ -184,6 +184,11 @@ test_expect_success 'word diff with a regular expression' '\n \tword_diff --color-words=\"[a-z]+\"\n '\n \n+test_expect_success 'word diff with zero length matches' '\n+\tcp expect.letter-runs-are-words expect &&\n+\tword_diff --color-words=\"[a-z${LF}]*\"\n+'\n+\n test_expect_success 'set up a diff driver' '\n \tgit config diff.testdriver.wordRegex \"[^[:space:]]\" &&\n \tcat <<-\\EOF >.gitattributes\n\nbase-commit: 7e391989789db82983665667013a46eabc6fc570\n-- \ngitgitgadget\n"}]}