-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathfind_and_replace.go
263 lines (238 loc) · 6.71 KB
/
find_and_replace.go
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package main
import (
"github.com/mattn/go-gtk/gtk"
"github.com/mattn/go-gtk/gdk"
"strings"
"strconv"
)
func fnr_cb() {
fnr_dialog()
}
func fnr_dialog() {
var fnr_cnt int = 0
var scope_be, scope_en gtk.TextIter
if MAX_SEL_LEN < len(source_selection()) {
source_buf.GetSelectionBounds(&scope_be, &scope_en)
} else {
source_buf.GetStartIter(&scope_be)
source_buf.GetEndIter(&scope_en)
}
source_buf.CreateMark("fnr_be", &scope_be, true)
source_buf.CreateMark("fnr_en", &scope_en, false)
var map_filled bool = false
var global_map map[string]int
var insert_set bool = false
dialog := gtk.NewDialog()
dialog.SetTitle("Find and Replace")
dialog.AddButton("_Find Next", gtk.RESPONSE_OK)
dialog.AddButton("_Replace", gtk.RESPONSE_YES)
dialog.AddButton("Replace _All", gtk.RESPONSE_APPLY)
dialog.AddButton("_Close", gtk.RESPONSE_CLOSE)
dialog.AddAccelGroup(accel_group)
entry := find_entry_with_history()
replacement := find_entry_with_history()
global_button := gtk.NewCheckButtonWithLabel("Global")
global_button.SetVisible(true)
global_button.SetActive(prev_global)
vbox := dialog.GetVBox()
vbox.Add(entry)
vbox.Add(replacement)
vbox.Add(global_button)
find_next_button := dialog.GetWidgetForResponse(int(gtk.RESPONSE_OK))
replace_button := dialog.GetWidgetForResponse(int(gtk.RESPONSE_YES))
replace_all_button := dialog.GetWidgetForResponse(int(gtk.RESPONSE_APPLY))
close_button := dialog.GetWidgetForResponse(int(gtk.RESPONSE_CLOSE))
find_next_button.Connect("clicked", func() {
fnr_pre_cb(global_button, &insert_set)
if !fnr_find_next(entry.GetActiveText(), prev_global, &map_filled, &global_map) {
fnr_close_and_report(dialog, fnr_cnt)
}
},
nil)
find_next_button.AddAccelerator("clicked", accel_group, gdk.KEY_Return,
0, gtk.ACCEL_VISIBLE)
replace_button.Connect("clicked", func() {
fnr_pre_cb(global_button, &insert_set)
done, next_found := fnr_replace(entry.GetActiveText(), replacement.GetActiveText(),
prev_global, &map_filled, &global_map)
fnr_cnt += done
if !next_found {
fnr_close_and_report(dialog, fnr_cnt)
}
},
nil)
replace_all_button.Connect("clicked", func() {
insert_set = false
fnr_pre_cb(global_button, &insert_set)
fnr_cnt += fnr_replace_all_local(entry.GetActiveText(), replacement.GetActiveText())
if prev_global {
fnr_cnt += fnr_replace_all_global(entry.GetActiveText(), replacement.GetActiveText())
file_tree_store()
}
fnr_close_and_report(dialog, fnr_cnt)
},
nil)
close_button.Connect("clicked", func() { dialog.Destroy() }, nil)
dialog.Run()
}
func fnr_replace_all_local(entry string, replacement string) int {
cnt := 0
var t bool = true
if !fnr_find_next(entry, false, &t, nil) {
return 0
}
for {
done, next_found := fnr_replace(entry, replacement, false, &t, nil)
cnt += done
if !next_found {
break
}
}
return cnt
}
func fnr_replace_all_global(entry, replacement string) int {
total_cnt := 0
lent := len(entry)
lrep := len(replacement)
inds := make(map[int]int)
for file, rec := range file_map {
if file == cur_file {
continue
}
cnt := 0
scope := rec.buf[:]
for {
pos := strings.Index(string(scope), entry)
if -1 == pos {
break
}
inds[cnt] = pos
cnt++
scope = scope[pos+lent:]
}
if 0 == cnt {
continue
}
buf := make([]byte, len(rec.buf)+cnt*(lrep-lent))
scope = rec.buf[:]
dest_scope := buf[:]
for y := 0; y < cnt; y++ {
shift := inds[y]
copy(dest_scope, scope[:shift])
dest_scope = dest_scope[shift:]
copy(dest_scope, replacement)
dest_scope = dest_scope[lrep:]
scope = scope[shift+lent:]
}
copy(dest_scope, scope)
rec.buf = buf
rec.modified = true
total_cnt += cnt
}
return total_cnt
}
func fnr_pre_cb(global_button *gtk.CheckButton, insert_set *bool) {
prev_global = global_button.GetActive()
fnr_refresh_scope(prev_global)
fnr_set_insert(insert_set)
}
func fnr_close_and_report(dialog *gtk.Dialog, fnr_cnt int) {
dialog.Destroy()
bump_message(strconv.Itoa(fnr_cnt) + " replacements were done.")
}
func fnr_set_insert(insert_set *bool) {
if false == *insert_set {
*insert_set = true
var scope_be gtk.TextIter
get_iter_at_mark_by_name("fnr_be", &scope_be)
source_buf.MoveMarkByName("insert", &scope_be)
source_buf.MoveMarkByName("selection_bound", &scope_be)
}
}
func fnr_refresh_scope(global bool) {
var be, en gtk.TextIter
if global {
source_buf.GetStartIter(&be)
source_buf.GetEndIter(&en)
source_buf.CreateMark("fnr_be", &be, true)
source_buf.CreateMark("fnr_en", &en, false)
}
}
func fnr_find_next(pattern string, global bool, map_filled *bool, m *map[string]int) bool {
var be, en, scope_en gtk.TextIter
get_iter_at_mark_by_name("fnr_en", &scope_en)
get_iter_at_mark_by_name("selection_bound", &en)
if en.ForwardSearch(pattern, 0, &be, &en, &scope_en) {
move_focus_and_selection(&be, &en)
return true
}
// Have to switch to next file or to beginning of current depending on <global>.
if global {
// Switch to next file.
fnr_find_next_fill_global_map(pattern, m, map_filled)
next_file := pop_string_from_map(m)
if "" == next_file {
return false
}
file_save_current()
file_switch_to(next_file)
fnr_refresh_scope(true)
source_buf.GetStartIter(&be)
source_buf.MoveMarkByName("insert", &be)
source_buf.MoveMarkByName("selection_bound", &be)
return fnr_find_next(pattern, global, map_filled, m)
} else {
// Temporary fix. Is there necessity to search the document all over again?
return false
// Start search from beginning of scope.
// get_iter_at_mark_by_name("fnr_be", &be)
// if be.ForwardSearch(pattern, 0, &be, &en, &scope_en) {
// move_focus_and_selection(&be, &en)
// return true
//} else {
// return false
//}
}
return false
}
func fnr_find_next_fill_global_map(pattern string, m *map[string]int, map_filled *bool) {
if *map_filled {
return
}
*map_filled = true
*m = make(map[string]int)
for file, rec := range file_map {
if cur_file == file {
continue
}
if -1 != strings.Index(string(rec.buf), pattern) {
(*m)[file] = 1
}
}
}
// Returns (done, next_found)
func fnr_replace(entry string, replacement string, global bool, map_filled *bool, global_map *map[string]int) (int, bool) {
if entry != source_selection() {
return 0, true
}
source_buf.DeleteSelection(false, true)
source_buf.InsertAtCursor(replacement)
var be, en gtk.TextIter
source_buf.GetSelectionBounds(&be, &en)
source_buf.MoveMarkByName("insert", &en)
return 1, fnr_find_next(entry, global, map_filled, global_map)
}
func pop_string_from_map(m *map[string]int) string {
if 0 == len(*m) {
return ""
}
for s, _ := range *m {
delete(*m, s)
return s
}
return ""
}
func get_iter_at_mark_by_name(mark_name string, iter *gtk.TextIter) {
mark := source_buf.GetMark(mark_name)
source_buf.GetIterAtMark(iter, mark)
}