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
|
" Constructs a Differ object that is still unbound. To initialize the object
" with data, `Init(from, to)` needs to be invoked on that object.
function! linediff#differ#New(sign_name, sign_number)
let differ = {
\ 'original_buffer': -1,
\ 'diff_buffer': -1,
\ 'filetype': '',
\ 'from': -1,
\ 'to': -1,
\ 'sign_name': a:sign_name,
\ 'sign_number': a:sign_number,
\ 'sign_text': a:sign_number.'-',
\ 'is_blank': 1,
\ 'other_differ': {},
\
\ 'Init': function('linediff#differ#Init'),
\ 'IsBlank': function('linediff#differ#IsBlank'),
\ 'Reset': function('linediff#differ#Reset'),
\ 'Lines': function('linediff#differ#Lines'),
\ 'CreateDiffBuffer': function('linediff#differ#CreateDiffBuffer'),
\ 'SetupDiffBuffer': function('linediff#differ#SetupDiffBuffer'),
\ 'CloseDiffBuffer': function('linediff#differ#CloseDiffBuffer'),
\ 'UpdateOriginalBuffer': function('linediff#differ#UpdateOriginalBuffer'),
\ 'PossiblyUpdateOtherDiffer': function('linediff#differ#PossiblyUpdateOtherDiffer'),
\ 'SetupSigns': function('linediff#differ#SetupSigns')
\ }
exe "sign define ".differ.sign_name." text=".differ.sign_text." texthl=Search"
return differ
endfunction
" Sets up the Differ with data from the argument list and from the current
" file.
function! linediff#differ#Init(from, to) dict
let self.original_buffer = bufnr('%')
let self.filetype = &filetype
let self.from = a:from
let self.to = a:to
call self.SetupSigns()
let self.is_blank = 0
endfunction
" Returns true if the differ is blank, which means not initialized with data.
function! linediff#differ#IsBlank() dict
return self.is_blank
endfunction
" Resets the differ to the blank state. Invoke `Init(from, to)` on it later to
" make it usable again.
function! linediff#differ#Reset() dict
call self.CloseDiffBuffer()
let self.original_buffer = -1
let self.diff_buffer = -1
let self.filetype = ''
let self.from = -1
let self.to = -1
let self.other_differ = {}
exe "sign unplace ".self.sign_number."1"
exe "sign unplace ".self.sign_number."2"
let self.is_blank = 1
endfunction
" Extracts the relevant lines from the original buffer and returns them as a
" list.
function! linediff#differ#Lines() dict
return getbufline(self.original_buffer, self.from, self.to)
endfunction
" Creates the buffer used for the diffing and connects it to this differ
" object.
function! linediff#differ#CreateDiffBuffer(edit_command) dict
let lines = self.Lines()
let temp_file = tempname()
exe a:edit_command . " " . temp_file
call append(0, lines)
normal! Gdd
set nomodified
let self.diff_buffer = bufnr('%')
call self.SetupDiffBuffer()
diffthis
endfunction
" Sets up the temporary buffer's filetype and statusline.
"
" Attempts to leave the current statusline as it is, and simply add the
" relevant information in the place of the current filename. If that fails,
" replaces the whole statusline.
function! linediff#differ#SetupDiffBuffer() dict
let b:differ = self
let statusline = printf('[%s:%%{b:differ.from}-%%{b:differ.to}]', bufname(self.original_buffer))
if &statusline =~ '%[fF]'
let statusline = substitute(&statusline, '%[fF]', statusline, '')
endif
exe "setlocal statusline=" . escape(statusline, ' |')
exe "set filetype=" . self.filetype
setlocal bufhidden=hide
autocmd BufWrite <buffer> silent call b:differ.UpdateOriginalBuffer()
endfunction
function! linediff#differ#CloseDiffBuffer() dict
if bufexists(self.diff_buffer)
exe "bdelete ".self.diff_buffer
endif
endfunction
function! linediff#differ#SetupSigns() dict
exe "sign unplace ".self.sign_number."1"
exe "sign unplace ".self.sign_number."2"
exe printf("sign place %d1 name=%s line=%d buffer=%d", self.sign_number, self.sign_name, self.from, self.original_buffer)
exe printf("sign place %d2 name=%s line=%d buffer=%d", self.sign_number, self.sign_name, self.to, self.original_buffer)
endfunction
" Updates the original buffer after saving the temporary one. It might also
" update the other differ's data, provided a few conditions are met. See
" linediff#differ#PossiblyUpdateOtherDiffer() for details.
function! linediff#differ#UpdateOriginalBuffer() dict
let new_lines = getbufline('%', 0, '$')
" Switch to the original buffer, delete the relevant lines, add the new
" ones, switch back to the diff buffer.
call linediff#util#SwitchBuffer(self.original_buffer)
let saved_cursor = getpos('.')
call cursor(self.from, 1)
exe "normal! ".(self.to - self.from + 1)."dd"
call append(self.from - 1, new_lines)
call setpos('.', saved_cursor)
call linediff#util#SwitchBuffer(self.diff_buffer)
" Keep the difference in lines to know how to update the other differ if
" necessary.
let line_count = self.to - self.from + 1
let new_line_count = len(new_lines)
let self.to = self.from + len(new_lines) - 1
call self.SetupDiffBuffer()
call self.SetupSigns()
call self.PossiblyUpdateOtherDiffer(new_line_count - line_count)
endfunction
" If the other differ originates from the same buffer and it's located below
" this one, we need to update its starting and ending lines, since any change
" would result in a line shift.
"
" a:delta is the change in the number of lines.
function! linediff#differ#PossiblyUpdateOtherDiffer(delta) dict
let other = self.other_differ
if self.original_buffer == other.original_buffer
\ && self.to <= other.from
\ && a:delta != 0
let other.from = other.from + a:delta
let other.to = other.to + a:delta
call other.SetupSigns()
endif
endfunction
|