Skip to content

Commit fac661e

Browse files
committed
Correctly highlight the changed characters. Refactor the code
1 parent 8ff0923 commit fac661e

File tree

1 file changed

+87
-46
lines changed

1 file changed

+87
-46
lines changed

autoload/greplace.vim

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
" Script to search and replace pattern across multiple files
33
" Author: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
44
" Version: 2.0
5-
" Last Modified: March 3, 2018
5+
" Last Modified: March 4, 2018
66
"
77
" Copyright: Copyright (C) 2007-2018 Yegappan Lakshmanan
88
" Permission is hereby granted to use and distribute this code,
@@ -34,20 +34,9 @@ endfunction
3434

3535
highlight GReplaceText term=reverse cterm=reverse gui=reverse
3636

37-
" gReplace()
38-
" Get a list of lines changed in the replace buffer and merge the changes
39-
" back into the files.
40-
function! s:gReplace(bang)
41-
if empty(s:save_qf_list)
42-
return
43-
endif
44-
45-
if a:bang == "!"
46-
let change_all = 1
47-
else
48-
let change_all = 0
49-
endif
50-
37+
" get_changeset()
38+
" Parse the lines in the Replace buffer and get a List of changed lines
39+
function! s:get_changeset()
5140
let changeset = {}
5241

5342
" Parse the replace buffer contents and get a List of changed lines
@@ -64,6 +53,7 @@ function! s:gReplace(bang)
6453

6554
let key = fname . ':' . lnum
6655

56+
" Check whether the file and line number exists in the original list
6757
if !has_key(s:save_qf_list, key)
6858
" User might have modified the filename or line number
6959
continue
@@ -82,15 +72,65 @@ function! s:gReplace(bang)
8272
let changeset[fname][lnum] = text
8373
endfor
8474

85-
if empty(changeset)
86-
" The replace buffer is not modified by the user
87-
call s:warn_msg('Error: No changes in the replace buffer')
88-
return
75+
return changeset
76+
endfunction
77+
78+
" diff_line()
79+
" Compute the diff between the old and new lines
80+
" Returns the start index of the diff in the old line and the end index of the
81+
" diff in the old and new lines as a list
82+
function! s:diff_line(old_line, new_line)
83+
" Find the index of the leftmost character that is different
84+
let s_idx = 0
85+
while a:old_line[s_idx] ==# a:new_line[s_idx]
86+
let s_idx += 1
87+
endwhile
88+
89+
" Find the index of the rightmost character that is different
90+
let e_idx1 = strlen(a:old_line) - 1
91+
let e_idx2 = strlen(a:new_line) - 1
92+
while e_idx1 >= 0 && e_idx1 > s_idx &&
93+
\ a:old_line[e_idx1] ==# a:new_line[e_idx2]
94+
let e_idx1 -= 1
95+
let e_idx2 -= 1
96+
endwhile
97+
98+
return [s_idx, e_idx1, e_idx2]
99+
endfunction
100+
101+
" highlight_diff()
102+
" Highlight the characters between the start and end indexes in the specified
103+
" line.
104+
function! s:highlight_diff(lnum, s_idx, e_idx)
105+
let scol = a:s_idx
106+
let ecol = a:e_idx
107+
108+
" We are going to highlight characters greater than s_idx and less
109+
" than e_idx (between s_idx + 1 and e_idx - 1). Highlighting
110+
" uses columns which start at 1 while string index starts at 0. So
111+
" increment e_idx by 2.
112+
let ecol += 2
113+
114+
" If there is nothing to highlight, then highlight the last character
115+
if (scol + 1) == ecol
116+
let ecol += 1
117+
elseif scol == ecol
118+
let ecol += 2
89119
endif
90120

121+
let hl_pat = '/\%'.a:lnum.'l\%>'.scol.'c.*\%<'.ecol.'c/'
122+
exe '2match GReplaceText ' . hl_pat
123+
redraw!
124+
endfunction
125+
126+
" process_change_set()
127+
" Merge the changes made by the user to the corresponding files/buffers
128+
function! s:process_changeset(changeset, bang)
129+
let change_all = (a:bang == '!')
130+
91131
" Merge the changes made by the user to the buffers
92-
for f in keys(changeset)
93-
let f_l = changeset[f]
132+
for f in keys(a:changeset)
133+
let f_l = a:changeset[f]
94134
if !filereadable(f)
95135
continue
96136
endif
@@ -110,38 +150,20 @@ function! s:gReplace(bang)
110150
for lnum in keys(f_l)
111151
exe lnum
112152

113-
let cur_ltext = getline(lnum)
114-
let new_ltext = f_l[lnum]
115-
116-
let s_idx = 0
117-
while cur_ltext[s_idx] ==# new_ltext[s_idx]
118-
let s_idx += 1
119-
endwhile
153+
let prev_line = getline(lnum)
154+
let new_line = f_l[lnum]
120155

121-
let e_idx1 = strlen(cur_ltext) - 1
122-
let e_idx2 = strlen(new_ltext) - 1
123-
while e_idx1 >= 0 && cur_ltext[e_idx1] ==# new_ltext[e_idx2]
124-
let e_idx1 -= 1
125-
let e_idx2 -= 1
126-
endwhile
127-
128-
let e_idx1 += 2
129-
130-
if (s_idx + 1) == e_idx1
131-
" If there is nothing to highlight, then highlight the
132-
" last character
133-
let e_idx1 += 1
134-
endif
156+
" Compute the diff between the old and new lines
157+
let [s_idx, e_idx1, e_idx2] = s:diff_line(prev_line, new_line)
135158

136-
let hl_pat = '/\%'.lnum.'l\%>'.s_idx.'c.*\%<'.e_idx1.'c/'
137-
exe '2match GReplaceText ' . hl_pat
138-
redraw!
159+
" Highlight the diff
160+
call s:highlight_diff(lnum, s_idx, e_idx1)
139161

140162
try
141163
let change_line = 0
142164

143165
if !change_all && !change_buf_all
144-
let new_text_frag = strpart(new_ltext, s_idx,
166+
let new_text_frag = strpart(new_line, s_idx,
145167
\ e_idx2 - s_idx + 1)
146168

147169
echo "Replace with '" . new_text_frag . "' (y/n/a/b/q)?"
@@ -173,6 +195,25 @@ function! s:gReplace(bang)
173195
endfor
174196
endfunction
175197

198+
" gReplace()
199+
" Get a list of lines changed in the replace buffer and merge the changes
200+
" back into the files.
201+
function! s:gReplace(bang)
202+
if empty(s:save_qf_list)
203+
return
204+
endif
205+
206+
let changeset = s:get_changeset()
207+
if empty(changeset)
208+
" The replace buffer is not modified by the user
209+
call s:warn_msg('Error: No changes in the replace buffer')
210+
return
211+
endif
212+
213+
" Merge the changes made by the user to the buffers
214+
call s:process_changeset(changeset, a:bang)
215+
endfunction
216+
176217
" gRepl_Jump_To_File
177218
" Jump to the file under the cursor and position the cursor on the
178219
" line with the line number under the cursor

0 commit comments

Comments
 (0)