#include "ide.h" #define LTIMING(x) // RTIMING(x) String FormatNest(String nest) { int q = nest.Find("@"); if(q >= 0) { nest.Trim(q); nest << "[anonymous]"; } return nest; } int CharFilterNavigator(int c) { return c == ':' ? '.' : IsAlNum(c) || c == '_' || c == '.' ? c : 0; } void PaintTeXt(Draw& w, int& x, int y, const String& text, Font font, Color ink) { static int maxascent = MaxAscent(StdFont()); Size sz = GetTextSize(text, font); w.DrawText(x, y + maxascent - font.GetAscent(), text, font, ink); x += sz.cx; } int DrawFileName0(Draw& w, const Rect& r, const String& h, Color ink, int x) { if(h.GetCount() == 0) return 0; int q = h.Find("\xff"); String ns; String fn = h; if(q >= 0) { ns = h.Mid(0, q) + ' '; fn = h.Mid(q + 1); } String s = GetFileName(GetFileFolder(h)) + "/"; x += r.left; if(ns.GetCount()) { PaintTeXt(w, x, r.top, ns, StdFont().Bold(), ink); PaintTeXt(w, x, r.top, "(", StdFont(), ink); } PaintTeXt(w, x, r.top, s, StdFont(), ink); s = GetFileName(h); PaintTeXt(w, x, r.top, s, StdFont().Bold(), ink); if(ns.GetCount()) PaintTeXt(w, x, r.top, ")", StdFont(), ink); return x - r.left; } Size GetDrawFileNameSize(const String& h) { NilDraw w; return Size(DrawFileName0(w, Size(999999, 999999), h, Null, 0), StdFont().Bold().GetCy()); } void DrawFileName(Draw& w, const Rect& r, const String& h, Color ink) { DrawFileName0(w, r, h, ink, min(r.GetWidth() - GetDrawFileNameSize(h).cx, 0)); } int PaintFileName(Draw& w, const Rect& r, String h, Color ink) { if(*h == '\xff') h.Remove(0, 1); return DrawFileName0(w, r, h, ink, 0); } Navigator::Navigator() : navidisplay(litem) { scope_display.navigator = this; scope.NoHeader(); scope.AddColumn().SetDisplay(scope_display); scope.NoWantFocus(); scope.WhenSel = THISBACK(Scope); scope.WhenLeftDouble = THISBACK(ScopeDblClk); list.NoHeader(); list.AddRowNumColumn().SetDisplay(navidisplay); list.SetLineCy(max(16, GetStdFontCy())); list.NoWantFocus(); list.WhenLeftClick = THISBACK(NavigatorClick); list.WhenSel = THISBACK(SyncNavLines); list.WhenLineEnabled = THISBACK(ListLineEnabled); navlines.NoHeader().NoWantFocus(); navlines.WhenLeftClick = THISBACK(GoToNavLine); navlines.AddColumn().SetDisplay(Single()); search <<= THISBACK(TriggerSearch); search.SetFilter(CharFilterNavigator); search.WhenEnter = THISBACK(NavigatorEnter); sortitems.Image(BrowserImg::Sort()); sortitems <<= THISBACK(NaviSort); sorting = false; dlgmode = false; } void Navigator::SyncCursor() { String k = "(" + GetKeyDesc(IdeKeys::AK_GOTO().key[0]) + ") "; search.NullText("Symbol/lineno " + k); search.Tip(IsNull(search) ? String() : "Clear " + k); if(!navigating && theide->editfile.GetCount()) { navlines.KillCursor(); int q = linefo.Find(GetSourceFileIndex(theide->editfile)); if(q < 0) return; navigating = true; SortedVectorMap& m = linefo[q]; q = m.FindUpperBound(GetCurrentLine() + 1) - 1; if(q >= 0 && q < m.GetCount()) list.SetCursor(m[q]); navigating = false; } SyncLines(); if(scope.IsCursor()) scope.RefreshRow(scope.GetCursor()); } void Navigator::SyncLines() { if(IsNull(theide->editfile) || navigating) return; int ln = GetCurrentLine() + 1; int fi = GetSourceFileIndex(theide->editfile); int q = -1; for(int i = 0; i < navlines.GetCount(); i++) { const NavLine& l = navlines.Get(i, 0).To(); if(l.file == fi && l.line <= ln && i < navlines.GetCount()) q = i; } if(dlgmode) navlines.GoBegin(); else if(q >= 0) navlines.SetCursor(q); } void Navigator::SyncNavLines() { int sc = navlines.GetScroll(); navlines.Clear(); int ii = list.GetCursor(); if(ii >= 0 && ii < litem.GetCount()) { Vector l = GetNavLines(*litem[ii]); for(int i = 0; i < l.GetCount(); i++) { String p = GetSourceFilePath(l[i].file); navlines.Add(RawToValue(l[i])); } navlines.ScrollTo(sc); SyncLines(); } } int Navigator::LineDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style, int x) const { w.DrawRect(r, paper); const NavLine& l = q.To(); x += r.left; String p = GetSourceFilePath(l.file); int y = r.top + (r.GetHeight() - StdFont().GetCy()) / 2; PaintTeXt(w, x, y, GetFileName(GetFileFolder(p)) + "/", StdFont(), ink); PaintTeXt(w, x, y, GetFileName(p), StdFont().Bold(), ink); PaintTeXt(w, x, y, " (", StdFont(), ink); PaintTeXt(w, x, y, AsString(l.line), StdFont().Bold(), ink); PaintTeXt(w, x, y, ")", StdFont(), ink); return x - r.left; } void Navigator::LineDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { DoPaint(w, r, q, ink, paper, style, min(r.GetWidth() - GetStdSize(q).cx, 0)); } Size Navigator::LineDisplay::GetStdSize(const Value& q) const { NilDraw w; return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0, 0), StdFont().Bold().GetCy()); } void Navigator::GoToNavLine() { if(dlgmode) return; int ii = navlines.GetClickPos().y; if(ii >= 0 && ii < navlines.GetCount() && theide) { const NavLine& l = navlines.Get(ii, 0).To(); theide->GotoPos(GetSourceFilePath(l.file), l.line); } } bool Navigator::NavLine::operator<(const NavLine& b) const { String p1 = GetSourceFilePath(file); String p2 = GetSourceFilePath(b.file); return CombineCompare/*(!impl, !b.impl)*/ (GetFileExt(p2), GetFileExt(p1)) // .h > .c (GetFileName(p1), GetFileName(p2)) (p1, p2) (line, b.line) < 0; } Vector Navigator::GetNavLines(const NavItem& m) { Vector l; int q = CodeBase().Find(m.nest); if(q < 0 || IsNull(m.qitem)) return l; const Array& a = CodeBase()[q]; for(int i = 0; i < a.GetCount(); i++) { const CppItem& mm = a[i]; if(mm.qitem == m.qitem) { NavLine& nl = l.Add(); nl.impl = mm.impl; nl.file = mm.file; nl.line = mm.line; } } Sort(l); return l; } void Navigator::Navigate() { if(navigating) return; navigating = true; int ii = list.GetCursor(); if(theide && ii >= 0 && ii < litem.GetCount()) { int ln = GetCurrentLine() + 1; const NavItem& m = *litem[ii]; if(m.kind == KIND_LINE || IsNull(search)) { theide->GotoPos(Null, m.line); if(m.kind == KIND_LINE) { // Go to line - restore file view search.Clear(); Search(); navigating = false; } SyncCursor(); } else { Vector l = GetNavLines(m); int q = l.GetCount() - 1; for(int i = 0; i < l.GetCount(); i++) if(GetSourceFilePath(l[i].file) == NormalizeSourcePath(theide->editfile) && l[i].line == ln) { q = (i + l.GetCount() + 1) % l.GetCount(); break; } if(q >= 0 && q < l.GetCount()) { String path = GetSourceFilePath(l[q].file); if(!theide->GotoDesignerFile(path, m.nest, m.name, l[q].line)) theide->GotoPos(path, l[q].line); } } } navigating = false; } void Navigator::ScopeDblClk() { if(!scope.IsCursor()) return; String h = scope.GetKey(); if((byte)*h == 0xff) theide->GotoPos(h.Mid(1), 1); else { list.GoBegin(); Navigate(); } } void Navigator::NavigatorClick() { if(dlgmode) return; int q = list.GetClickPos().y; if(q >= 0 && q < list.GetCount()) Navigate(); } void Navigator::NavigatorEnter() { if(list.GetCount()) { list.GoBegin(); Navigate(); } } void Ide::ToggleNavigator() { editor.Navigator(!editor.navigator); } void Ide::SearchCode() { if(!editor.navigator) editor.Navigator(true); if(!IsNull(~editor.search)) { editor.search.Clear(); editor.Search(); editor.SetFocus(); } else { String h = editor.GetWord(); if(h.GetCount()) { editor.search <<= h; editor.search.SelectAll(); editor.Search(); } editor.search.SetFocus(); } } void Ide::SwitchHeader() { int c = filelist.GetCursor(); if(c < 0) return; String currfile = filelist[c]; const char *ext = GetFileExtPos(currfile); if(!stricmp(ext, ".h") || !stricmp(ext, ".hpp") || !stricmp(ext, ".lay") || !stricmp(ext, ".iml")) { int f = filelist.Find(ForceExt(currfile, ".cpp")); if(f < 0) f = filelist.Find(ForceExt(currfile, ".c")); if(f < 0) f = filelist.Find(ForceExt(currfile, ".cc")); if(f >= 0) filelist.SetCursor(f); } } void Navigator::NavItem::Set(const CppItem& m) { qitem = m.qitem; name = m.name; uname = m.uname; natural = m.natural; type = m.type; pname = m.pname; ptype = m.ptype; tname = m.tname; ctname = m.ctname; access = m.access; kind = m.kind; at = m.at; line = m.line; file = m.file; impl = m.impl; pass = false; } void Navigator::NavigatorDisplay::PaintBackground(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { int ii = q; if(ii < 0 || ii >= item.GetCount()) return; const NavItem& m = *item[ii]; bool focuscursor = (style & (FOCUS|CURSOR)) == (FOCUS|CURSOR) || (style & SELECT); if(findarg(m.kind, KIND_FILE, KIND_NEST) >= 0) w.DrawRect(r, focuscursor ? paper : m.kind == KIND_NEST ? Blend(SColorMark, SColorPaper, 220) : SColorFace); else w.DrawRect(r, paper); } int Navigator::NavigatorDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { int ii = q; if(ii < 0 || ii >= item.GetCount()) return 0; const NavItem& m = *item[ii]; bool focuscursor = (style & (FOCUS|CURSOR)) == (FOCUS|CURSOR) || (style & SELECT); int x = r.left; int ry = r.top + r.GetHeight() / 2; int y = ry - Draw::GetStdFontCy() / 2; if(findarg(m.kind, KIND_FILE, KIND_NEST) >= 0) { w.DrawRect(r, focuscursor ? paper : m.kind == KIND_NEST ? Blend(SColorMark, SColorPaper, 220) : SColorFace); if(m.kind == KIND_FILE) return PaintFileName(w, r, m.type, ink); String h = FormatNest(m.type); w.DrawText(x, y, h, StdFont().Bold(), ink); return GetTextSize(h, StdFont().Bold()).cx; } w.DrawRect(r, paper); if(m.kind == KIND_LINE) { w.DrawText(x, y, m.type, StdFont().Bold(), ink); return GetTextSize(m.type, StdFont().Bold()).cx; } PaintCppItemImage(w, x, ry, m.access, m.kind, focuscursor); x += Zx(15); Vector n = ParseItemNatural(m.name, m.natural, m.ptype, m.pname, m.type, m.tname, m.ctname, ~m.natural + m.at); int starti = 0; for(int i = 0; i < n.GetCount(); i++) if(n[i].type == ITEM_NAME) { starti = i; break; } PaintText(w, x, y, m.natural, n, starti, n.GetCount(), focuscursor, ink, false); if(starti) { const char *h = " : "; w.DrawText(x, y, h, BrowserFont(), SColorText); x += GetTextSize(h, BrowserFont()).cx; } PaintText(w, x, y, m.natural, n, 0, starti, focuscursor, ink, false); return x; } void Navigator::NavigatorDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { DoPaint(w, r, q, ink, paper, style); } Size Navigator::NavigatorDisplay::GetStdSize(const Value& q) const { NilDraw w; return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0), Draw::GetStdFontCy()); } void Navigator::TriggerSearch() { search_trigger.KillSet(100, THISBACK(Search)); } void Navigator::NavGroup(bool local) { for(int i = 0; i < nitem.GetCount(); i++) { NavItem& m = nitem[i]; String g = m.nest; if(m.kind == TYPEDEF) g.Trim(max(g.ReverseFind("::"), 0)); if(IsNull(g) || CodeBase().namespaces.Find(m.nest) >= 0) { if(g.GetCount()) // We want to show the namespace g << '\xff'; else g.Clear(); g << GetSourceFilePath(m.decl_file); g = '\xff' + g; } if(!local) g = (char)(m.pass + 10) + g; if(local) if(gitem.GetCount() && gitem.TopKey() == g) gitem.Top().Add(&m); else gitem.Add(g).Add(&m); else gitem.GetAdd(g).Add(&m); } } force_inline bool Navigator::SortByLines(const NavItem *a, const NavItem *b) { return CombineCompare(a->decl_file, b->decl_file)(a->decl_line, b->decl_line) < 0; } force_inline bool Navigator::SortByNames(const NavItem *a, const NavItem *b) { return CombineCompare(a->name, b->name)(a->qitem, b->qitem) < 0; } void Navigator::Search() { sortitems.Check(sorting); int sc = scope.GetScroll(); String key = scope.GetKey(); String s = TrimBoth(~search); String search_name, search_nest; bool wholeclass = false; bool both = false; navigator_global = false; if(s.Find('.') >= 0) { Vector h = Split((String)~search, '.'); if(*s.Last() == '.') search_nest = Join(h, "::"); else { search_name = h.Pop(); if(h.GetCount()) search_nest = Join(h, "::"); } wholeclass = *s == '.' && search_nest.GetCount(); } else { search_name = search_nest = ~search; both = true; } s = Join(Split(s, '.'), "::") + (s.EndsWith(".") ? "::" : ""); int lineno = StrInt(s); gitem.Clear(); nitem.Clear(); if(IsNull(theide->editfile)) return; int fileii = GetSourceFileIndex(theide->editfile); if(!IsNull(lineno)) { NavItem& m = nitem.Add(); m.type = "Go to line " + AsString(lineno); m.kind = KIND_LINE; m.line = lineno; gitem.Add(Null).Add(&m); } else if(IsNull(s) && !sorting) { const CppBase& b = CodeBase(); bool sch = GetFileExt(theide->editfile) == ".sch"; for(int i = 0; i < b.GetCount(); i++) { String nest = b.GetKey(i); const Array& ci = b[i]; for(int j = 0; j < ci.GetCount(); j++) { const CppItem& m = ci[j]; if(m.file == fileii && (!sch || !m.IsData() || m.type != "SqlId")) { NavItem& n = nitem.Add(); n.Set(m); n.nest = nest; n.decl_line = m.line; n.decl_file = m.file; n.decl = !m.impl; NavLine& l = n.linefo.Add(); l.impl = m.impl; l.file = m.file; l.line = m.line; } } } Sort(nitem, FieldRelation(&NavItem::line, StdLess())); NavGroup(true); } else { navigator_global = true; const CppBase& b = CodeBase(); String usearch_nest = ToUpper(search_nest); String usearch_name = ToUpper(search_name); ArrayMap imap; bool local = sorting && IsNull(s); VectorMap nest_pass; for(int pass = -1; pass < 2; pass++) { for(int i = 0; i < b.GetCount(); i++) { String nest = b.GetKey(i); bool foundnest = (wholeclass ? pass < 0 ? false : pass ? ToUpper(nest) == usearch_nest : nest == search_nest : pass < 0 ? nest == search_nest : (pass ? ToUpper(nest).Find(usearch_nest) >= 0 : nest.StartsWith(search_nest))) && nest.Find('@') < 0; if(local || foundnest || both) { const Array& ci = b[i]; for(int j = 0; j < ci.GetCount(); j++) { const CppItem& m = ci[j]; if(local ? m.file == fileii : m.uname.Find('@') < 0 && (pass < 0 ? m.name == search_name : pass ? m.uname.Find(usearch_name) >= 0 : m.name.StartsWith(search_name)) || both && foundnest) { String key = nest + '\1' + m.qitem; int q = nest_pass.Find(nest); int p = pass; if(q < 0) // We do not want classes to be split based on pass nest_pass.Add(nest, pass); else p = nest_pass[q]; q = imap.Find(key); if(q < 0) { NavItem& ni = imap.Add(key); ni.Set(m); ni.nest = nest; ni.decl_line = ni.line; ni.decl_file = ni.file; ni.decl = !ni.impl; ni.pass = p; NavLine& l = ni.linefo.Add(); l.impl = m.impl; l.file = m.file; l.line = m.line; } else { NavItem& mm = imap[q]; if(!m.impl && (!mm.decl || CombineCompare(mm.decl_file, m.file)(mm.decl_line, m.line) < 0)) { mm.decl = true; mm.decl_line = m.line; mm.decl_file = m.file; mm.natural = m.natural; } NavLine& l = mm.linefo.Add(); l.impl = m.impl; l.file = m.file; l.line = m.line; } } } } } } nitem = imap.PickValues(); NavGroup(false); SortByKey(gitem); Vector keys = gitem.PickKeys(); Vector > values = gitem.PickValues(); IndexSort(keys, values); for(int i = 0; i < keys.GetCount(); i++) keys[i].Remove(0); VectorMap > h(pick(keys), pick(values)); gitem = pick(h); for(int i = 0; i < gitem.GetCount(); i++) Sort(gitem[i], sorting ? SortByNames : SortByLines); } scope.Clear(); scope.Add(Null); Index done; for(int i = 0; i < gitem.GetCount(); i++) { String s = gitem.GetKey(i); if(done.Find(s) < 0) { done.Add(s); scope.Add(s); } } scope.ScrollTo(sc); if(!navigator_global || !scope.FindSetCursor(key)) scope.GoBegin(); } int Navigator::ScopeDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { w.DrawRect(r, paper); if(IsNull(q)) { const char *txt = "* "; int x = 0; w.DrawText(r.left, r.top, txt, StdFont().Bold().Italic(), style & CURSOR ? ink : HighlightSetup::GetHlStyle(HighlightSetup::INK_KEYWORD).color); x += GetTextSize(txt, StdFont().Bold().Italic()).cx; int ii = navigator->list.GetCursor(); if(ii >= 0 && ii < navigator->litem.GetCount()) { const NavItem& m = *navigator->litem[ii]; String txt = m.nest; if(IsCppCode(m.kind)) txt << "::" << m.name; w.DrawText(r.left + x, r.top, txt, StdFont().Bold(), ink); x += GetTextSize(txt, StdFont().Bold()).cx; } return x; } String h = q; if(*h == '\xff') return PaintFileName(w, r, h, ink); else h = FormatNest(h); w.DrawText(r.left, r.top, h, StdFont(), ink); return GetTextSize(h, StdFont()).cx; } void Navigator::ScopeDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const { DoPaint(w, r, q, ink, paper, style); } Size Navigator::ScopeDisplay::GetStdSize(const Value& q) const { NilDraw w; return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0), StdFont().Bold().GetCy()); } void Navigator::Scope() { LTIMING("FINALIZE"); litem.Clear(); nest_item.Clear(); linefo.Clear(); bool all = scope.GetCursor() <= 0; String sc = scope.GetKey(); for(int i = 0; i < gitem.GetCount(); i++) { String grp = gitem.GetKey(i); int kind = KIND_NEST; if(*grp == '\xff') kind = KIND_FILE; if(all) { NavItem& m = nest_item.Add(); m.kind = kind; m.type = FormatNest(grp); litem.Add(&m); } else if(grp != sc) continue; const Vector& ia = gitem[i]; for(int i = 0; i < ia.GetCount(); i++) { NavItem *m = ia[i]; for(int j = 0; j < m->linefo.GetCount(); j++) linefo.GetAdd(m->linefo[j].file).Add(m->linefo[j].line, litem.GetCount()); litem.Add(m); } } list.Clear(); list.SetVirtualCount(litem.GetCount()); } void Navigator::ListLineEnabled(int i, bool& b) { if(i >= 0 && i < litem.GetCount()) { int kind = litem[i]->kind; if(findarg(kind, KIND_FILE, KIND_NEST) >= 0) b = false; } } void Navigator::NaviSort() { sorting = !sorting; Search(); }