#include "ide.h" #if 0 #define LDUMP(x) DDUMP(x) #define LDUMPC(x) DDUMPC(x) #define LLOG(x) DLOG(x) #else #define LDUMP(x) #define LDUMPC(x) #define LLOG(x) #endif bool GetIdScope(String& os, const String& scope, const String& id, Index& done) { if(done.Find(scope) >= 0) return Null; done.Add(scope); Vector tparam; String n = ParseTemplatedType(scope, tparam); int q = CodeBase().Find(n); if(q < 0) return Null; const Array& m = CodeBase()[q]; Vector r; if(FindName(m, id) >= 0) { os = n; return true; } for(int i = 0; i < m.GetCount(); i++) { const CppItem& im = m[i]; if(im.IsType()) { Vector b = Split(im.qptype, ';'); ResolveTParam(b, tparam); for(int i = 0; i < b.GetCount(); i++) { if(GetIdScope(os, b[i], id, done)) return true; } } } return false; } bool GetIdScope(String& os, const String& scope, const String& id) { Index done; return GetIdScope(os, scope, id, done); } bool IsPif(const String& l) { return l.Find("#if") >= 0; } bool IsPelse(const String& l) { return l.Find("#el") >= 0; } bool IsPendif(const String& l) { return l.Find("#endif") >= 0; } void Ide::FindId(const String& id) { int pos = editor.GetCursor(); int h = min(editor.GetLength(), pos + 4000); for(;;) { if(pos >= h || editor[pos] == ';') break; if(iscib(editor[pos])) { int p0 = pos; String tid; while(pos < h && iscid(editor[pos])) { tid.Cat(editor[pos]); pos++; } if(tid == id) { editor.SetCursor(p0); return; } } else pos++; } } String RemoveTemplateParams(const String& s) { Vector dummy; return ParseTemplatedType(s, dummy); } void Ide::ContextGoto0(int pos) { if(designer) return; int li = editor.GetLine(pos); String l = editor.GetUtf8Line(li); if(IsPif(l) || IsPelse(l)) { int lvl = 0; while(li + 1 < editor.GetLineCount()) { l = editor.GetUtf8Line(++li); if(IsPif(l)) lvl++; if(IsPelse(l) && lvl == 0) break; if(IsPendif(l)) { if(lvl == 0) break; lvl--; } } AddHistory(); editor.SetCursor(editor.GetPos(li)); return; } if(IsPendif(l)) { int lvl = 0; while(li - 1 >= 0) { l = editor.GetUtf8Line(--li); if(IsPif(l)) { if(lvl == 0) break; lvl--; } if(IsPendif(l)) lvl++; } AddHistory(); editor.SetCursor(editor.GetPos(li)); return; } int cr = editor.Ch(pos); int cl = editor.Ch(pos - 1); if(!IsAlNum(cr)) { if(islbrkt(cr)) { AddHistory(); editor.MoveNextBrk(false); return; } if(isrbrkt(cr)) { AddHistory(); editor.MovePrevBrk(false); return; } if(islbrkt(cl)) { AddHistory(); editor.MoveNextBrk(false); return; } if(isrbrkt(cl)) { AddHistory(); editor.MovePrevBrk(false); return; } } try { CParser p(l); if(p.Char('#') && p.Id("include")) { String path = FindIncludeFile(p.GetPtr(), GetFileFolder(editfile), SplitDirs(GetIncludePath())); if(!IsNull(path)) { AddHistory(); EditFile(path); editor.SetCursor(0); AddHistory(); } return; } } catch(CParser::Error) {} int q = pos; while(iscid(editor.Ch(q - 1))) q--; String tp; Vector xp = editor.ReadBack(q, Index()); // try to load expression like "x[i]." or "ptr->" Index type; Parser parser; int ci = pos; for(;;) { int c = editor.Ch(ci); if(c == '{' && editor.Ch(ci + 1)) { ci++; break; } if(c == '}' || c == 0 || c == ';') break; ci++; } editor.Context(parser, ci); if(xp.GetCount()) { type = editor.EvaluateExpressionType(parser, xp); if(type.GetCount() == 0) return; } String id = editor.GetWord(pos); if(id.GetCount() == 0) return; String qual; // Try to load type qualification like Foo::Bar, Vector::Iterator while(editor.Ch(q - 1) == ' ') q--; if(editor.Ch(q - 1) == ':' && editor.Ch(q - 2) == ':') { q -= 3; while(q >= 0) { int c = editor.Ch(q); if(iscid(c) || findarg(c, '<', '>', ':', ',', ' ') >= 0) { if(c != ' ') qual = (char)c + qual; q--; } else break; } if(qual.GetCount() == 0) qual = ":"; } Vector scope; Vector istype; // prefer type (e.g. struct Foo) over constructor (Foo::Foo()) for(int i = 0; i < type.GetCount(); i++) { // 'x.attr' Index done; String r; if(GetIdScope(r, type[i], id, done)) { Vector todo; todo.Add(r); while(scope.GetCount() < 100 && todo.GetCount()) { // Add base classes String t = todo[0]; todo.Remove(0); if(t.EndsWith("::")) t.Trim(t.GetCount() - 2); if(t.GetCount()) { scope.Add(t); istype.Add(false); ScopeInfo f(CodeBase(), t); // Try base classes too! todo.Append(f.GetBases()); } } } } Vector ns = parser.GetNamespaces(); if(qual.GetCount() > 2 && qual.StartsWith("::")) qual = qual.Mid(2); if(qual.GetCount()) { // Ctrl::MOUSELEFT, Vector::Iterator Vector todo; String qa = Qualify(CodeBase(), parser.current_scope, *qual == ':' ? id : qual + "::" + id, parser.context.namespace_using); qa = RemoveTemplateParams(qa); if(CodeBase().Find(qa) < 0) { // Upp::FileTabs::RenameFile int q = qa.ReverseFind("::"); if(q > 0) { String h = qa.Mid(0, q); if(CodeBase().Find(h) >= 0) { scope.Add(h); istype.Add(false); } } else { scope.Add(Null); // Add global namespace istype.Add(false); } } todo.Add(qa); while(scope.GetCount() < 100 && todo.GetCount()) { String t = todo[0]; if(t.EndsWith("::")) t.Trim(t.GetCount() - 2); todo.Remove(0); if(CodeBase().Find(t) >= 0) { // Ctrl::MOUSELEFT scope.Add(t); istype.Add(true); } String tt = t; tt << "::" << id; if(CodeBase().Find(tt) >= 0) { // Vector::Iterator scope.Add(tt); istype.Add(true); } ScopeInfo f(CodeBase(), t); // Try base classes too! todo.Append(f.GetBases()); } } else { Vector todo; todo.Add(parser.current_scope); while(scope.GetCount() < 100 && todo.GetCount()) { // Add base classes String t = todo[0]; todo.Remove(0); if(t.EndsWith("::")) t.Trim(t.GetCount() - 2); if(t.GetCount()) { scope.Add(t); istype.Add(false); ScopeInfo f(CodeBase(), t); // Try base classes too! todo.Append(f.GetBases()); } } if(xp.GetCount() == 0) { q = parser.local.Find(id); if(q >= 0) { // Try locals AddHistory(); editor.SetCursor(editor.GetPos(parser.local[q].line - 1)); FindId(id); return; } } // Can be unqualified type name like 'String' String t = RemoveTemplateParams(Qualify(CodeBase(), parser.current_scope, id, parser.context.namespace_using)); for(int i = 0; i < ns.GetCount(); i++) { String tt = Merge("::", ns[i], t); if(CodeBase().Find(tt) >= 0) { scope.Add(tt); istype.Add(true); } } } Vector usings = Split(parser.context.namespace_using, ';'); usings.Add(""); // Add global namespace too Index done; for(int i = 0; i < ns.GetCount(); i++) { String r; if(GetIdScope(r, ns[i], id, done)) { scope.Add(r); istype.Add(false); } } for(int j = 0; j < scope.GetCount(); j++) { q = CodeBase().Find(scope[j]); if(q >= 0) { int ii = -1; const Array& n = CodeBase()[q]; for(int i = 0; i < n.GetCount(); i++) { const CppItem& m = n[i]; if(n[i].name == id) { if(ii < 0) ii = i; else { const CppItem& mm = n[ii]; if(CombineCompare(findarg(mm.kind, CONSTRUCTOR) < 0, findarg(m.kind, CONSTRUCTOR) < 0) (!istype[j] || mm.IsType(), !istype[j] || m.IsType()) (findarg(mm.filetype, FILE_CPP, FILE_C) >= 0, findarg(m.filetype, FILE_CPP, FILE_C) >= 0) (mm.line, m.line) < 0) ii = i; } } } if(ii >= 0) { JumpToDefinition(n, ii, scope[j]); FindId(id); return; } } } } void Ide::ContextGoto() { ContextGoto0(editor.GetCursor()); } void Ide::CtrlClick(int pos) { ContextGoto0(pos); } bool Ide::GotoDesignerFile(const String& path, const String& scope, const String& name, int line) { if(ToLower(GetFileExt(path)) == ".lay") { AddHistory(); EditFile(path); LayDesigner *l = dynamic_cast(~designer); if(l) { if(scope.StartsWith("With")) l->FindLayout(scope.Mid(4), name); else if(name.StartsWith("SetLayout_")) l->FindLayout(name.Mid(10), Null); } else { editor.SetCursor(editor.GetPos(line - 1)); editor.TopCursor(4); editor.SetFocus(); } AddHistory(); return true; } else if(ToLower(GetFileExt(path)) == ".iml") { AddHistory(); EditFile(path); IdeIconDes *l = dynamic_cast(~designer); if(l) l->FindId(name); else editor.SetFocus(); AddHistory(); return true; } return false; } void Ide::JumpToDefinition(const Array& n, int q, const String& scope) { String qitem = n[q].qitem; int i = q; int qml = 0; int qcpp = -1; int qcppml = 0; int qimpl = -1; int qimplml = 0; String currentfile = editfile; while(i < n.GetCount() && n[i].qitem == qitem) { const CppItem& m = n[i]; int ml = GetMatchLen(editfile, GetSourceFilePath(m.file)); if(m.impl && ml > qimplml) { qimplml = ml; qimpl = i; } if((m.filetype == FILE_CPP || m.filetype == FILE_C) && ml > qcppml) { qcpp = i; qcppml = ml; } if(ml > qml) { q = i; qml = ml; } i++; } const CppItem& pos = n[qimpl >= 0 ? qimpl : qcpp >= 0 ? qcpp : q]; String path = GetSourceFilePath(pos.file); editastext.RemoveKey(path); editashex.RemoveKey(path); if(!GotoDesignerFile(path, scope, pos.name, pos.line)) GotoCpp(pos); } void Ide::GotoFileAndId(const String& path, const String& id) { AddHistory(); EditFile(path); WString wid = id.ToWString(); if(editor.GetLength() < 100000) { for(int i = 0; i < editor.GetLineCount(); i++) { WString ln = editor.GetWLine(i); int q = ln.Find(wid); while(q >= 0) { if(q == 0 || !iscid(ln[q - 1]) && !iscid(ln[q + wid.GetCount()])) { editor.SetCursor(editor.GetPos(i, q)); editor.CenterCursor(); return; } if(q + 1 >= ln.GetCount()) break; q = ln.Find(wid, q + 1); } } } AddHistory(); } void IdeGotoFileAndId(const String& path, const String& id) { Ide *ide = dynamic_cast(TheIde()); if(ide) ide->GotoFileAndId(path, id); }