mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
1141 lines
28 KiB
C++
1141 lines
28 KiB
C++
#include "ide.h"
|
|
|
|
String ViewCache()
|
|
{
|
|
return ConfigFile("view_maps");
|
|
}
|
|
|
|
String ViewFileHash(const String& path)
|
|
{
|
|
RTIMING("ViewFileHash");
|
|
Sha1Stream s;
|
|
FindFile ff(path);
|
|
if(ff) {
|
|
Sha1Stream sha;
|
|
sha << path << ';' << Time(ff.GetLastWriteTime()) << ';' << ff.GetLength();
|
|
return AppendFileName(ViewCache(), sha.FinishString());
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
void ViewFile(LineEdit& edit, Stream& view_file, const String& path, byte charset)
|
|
{
|
|
edit.View(view_file, charset);
|
|
String f = ViewFileHash(path);
|
|
if(f.GetCount())
|
|
LoadFromFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
|
|
}
|
|
|
|
void CacheViewFile(LineEdit& edit, const String& path)
|
|
{
|
|
if(edit.IsView()) {
|
|
ReduceCacheFolder(ViewCache(), 20000000);
|
|
String f = ViewFileHash(path);
|
|
RealizePath(f);
|
|
if(f.GetCount())
|
|
StoreToFile([&](Stream& s) { edit.SerializeViewMap(s); }, f);
|
|
}
|
|
}
|
|
|
|
void Ide::SetupEditor(int f, String hl, String path)
|
|
{
|
|
if(IsNull(hl)) {
|
|
hl = EditorSyntax::GetSyntaxForFilename(path);
|
|
if(IsNull(hl))
|
|
hl = EditorSyntax::GetSyntaxForFilename(ToLower(path));
|
|
if(IsNull(hl) && IsNull(GetFileExt(path))) {
|
|
FileIn in(path);
|
|
String h = in.Get(4096);
|
|
try {
|
|
CParser p(h);
|
|
while(!p.IsEof()) {
|
|
if(p.Char('#')) {
|
|
if(p.Id("define") || p.Id("ipathdef") || p.Id("ifdef") || p.Id("include") || p.Id("pragma")) {
|
|
hl = "cpp";
|
|
break;
|
|
}
|
|
}
|
|
p.SkipTerm();
|
|
}
|
|
}
|
|
catch(CParser::Error) {}
|
|
}
|
|
}
|
|
switch(f) {
|
|
case 1: editor.SetFont(font1); break;
|
|
case 2: editor.SetFont(font2); break;
|
|
case 3: editor.SetFont(consolefont); break;
|
|
default: editor.SetFont(editorsplit.GetZoom() < 0 && editorsplit.IsHorz() ? veditorfont :
|
|
hl == "t" ? tfont : editorfont); break;
|
|
}
|
|
editor.Highlight(hl);
|
|
|
|
editor.WarnWhiteSpace(warnwhitespace &&
|
|
findarg(hl, "cpp", "java", "js", "cs", "json", "css", "lay", "sch", "t", "usc") >= 0);
|
|
|
|
editor.WordwrapComments(wordwrap_comments);
|
|
editor.WordWrap(wordwrap);
|
|
}
|
|
|
|
void Ide::SetupEditor()
|
|
{
|
|
if(!IsActiveFile())
|
|
return;
|
|
Package::File& f = ActiveFile();
|
|
String p = GetActiveFileName();
|
|
if(p != HELPNAME)
|
|
p = GetActiveFilePath();
|
|
SetupEditor(f.font, f.highlight, p);
|
|
editor.SyncNavigatorPlacement();
|
|
}
|
|
|
|
void Ide::FileCursor()
|
|
{
|
|
WorkspaceWork::FileCursor();
|
|
if(IsActiveFile() && !filelist[filelist.GetCursor()].isdir) {
|
|
Package::File& f = ActiveFile();
|
|
editor.SetEditable(!f.readonly);
|
|
editor.TabSize(f.tabsize > 0 ? f.tabsize : actual.tabsize > 0 ? actual.tabsize : editortabsize);
|
|
SetupEditor();
|
|
String headername;
|
|
if(insert_include)
|
|
for(int i = insert_include == 1 ? 0 : filelist.GetCursor();
|
|
i >= 0 && i < filelist.GetCount();
|
|
i += insert_include == 1 ? 1 : -1) {
|
|
String nm = FileName(i);
|
|
if(!IsSeparator(i) && GetFileExt(nm) == ".h") {
|
|
headername = nm;
|
|
break;
|
|
}
|
|
}
|
|
String p = GetActiveFileName();
|
|
if(p != HELPNAME)
|
|
p = GetActiveFilePath();
|
|
EditFile0(p, f.charset ? f.charset : actual.charset ? actual.charset : default_charset,
|
|
Nvl(f.spellcheck_comments, actual.spellcheck_comments, spellcheck_comments),
|
|
headername);
|
|
}
|
|
}
|
|
|
|
bool Ide::HasFileData(const String& file)
|
|
{
|
|
return filedata.Find(NormalizePath(file)) >= 0;
|
|
}
|
|
|
|
Ide::FileData& Ide::Filedata(const String& file) {
|
|
return filedata.GetAdd(NormalizePath(file));
|
|
}
|
|
|
|
void DlCharsetD(DropList& d)
|
|
{
|
|
d.Add(0, "Default");
|
|
DlCharset(d);
|
|
}
|
|
|
|
void Ide::ChangeFileCharset(const String& name, Package& p, byte charset)
|
|
{
|
|
if(IsNull(name))
|
|
return;
|
|
bool sv = false;
|
|
for(int i = 0; i < p.file.GetCount(); i++)
|
|
if(SourcePath(name, p.file[i]) == editfile && p.file[i].charset != charset) {
|
|
p.file[i].charset = charset;
|
|
sv = true;
|
|
}
|
|
if(sv) {
|
|
String pp = PackagePathA(name);
|
|
p.Save(pp);
|
|
}
|
|
}
|
|
|
|
void Ide::FileProperties()
|
|
{
|
|
if(!IsActiveFile()) return;
|
|
WithFileFormatLayout<TopWindow> d;
|
|
CtrlLayoutOKCancel(d, "File properties");
|
|
Package::File& f = ActiveFile();
|
|
DlCharsetD(d.charset);
|
|
d.font.Add(0, "Normal");
|
|
d.font.Add(1, "Small");
|
|
d.font.Add(2, "Special");
|
|
d.font.Add(3, "Console");
|
|
d.highlight.Add(Null, "Default");
|
|
for(int i = 0; i < EditorSyntax::GetSyntaxCount(); i++) {
|
|
String desc = EditorSyntax::GetSyntaxDescription(i);
|
|
if(desc.GetCount())
|
|
d.highlight.Add(EditorSyntax::GetSyntax(i), desc);
|
|
}
|
|
d.highlight.Add("none", "None");
|
|
d.tabsize <<= f.tabsize > 0 ? f.tabsize : Null;
|
|
d.tabsize <<= d.Breaker(111);
|
|
d.tabsize.MinMax(1, 100);
|
|
d.charset <<= (int)f.charset;
|
|
d.font <<= f.font;
|
|
d.font <<= d.Breaker(111);
|
|
d.highlight <<= f.highlight;
|
|
d.highlight <<= d.Breaker(111);
|
|
d.line_endings.Add(CRLF, "CRLF");
|
|
d.line_endings.Add(LF, "LF");
|
|
d.line_endings <<= findarg(Nvl(editfile_line_endings, line_endings), LF, DETECT_LF) >= 0 ? LF : CRLF;
|
|
d.line_endings.Enable(findarg(line_endings, DETECT_CRLF, DETECT_LF) >= 0);
|
|
d.spellcheck_comments.Add(Null, "Default");
|
|
DlSpellerLangs(d.spellcheck_comments);
|
|
d.spellcheck_comments <<= f.spellcheck_comments;
|
|
for(;;) {
|
|
switch(d.Run()) {
|
|
case IDCANCEL:
|
|
FlushFile();
|
|
FileCursor();
|
|
return;
|
|
case IDOK:
|
|
int c = filelist.GetCursor();
|
|
FlushFile();
|
|
f.charset = (byte)(int)~d.charset;
|
|
f.tabsize = Nvl((int)~d.tabsize);
|
|
f.font = Nvl((int)~d.font);
|
|
f.highlight = ~d.highlight;
|
|
f.spellcheck_comments = ~d.spellcheck_comments;
|
|
SavePackage();
|
|
PackageCursor();
|
|
filelist.SetCursor(c);
|
|
editor.ClearUndo();
|
|
if(editfile_line_endings != ~d.line_endings) {
|
|
editfile_line_endings = ~d.line_endings;
|
|
SaveFile(true);
|
|
}
|
|
MakeTitle();
|
|
return;
|
|
}
|
|
if(!IsNull(editfile)) {
|
|
int ts = Nvl((int)~d.tabsize);
|
|
editor.TabSize(ts ? ts : editortabsize);
|
|
SetupEditor(Nvl((int)~d.font), ~d.highlight, editfile);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ide::ChangeCharset()
|
|
{
|
|
if(!IsActiveFile()) return;
|
|
Package::File& f = ActiveFile();
|
|
SaveFile();
|
|
WithCharsetLayout<TopWindow> dlg;
|
|
CtrlLayoutOKCancel(dlg, "Save file with enconding");
|
|
DlCharsetD(dlg.charset);
|
|
dlg.charset <<= editor.GetCharset();
|
|
dlg.ActiveFocus(dlg.charset);
|
|
byte cs;
|
|
for(;;) {
|
|
if(dlg.Run() != IDOK)
|
|
return;
|
|
cs = (byte)(int)~dlg.charset;
|
|
int q = editor.GetInvalidCharPos(cs);
|
|
if(q >= 0) {
|
|
if(PromptYesNo("File contains characters that cannot be saved using selected encoding.&"
|
|
"Save anyway? (If you choose Yes, they will be replaced with question marks.)"))
|
|
break;
|
|
editor.SetCursor(q);
|
|
return;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
f.charset = cs;
|
|
SavePackage();
|
|
editor.SetCharset(f.charset);
|
|
SaveFile(true);
|
|
FlushFile();
|
|
FileCursor();
|
|
editor.ClearUndo();
|
|
}
|
|
|
|
void Ide::PosSync()
|
|
{
|
|
if(designer || editfile.IsEmpty())
|
|
return;
|
|
for(int i = 0; i < 2; i++)
|
|
if(PathIsEqual(posfile[i], editfile))
|
|
editor.SetPtr(posline[i], posimg[i], i);
|
|
else
|
|
editor.SetPtr(-1, Image(), i);
|
|
editor.SyncTip();
|
|
}
|
|
|
|
bool Ide::IsProjectFile(const String& f) const
|
|
{
|
|
const Workspace& wspc = IdeWorkspace();
|
|
for(int i = 0; i < wspc.GetCount(); i++) {
|
|
const Package& pk = wspc.GetPackage(i);
|
|
String n = wspc[i];
|
|
for(int i = 0; i < pk.file.GetCount(); i++) {
|
|
String fn = pk.file[i];
|
|
String path = SourcePath(n, fn);
|
|
if(f == path)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
String ConvertTLine(const String& line, int flag)
|
|
{
|
|
String r;
|
|
const char *s = line;
|
|
|
|
while(*s) {
|
|
if(*s == '\"') {
|
|
CParser p(s);
|
|
p.NoSkipSpaces();
|
|
try {
|
|
r.Cat(AsCString(p.ReadOneString(), INT_MAX, NULL, flag));
|
|
}
|
|
catch(CParser::Error) {
|
|
return line;
|
|
}
|
|
s = p.GetPtr();
|
|
}
|
|
else
|
|
r.Cat(*s++);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void Ide::SaveFile(bool always)
|
|
{
|
|
issaving++;
|
|
SaveFile0(always);
|
|
issaving--;
|
|
}
|
|
|
|
int64 Ide::EditorHash()
|
|
{
|
|
xxHash64Stream ss;
|
|
for(int i = 0; i < editor.GetLineCount(); i++) {
|
|
ss.Put(editor.GetUtf8Line(i));
|
|
ss.Put("\n");
|
|
}
|
|
return ss.Finish();
|
|
}
|
|
|
|
void Ide::SaveEditorFile(Stream& out)
|
|
{
|
|
if(GetFileExt(editfile) == ".t") {
|
|
for(int i = 0; i < editor.GetLineCount(); i++) {
|
|
if(i) out.PutCrLf();
|
|
out.Put(ConvertTLine(editor.GetUtf8Line(i), ASCSTRING_OCTALHI));
|
|
}
|
|
}
|
|
else {
|
|
int le = line_endings;
|
|
if(le == DETECT_CRLF)
|
|
le = Nvl(editfile_line_endings, CRLF);
|
|
if(le == DETECT_LF)
|
|
le = Nvl(editfile_line_endings, LF);
|
|
editor.Save(out, editor.GetCharset(), le == LF ? TextCtrl::LE_LF : TextCtrl::LE_CRLF);
|
|
}
|
|
}
|
|
|
|
void Ide::SaveFile0(bool always)
|
|
{
|
|
if(designer) {
|
|
String fn = designer->GetFileName();
|
|
Time tm = FileGetTime(fn);
|
|
designer->Save();
|
|
if(tm != FileGetTime(fn))
|
|
TouchFile(fn);
|
|
if(IsProjectFile(fn) && ToUpper(GetFileExt(fn)) == ".LAY")
|
|
CodeBaseScanFile(fn, auto_check);
|
|
return;
|
|
}
|
|
|
|
if(editfile.IsEmpty())
|
|
return;
|
|
|
|
FileData& fd = Filedata(editfile);
|
|
fd.lineinfo = editor.GetLineInfo();
|
|
fd.lineinforem = editor.GetLineInfoRem();
|
|
fd.editpos = editor.GetEditPos();
|
|
fd.columnline = editor.GetColumnLine(fd.editpos.cursor);
|
|
fd.filetime = edittime;
|
|
if(editor.IsView())
|
|
return;
|
|
if(!editor.IsDirty() && !always)
|
|
return;
|
|
TouchFile(editfile);
|
|
if(!FileExists(editfile))
|
|
InvalidateIncludes();
|
|
for(;;) {
|
|
FileOut out(editfile);
|
|
SaveEditorFile(out);
|
|
if(!out.IsError())
|
|
break;
|
|
int art = Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
|
|
"Unable to save current file.&"
|
|
"Retry save, ignore it or save file to another location?",
|
|
"Save as...", "Retry", "Ignore");
|
|
if(art < 0)
|
|
break;
|
|
if(IsDeactivationSave())
|
|
return;
|
|
if(art && AnySourceFs().ExecuteSaveAs()) {
|
|
editfile = AnySourceFs();
|
|
MakeTitle();
|
|
PosSync();
|
|
break;
|
|
}
|
|
}
|
|
|
|
FindFile ff(editfile);
|
|
fd.filetime = edittime = ff.GetLastWriteTime();
|
|
|
|
if(editor.IsDirty()) {
|
|
text_updated.Kill();
|
|
if(IsCppBaseFile())
|
|
CodeBaseScanFile(editfile, auto_check);
|
|
}
|
|
|
|
editor.ClearDirty();
|
|
|
|
if(ToLower(GetFileExt(editfile)) == ".usc")
|
|
SyncUsc();
|
|
|
|
MakeTitle();
|
|
}
|
|
|
|
void Ide::FlushFile() {
|
|
editor.CloseAssist();
|
|
SaveFile();
|
|
CacheViewFile(editor, editfile);
|
|
editor.assist_active = false;
|
|
if(designer) {
|
|
designer->SaveEditPos();
|
|
designer->DesignerCtrl().SetFrame(NullFrame());
|
|
if(dynamic_cast<TopicEditor *>(&designer->DesignerCtrl()))
|
|
RefreshBrowser();
|
|
}
|
|
else
|
|
if(!editfile.IsEmpty() && !editor.IsView()) {
|
|
FileData& fd = Filedata(editfile);
|
|
fd.undodata = editor.PickUndoData();
|
|
fd.filehash = EditorHash();
|
|
}
|
|
editfile.Clear();
|
|
editfile_repo = NOT_REPO_DIR;
|
|
editfile_isfolder = false;
|
|
repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
|
|
editor.Clear();
|
|
editor.Disable();
|
|
editor.SetEditable();
|
|
editorsplit.Ctrl::Remove();
|
|
designer.Clear();
|
|
view_file.Close();
|
|
SetBar();
|
|
}
|
|
|
|
int CharFilterMacro(int c)
|
|
{
|
|
return iscid(c) ? c : '_';
|
|
}
|
|
|
|
void Ide::FileRename(const String& nm)
|
|
{
|
|
tabs.RenameFile(editfile, nm);
|
|
InvalidateIncludes();
|
|
}
|
|
|
|
bool Ide::FileRemove()
|
|
{
|
|
InvalidateIncludes();
|
|
int q = FindIndex(tablru, editfile);
|
|
if(q >= 0)
|
|
tablru.Remove(q);
|
|
q = tabs.GetCursor();
|
|
if(q >= 0) {
|
|
tabs.CloseForce(q, false);
|
|
if(filelist.GetCount())
|
|
return true;
|
|
if(tabs.GetCount())
|
|
TabFile();
|
|
else {
|
|
tabs.Refresh();
|
|
FlushFile();
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Ide::EditFile0(const String& path, byte charset, int spellcheck_comments, const String& headername)
|
|
{
|
|
text_updated.Kill();
|
|
|
|
AKEditor();
|
|
editor.CheckEdited(false);
|
|
editor.CloseAssist();
|
|
if(path.IsEmpty()) return;
|
|
FlushFile();
|
|
|
|
editfile = path;
|
|
editor.SetCharset(charset);
|
|
editor.SpellcheckComments(spellcheck_comments);
|
|
AddLru();
|
|
|
|
editfile_isfolder = IsFolder(editfile) || IsHelpName(editfile);
|
|
repo_dirs = RepoDirs(true).GetCount(); // Perhaps not the best place, but should be ok
|
|
|
|
bool candesigner = !(debugger && !editfile_isfolder && (PathIsEqual(path, posfile[0]) || PathIsEqual(path, posfile[0])))
|
|
&& editastext.Find(path) < 0 && editashex.Find(path) < 0 && !IsNestReadOnly(editfile);
|
|
|
|
if(candesigner) {
|
|
for(int i = 0; i < GetIdeModuleCount() && !designer; i++)
|
|
designer = GetIdeModule(i).CreateDesigner(this, path, charset);
|
|
}
|
|
|
|
if(!designer && editastext.Find(path) < 0 &&
|
|
(findarg(GetFileExt(path), ".log") < 0 &&
|
|
findarg(charset, CHARSET_UTF8_BOM, CHARSET_UTF16_LE, CHARSET_UTF16_BE,
|
|
CHARSET_UTF16_LE_BOM, CHARSET_UTF16_BE_BOM) < 0 &&
|
|
FileIsBinary(path) || editashex.Find(path) >= 0))
|
|
designer.Create<FileHexView>().Open(path);
|
|
|
|
ManageDisplayVisibility();
|
|
|
|
if(designer) {
|
|
editpane.Add(designer->DesignerCtrl().SizePos());
|
|
designer->DesignerCtrl().SetFocus();
|
|
designer->RestoreEditPos();
|
|
if(filetabs)
|
|
tabs.SetAddFile(editfile);
|
|
MakeTitle();
|
|
SetBar();
|
|
editor.SyncNavigatorShow();
|
|
return;
|
|
}
|
|
|
|
tabs.SetAddFile(editfile);
|
|
tabs.SetSplitColor(editfile2, Yellow);
|
|
editor.Enable();
|
|
editpane.Add(editorsplit);
|
|
editor.HiliteScope(hilite_scope);
|
|
editor.OverWriteMode(false);
|
|
ActiveFocus(editor);
|
|
FileData& fd = Filedata(editfile);
|
|
FindFile ff(editfile);
|
|
bool tfile = GetFileExt(editfile) == ".t";
|
|
if(ff) {
|
|
edittime = ff.GetLastWriteTime();
|
|
if(edittime != fd.filetime || IsNull(fd.filetime))
|
|
fd.undodata.Clear();
|
|
view_file.Open(editfile);
|
|
if(view_file) {
|
|
if(tfile && editastext.Find(editfile) < 0) {
|
|
String f;
|
|
String ln;
|
|
for(;;) {
|
|
int c = view_file.Get();
|
|
if(c < 0) {
|
|
f.Cat(ConvertTLine(ln, 0));
|
|
break;
|
|
}
|
|
if(c != '\r') {
|
|
ln.Cat(c);
|
|
if(c == '\n') {
|
|
f.Cat(ConvertTLine(ln, 0));
|
|
ln.Clear();
|
|
}
|
|
}
|
|
}
|
|
editor.Set(f);
|
|
editor.SetCharset(CHARSET_UTF8);
|
|
}
|
|
else {
|
|
String s = view_file.Get(3);
|
|
if(s.GetCount() >= 2) {
|
|
if((byte)s[0] == 0xff && (byte)s[1] == 0xfe)
|
|
charset = CHARSET_UTF16_LE_BOM;
|
|
if((byte)s[0] == 0xfe && (byte)s[1] == 0xff)
|
|
charset = CHARSET_UTF16_BE_BOM;
|
|
}
|
|
if(s.GetCount() >= 3 && (byte)s[0] == 0xef && (byte)s[1] == 0xbb && (byte)s[2] == 0xbf)
|
|
charset = CHARSET_UTF8_BOM;
|
|
view_file.Seek(0);
|
|
int le = Null;
|
|
#ifdef CPU_64
|
|
const int64 max_size = (int64)4096*1024*1024;
|
|
#else
|
|
const int64 max_size = 768*1024*1024;
|
|
#endif
|
|
const int view_limit = 256*1024*1024;
|
|
if(view_file.GetSize() < view_limit || editastext.Find(editfile) >= 0 && view_file.GetSize() < max_size) {
|
|
le = editor.Load(view_file, charset);
|
|
view_file.Close();
|
|
}
|
|
else
|
|
ViewFile(editor, view_file, editfile, charset);
|
|
|
|
editfile_line_endings = le == TextCtrl::LE_CRLF ? CRLF : le == TextCtrl::LE_LF ? LF : (int)Null;
|
|
}
|
|
}
|
|
else
|
|
Exclamation("Failed to read the file.");
|
|
editor.SetEditPos(fd.editpos);
|
|
if(!IsNull(fd.columnline) && fd.columnline.y >= 0 && fd.columnline.y < editor.GetLineCount())
|
|
editor.SetCursor(editor.GetColumnLinePos(fd.columnline));
|
|
editor.SetEditPosSbOnly(fd.editpos);
|
|
if(!editor.IsView()) {
|
|
if(EditorHash() != fd.filehash)
|
|
fd.undodata.Clear();
|
|
editor.SetPickUndoData(pick(fd.undodata));
|
|
editor.SetLineInfo(fd.lineinfo);
|
|
editor.SetLineInfoRem(pick(fd.lineinforem));
|
|
}
|
|
if(ff.IsReadOnly() || IsNestReadOnly(editfile) || editor.IsTruncated() || editor.IsView()) {
|
|
editor.SetReadOnly();
|
|
editor.NoShowReadOnly();
|
|
}
|
|
fd.ClearP();
|
|
PosSync();
|
|
editor.ClearDirty();
|
|
}
|
|
else {
|
|
RealizePath(editfile);
|
|
if(GetFileExt(editfile) == ".h") {
|
|
String n = '_' + Filter(String().Cat() << GetActivePackage() << '_' << GetFileTitle(editfile) << "_h_",
|
|
CharFilterMacro);
|
|
String s;
|
|
s << "#ifndef " << n << "\r\n";
|
|
s << "#define " << n << "\r\n";
|
|
s << "\r\n";
|
|
s << "#endif\r\n";
|
|
editor <<= s;
|
|
editor.SetCursor(editor.GetPos64(2));
|
|
}
|
|
if(IsCSourceFile(editfile) && !IsNull(headername)) {
|
|
editor <<= "#include \"" + headername + "\"\r\n";
|
|
editor.SetCursor(editor.GetPos64(1));
|
|
}
|
|
editor.SetCharset(tfile ? CHARSET_UTF8 : charset);
|
|
}
|
|
editor.SetFocus();
|
|
MakeTitle();
|
|
SetBar();
|
|
editor.SyncNavigatorShow();
|
|
editor.assist_active = IsProjectFile(editfile) && IsCppBaseFile();
|
|
editor.CheckEdited(true);
|
|
editor.Annotate(editfile);
|
|
editor.SyncNavigator();
|
|
editfile_repo = GetRepoKind(editfile);
|
|
editfile_includes = IncludesMD5();
|
|
}
|
|
|
|
String Ide::IncludesMD5()
|
|
{ // keep track of includes for Assist++, so that we know when to save file and sync base
|
|
int n = min(editor.GetLineCount(), 1000); // ignore big files
|
|
Md5Stream md5;
|
|
for(int i = 0; i < n; i++) {
|
|
String l = editor.GetUtf8Line(i);
|
|
try {
|
|
CParser p(l);
|
|
if(p.Char('#') && p.Id("include"))
|
|
md5.Put(l);
|
|
}
|
|
catch(CParser::Error) {}
|
|
}
|
|
return md5.FinishString();
|
|
}
|
|
|
|
void Ide::ScanFile(bool check_includes)
|
|
{
|
|
if(IsCppBaseFile()) {
|
|
if(check_includes) {
|
|
String imd5 = IncludesMD5();
|
|
if(editfile_includes != imd5) {
|
|
editfile_includes = imd5;
|
|
SaveFile(true);
|
|
return;
|
|
}
|
|
}
|
|
String s = ~editor;
|
|
StringStream ss(s);
|
|
CodeBaseScanFile(ss, editfile);
|
|
}
|
|
}
|
|
|
|
void Ide::EditFileAssistSync2()
|
|
{
|
|
editor.Annotate(editfile);
|
|
editor.SyncNavigator();
|
|
}
|
|
|
|
void Ide::EditFileAssistSync()
|
|
{
|
|
ScanFile(false);
|
|
EditFileAssistSync2();
|
|
}
|
|
|
|
void Ide::TriggerAssistSync()
|
|
{
|
|
if(auto_rescan && editor.GetLength64() < 500000 && !file_scan) {
|
|
text_updated.KillSet(1000, [=] {
|
|
if(!file_scan && IsCppBaseFile()) {
|
|
String s = ~editor;
|
|
String fn = editfile;
|
|
file_scan++;
|
|
if(!CoWork::TrySchedule([=] {
|
|
StringStream ss(s);
|
|
CodeBaseScanFile(ss, editfile);
|
|
file_scan--;
|
|
file_scanned = true;
|
|
}))
|
|
file_scan--;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void Ide::EditAsHex()
|
|
{
|
|
String path = editfile;
|
|
if(editashex.Find(path) >= 0)
|
|
return;
|
|
editastext.RemoveKey(editfile);
|
|
editashex.FindPut(editfile);
|
|
byte cs = editor.GetCharset();
|
|
int sc = editor.GetSpellcheckComments();
|
|
FlushFile();
|
|
EditFile0(path, cs, sc);
|
|
}
|
|
|
|
bool Ide::IsDesignerFile(const String& path)
|
|
{
|
|
for(int i = 0; i < GetIdeModuleCount(); i++)
|
|
if(GetIdeModule(i).AcceptsFile(path))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void Ide::DoEditAsText(const String& path)
|
|
{
|
|
editastext.FindAdd(path);
|
|
editashex.RemoveKey(editfile);
|
|
}
|
|
|
|
void Ide::EditAsText()
|
|
{
|
|
String path = editfile;
|
|
if(editastext.Find(path) >= 0)
|
|
return;
|
|
// if(!FileExists(path))
|
|
// return;
|
|
String layout;
|
|
if(auto *l = dynamic_cast<LayDesigner *>(~designer))
|
|
layout = l->GetCurrentLayout();
|
|
DoEditAsText(path);
|
|
byte cs = editor.GetCharset();
|
|
int sc = editor.GetSpellcheckComments();
|
|
FlushFile();
|
|
EditFile0(path, cs, sc);
|
|
if(layout.GetCount()) {
|
|
layout = "LAYOUT(" + layout + ",";
|
|
for(int i = 0; i < editor.GetLineCount(); i++)
|
|
if(editor.GetUtf8Line(i).StartsWith(layout)) {
|
|
editor.GotoLine(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ide::EditUsingDesigner()
|
|
{
|
|
String path = editfile;
|
|
if (editastext.Find(editfile) < 0 && editashex.Find(editfile) < 0)
|
|
return;
|
|
editashex.RemoveKey(editfile);
|
|
editastext.RemoveKey(editfile);
|
|
byte cs = editor.GetCharset();
|
|
int sc = editor.GetSpellcheckComments();
|
|
FlushFile();
|
|
EditFile0(path, cs, sc);
|
|
}
|
|
|
|
void Ide::AddEditFile(const String& path)
|
|
{
|
|
actual.file.AddPick(Package::File(path));
|
|
if(IsAux())
|
|
SaveLoadPackageNS(false);
|
|
else
|
|
SaveLoadPackage(false);
|
|
ShowFile(package.GetCount() - 1);
|
|
filelist.SetCursor(filelist.GetCount() - 1);
|
|
}
|
|
|
|
void Ide::EditFile(const String& p)
|
|
{
|
|
if(p.IsEmpty()) {
|
|
FlushFile();
|
|
return;
|
|
}
|
|
if(p == HELPNAME) {
|
|
if(designer && designer->GetFileName() == p)
|
|
return;
|
|
package.FindSetCursor(METAPACKAGE);
|
|
filelist.FindSetCursor(HELPNAME);
|
|
return;
|
|
}
|
|
|
|
String path = NormalizePath(p);
|
|
if(designer ? path == designer->GetFileName() : path == editfile)
|
|
return;
|
|
|
|
FlushFile();
|
|
if(path.IsEmpty())
|
|
return;
|
|
|
|
for(int i = 0; i < package.GetCount(); i++) {
|
|
String pkg = package[i].name;
|
|
Package p;
|
|
Fetch(p, pkg);
|
|
for(int j = 0; j < p.file.GetCount(); j++)
|
|
if(PathIsEqual(SourcePath(pkg, p.file[j]), path)) {
|
|
package.FindSetCursor(pkg);
|
|
ShowFile(j);
|
|
filelist.FindSetCursor(p.file[j]);
|
|
return;
|
|
}
|
|
if(GetFileExt(path) == ".tpp" && PathIsEqual(SourcePath(pkg, GetFileName(path)), path)) {
|
|
filelist.KillCursor();
|
|
package.KillCursor();
|
|
package.SetCursor(i);
|
|
AddEditFile(GetFileName(path));
|
|
return;
|
|
}
|
|
}
|
|
filelist.KillCursor();
|
|
package.KillCursor();
|
|
package.SetCursor(package.GetCount() - 2);
|
|
AddEditFile(path);
|
|
}
|
|
|
|
bool Ide::IsCppBaseFile()
|
|
{
|
|
return IsProjectFile(editfile) && (IsCSourceFile(editfile) || IsCHeaderFile(editfile) ||
|
|
ToUpper(GetFileExt(editfile)) == ".SCH");
|
|
}
|
|
|
|
void Ide::CheckFileUpdate()
|
|
{
|
|
if(editfile.IsEmpty() || !IsForeground() || designer) return;
|
|
FindFile ff(editfile);
|
|
if(!ff) return;
|
|
FileTime tm = ff.GetLastWriteTime();
|
|
if(tm == edittime) return;
|
|
edittime = tm;
|
|
if(editor.IsDirty() && !Prompt(Ctrl::GetAppName(), CtrlImg::exclamation(),
|
|
"Current file was changed outside the IDE, but was also edited inside it.&"
|
|
"Would you like to reload the file or to keep changes made in the IDE ?",
|
|
"Reload", "Keep")) return;
|
|
|
|
if(IsCppBaseFile())
|
|
CodeBaseScanFile(editfile, auto_check);
|
|
ReloadFile();
|
|
}
|
|
|
|
typedef Index<dword> HashBase;
|
|
|
|
static void GetLineIndex(String file, HashBase& hash, Vector<String>& lines)
|
|
{
|
|
const char *p = file;
|
|
while(*p)
|
|
{
|
|
while(*p && *p != '\n' && (byte)*p <= ' ')
|
|
p++;
|
|
const char *b = p;
|
|
while(*p && *p++ != '\n')
|
|
;
|
|
const char *e = p;
|
|
while(e > b && (byte)e[-1] <= ' ')
|
|
e--;
|
|
String s(b, e);
|
|
hash.Add(FoldHash(GetHashValue(s)));
|
|
lines.Add(s);
|
|
}
|
|
}
|
|
|
|
int LocateLine(String old_file, int old_line, String new_file)
|
|
{
|
|
HashBase old_hash, new_hash;
|
|
Vector<String> old_lines, new_lines;
|
|
GetLineIndex(old_file, old_hash, old_lines);
|
|
GetLineIndex(new_file, new_hash, new_lines);
|
|
if(old_line <= 0)
|
|
return 0;
|
|
if(old_line >= old_lines.GetCount())
|
|
return new_lines.GetCount();
|
|
String line = old_lines[old_line];
|
|
//int hash = old_hash[old_line]; Mirek: unused
|
|
//int fore_count = old_lines.GetCount() - old_line - 1;
|
|
int best_match = 0, best_value = 0;
|
|
for(int r = 0; r < 10 && !best_value; r++)
|
|
{
|
|
int src = (r & 1 ? old_line + (r >> 1) + 1 : old_line - (r >> 1));
|
|
if(src < 0 || src >= old_lines.GetCount())
|
|
continue;
|
|
dword hash = old_hash[src];
|
|
for(int i = new_hash.Find(hash); i >= 0; i = new_hash.FindNext(i))
|
|
if(new_lines[i] == old_lines[src])
|
|
{
|
|
int max_back = min(i, src);
|
|
int max_fore = min(new_lines.GetCount() - i, old_lines.GetCount() - src) - 1;
|
|
if(max_back + max_fore <= best_value)
|
|
continue;
|
|
int back = 1;
|
|
while(back <= max_back && new_hash[i - back] == old_hash[src - back]
|
|
&& new_lines[i - back] == old_lines[src - back])
|
|
back++;
|
|
int fore = 1;
|
|
while(fore < max_fore && new_hash[i + fore] == old_hash[src + fore]
|
|
&& new_lines[i + fore] == old_lines[src + fore])
|
|
fore++;
|
|
if(back + fore > best_value)
|
|
{
|
|
best_value = back + fore;
|
|
best_match = minmax(i, 0, new_lines.GetCount());
|
|
}
|
|
}
|
|
}
|
|
return best_match;
|
|
}
|
|
|
|
void Ide::ReloadFile()
|
|
{
|
|
if(editfile.IsEmpty())
|
|
return;
|
|
String fn = editfile;
|
|
String data = ~editor;
|
|
int ln = editor.GetCursorLine();
|
|
editfile.Clear();
|
|
int sc = filelist.GetSbPos();
|
|
EditFile0(fn, editor.GetCharset(), editor.GetSpellcheckComments());
|
|
filelist.SetSbPos(sc);
|
|
int l = LocateLine(data, ln, ~editor);
|
|
editor.SetCursor(editor.GetPos64(l));
|
|
}
|
|
|
|
void Ide::EditAnyFile() {
|
|
FileSel& fs = AnySourceFs();
|
|
#if 0
|
|
fs.Multi(false);
|
|
if(!fs.ExecuteOpen()) return;
|
|
EditFile(fs);
|
|
FileSelected();
|
|
#endif
|
|
if(fs.ExecuteOpen())
|
|
for(int i = 0; i < fs.GetCount(); i++) {
|
|
EditFile(fs[i]);
|
|
FileSelected();
|
|
}
|
|
}
|
|
|
|
void Ide::DragAndDrop(Point, PasteClip& d)
|
|
{
|
|
if(AcceptFiles(d)) {
|
|
Vector<String> f = GetFiles(d);
|
|
for(int i = 0; i < f.GetCount(); i++)
|
|
if(FileExists(f[i])) {
|
|
EditFile(f[i]);
|
|
FileSelected();
|
|
editor.SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ide::AddLru()
|
|
{
|
|
if(editfile.IsEmpty() || tabi) return;
|
|
LruAdd(tablru, editfile, 200);
|
|
}
|
|
|
|
static String sExFiles(const char *fn, const char **ext, int cnt)
|
|
{
|
|
for(int i = 0; i < cnt; i++) {
|
|
String f = ForceExt(fn, ext[i]);
|
|
if(FileExists(f))
|
|
return f;
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
String Ide::GetOpposite()
|
|
{
|
|
static const char *cpp[] = { ".c", ".cpp", ".cc", ".cxx" };
|
|
static const char *hdr[] = { ".h", ".hpp", ".hh", ".hxx" };
|
|
if(IsNull(editfile) || designer)
|
|
return Null;
|
|
String ext = GetFileExt(editfile);
|
|
for(int i = 0; i < __countof(cpp); i++)
|
|
if(ext == cpp[i])
|
|
return sExFiles(editfile, hdr, __countof(hdr));
|
|
for(int i = 0; i < __countof(hdr); i++)
|
|
if(ext == hdr[i])
|
|
return sExFiles(editfile, cpp, __countof(cpp));
|
|
return Null;
|
|
}
|
|
|
|
void Ide::GoOpposite()
|
|
{
|
|
String fn = GetOpposite();
|
|
if(!IsNull(fn))
|
|
EditFile(fn);
|
|
}
|
|
|
|
void Ide::PassEditor()
|
|
{
|
|
editorsplit.NoZoom();
|
|
SyncEditorSplit();
|
|
SetupEditor();
|
|
editfile2 = editfile;
|
|
editor2.SetFont(editor.GetFont());
|
|
editor2.Highlight(editor.GetHighlight());
|
|
editor2.LoadHlStyles(editor.StoreHlStyles());
|
|
editor2.NoShowReadOnly();
|
|
byte charset = editor.GetCharset();
|
|
editor2.CheckEdited(false);
|
|
view_file2.Close();
|
|
if(editor.IsView()) {
|
|
view_file2.Open(editfile2);
|
|
ViewFile(editor2, view_file2, editfile2, charset);
|
|
}
|
|
else
|
|
editor2.Set(editor.Get(charset), charset);
|
|
editor2.SetEditPosSb(editor.GetEditPos());
|
|
editor2.CheckEdited();
|
|
editor.SetFocus();
|
|
editor.ScrollIntoCursor();
|
|
editor2.Annotate(editfile2);
|
|
editor2.SpellcheckComments(editor.GetSpellcheckComments());
|
|
}
|
|
|
|
void Ide::ClearEditedFile()
|
|
{
|
|
editor.ClearEdited();
|
|
}
|
|
|
|
void Ide::ClearEditedAll()
|
|
{
|
|
ClearEditedFile();
|
|
for(int i = 0; i < filedata.GetCount(); i++) {
|
|
LineInfo li = editor.GetLineInfo();
|
|
LineInfoRem lir = editor.GetLineInfoRem();
|
|
FileData& fd = Filedata(filedata.GetKey(i));
|
|
editor.SetLineInfo(fd.lineinfo);
|
|
editor.SetLineInfoRem(pick(fd.lineinforem));
|
|
ClearEditedFile();
|
|
fd.lineinfo = editor.GetLineInfo();
|
|
fd.lineinforem = editor.GetLineInfoRem();
|
|
editor.SetLineInfo(li);
|
|
}
|
|
}
|
|
|
|
void Ide::SplitEditor(bool horz)
|
|
{
|
|
if(editorsplit.GetZoom() < 0)
|
|
CloseSplit();
|
|
|
|
if(horz)
|
|
editorsplit.Horz(editor2, editor);
|
|
else
|
|
editorsplit.Vert(editor2, editor);
|
|
|
|
tabs.SetSplitColor(editfile, Yellow);
|
|
PassEditor();
|
|
}
|
|
|
|
void Ide::SwapEditors()
|
|
{
|
|
String f = editfile2;
|
|
CodeEditor::EditPos p = editor2.GetEditPos();
|
|
if(editorsplit.GetFirstChild() == &editor)
|
|
if(editorsplit.IsVert())
|
|
editorsplit.Vert(editor2, editor);
|
|
else
|
|
editorsplit.Horz(editor2, editor);
|
|
else
|
|
if(editorsplit.IsVert())
|
|
editorsplit.Vert(editor, editor2);
|
|
else
|
|
editorsplit.Horz(editor, editor2);
|
|
PassEditor();
|
|
EditFile(f);
|
|
editor.SetEditPos(p);
|
|
}
|
|
|
|
void Ide::CloseSplit()
|
|
{
|
|
editorsplit.Vert(editor, editor2);
|
|
editorsplit.Zoom(0);
|
|
view_file2.Close();
|
|
editfile2.Clear();
|
|
tabs.ClearSplitColor();
|
|
SyncEditorSplit();
|
|
editor.SetFocus();
|
|
SetupEditor();
|
|
}
|
|
|
|
void Ide::KeySplit(bool horz)
|
|
{
|
|
if(editorsplit.GetZoom() >= 0)
|
|
SplitEditor(horz);
|
|
else
|
|
CloseSplit();
|
|
}
|
|
|
|
void Ide::SyncEditorSplit()
|
|
{
|
|
editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
|
|
editor.topsbbutton <<= THISBACK1(SplitEditor, false);
|
|
editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
|
|
editor.topsbbutton1 <<= THISBACK1(SplitEditor, true);
|
|
editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::split()).Tip("Split (Ctrl+[-])");
|
|
editor2.topsbbutton <<= THISBACK1(SplitEditor, false);
|
|
editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::vsplit()).Tip("Split (Ctrl+[\\])");
|
|
editor2.topsbbutton1 <<= THISBACK1(SplitEditor, true);
|
|
if(editorsplit.GetZoom() >= 0)
|
|
return;
|
|
if(editorsplit.IsVert()) {
|
|
editor.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
|
|
editor.topsbbutton <<= THISBACK(CloseSplit);
|
|
editor2.topsbbutton.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[-])");
|
|
editor2.topsbbutton <<= THISBACK(CloseSplit);
|
|
}
|
|
else {
|
|
editor.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
|
|
editor.topsbbutton1 <<= THISBACK(CloseSplit);
|
|
editor2.topsbbutton1.ScrollStyle().SetMonoImage(IdeImg::closesplit()).Tip("Close (Ctrl+[\\])");
|
|
editor2.topsbbutton1 <<= THISBACK(CloseSplit);
|
|
}
|
|
}
|
|
|
|
bool Ide::HotKey(dword key)
|
|
{
|
|
if(designer && designer->DesignerCtrl().HotKey(key))
|
|
return true;
|
|
if(designer && dynamic_cast<FileHexView *>(~designer) && Match(IdeKeys::AK_EDITASHEX, key)) {
|
|
EditUsingDesigner();
|
|
return true;
|
|
}
|
|
return TopWindow::HotKey(key);
|
|
}
|
|
|
|
String Ide::IdeGetFileName()
|
|
{
|
|
return editfile;
|
|
}
|
|
|
|
String Ide::IdeGetNestFolder()
|
|
{
|
|
Vector<String> w = GetUppDirs();
|
|
for(int i = 0; i < w.GetCount(); i++)
|
|
if(editfile.StartsWith(w[i]))
|
|
return w[i];
|
|
return Null;
|
|
}
|