aboutsummaryrefslogtreecommitdiff
path: root/autoload/diff/prune.vim
blob: 554d65d8b32df5d918f70015a374c8dd2c5dca2d (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 range = '''<,''>'
  else
    let range = '''[,'']'
  endif

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

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

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

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

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

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

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

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

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

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

  endfor

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

endfunction