1 #include "ide.h"
2 #include "ide.h"
3
sSD()4 FileSel& sSD()
5 {
6 static bool b;
7 static FileSel fs;
8 if(!b) {
9 fs.AllFilesType();
10 b = true;
11 }
12 return fs;
13 }
14
SerializeFindInFiles(Stream & s)15 void Ide::SerializeFindInFiles(Stream& s) {
16 int version = 7;
17 s / version;
18 s % ff.files;
19 ff.files.SerializeList(s);
20 s % ff.folder;
21 ff.folder.SerializeList(s);
22 if(version >= 2)
23 {
24 s % ff.replace;
25 ff.replace.SerializeList(s);
26 s % ff.style;
27 }
28 if(version >= 1)
29 s % sSD();
30 if(version >= 3 && version < 7) {
31 DropList dummy;
32 s % dummy;
33 }
34
35 if(version >= 4)
36 s % ff.samecase;
37 if(version >= 5)
38 s % ff.regexp;
39 if(version >= 6)
40 s % ff.workspace;
41 }
42
SearchForFiles(Index<String> & files,String dir,String mask,int readonly,Progress & pi)43 void SearchForFiles(Index<String>& files, String dir, String mask, int readonly, Progress& pi) {
44 FindFile ff(AppendFileName(dir, "*.*"));
45 while(ff) {
46 if(ff.IsFolder() && *ff.GetName() != '.')
47 SearchForFiles(files, AppendFileName(dir, ff.GetName()), mask, readonly, pi);
48 else
49 if(ff.IsFile() && PatternMatchMulti(mask, ff.GetName())) {
50 if(IsNull(readonly) || !!readonly == !!ff.IsReadOnly()) {
51 if(pi.StepCanceled()) return;
52 files.FindAdd(AppendFileName(dir, ff.GetName()));
53 }
54 }
55 ff.Next();
56 }
57 }
58
59 enum {
60 WILDANY = 16,
61 WILDONE,
62 WILDSPACE,
63 WILDNUMBER,
64 WILDID,
65 };
66
Match(const char * f,const char * s,bool we,bool ignorecase,int & count)67 bool Match(const char *f, const char *s, bool we, bool ignorecase, int& count) {
68 const char *b = s;
69 while(*f) {
70 if(*f == WILDANY) {
71 f++;
72 for(;;) {
73 if(Match(f, s, we, ignorecase, count)) {
74 count += int(s - b);
75 return true;
76 }
77 if(!*s++) break;
78 }
79 return false;
80 }
81 else
82 if(*f == WILDONE) {
83 if(!*s++) return false;
84 }
85 else
86 if(*f == WILDSPACE) {
87 if(*s != ' ' && *s != '\t') return false;
88 s++;
89 while(*s == ' ' || *s == '\t')
90 s++;
91 }
92 else
93 if(*f == WILDNUMBER) {
94 if(*s < '0' || *s > '9') return false;
95 s++;
96 while(*s >= '0' && *s <= '9')
97 s++;
98 }
99 else
100 if(*f == WILDID) {
101 if(!iscib(*s)) return false;
102 s++;
103 while(iscid(*s)) s++;
104 }
105 else {
106 if(ignorecase ? ToUpper(*s) != ToUpper(*f) : *s != *f) return false;
107 s++;
108 }
109 f++;
110 }
111 count = int(s - b);
112 return we && iscid(*s) ? false : true;
113 }
114
AddFoundFile(const String & fn,int ln,const String & line,int pos,int count)115 void Ide::AddFoundFile(const String& fn, int ln, const String& line, int pos, int count)
116 {
117 ErrorInfo f;
118 f.file = fn;
119 f.lineno = ln;
120 f.linepos = pos + 1;
121 f.len = count;
122 f.kind = 0;
123 f.message = "\1" + EditorSyntax::GetSyntaxForFilename(fn) + "\1" +
124 AsString(pos) + "\1" + AsString(count) + "\1" + (line.GetCount() > 300 ? line.Mid(0, 300) : line);
125 FFound().Add(fn, ln, f.message, RawToValue(f));
126 }
127
SearchInFile(const String & fn,const String & pattern,bool wholeword,bool ignorecase,int & n,RegExp * regexp)128 bool Ide::SearchInFile(const String& fn, const String& pattern, bool wholeword, bool ignorecase,
129 int& n, RegExp *regexp) {
130 FileIn in(fn);
131 if(!in) return true;
132 int ln = 1;
133 bool wb = wholeword ? iscid(*pattern) : false;
134 bool we = wholeword ? iscid(*pattern.Last()) : false;
135 int infile = 0;
136 bool sync = false;
137 while(!in.IsEof()) {
138 String line = in.GetLine();
139 bool bw = true;
140 int count;
141 if(regexp) {
142 if(regexp->Match(line)) {
143 AddFoundFile(fn, ln, line, regexp->GetOffset(), regexp->GetLength());
144 sync = true;
145 }
146 }
147 else
148 for(const char *s = line; *s; s++) {
149 if(bw && Match(pattern, s, we, ignorecase, count)) {
150 AddFoundFile(fn, ln, line, int(s - line), count);
151 sync = true;
152 infile++;
153 n++;
154 break;
155 }
156 if(wb) bw = !iscid(*s);
157 }
158 ln++;
159 }
160
161 if(sync)
162 FFound().Sync();
163
164 in.Close();
165 int ffs = ~ff.style;
166 if(infile && ffs != STYLE_NO_REPLACE)
167 {
168 EditFile(fn);
169 if(!editor.IsReadOnly()) {
170 bool doit = true;
171 if(ffs == STYLE_CONFIRM_REPLACE)
172 {
173 editor.SetCursor(0);
174 editor.Find(false, true);
175 switch(PromptYesNoCancel(Format("Replace %d lines in [* \1%s\1]?", infile, fn)))
176 {
177 case 1: break;
178 case 0: doit = false; break;
179 case -1: return false;
180 }
181 }
182 if(doit)
183 {
184 editor.SelectAll();
185 editor.BlockReplace();
186 SaveFile();
187 FFound().Add(fn, Null, AsString(infile) + " replacements made");
188 FFound().Sync();
189 }
190 }
191 }
192
193 return true;
194 }
195
FindInFiles(bool replace)196 void Ide::FindInFiles(bool replace) {
197 CodeEditor::FindReplaceData d = editor.GetFindReplaceData();
198 CtrlRetriever rf;
199 ff.output <<= ffoundi_next;
200 rf(ff.find, d.find)
201 (ff.replace, d.replace)
202 (ff.ignorecase, d.ignorecase)
203 (ff.samecase, d.samecase)
204 (ff.wholeword, d.wholeword)
205 (ff.wildcards, d.wildcards)
206 (ff.regexp, d.regexp)
207 ;
208 WriteList(ff.find, d.find_list);
209 WriteList(ff.replace, d.replace_list);
210 ff.Sync();
211 if(IsNull(~ff.folder))
212 ff.folder <<= GetUppDir();
213 ff.style <<= STYLE_NO_REPLACE;
214 ff.Sync();
215 ff.itext = editor.GetI();
216 ff.Setup(replace);
217
218 int c = ff.Execute();
219
220 ff.find.AddHistory();
221 ff.replace.AddHistory();
222
223 rf.Retrieve();
224 d.find_list = ReadList(ff.find);
225 d.replace_list = ReadList(ff.replace);
226 editor.SetFindReplaceData(d);
227
228 if(c == IDOK) {
229 SaveFile();
230
231 SetFFound(~ff.output);
232
233 FFound().HeaderTab(2).SetText("Source line");
234 Renumber();
235 ff.find.AddHistory();
236 ff.files.AddHistory();
237 ff.folder.AddHistory();
238 ff.replace.AddHistory();
239 Progress pi("Found %d files to search.");
240 pi.AlignText(ALIGN_LEFT);
241 Index<String> files;
242 if(ff.workspace) {
243 const Workspace& wspc = GetIdeWorkspace();
244 for(int i = 0; i < wspc.GetCount(); i++)
245 SearchForFiles(files, GetFileFolder(PackagePath(wspc[i])),
246 ~ff.files, ~ff.readonly, pi);
247 }
248 else
249 SearchForFiles(files, NormalizePath(~~ff.folder, GetUppDir()), ~ff.files, ~ff.readonly, pi);
250 if(!pi.Canceled()) {
251 String pattern;
252 RegExp rx, *regexp = NULL;
253 if(ff.regexp) {
254 rx.SetPattern(~ff.find);
255 regexp = ℞
256 pattern = "dummy";
257 }
258 else
259 if(ff.wildcards) {
260 String q = ~ff.find;
261 for(const char *s = q; *s; s++)
262 if(*s == '\\') {
263 s++;
264 if(*s == '\0') break;
265 pattern.Cat(*s);
266 }
267 else
268 switch(*s) {
269 case '*': pattern.Cat(WILDANY); break;
270 case '?': pattern.Cat(WILDONE); break;
271 case '%': pattern.Cat(WILDSPACE); break;
272 case '#': pattern.Cat(WILDNUMBER); break;
273 case '$': pattern.Cat(WILDID); break;
274 default: pattern.Cat(*s);
275 }
276 }
277 else
278 pattern = ~ff.find;
279 pi.SetTotal(files.GetCount());
280 FFound().Clear();
281 pi.SetPos(0);
282 int n = 0;
283 for(int i = 0; i < files.GetCount(); i++) {
284 pi.SetText(files[i]);
285 if(pi.StepCanceled()) break;
286 if(!IsNull(pattern)) {
287 if(!SearchInFile(files[i], pattern, ff.wholeword, ff.ignorecase, n, regexp))
288 break;
289 }
290 else {
291 ErrorInfo f;
292 f.file = files[i];
293 f.lineno = 1;
294 f.linepos = 0;
295 f.kind = 0;
296 f.message = files[i];
297 FFound().Add(f.file, 1, f.message, RawToValue(f));
298 FFound().Sync();
299 n++;
300 }
301 }
302 if(!IsNull(pattern))
303 FFound().Add(Null, Null, AsString(n) + " occurrence(s) have been found.");
304 else
305 FFound().Add(Null, Null, AsString(n) + " matching file(s) have been found.");
306 FFound().HeaderTab(2).SetText(Format("Source line (%d)", FFound().GetCount()));
307 }
308 }
309 }
310
311
FindFileAll(const Vector<Tuple<int64,int>> & f)312 void Ide::FindFileAll(const Vector<Tuple<int64, int>>& f)
313 {
314 SetFFound(ffoundi_next);
315 FFound().Clear();
316 for(auto pos : f) {
317 editor.CachePos(pos.a);
318 int linei = editor.GetLinePos64(pos.a);
319 WString ln = editor.GetWLine(linei);
320 AddFoundFile(editfile, linei + 1, ln.ToString(), lenAsUtf8(~ln, (int)pos.a), lenAsUtf8(~ln + pos.a, pos.b));
321 }
322 FFound().HeaderTab(2).SetText(Format("Source line (%d)", FFound().GetCount()));
323 FFound().Add(Null, Null, AsString(f.GetCount()) + " occurrence(s) have been found.");
324 }
325
FindString(bool back)326 void Ide::FindString(bool back)
327 {
328 if(!editor.FindString(back))
329 BeepMuteExclamation();
330 }
331
TranslateString()332 void Ide::TranslateString()
333 {
334 if(editor.IsReadOnly()) return;
335 int l, h;
336 if(editor.GetSelection(l, h)) {
337 editor.Insert(l, "t_(");
338 editor.Insert(h + 3, ")");
339 editor.SetCursor(h + 4);
340 FindString(false);
341 }
342 }
343
InsertWildcard(const char * s)344 void Ide::InsertWildcard(const char *s) {
345 iwc = s;
346 }
347
FindWildcard()348 void Ide::FindWildcard() {
349 int l, h;
350 ff.find.GetSelection(l, h);
351 iwc = 0;
352 FindWildcardMenu(THISBACK(InsertWildcard), ff.find.GetPushScreenRect().TopRight(), false, NULL, ff.regexp);
353 if(iwc.GetCount()) {
354 ff.wildcards = true;
355 ff.find.SetFocus();
356 ff.find.SetSelection(l, h);
357 ff.find.RemoveSelection();
358 ff.find.Insert(iwc);
359 }
360 }
361
FindSetStdDir(String n)362 void Ide::FindSetStdDir(String n)
363 {
364 ff.folder <<= n;
365 }
366
FindStdDir()367 void Ide::FindStdDir()
368 {
369 String n = GetFileFolder(editfile);
370 MenuBar menu;
371 if(!IsNull(n))
372 menu.Add(n, THISBACK1(FindSetStdDir, n));
373 Vector<String> d = GetUppDirs();
374 for(int i = 0; i < d.GetCount(); i++)
375 menu.Add(d[i], THISBACK1(FindSetStdDir, d[i]));
376 menu.Execute(&ff.folder, ff.folder.GetPushScreenRect().BottomLeft());
377 }
378
FindFolder()379 void Ide::FindFolder()
380 {
381 if(!sSD().ExecuteSelectDir()) return;
382 ff.folder <<= ~sSD();
383 }
384
SyncFindInFiles()385 void Ide::SyncFindInFiles()
386 {
387 ff.samecase.Enable(ff.ignorecase);
388 }
389
ConstructFindInFiles()390 void Ide::ConstructFindInFiles() {
391 ff.find.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Wildcard") <<= THISBACK(FindWildcard);
392 static const char *defs = "*.cpp *.h *.hpp *.c *.m *.C *.M *.cxx *.cc *.mm *.MM *.icpp *.sch *.lay *.rc";
393 ff.files <<= String(defs);
394 ff.files.AddList((String)defs);
395 ff.files.AddList((String)"*.txt");
396 ff.files.AddList((String)"*.*");
397 ff.folder.AddButton().SetMonoImage(CtrlImg::smalldown()).Tip("Related folders") <<= THISBACK(FindStdDir);
398 ff.folder.AddButton().SetMonoImage(CtrlImg::smallright()).Tip("Select folder") <<= THISBACK(FindFolder);
399 editor.PutI(ff.find);
400 editor.PutI(ff.replace);
401 CtrlLayoutOKCancel(ff, "Find In Files");
402 ff.ignorecase <<= THISBACK(SyncFindInFiles);
403 ff.samecase <<= true;
404 SyncFindInFiles();
405 }
406
Sync()407 void FindInFilesDlg::Sync()
408 {
409 replace.Enable((int)~style);
410 bool b = !regexp;
411 wildcards.Enable(b);
412 ignorecase.Enable(b);
413 wholeword.Enable(b);
414 folder.Enable(!workspace);
415 folder_lbl.Enable(!workspace);
416 }
417
FindInFilesDlg()418 FindInFilesDlg::FindInFilesDlg()
419 {
420 regexp <<= style <<= THISBACK(Sync);
421 readonly.Add(Null, "All files");
422 readonly.Add(0, "Writable");
423 readonly.Add(1, "Read only");
424 readonly <<= Null;
425 workspace <<= THISBACK(Sync);
426 }
427
Setup(bool replacing)428 void FindInFilesDlg::Setup(bool replacing)
429 {
430 Title(replacing ? "Find and replace in files" : "Find in files");
431 replace_lbl.Show(replacing);
432 style.Show(replacing);
433 replace_lbl2.Show(replacing);
434 replace.Show(replacing);
435 Size sz = GetLayoutSize();
436 if(!replacing)
437 sz.cy -= replace.GetRect().bottom - folder.GetRect().bottom;
438 Rect r = GetRect();
439 r.SetSize(sz);
440 SetRect(r);
441 ActiveFocus(find);
442 }
443
Key(dword key,int count)444 bool FindInFilesDlg::Key(dword key, int count)
445 {
446 if(key == K_CTRL_I) {
447 if(find.HasFocus()) {
448 find <<= itext;
449 return true;
450 }
451 if(replace.HasFocus()) {
452 replace <<= itext;
453 return true;
454 }
455 }
456 return TopWindow::Key(key, count);
457 }
458
SetFFound(int ii)459 void Ide::SetFFound(int ii)
460 {
461 ii = clamp(ii, 0, 2);
462 SetBottom(BFINDINFILES1 + ii);
463 ffoundi_next = (ii + 1) % 3;
464 }
465
FFound()466 ArrayCtrl& Ide::FFound()
467 {
468 int i = btabs.GetCursor() - BFINDINFILES1;
469 return i >= 0 && i < 3 ? ffound[i] : ffound[0];
470 }
471
CopyFound(bool all)472 void Ide::CopyFound(bool all)
473 {
474 String txt;
475 for(int i = 0; i < FFound().GetCount(); i++) {
476 if(all)
477 txt << FFound().Get(i, 0) << " (" << FFound().Get(i, 1) << "): ";
478 String h = FFound().Get(i, 2);
479 if(*h == '\1')
480 h = Split(~h + 1, '\1', false).Top();
481 txt << h << "\r\n";
482 }
483 WriteClipboardText(txt);
484 }
485
FFoundMenu(Bar & bar)486 void Ide::FFoundMenu(Bar& bar)
487 {
488 bar.Add("Copy text", THISBACK1(CopyFound, false));
489 bar.Add("Copy all", THISBACK1(CopyFound, true));
490 }
491