aboutsummaryrefslogtreecommitdiff
path: root/autoload/diff/prune.vim
blob: a562d716d852fcfbe5513716ba24a0e8c839706e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
" Undo some diff lines
function! diff#prune#Prune(type) abort

  " Choose appropriate line ranges depending on mode
  if a:type ==# 'v' || a:type ==# 'V' || a:type ==# "\<C-V>"
    let l:range = '''<,''>'
  else
    let l:range = '''[,'']'
  endif

  " Reinstate removals and remove addenda; preserve search pattern
  let l:search_save = @/
  silent execute l:range.'substitute/^-/ /e'
  silent execute l:range.'global/^+/d'
  let @/ = l:search_save

  " Now we need to look for any blocks or files to remove if they have no
  " changes in them anymore
  let l:file_changes = 0
  let l:block_changes = 0
  let l:deletions = {}
  for l:li in range(1, line('$') + 1)

    " Flag for the end of the buffer (one past the last line)
    let l:eof = l:li > line('$')

    " If this index corresponds to a real line, cache its value
    if !l:eof
      let l:line = getline(l:li)
      let l:deletions[l:li] = 0
    endif

    " Flags for whether this iteration constitutes the start of a new file, a
    " new block, or a changed line
    let l:file = stridx(l:line, 'diff') == 0 && !l:eof
    let l:block = stridx(l:line, '@@') == 0 && !l:eof
    let l:change = (stridx(l:line, '+') == 0 || stridx(l:line, '-') == 0)
          \ && !l:eof
          \ && exists('l:block_start')

    " End of old file: flag previous file lines for deletion if no changes,
    " clear file start and changes variables
    if l:file || l:eof
      if exists('l:file_start') && l:file_changes == 0
        for l:di in range(l:file_start, l:li - 1)
          let l:deletions[l:di] = 1
        endfor
      endif
      unlet! l:file_start l:file_changes
    endif

    " Start of new file: set start line, start new changes counter
    if l:file
      let l:file_start = l:li
      let l:file_changes = 0
    endif

    " End of old block: flag previous block lines for deletion if no changes,
    " clear block start and changes variables
    if l:block || l:file || l:eof
      if exists('l:block_start') && l:block_changes == 0
        for l:di in range(l:block_start, l:li - 1)
          let l:deletions[l:di] = 1
        endfor
      endif
      unlet! l:block_start l:block_changes
    endif

    " Start of new block: set start line, start new changes counter
    if l:block
      let l:block_start = l:li
      let l:block_changes = 0
    endif

    " If this is a changed line, bump the counters for this file and block
    if l:change
      let l:file_changes += 1
      let l:block_changes += 1
    endif

  endfor

  " Delete any flagged lines, going in reverse order so we don't reset any
  " indices as we go
  let l:di = line('$')
  while l:di > 0
    if l:deletions[l:di]
      silent execute l:di.'delete'
    endif
    let l:di -= 1
  endwhile

endfunction