-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdiff.vim
193 lines (179 loc) · 5.05 KB
/
diff.vim
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
let s:normal = diff#normal#import()
let s:myersond = diff#myersond#import()
let s:wuonp = diff#wuonp#import()
let s:histogram = diff#histogram#import()
let s:patience = diff#patience#import()
function diff#diffexpr()
return diff#wuonpdiffexpr()
endfunction
function diff#myersonddiffexpr()
let options = {}
let options.algorithm = 'myersond'
let options.iwhite = (&diffopt =~ 'iwhite')
let options.icase = (&diffopt =~ 'icase')
call diff#fnormal(v:fname_in, v:fname_new, v:fname_out, options)
endfunction
function diff#wuonpdiffexpr()
let options = {}
let options.algorithm = 'wuonp'
let options.iwhite = (&diffopt =~ 'iwhite')
let options.icase = (&diffopt =~ 'icase')
call diff#fnormal(v:fname_in, v:fname_new, v:fname_out, options)
endfunction
function diff#histogramdiffexpr()
let options = {}
let options.algorithm = 'histogram'
let options.iwhite = (&diffopt =~ 'iwhite')
let options.icase = (&diffopt =~ 'icase')
call diff#fnormal(v:fname_in, v:fname_new, v:fname_out, options)
endfunction
function diff#patiencediffexpr()
let options = {}
let options.algorithm = 'patience'
let options.iwhite = (&diffopt =~ 'iwhite')
let options.icase = (&diffopt =~ 'icase')
call diff#fnormal(v:fname_in, v:fname_new, v:fname_out, options)
endfunction
function diff#normal(old, new, ...)
let options = get(a:000, 0, {})
return diff#bnormal(a:old + [''], a:new + [''], options)
endfunction
function diff#bnormal(old, new, ...)
let options = get(a:000, 0, {})
let algorithm = get(options, 'algorithm', 'wuonp')
let iwhite = get(options, 'iwhite', 0)
let icase = get(options, 'icase', 0)
let [A, Aeol] = s:fixeol(copy(a:old))
let [B, Beol] = s:fixeol(copy(a:new))
let Acmp = s:makecmpbuf(copy(A), Aeol, iwhite, icase)
let Bcmp = s:makecmpbuf(copy(B), Beol, iwhite, icase)
if algorithm == 'histogram'
let path = s:histogram.HistogramDiff.diff(Acmp, Bcmp)
elseif algorithm == 'patience'
let path = s:patience.PatienceDiff.diff(Acmp, Bcmp)
elseif algorithm == 'wuonp'
let path = s:wuonp.WuOnpDiff.diff(Acmp, Bcmp)
elseif algorithm == 'myersond'
let path = s:myersond.MyersOnd.diff(Acmp, Bcmp)
else
throw 'Unknown algorithm: ' . algorithm
endif
let path = s:change_compact(path, Acmp, Bcmp)
return s:normal.Normal.format(path, A, Aeol, B, Beol)
endfunction
function diff#fnormal(oldfile, newfile, outfile, ...)
let options = get(a:000, 0, {})
let old = readfile(a:oldfile, 'b')
let new = readfile(a:newfile, 'b')
let out = diff#bnormal(old, new, options)
if !empty(out)
call add(out, '')
endif
call writefile(out, a:outfile, 'b')
endfunction
function s:fixeol(lines)
let eol = 0
if !empty(a:lines) && a:lines[-1] ==# ''
let eol = 1
unlet a:lines[-1]
endif
return [a:lines, eol]
endfunction
function s:makecmpbuf(lines, eol, iwhite, icase)
" Add x to avoid empty key for Dictionary.
" Add \n to detect noeol.
call map(a:lines, '"x" . v:val . "\n"')
if !empty(a:lines) && !a:eol
let a:lines[-1] = a:lines[-1][0:-2]
endif
if a:iwhite
call map(a:lines, 'substitute(v:val, ''[ \t\r\n]\+\|[ \t\r\n]*$'', " ", "g")')
endif
if a:icase
call map(a:lines, 'tolower(v:val)')
endif
return a:lines
endfunction
" Move back and forward change groups for a consistent and pretty diff output.
function s:change_compact(path, al, bl)
let [ad, bd] = s:path_to_diff(a:path)
let ad = s:change_compact_sub(ad, a:al)
let bd = s:change_compact_sub(bd, a:bl)
return s:diff_to_path(ad, bd)
endfunction
function s:path_to_diff(path)
let ad = filter(copy(a:path), 'v:val <= 0')
let bd = filter(copy(a:path), 'v:val >= 0')
return [ad, bd]
endfunction
function s:diff_to_path(ad, bd)
let path = []
let a = 0
let b = 0
while a < len(a:ad) && b < len(a:bd)
if a:ad[a] == 0 && a:bd[b] == 0
call add(path, 0)
let a += 1
let b += 1
elseif a:ad[a] != 0
call add(path, -1)
let a += 1
else
call add(path, 1)
let b += 1
endif
endwhile
if a < len(a:ad)
while a < len(a:ad)
call add(path, -1)
let a += 1
endwhile
endif
if b < len(a:bd)
while b < len(a:bd)
call add(path, 1)
let b += 1
endwhile
endif
return path
endfunction
function s:change_compact_sub(diff, lines)
let i = 0
while i < len(a:diff)
while i < len(a:diff) && a:diff[i] == 0
let i += 1
endwhile
let s = i
while i < len(a:diff) && a:diff[i] != 0
let i += 1
endwhile
let e = i
if s == e
break
endif
let start = s
let end = e
while 0 < s && a:lines[s - 1] ==# a:lines[e - 1]
let a:diff[s - 1] = a:diff[e - 1]
let a:diff[e - 1] = 0
let e -= 1
while 0 < s && a:diff[s - 1] != 0
let s -= 1
endwhile
endwhile
while e < len(a:lines) && a:lines[s] ==# a:lines[e]
let a:diff[e] = a:diff[s]
let a:diff[s] = 0
let s += 1
while e < len(a:diff) && a:diff[e] != 0
let e += 1
endwhile
endwhile
if start != s || end != e
let i = s
else
let i = e
endif
endwhile
return a:diff
endfunction