mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-08 11:33:48 -05:00
Enable intra-line highlights to handle multiple regions
For example consider:
-The cat in the hat.
+The ox in the box.
Before this change the highlights would be:
-The cat in the hat.
^^^^^^^^^^^^^^
+The ox in the box.
^^^^^^^^^^^^^
After this change the highlights are:
-The cat in the hat.
^^^ ^^^
+The ox in the box.
^^ ^^^
Another example; before:
-The quick brown fox jumped
+The (quick) brown (fox) jumped
^^^^^^^^^^^^^^^^^^^
And after:
-The quick brown fox jumped
+The (quick) brown (fox) jumped
^ ^ ^ ^
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
" This is the minimum number of characters required between regions of change
|
||||||
|
" in a line. It's somewhat arbitrary: higher values mean less visual busyness;
|
||||||
|
" lower values mean more detail.
|
||||||
|
let s:gap_between_regions = 5
|
||||||
|
|
||||||
|
|
||||||
" Calculates the changed portions of lines.
|
" Calculates the changed portions of lines.
|
||||||
"
|
"
|
||||||
" Based on:
|
" Based on:
|
||||||
@@ -42,70 +48,121 @@ function! gitgutter#diff_highlight#process(hunk_body)
|
|||||||
let rline = a:hunk_body[i]
|
let rline = a:hunk_body[i]
|
||||||
let aline = a:hunk_body[i + removed]
|
let aline = a:hunk_body[i + removed]
|
||||||
|
|
||||||
let prefix = s:common_prefix(rline, aline)
|
call s:diff(rline, aline, i, i+removed, 0, 0, regions, 1)
|
||||||
let [rsuffix, asuffix] = s:common_suffix(rline, aline, prefix+1)
|
|
||||||
|
|
||||||
let rtext = rline[prefix+1:rsuffix-1]
|
|
||||||
let atext = aline[prefix+1:asuffix-1]
|
|
||||||
|
|
||||||
" singular insertion
|
|
||||||
if empty(rtext)
|
|
||||||
if len(atext) != len(aline) " not whole line
|
|
||||||
call add(regions, [i+1+removed, '+', prefix+1+1, asuffix+1-1])
|
|
||||||
endif
|
|
||||||
continue
|
|
||||||
endif
|
|
||||||
|
|
||||||
" singular deletion
|
|
||||||
if empty(atext)
|
|
||||||
if len(rtext) != len(rline) " not whole line
|
|
||||||
call add(regions, [i+1, '-', prefix+1+1, rsuffix+1-1])
|
|
||||||
endif
|
|
||||||
continue
|
|
||||||
endif
|
|
||||||
|
|
||||||
" two insertions
|
|
||||||
let j = stridx(atext, rtext)
|
|
||||||
if j != -1
|
|
||||||
call add(regions, [i+1+removed, '+', prefix+1+1, prefix+j+1])
|
|
||||||
call add(regions, [i+1+removed, '+', prefix+1+1+j+len(rtext), asuffix+1-1])
|
|
||||||
continue
|
|
||||||
endif
|
|
||||||
|
|
||||||
" two deletions
|
|
||||||
let j = stridx(rtext, atext)
|
|
||||||
if j != -1
|
|
||||||
call add(regions, [i+1, '-', prefix+1+1, prefix+j+1])
|
|
||||||
call add(regions, [i+1, '-', prefix+1+1+j+len(atext), rsuffix+1-1])
|
|
||||||
continue
|
|
||||||
endif
|
|
||||||
|
|
||||||
" fall back to highlighting entire changed area
|
|
||||||
|
|
||||||
" if a change (but not the whole line)
|
|
||||||
if (prefix != 0 || rsuffix != len(rline)) && prefix+1 < rsuffix
|
|
||||||
call add(regions, [i+1, '-', prefix+1+1, rsuffix+1-1])
|
|
||||||
endif
|
|
||||||
|
|
||||||
" if a change (but not the whole line)
|
|
||||||
if (prefix != 0 || asuffix != len(aline)) && prefix+1 < asuffix
|
|
||||||
call add(regions, [i+1+removed, '+', prefix+1+1, asuffix+1-1])
|
|
||||||
endif
|
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
return regions
|
return regions
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:diff(rline, aline, rlinenr, alinenr, rprefix, aprefix, regions, whole_line)
|
||||||
|
" diff marker does not count as a difference in prefix
|
||||||
|
let start = a:whole_line ? 1 : 0
|
||||||
|
let prefix = s:common_prefix(a:rline[start:], a:aline[start:])
|
||||||
|
if a:whole_line
|
||||||
|
let prefix += 1
|
||||||
|
endif
|
||||||
|
let [rsuffix, asuffix] = s:common_suffix(a:rline, a:aline, prefix+1)
|
||||||
|
|
||||||
|
let rtext = a:rline[prefix+1:rsuffix-1]
|
||||||
|
let atext = a:aline[prefix+1:asuffix-1]
|
||||||
|
|
||||||
|
" singular insertion
|
||||||
|
if empty(rtext)
|
||||||
|
if !a:whole_line || len(atext) != len(a:aline) " not whole line
|
||||||
|
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" singular deletion
|
||||||
|
if empty(atext)
|
||||||
|
if !a:whole_line || len(rtext) != len(a:rline) " not whole line
|
||||||
|
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" two insertions
|
||||||
|
let j = stridx(atext, rtext)
|
||||||
|
if j != -1
|
||||||
|
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+prefix+j+1])
|
||||||
|
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1+j+len(rtext), a:aprefix+asuffix+1-1])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" two deletions
|
||||||
|
let j = stridx(rtext, atext)
|
||||||
|
if j != -1
|
||||||
|
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+prefix+j+1])
|
||||||
|
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1+j+len(atext), a:rprefix+rsuffix+1-1])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" two edits
|
||||||
|
let lcs = s:lcs(rtext, atext)
|
||||||
|
if len(lcs) > s:gap_between_regions
|
||||||
|
let redits = split(rtext, lcs)
|
||||||
|
let aedits = split(atext, lcs)
|
||||||
|
call s:diff(redits[0], aedits[0], a:rlinenr, a:alinenr, prefix+1, prefix+1, a:regions, 0)
|
||||||
|
call s:diff(redits[1], aedits[1], a:rlinenr, a:alinenr, prefix+1+len(redits[0])+len(lcs), prefix+1+len(aedits[0])+len(lcs), a:regions, 0)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" fall back to highlighting entire changed area
|
||||||
|
|
||||||
|
" if a change (but not the whole line)
|
||||||
|
if !a:whole_line || ((prefix != 0 || rsuffix != len(a:rline)) && prefix+1 < rsuffix)
|
||||||
|
call add(a:regions, [a:rlinenr+1, '-', a:rprefix+prefix+1+1, a:rprefix+rsuffix+1-1])
|
||||||
|
endif
|
||||||
|
|
||||||
|
" if a change (but not the whole line)
|
||||||
|
if !a:whole_line || ((prefix != 0 || asuffix != len(a:aline)) && prefix+1 < asuffix)
|
||||||
|
call add(a:regions, [a:alinenr+1, '+', a:aprefix+prefix+1+1, a:aprefix+asuffix+1-1])
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:lcs(s1, s2)
|
||||||
|
if empty(a:s1) || empty(a:s2)
|
||||||
|
return ''
|
||||||
|
endif
|
||||||
|
|
||||||
|
let matrix = map(repeat([repeat([0], len(a:s2)+1)], len(a:s1)+1), 'copy(v:val)')
|
||||||
|
|
||||||
|
let maxlength = 0
|
||||||
|
let endindex = len(a:s1)
|
||||||
|
|
||||||
|
for i in range(1, len(a:s1))
|
||||||
|
for j in range(1, len(a:s2))
|
||||||
|
if a:s1[i] ==# a:s2[j]
|
||||||
|
let matrix[i][j] = 1 + matrix[i-1][j-1]
|
||||||
|
if matrix[i][j] > maxlength
|
||||||
|
let maxlength = matrix[i][j]
|
||||||
|
let endindex = i
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return a:s1[endindex - maxlength + 1 : endindex]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
if $VIM_GITGUTTER_TEST
|
||||||
|
function! gitgutter#diff_highlight#lcs(s1, s2)
|
||||||
|
return s:lcs(a:s1, a:s2)
|
||||||
|
endfunction
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
" Returns 0-based index of last character of common prefix
|
" Returns 0-based index of last character of common prefix
|
||||||
" Does not treat leading +/- as different.
|
" If there is no common prefix, returns -1.
|
||||||
"
|
"
|
||||||
" a, b - strings
|
" a, b - strings
|
||||||
"
|
"
|
||||||
function! s:common_prefix(a, b)
|
function! s:common_prefix(a, b)
|
||||||
let len = min([len(a:a), len(a:b)])
|
let len = min([len(a:a), len(a:b)])
|
||||||
" ignore initial +/-
|
for i in range(len)
|
||||||
for i in range(1, len - 1)
|
|
||||||
if a:a[i:i] != a:b[i:i]
|
if a:a[i:i] != a:b[i:i]
|
||||||
return i - 1
|
return i - 1
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -917,14 +917,22 @@ endfunction
|
|||||||
|
|
||||||
function Test_common_prefix()
|
function Test_common_prefix()
|
||||||
" nothing in common
|
" nothing in common
|
||||||
call assert_equal(0, gitgutter#diff_highlight#common_prefix('-abcde', '+pqrst'))
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+pqrst'))
|
||||||
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('abcde', 'pqrst'))
|
||||||
" something in common
|
" something in common
|
||||||
call assert_equal(3, gitgutter#diff_highlight#common_prefix('-abcde', '+abcpq'))
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abcpq'))
|
||||||
|
call assert_equal(2, gitgutter#diff_highlight#common_prefix('abcde', 'abcpq'))
|
||||||
|
call assert_equal(0, gitgutter#diff_highlight#common_prefix('abc', 'apq'))
|
||||||
" everything in common
|
" everything in common
|
||||||
call assert_equal(5, gitgutter#diff_highlight#common_prefix('-abcde', '+abcde'))
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abcde'))
|
||||||
|
call assert_equal(4, gitgutter#diff_highlight#common_prefix('abcde', 'abcde'))
|
||||||
" different lengths
|
" different lengths
|
||||||
call assert_equal(2, gitgutter#diff_highlight#common_prefix('-abcde', '+abx'))
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abx'))
|
||||||
call assert_equal(2, gitgutter#diff_highlight#common_prefix('-abx', '+abcde'))
|
call assert_equal(1, gitgutter#diff_highlight#common_prefix('abcde', 'abx'))
|
||||||
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abx', '+abcde'))
|
||||||
|
call assert_equal(1, gitgutter#diff_highlight#common_prefix('abx', 'abcde'))
|
||||||
|
call assert_equal(-1, gitgutter#diff_highlight#common_prefix('-abcde', '+abc'))
|
||||||
|
call assert_equal(2, gitgutter#diff_highlight#common_prefix('abcde', 'abc'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
@@ -1010,5 +1018,12 @@ function Test_diff_highlight()
|
|||||||
|
|
||||||
" two edits
|
" two edits
|
||||||
let hunk = ['-The cat in the hat.', '+The ox in the box.']
|
let hunk = ['-The cat in the hat.', '+The ox in the box.']
|
||||||
call assert_equal([[1, '-', 6, 8], [1, '-', 17, 19], [2, '+', 6, 7], [2, '+', 16, 18]], gitgutter#diff_highlight#process(hunk))
|
call assert_equal([[1, '-', 6, 8], [2, '+', 6, 7], [1, '-', 17, 19], [2, '+', 16, 18]], gitgutter#diff_highlight#process(hunk))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function Test_lcs()
|
||||||
|
call assert_equal('', gitgutter#diff_highlight#lcs('', 'foo'))
|
||||||
|
call assert_equal('', gitgutter#diff_highlight#lcs('foo', ''))
|
||||||
|
call assert_equal('bar', gitgutter#diff_highlight#lcs('foobarbaz', 'bbart'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|||||||
Reference in New Issue
Block a user