1 #include "ide.h"
2 
ViewCache()3 String ViewCache()
4 {
5 	return ConfigFile("view_maps");
6 }
7 
ViewFileHash(const String & path)8 String ViewFileHash(const String& path)
9 {
10 	Sha1Stream s;
11 	FindFile ff(path);
12 	if(ff) {
13 		Sha1Stream sha;
14 		sha << path << ';' << Time(ff.GetLastWriteTime()) << ';' << ff.GetLength();
15 		return AppendFileName(ViewCache(), sha.FinishString());
16 	}
17 	return Null;
18 }
19 
ViewFile(LineEdit & edit,Stream & view_file,const String & path,byte charset)20 void ViewFile(LineEdit& edit, Stream& view_file, const String& path, byte charset)
21 {
22 	edit.View(view_file, charset);
23 	String f = ViewFileHash(path);
24 	if(f.GetCount())
25 		LoadFromFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
26 }
27 
CacheViewFile(LineEdit & edit,const String & path)28 void CacheViewFile(LineEdit& edit, const String& path)
29 {
30 	if(edit.IsView()) {
31 		ReduceCacheFolder(ViewCache(), 20000000);
32 		String f = ViewFileHash(path);
33 		RealizePath(f);
34 		if(f.GetCount())
35 			StoreToFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
36 	}
37 }
38 
SetupEditor(int f,String hl,String path)39 void Ide::SetupEditor(int f, String hl, String path)
40 {
41 	if(IsNull(hl)) {
42 		hl = EditorSyntax::GetSyntaxForFilename(path);
43 		if(IsNull(hl))
44 			hl = EditorSyntax::GetSyntaxForFilename(ToLower(path));
45 		if(IsNull(hl) && IsNull(GetFileExt(path))) {
46 			FileIn in(path);
47 			String h = in.Get(4096);
48 			try {
49 				CParser p(h);
50 				while(!p.IsEof()) {
51 					if(p.Char('#')) {
52 						if(p.Id("define") || p.Id("ipathdef") || p.Id("ifdef") || p.Id("include") || p.Id("pragma")) {
53 							hl = "cpp";
54 							break;
55 						}
56 					}
57 					p.SkipTerm();
58 				}
59 			}
60 			catch(CParser::Error) {}
61 		}
62 	}
63 	switch(f) {
64 	case 1:  editor.SetFont(font1); break;
65 	case 2:  editor.SetFont(font2); break;
66 	case 3:  editor.SetFont(consolefont); break;
67 	default: editor.SetFont(editorsplit.GetZoom() < 0 && editorsplit.IsHorz() ? veditorfont :
68 	                        hl == "t" ? tfont : editorfont); break;
69 	}
70 	editor.Highlight(hl);
71 
72 	editor.WarnWhiteSpace(warnwhitespace &&
73 	                      findarg(hl, "cpp", "java", "js", "cs", "json", "css", "lay", "sch", "t", "usc") >= 0);
74 
75 	editor.WordwrapComments(wordwrap_comments);
76 	editor.WordWrap(wordwrap);
77 }
78 
SetupEditor()79 void Ide::SetupEditor()
80 {
81 	if(!IsActiveFile())
82 		return;
83 	Package::File& f = ActiveFile();
84 	String p = GetActiveFileName();
85 	if(p != HELPNAME)
86 		p = GetActiveFilePath();
87 	SetupEditor(f.font, f.highlight, p);
88 	editor.SyncNavigatorPlacement();
89 }
90 
FileCursor()91 void Ide::FileCursor()
92 {
93 	WorkspaceWork::FileCursor();
94 	if(IsActiveFile() && !filelist[filelist.GetCursor()].isdir) {
95 		Package::File& f = ActiveFile();
96 		editor.SetEditable(!f.readonly);
97 		editor.TabSize(f.tabsize > 0 ? f.tabsize : actual.tabsize > 0 ? actual.tabsize : editortabsize);
98 		SetupEditor();
99 		String headername;
100 		if(insert_include)
101 			for(int i = insert_include == 1 ? 0 : filelist.GetCursor();
102 			    i >= 0 && i < filelist.GetCount();
103 			    i += insert_include == 1 ? 1 : -1) {
104 					String nm = FileName(i);
105 					if(!IsSeparator(i) && GetFileExt(nm) == ".h") {
106 						headername = nm;
107 						break;
108 					}
109 			}
110 		String p = GetActiveFileName();
111 		if(p != HELPNAME)
112 			p = GetActiveFilePath();
113 		EditFile0(p, f.charset ? f.charset : actual.charset ? actual.charset : default_charset,
114 		          Nvl(f.spellcheck_comments, actual.spellcheck_comments, spellcheck_comments),
115 		          headername);
116 	}
117 }
118 
HasFileData(const String & file)119 bool Ide::HasFileData(const String& file)
120 {
121 	return filedata.Find(NormalizePath(file)) >= 0;
122 }
123 
Filedata(const String & file)124 Ide::FileData& Ide::Filedata(const String& file) {
125 	return filedata.GetAdd(NormalizePath(file));
126 }
127 
DlCharsetD(DropList & d)128 void DlCharsetD(DropList& d)
129 {
130 	d.Add(0, "Default");
131 	DlCharset(d);
132 }
133 
ChangeFileCharset(const String & name,Package & p,byte charset)134 void Ide::ChangeFileCharset(const String& name, Package& p, byte charset)
135 {
136 	if(IsNull(name))
137 		return;
138 	bool sv = false;
139 	for(int i = 0; i < p.file.GetCount(); i++)
140 		if(SourcePath(name, p.file[i]) == editfile && p.file[i].charset != charset) {
141 			p.file[i].charset = charset;
142 			sv = true;
143 		}
144 	if(sv) {
145 		String pp = PackagePathA(name);
146 		RealizePath(pp);
147 		p.Save(pp);
148 	}
149 }
150 
FileProperties()151 void Ide::FileProperties()
152 {
153 	if(!IsActiveFile()) return;
154 	WithFileFormatLayout<TopWindow> d;
155 	CtrlLayoutOKCancel(d, "File properties");
156 	Package::File& f = ActiveFile();
157 	DlCharsetD(d.charset);
158 	d.font.Add(0, "Normal");
159 	d.font.Add(1, "Small");
160 	d.font.Add(2, "Special");
161 	d.font.Add(3, "Console");
162 	d.highlight.Add(Null, "Default");
163 	for(int i = 0; i < EditorSyntax::GetSyntaxCount(); i++) {
164 		String desc = EditorSyntax::GetSyntaxDescription(i);
165 		if(desc.GetCount())
166 			d.highlight.Add(EditorSyntax::GetSyntax(i), desc);
167 	}
168 	d.highlight.Add("none", "None");
169 	d.tabsize <<= f.tabsize > 0 ? f.tabsize : Null;
170 	d.tabsize <<= d.Breaker(111);
171 	d.tabsize.MinMax(1, 100);
172 	d.charset <<= (int)f.charset;
173 	d.font <<= f.font;
174 	d.font <<= d.Breaker(111);
175 	d.highlight <<= f.highlight;
176 	d.highlight <<= d.Breaker(111);
177 	d.line_endings.Add(CRLF, "CRLF");
178 	d.line_endings.Add(LF, "LF");
179 	d.line_endings <<= findarg(Nvl(editfile_line_endings, line_endings), LF, DETECT_LF) >= 0 ? LF : CRLF;
180 	d.line_endings.Enable(findarg(line_endings, DETECT_CRLF, DETECT_LF) >= 0);
181 	d.spellcheck_comments.Add(Null, "Default");
182 	DlSpellerLangs(d.spellcheck_comments);
183 	d.spellcheck_comments <<= f.spellcheck_comments;
184 	for(;;) {
185 		switch(d.Run()) {
186 		case IDCANCEL:
187 			FlushFile();
188 			FileCursor();
189 			return;
190 		case IDOK:
191 			int c = filelist.GetCursor();
192 			FlushFile();
193 			f.charset = (byte)(int)~d.charset;
194 			f.tabsize = Nvl((int)~d.tabsize);
195 			f.font = Nvl((int)~d.font);
196 			f.highlight = ~d.highlight;
197 			f.spellcheck_comments = ~d.spellcheck_comments;
198 			SavePackage();
199 			PackageCursor();
200 			filelist.SetCursor(c);
201 			editor.ClearUndo();
202 			if(editfile_line_endings != ~d.line_endings) {
203 				editfile_line_endings = ~d.line_endings;
204 				SaveFile(true);
205 			}
206 			MakeTitle();
207 			return;
208 		}
209 		if(!IsNull(editfile)) {
210 			int ts = Nvl((int)~d.tabsize);
211 			editor.TabSize(ts ? ts : editortabsize);
212 			SetupEditor(Nvl((int)~d.font), ~d.highlight, editfile);
213 		}
214 	}
215 }
216 
ChangeCharset()217 void Ide::ChangeCharset()
218 {
219 	if(!IsActiveFile()) return;
220 	Package::File& f = ActiveFile();
221 	SaveFile();
222 	WithCharsetLayout<TopWindow> dlg;
223 	CtrlLayoutOKCancel(dlg, "Save file with enconding");
224 	DlCharsetD(dlg.charset);
225 	dlg.charset <<= editor.GetCharset();
226 	dlg.ActiveFocus(dlg.charset);
227 	byte cs;
228 	for(;;) {
229 		if(dlg.Run() != IDOK)
230 			return;
231 		cs = (byte)(int)~dlg.charset;
232 		int q = editor.GetInvalidCharPos(cs);
233 		if(q >= 0) {
234 			if(PromptYesNo("File contains characters that cannot be saved using selected encoding.&"
235 			               "Save anyway? (If you choose Yes, they will be replaced with question marks.)"))
236 								break;
237 			editor.SetCursor(q);
238 			return;
239 		}
240 		else
241 			break;
242 	}
243 	f.charset = cs;
244 	SavePackage();
245 	editor.SetCharset(f.charset);
246 	SaveFile(true);
247 	FlushFile();
248 	FileCursor();
249 	editor.ClearUndo();
250 }
251 
PosSync()252 void Ide::PosSync()
253 {
254 	if(designer || editfile.IsEmpty())
255 		return;
256 	for(int i = 0; i < 2; i++)
257 		if(PathIsEqual(posfile[i], editfile))
258 			editor.SetPtr(posline[i], posimg[i], i);
259 		else
260 			editor.SetPtr(-1, Image(), i);
261 	editor.SyncTip();
262 }
263 
IsProjectFile(const String & f) const264 bool Ide::IsProjectFile(const String& f) const
265 {
266 	const Workspace& wspc = IdeWorkspace();
267 	for(int i = 0; i < wspc.GetCount(); i++) {
268 		const Package& pk = wspc.GetPackage(i);
269 		String n = wspc[i];
270 		for(int i = 0; i < pk.file.GetCount(); i++) {
271 			String fn = pk.file[i];
272 			String path = SourcePath(n, fn);
273 			if(f == path)
274 				return true;
275 		}
276 	}
277 	return false;
278 }
279 
ConvertTLine(const String & line,int flag)280 String ConvertTLine(const String& line, int flag)
281 {
282 	String r;
283 	const char *s = line;
284 
285 	while(*s) {
286 		if(*s == '\"') {
287 			CParser p(s);
288 			p.NoSkipSpaces();
289 			try {
290 				r.Cat(AsCString(p.ReadOneString(), INT_MAX, NULL, flag));
291 			}
292 			catch(CParser::Error) {
293 				return line;
294 			}
295 			s = p.GetPtr();
296 		}
297 		else
298 			r.Cat(*s++);
299 	}
300 	return r;
301 }
302 
SaveFile(bool always)303 void Ide::SaveFile(bool always)
304 {
305 	issaving++;
306 	SaveFile0(always);
307 	issaving--;
308 }
309 
SaveEditorFile(Stream & out)310 void Ide::SaveEditorFile(Stream& out)
311 {
312 	if(GetFileExt(editfile) == ".t") {
313 		for(int i = 0; i < editor.GetLineCount(); i++) {
314 			if(i) out.PutCrLf();
315 			out.Put(ConvertTLine(editor.GetUtf8Line(i), ASCSTRING_OCTALHI));
316 		}
317 	}
318 	else {
319 		int le = line_endings;
320 		if(le == DETECT_CRLF)
321 			le = Nvl(editfile_line_endings, CRLF);
322 		if(le == DETECT_LF)
323 			le = Nvl(editfile_line_endings, LF);
324 		editor.Save(out, editor.GetCharset(), le == LF ? TextCtrl::LE_LF : TextCtrl::LE_CRLF);
325 	}
326 }
327 
SaveFile0(bool always)328 void Ide::SaveFile0(bool always)
329 {
330 	if(designer) {
331 		String fn = designer->GetFileName();
332 		Time tm = FileGetTime(fn);
333 		designer->Save();
334 		if(tm != FileGetTime(fn))
335 			TouchFile(fn);
336 		if(IsProjectFile(fn) && ToUpper(GetFileExt(fn)) == ".LAY")
337 			CodeBaseScanFile(fn, auto_check);
338 		return;
339 	}
340 
341 	if(editfile.IsEmpty())
342 		return;
343 
344 	FileData& fd = Filedata(editfile);
345 	fd.lineinfo = editor.GetLineInfo();
346 	fd.lineinforem = editor.GetLineInfoRem();
347 	fd.editpos = editor.GetEditPos();
348 	fd.columnline = editor.GetColumnLine(fd.editpos.cursor);
349 	fd.filetime = edittime;
350 	if(editor.IsView())
351 		return;
352 	if(!editor.IsDirty() && !always)
353 		return;
354 	TouchFile(editfile);
355 	for(;;) {
356 		FileOut out(editfile);
357 		SaveEditorFile(out);
358 		if(!out.IsError())
359 			break;
360 		int art = Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
361 			"Unable to save current file.&"
362 			"Retry save, ignore it or save file to another location?",
363 			"Save as...", "Retry", "Ignore");
364 		if(art < 0)
365 			break;
366 		if(IsDeactivationSave())
367 			return;
368 		if(art && AnySourceFs().ExecuteSaveAs()) {
369 			editfile = AnySourceFs();
370 			MakeTitle();
371 			PosSync();
372 			break;
373 		}
374 	}
375 
376 	FindFile ff(editfile);
377 	fd.filetime = edittime = ff.GetLastWriteTime();
378 
379 	if(editor.IsDirty()) {
380 		text_updated.Kill();
381 		if(IsCppBaseFile())
382 			CodeBaseScanFile(editfile, auto_check);
383 	}
384 
385 	editor.ClearDirty();
386 
387 	if(ToLower(GetFileExt(editfile)) == ".usc")
388 		SyncUsc();
389 
390 	MakeTitle();
391 }
392 
FlushFile()393 void Ide::FlushFile() {
394 	editor.CloseAssist();
395 	SaveFile();
396 	CacheViewFile(editor, editfile);
397 	editor.assist_active = false;
398 	if(designer) {
399 		designer->SaveEditPos();
400 		designer->DesignerCtrl().SetFrame(NullFrame());
401 		if(dynamic_cast<TopicEditor *>(&designer->DesignerCtrl()))
402 			RefreshBrowser();
403 	}
404 	else
405 	if(!editfile.IsEmpty())
406 		Filedata(editfile).undodata = editor.PickUndoData();
407 	editfile.Clear();
408 	editfile_repo = NOT_REPO_DIR;
409 	editfile_isfolder = false;
410 	repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
411 	editor.Clear();
412 	editor.Disable();
413 	editor.SetEditable();
414 	editorsplit.Ctrl::Remove();
415 	designer.Clear();
416 	view_file.Close();
417 	SetBar();
418 }
419 
CharFilterMacro(int c)420 int CharFilterMacro(int c)
421 {
422 	return iscid(c) ? c : '_';
423 }
424 
FileRename(const String & nm)425 void Ide::FileRename(const String& nm)
426 {
427 	tabs.RenameFile(editfile, nm);
428 }
429 
FileRemove()430 bool Ide::FileRemove()
431 {
432 	int q = FindIndex(tablru, editfile);
433 	if(q >= 0)
434 		tablru.Remove(q);
435 	q = tabs.GetCursor();
436 	if(q >= 0) {
437 		tabs.CloseForce(q, false);
438 		if(filelist.GetCount())
439 			return true;
440 		if(tabs.GetCount())
441 			TabFile();
442 		else {
443 			tabs.Refresh();
444 			FlushFile();
445 		}
446 		return false;
447 	}
448 	return true;
449 }
450 
EditFile0(const String & path,byte charset,int spellcheck_comments,const String & headername)451 void Ide::EditFile0(const String& path, byte charset, int spellcheck_comments, const String& headername)
452 {
453 	text_updated.Kill();
454 
455 	AKEditor();
456 	editor.CheckEdited(false);
457 	editor.CloseAssist();
458 	if(path.IsEmpty()) return;
459 	FlushFile();
460 
461 	editfile = path;
462 	editor.SetCharset(charset);
463 	editor.SpellcheckComments(spellcheck_comments);
464 	AddLru();
465 
466 	editfile_isfolder = IsFolder(editfile) || IsHelpName(editfile);
467 	repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
468 
469 	bool candesigner = !(debugger && !editfile_isfolder && (PathIsEqual(path, posfile[0]) || PathIsEqual(path, posfile[0])))
470 	   && editastext.Find(path) < 0 && editashex.Find(path) < 0 && !IsNestReadOnly(editfile);
471 
472 	if(candesigner) {
473 		for(int i = 0; i < GetIdeModuleCount() && !designer; i++)
474 			designer = GetIdeModule(i).CreateDesigner(this, path, charset);
475 	}
476 
477 	if(!designer && editastext.Find(path) < 0 &&
478 	   (findarg(GetFileExt(path), ".log") < 0 &&
479 	    findarg(charset, CHARSET_UTF8_BOM, CHARSET_UTF16_LE, CHARSET_UTF16_BE,
480 	            CHARSET_UTF16_LE_BOM, CHARSET_UTF16_BE_BOM) < 0 &&
481 	    FileIsBinary(path) || editashex.Find(path) >= 0))
482 		designer.Create<FileHexView>().Open(path);
483 
484 	ManageDisplayVisibility();
485 
486 	if(designer) {
487 		editpane.Add(designer->DesignerCtrl().SizePos());
488 		designer->DesignerCtrl().SetFocus();
489 		designer->RestoreEditPos();
490 		if(filetabs)
491 			tabs.SetAddFile(editfile);
492 		MakeTitle();
493 		SetBar();
494 		editor.SyncNavigatorShow();
495 		return;
496 	}
497 
498 	tabs.SetAddFile(editfile);
499 	tabs.SetSplitColor(editfile2, Yellow);
500 	editor.Enable();
501 	editpane.Add(editorsplit);
502 	editor.HiliteScope(hilite_scope);
503 	editor.OverWriteMode(false);
504 	ActiveFocus(editor);
505 	FileData& fd = Filedata(editfile);
506 	FindFile ff(editfile);
507 	bool tfile = GetFileExt(editfile) == ".t";
508 	if(ff) {
509 		edittime = ff.GetLastWriteTime();
510 		if(edittime != fd.filetime || IsNull(fd.filetime))
511 			fd.undodata.Clear();
512 		view_file.Open(editfile);
513 		if(view_file) {
514 			if(tfile && editastext.Find(editfile) < 0) {
515 				String f;
516 				String ln;
517 				for(;;) {
518 					int c = view_file.Get();
519 					if(c < 0) {
520 						f.Cat(ConvertTLine(ln, 0));
521 						break;
522 					}
523 					if(c != '\r') {
524 						ln.Cat(c);
525 						if(c == '\n') {
526 							f.Cat(ConvertTLine(ln, 0));
527 							ln.Clear();
528 						}
529 					}
530 				}
531 				editor.Set(f);
532 				editor.SetCharset(CHARSET_UTF8);
533 			}
534 			else {
535 				String s = view_file.Get(3);
536 				if(s.GetCount() >= 2) {
537 					if((byte)s[0] == 0xff && (byte)s[1] == 0xfe)
538 						charset = CHARSET_UTF16_LE_BOM;
539 					if((byte)s[0] == 0xfe && (byte)s[1] == 0xff)
540 						charset = CHARSET_UTF16_BE_BOM;
541 				}
542 				if(s.GetCount() >= 3 && (byte)s[0] == 0xef && (byte)s[1] == 0xbb && (byte)s[2] == 0xbf)
543 					charset = CHARSET_UTF8_BOM;
544 				view_file.Seek(0);
545 				int le = Null;
546 			#ifdef CPU_64
547 				const int64 max_size = (int64)4096*1024*1024;
548 			#else
549 				const int64 max_size = 768*1024*1024;
550 			#endif
551 				const int view_limit = 256*1024*1024;
552 				if(view_file.GetSize() < view_limit || editastext.Find(editfile) >= 0 && view_file.GetSize() < max_size) {
553 					le = editor.Load(view_file, charset);
554 					view_file.Close();
555 				}
556 				else
557 					ViewFile(editor, view_file, editfile, charset);
558 
559 				editfile_line_endings = le == TextCtrl::LE_CRLF ? CRLF : le == TextCtrl::LE_LF ? LF : (int)Null;
560 			}
561 		}
562 		else
563 			Exclamation("Failed to read the file.");
564 		editor.SetEditPos(fd.editpos);
565 		if(!IsNull(fd.columnline) && fd.columnline.y >= 0 && fd.columnline.y < editor.GetLineCount())
566 			editor.SetCursor(editor.GetColumnLinePos(fd.columnline));
567 		editor.SetEditPosSbOnly(fd.editpos);
568 		if(!editor.IsView()) {
569 			editor.SetPickUndoData(pick(fd.undodata));
570 			editor.SetLineInfo(fd.lineinfo);
571 			editor.SetLineInfoRem(pick(fd.lineinforem));
572 		}
573 		if(ff.IsReadOnly() || IsNestReadOnly(editfile) || editor.IsTruncated() || editor.IsView()) {
574 			editor.SetReadOnly();
575 			editor.NoShowReadOnly();
576 		}
577 		fd.ClearP();
578 		PosSync();
579 		editor.ClearDirty();
580 	}
581 	else {
582 		RealizePath(editfile);
583 		if(GetFileExt(editfile) == ".h") {
584 			String n = '_' + Filter(String().Cat() << GetActivePackage() << '_' << GetFileTitle(editfile) << "_h_",
585 			                        CharFilterMacro);
586 			String s;
587 			s << "#ifndef " << n << "\r\n";
588 			s << "#define " << n << "\r\n";
589 			s << "\r\n";
590 			s << "#endif\r\n";
591 			editor <<= s;
592 			editor.SetCursor(editor.GetPos64(2));
593 		}
594 		if(IsCSourceFile(editfile) && !IsNull(headername)) {
595 			editor <<= "#include \"" + headername + "\"\r\n";
596 			editor.SetCursor(editor.GetPos64(1));
597 		}
598 		editor.SetCharset(tfile ? CHARSET_UTF8 : charset);
599 	}
600 	editor.SetFocus();
601 	MakeTitle();
602 	SetBar();
603 	editor.SyncNavigatorShow();
604 	editor.assist_active = IsProjectFile(editfile) && IsCppBaseFile();
605 	editor.CheckEdited(true);
606 	editor.Annotate(editfile);
607 	editor.SyncNavigator();
608 	editfile_repo = GetRepoKind(editfile);
609 	editfile_includes = IncludesMD5();
610 }
611 
IncludesMD5()612 String Ide::IncludesMD5()
613 { // keep track of includes for Assist++, so that we know when to save file and sync base
614 	int n = min(editor.GetLineCount(), 1000); // ignore big files
615 	Md5Stream md5;
616 	for(int i = 0; i < n; i++) {
617 		String l = editor.GetUtf8Line(i);
618 		try {
619 			CParser p(l);
620 			if(p.Char('#') && p.Id("include"))
621 				md5.Put(l);
622 		}
623 		catch(CParser::Error) {}
624 	}
625 	return md5.FinishString();
626 }
627 
ScanFile(bool check_includes)628 void Ide::ScanFile(bool check_includes)
629 {
630 	if(IsCppBaseFile()) {
631 		if(check_includes) {
632 			String imd5 = IncludesMD5();
633 			if(editfile_includes != imd5) {
634 				editfile_includes = imd5;
635 				SaveFile(true);
636 				return;
637 			}
638 		}
639 		String s = ~editor;
640 		StringStream ss(s);
641 		CodeBaseScanFile(ss, editfile);
642 	}
643 }
644 
EditFileAssistSync2()645 void Ide::EditFileAssistSync2()
646 {
647 	editor.Annotate(editfile);
648 	editor.SyncNavigator();
649 }
650 
EditFileAssistSync()651 void Ide::EditFileAssistSync()
652 {
653 	ScanFile(false);
654 	EditFileAssistSync2();
655 }
656 
TriggerAssistSync()657 void Ide::TriggerAssistSync()
658 {
659 	if(auto_rescan && editor.GetLength64() < 500000 && !file_scan) {
660 		text_updated.KillSet(1000, [=] {
661 			if(!file_scan && IsCppBaseFile()) {
662 				String s = ~editor;
663 				String fn = editfile;
664 				file_scan++;
665 				if(!CoWork::TrySchedule([=] {
666 					StringStream ss(s);
667 					CodeBaseScanFile(ss, editfile);
668 					file_scan--;
669 					file_scanned = true;
670 				}))
671 					file_scan--;
672 			}
673 		});
674 	}
675 }
676 
EditAsHex()677 void Ide::EditAsHex()
678 {
679 	String path = editfile;
680 	if(editashex.Find(path) >= 0)
681 		return;
682 	editastext.RemoveKey(editfile);
683 	editashex.FindPut(editfile);
684 	byte cs = editor.GetCharset();
685 	int sc = editor.GetSpellcheckComments();
686 	FlushFile();
687 	EditFile0(path, cs, sc);
688 }
689 
IsDesignerFile(const String & path)690 bool Ide::IsDesignerFile(const String& path)
691 {
692 	for(int i = 0; i < GetIdeModuleCount(); i++)
693 		if(GetIdeModule(i).AcceptsFile(path))
694 			return true;
695 	return false;
696 }
697 
DoEditAsText(const String & path)698 void Ide::DoEditAsText(const String& path)
699 {
700 	editastext.FindAdd(path);
701 	editashex.RemoveKey(editfile);
702 }
703 
EditAsText()704 void Ide::EditAsText()
705 {
706 	String path = editfile;
707 	if(editastext.Find(path) >= 0)
708 		return;
709 	if(!FileExists(path))
710 		return;
711 	DoEditAsText(path);
712 	byte cs = editor.GetCharset();
713 	int sc = editor.GetSpellcheckComments();
714 	FlushFile();
715 	EditFile0(path, cs, sc);
716 }
717 
EditUsingDesigner()718 void Ide::EditUsingDesigner()
719 {
720 	String path = editfile;
721 	if (editastext.Find(editfile) < 0 && editashex.Find(editfile) < 0)
722 		return;
723 	editashex.RemoveKey(editfile);
724 	editastext.RemoveKey(editfile);
725 	byte cs = editor.GetCharset();
726 	int sc = editor.GetSpellcheckComments();
727 	FlushFile();
728 	EditFile0(path, cs, sc);
729 }
730 
AddEditFile(const String & path)731 void Ide::AddEditFile(const String& path)
732 {
733 	actual.file.AddPick(Package::File(path));
734 	if(IsAux())
735 		SaveLoadPackageNS(false);
736 	else
737 		SaveLoadPackage(false);
738 	ShowFile(package.GetCount() - 1);
739 	filelist.SetCursor(filelist.GetCount() - 1);
740 }
741 
EditFile(const String & p)742 void Ide::EditFile(const String& p)
743 {
744 	if(p.IsEmpty()) {
745 		FlushFile();
746 		return;
747 	}
748 	if(p == HELPNAME) {
749 		if(designer && designer->GetFileName() == p)
750 			return;
751 		package.FindSetCursor(METAPACKAGE);
752 		filelist.FindSetCursor(HELPNAME);
753 		return;
754 	}
755 
756 	String path = NormalizePath(p);
757 	if(designer ? path == designer->GetFileName() : path == editfile)
758 		return;
759 
760 	FlushFile();
761 	if(path.IsEmpty())
762 		return;
763 
764 	for(int i = 0; i < package.GetCount(); i++) {
765 		String pkg = package[i].name;
766 		Package p;
767 		p.Load(PackagePathA(pkg));
768 		for(int j = 0; j < p.file.GetCount(); j++)
769 			if(PathIsEqual(SourcePath(pkg, p.file[j]), path)) {
770 				package.FindSetCursor(pkg);
771 				ShowFile(j);
772 				filelist.FindSetCursor(p.file[j]);
773 				return;
774 			}
775 		if(GetFileExt(path) == ".tpp" && PathIsEqual(SourcePath(pkg, GetFileName(path)), path)) {
776 			filelist.KillCursor();
777 			package.KillCursor();
778 			package.SetCursor(i);
779 			AddEditFile(GetFileName(path));
780 			return;
781 		}
782 	}
783 	filelist.KillCursor();
784 	package.KillCursor();
785 	package.SetCursor(package.GetCount() - 2);
786 	AddEditFile(path);
787 }
788 
IsCppBaseFile()789 bool Ide::IsCppBaseFile()
790 {
791 	return IsProjectFile(editfile) && (IsCSourceFile(editfile) || IsCHeaderFile(editfile) ||
792 	                                   ToUpper(GetFileExt(editfile)) == ".SCH");
793 }
794 
CheckFileUpdate()795 void Ide::CheckFileUpdate()
796 {
797 	if(editfile.IsEmpty() || !IsForeground() || designer) return;
798 	FindFile ff(editfile);
799 	if(!ff) return;
800 	FileTime tm = ff.GetLastWriteTime();
801 	if(tm == edittime) return;
802 	edittime = tm;
803 	if(editor.IsDirty() && !Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
804 		"Current file was changed outside the IDE, but was also edited inside it.&"
805 		"Would you like to reload the file or to keep changes made in the IDE ?",
806 		"Reload", "Keep")) return;
807 
808 	if(IsCppBaseFile())
809 		CodeBaseScanFile(editfile, auto_check);
810 	ReloadFile();
811 }
812 
813 typedef Index<dword> HashBase;
814 
GetLineIndex(String file,HashBase & hash,Vector<String> & lines)815 static void GetLineIndex(String file, HashBase& hash, Vector<String>& lines)
816 {
817 	const char *p = file;
818 	while(*p)
819 	{
820 		while(*p && *p != '\n' && (byte)*p <= ' ')
821 			p++;
822 		const char *b = p;
823 		while(*p && *p++ != '\n')
824 			;
825 		const char *e = p;
826 		while(e > b && (byte)e[-1] <= ' ')
827 			e--;
828 		String s(b, e);
829 		hash.Add(FoldHash(GetHashValue(s)));
830 		lines.Add(s);
831 	}
832 }
833 
LocateLine(String old_file,int old_line,String new_file)834 int LocateLine(String old_file, int old_line, String new_file)
835 {
836 	HashBase old_hash, new_hash;
837 	Vector<String> old_lines, new_lines;
838 	GetLineIndex(old_file, old_hash, old_lines);
839 	GetLineIndex(new_file, new_hash, new_lines);
840 	if(old_line <= 0)
841 		return 0;
842 	if(old_line >= old_lines.GetCount())
843 		return new_lines.GetCount();
844 	String line = old_lines[old_line];
845 	//int hash = old_hash[old_line]; Mirek: unused
846 	//int fore_count = old_lines.GetCount() - old_line - 1;
847 	int best_match = 0, best_value = 0;
848 	for(int r = 0; r < 10 && !best_value; r++)
849 	{
850 		int src = (r & 1 ? old_line + (r >> 1) + 1 : old_line - (r >> 1));
851 		if(src < 0 || src >= old_lines.GetCount())
852 			continue;
853 		dword hash = old_hash[src];
854 		for(int i = new_hash.Find(hash); i >= 0; i = new_hash.FindNext(i))
855 			if(new_lines[i] == old_lines[src])
856 			{
857 				int max_back = min(i, src);
858 				int max_fore = min(new_lines.GetCount() - i, old_lines.GetCount() - src) - 1;
859 				if(max_back + max_fore <= best_value)
860 					continue;
861 				int back = 1;
862 				while(back <= max_back && new_hash[i - back] == old_hash[src - back]
863 					&& new_lines[i - back] == old_lines[src - back])
864 					back++;
865 				int fore = 1;
866 				while(fore < max_fore && new_hash[i + fore] == old_hash[src + fore]
867 					&& new_lines[i + fore] == old_lines[src + fore])
868 					fore++;
869 				if(back + fore > best_value)
870 				{
871 					best_value = back + fore;
872 					best_match = minmax(i, 0, new_lines.GetCount());
873 				}
874 			}
875 	}
876 	return best_match;
877 }
878 
ReloadFile()879 void Ide::ReloadFile()
880 {
881 	if(editfile.IsEmpty())
882 		return;
883 	String fn = editfile;
884 	String data = ~editor;
885 	int ln = editor.GetCursorLine();
886 	editfile.Clear();
887 	int sc = filelist.GetSbPos();
888 	EditFile0(fn, editor.GetCharset(), editor.GetSpellcheckComments());
889 	filelist.SetSbPos(sc);
890 	int l = LocateLine(data, ln, ~editor);
891 	editor.SetCursor(editor.GetPos64(l));
892 }
893 
EditAnyFile()894 void Ide::EditAnyFile() {
895 	FileSel& fs = AnySourceFs();
896 #if 0
897 	fs.Multi(false);
898 	if(!fs.ExecuteOpen()) return;
899 	EditFile(fs);
900 	FileSelected();
901 #endif
902 	if(fs.ExecuteOpen())
903 		for(int i = 0; i < fs.GetCount(); i++) {
904 			EditFile(fs[i]);
905 			FileSelected();
906 		}
907 }
908 
DragAndDrop(Point,PasteClip & d)909 void Ide::DragAndDrop(Point, PasteClip& d)
910 {
911 	if(AcceptFiles(d)) {
912 		Vector<String> f = GetFiles(d);
913 		for(int i = 0; i < f.GetCount(); i++)
914 			if(FileExists(f[i])) {
915 				EditFile(f[i]);
916 				FileSelected();
917 				editor.SetFocus();
918 			}
919 	}
920 }
921 
AddLru()922 void Ide::AddLru()
923 {
924 	if(editfile.IsEmpty() || tabi) return;
925 	LruAdd(tablru, editfile, 200);
926 }
927 
sExFiles(const char * fn,const char ** ext,int cnt)928 static String sExFiles(const char *fn, const char **ext, int cnt)
929 {
930 	for(int i = 0; i < cnt; i++) {
931 		String f = ForceExt(fn, ext[i]);
932 		if(FileExists(f))
933 			return f;
934 	}
935 	return Null;
936 }
937 
GetOpposite()938 String Ide::GetOpposite()
939 {
940 	static const char *cpp[] = { ".c", ".cpp", ".cc", ".cxx" };
941 	static const char *hdr[] = { ".h", ".hpp", ".hh", ".hxx" };
942 	if(IsNull(editfile) || designer)
943 		return Null;
944 	String ext = GetFileExt(editfile);
945 	for(int i = 0; i < __countof(cpp); i++)
946 		if(ext == cpp[i])
947 			return sExFiles(editfile, hdr, __countof(hdr));
948 	for(int i = 0; i < __countof(hdr); i++)
949 		if(ext == hdr[i])
950 			return sExFiles(editfile, cpp, __countof(cpp));
951 	return Null;
952 }
953 
GoOpposite()954 void Ide::GoOpposite()
955 {
956 	String fn = GetOpposite();
957 	if(!IsNull(fn))
958 		EditFile(fn);
959 }
960 
PassEditor()961 void Ide::PassEditor()
962 {
963 	editorsplit.NoZoom();
964 	SyncEditorSplit();
965 	SetupEditor();
966 	editfile2 = editfile;
967 	editor2.SetFont(editor.GetFont());
968 	editor2.Highlight(editor.GetHighlight());
969 	editor2.LoadHlStyles(editor.StoreHlStyles());
970 	editor2.NoShowReadOnly();
971 	byte charset = editor.GetCharset();
972 	editor2.CheckEdited(false);
973 	view_file2.Close();
974 	if(editor.IsView()) {
975 		view_file2.Open(editfile2);
976 		ViewFile(editor2, view_file2, editfile2, charset);
977 	}
978 	else
979 		editor2.Set(editor.Get(charset), charset);
980 	editor2.SetEditPosSb(editor.GetEditPos());
981 	editor2.CheckEdited();
982 	editor.SetFocus();
983 	editor.ScrollIntoCursor();
984 	editor2.Annotate(editfile2);
985 	editor2.SpellcheckComments(editor.GetSpellcheckComments());
986 }
987 
ClearEditedFile()988 void Ide::ClearEditedFile()
989 {
990 	editor.ClearEdited();
991 }
992 
ClearEditedAll()993 void Ide::ClearEditedAll()
994 {
995 	ClearEditedFile();
996 	for(int i = 0; i < filedata.GetCount(); i++) {
997 		LineInfo li = editor.GetLineInfo();
998 		LineInfoRem lir = editor.GetLineInfoRem();
999 		FileData& fd = Filedata(filedata.GetKey(i));
1000 		editor.SetLineInfo(fd.lineinfo);
1001 		editor.SetLineInfoRem(pick(fd.lineinforem));
1002 		ClearEditedFile();
1003 		fd.lineinfo = editor.GetLineInfo();
1004 		fd.lineinforem = editor.GetLineInfoRem();
1005 		editor.SetLineInfo(li);
1006 	}
1007 }
1008 
SplitEditor(bool horz)1009 void Ide::SplitEditor(bool horz)
1010 {
1011 	if(editorsplit.GetZoom() < 0)
1012 		CloseSplit();
1013 
1014 	if(horz)
1015 		editorsplit.Horz(editor2, editor);
1016 	else
1017 		editorsplit.Vert(editor2, editor);
1018 
1019 	tabs.SetSplitColor(editfile, Yellow);
1020 	PassEditor();
1021 }
1022 
SwapEditors()1023 void Ide::SwapEditors()
1024 {
1025 	String f = editfile2;
1026 	CodeEditor::EditPos p = editor2.GetEditPos();
1027 	if(editorsplit.GetFirstChild() == &editor)
1028 		if(editorsplit.IsVert())
1029 			editorsplit.Vert(editor2, editor);
1030 		else
1031 			editorsplit.Horz(editor2, editor);
1032 	else
1033 		if(editorsplit.IsVert())
1034 			editorsplit.Vert(editor, editor2);
1035 		else
1036 			editorsplit.Horz(editor, editor2);
1037 	PassEditor();
1038 	EditFile(f);
1039 	editor.SetEditPos(p);
1040 }
1041 
CloseSplit()1042 void Ide::CloseSplit()
1043 {
1044 	editorsplit.Vert(editor, editor2);
1045 	editorsplit.Zoom(0);
1046 	view_file2.Close();
1047 	editfile2.Clear();
1048 	tabs.ClearSplitColor();
1049 	SyncEditorSplit();
1050 	editor.SetFocus();
1051 	SetupEditor();
1052 }
1053 
KeySplit(bool horz)1054 void Ide::KeySplit(bool horz)
1055 {
1056 	if(editorsplit.GetZoom() >= 0)
1057 		SplitEditor(horz);
1058 	else
1059 		CloseSplit();
1060 }
1061 
SyncEditorSplit()1062 void Ide::SyncEditorSplit()
1063 {
1064 	editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
1065 	editor.topsbbutton <<= THISBACK1(SplitEditor, false);
1066 	editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
1067 	editor.topsbbutton1 <<= THISBACK1(SplitEditor, true);
1068 	editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
1069 	editor2.topsbbutton <<= THISBACK1(SplitEditor, false);
1070 	editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
1071 	editor2.topsbbutton1 <<= THISBACK1(SplitEditor, true);
1072 	if(editorsplit.GetZoom() >= 0)
1073 		return;
1074 	if(editorsplit.IsVert()) {
1075 		editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
1076 		editor.topsbbutton <<= THISBACK(CloseSplit);
1077 		editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
1078 		editor2.topsbbutton <<= THISBACK(CloseSplit);
1079 	}
1080 	else {
1081 		editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
1082 		editor.topsbbutton1 <<= THISBACK(CloseSplit);
1083 		editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
1084 		editor2.topsbbutton1 <<= THISBACK(CloseSplit);
1085 	}
1086 }
1087 
HotKey(dword key)1088 bool Ide::HotKey(dword key)
1089 {
1090 	if(designer && designer->DesignerCtrl().HotKey(key))
1091 		return true;
1092 	if(designer && dynamic_cast<FileHexView *>(~designer) && Match(IdeKeys::AK_EDITASHEX, key)) {
1093 		EditUsingDesigner();
1094 		return true;
1095 	}
1096 	return TopWindow::HotKey(key);
1097 }
1098 
IdeGetFileName()1099 String Ide::IdeGetFileName()
1100 {
1101 	return editfile;
1102 }
1103 
IdeGetNestFolder()1104 String Ide::IdeGetNestFolder()
1105 {
1106 	Vector<String> w = GetUppDirs();
1107 	for(int i = 0; i < w.GetCount(); i++)
1108 		if(editfile.StartsWith(w[i]))
1109 			return w[i];
1110 	return Null;
1111 }
1112