#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 class IndexSeparatorFrameCls : public CtrlFrame { virtual void FrameLayout(Rect& r) { r.right -= 1; } virtual void FramePaint(Draw& w, const Rect& r) { w.DrawRect(r.right - 1, r.top, 1, r.Height(), SColorShadow); } virtual void FrameAddSize(Size& sz) { sz.cx += 2; } }; AssistEditor::AssistEditor() { assist.NoHeader(); assist.NoGrid(); assist.AddColumn().Margin(0).SetDisplay(Single()); assist.WhenLeftClick = THISBACK(AssistInsert); type.NoHeader(); type.NoGrid(); type.AddColumn(); type.WhenCursor = THISBACK(SyncAssist); type.NoWantFocus(); popup.Horz(type, assist); popup.SetPos(2000); auto_assist = true; commentdp = false; InsertFrame(1, navigatorframe); navigatorframe.Left(navigatorpane, HorzLayoutZoom(140)); int cy = browser.search_scope.GetStdSize().cy; int c2 = cy + 2; scopepane.Add(browser.search_scope.HSizePos(0, 4 * cy + 2).TopPos(0, cy)); for(int i = 0; i < 4; i++) scopepane.Add(browser.rangebutton[i].RightPos((3 - i) * cy, cy).TopPos(0, cy)); scopepane.Add(browser.scope.HSizePos().VSizePos(c2, 0)); itempane.Add(browser.search_item.HSizePos(0, cy + 2).TopPos(0, cy)); itempane.Add(browser.sort.RightPos(0, cy).TopPos(0, cy)); itempane.Add(browser.item.HSizePos().VSizePos(c2, c2)); itempane.Add(browser.search.HSizePos().BottomPos(0, cy)); scope_item.Vert(scopepane, itempane); scope_item.SetPos(3000); navigatorpane.Add(scope_item); browser.scope.NoWantFocus(); browser.scope.WhenLeftDouble = THISBACK(GotoBrowserScope); browser.item.NoWantFocus(); browser.item.WhenLeftClick = browser.item.WhenLeftDouble = THISBACK(BrowserGoto); browser.WhenKeyItem = THISBACK(BrowserGotoNF); browser.WhenClear = THISBACK(SyncCursor); browser.NameStart(); navigator = NAV_BROWSER; WhenAnnotationMove = THISBACK(SyncAnnotationPopup); WhenAnnotationClick = THISBACK1(EditAnnotation, true); WhenAnnotationRightClick = THISBACK1(EditAnnotation, false); Annotations(12); annotation_popup.Background(White); annotation_popup.SetFrame(BlackFrame()); annotation_popup.Margins(6); annotation_popup.NoSb(); thisback = false; cachedpos = INT_MAX; cachedln = -1; parami = 0; param_info.Margins(2); param_info.Background(SWhite()); param_info.SetFrame(BlackFrame()); param_info.BackPaint(); param_info.NoSb(); } int CppItemInfoOrder(const Value& va, const Value& vb) { const CppItemInfo& a = ValueTo(va); const CppItemInfo& b = ValueTo(vb); return CombineCompare(a.name, b.name)(a.natural, b.natural); }; void AssistEditor::CloseAssist() { if(popup.IsOpen()) popup.Close(); if(annotation_popup.IsOpen()) annotation_popup.Close(); assist_item.Clear(); } String AssistEditor::ReadIdBack(int q) { String id; while(q > 0 && iscid(GetChar(q - 1))) q--; while(q < GetLength() && iscid(GetChar(q))) id << (char)GetChar(q++); return id; } void AssistEditor::DirtyFrom(int line) { if(line >= cachedln) { cachedpos = INT_MAX; cachedline.Clear(); cachedln = -1; } CodeEditor::DirtyFrom(line); } int AssistEditor::Ch(int i) { if(i >= 0 && i < GetLength()) { if(i < cachedpos || i - cachedpos > cachedline.GetCount()) { cachedln = GetLine(i); cachedline = GetWLine(cachedln); cachedpos = GetPos(cachedln); } i -= cachedpos; return i < cachedline.GetCount() ? cachedline[i] : '\n'; } return 0; } int AssistEditor::ParsBack(int q) { int level = 1; --q; while(q > 0) { if(isrbrkt(Ch(q))) level++; if(islbrkt(Ch(q))) if(--level <= 0) break; --q; } return max(0, q); } bool IsSpc(int c) { return c > 0 && c <= 32; } void AssistEditor::SkipSpcBack(int& q) { while(q > 0 && IsSpc(Ch(q - 1))) q--; } String AssistEditor::IdBack(int& qq) { String r; if(iscid(Ch(qq - 1))) { int q = qq; while(iscid(Ch(q - 1))) q--; if(iscib(Ch(q))) { qq = q; while(q < GetLength() && iscid(Ch(q))) r.Cat(Ch(q++)); } } return r; } String AssistEditor::CompleteIdBack(int& q) { String id; for(;;) { SkipSpcBack(q); if(Ch(q - 1) == '>') { q--; id = '>' + id; } else if(Ch(q - 1) == '<') { q--; id = '<' + id; } else if(Ch(q - 1) == ':') { while(Ch(q - 1) == ':') q--; id = "::" + id; } else { if(iscib(*id)) break; String nid = IdBack(q); if(IsNull(nid)) break; id = nid + id; } } return id; } Vector AssistEditor::ReadBack(int q) { Vector r; type.Clear(); bool wasid = true; for(;;) { if(r.GetCount() > 200) { r.Clear(); type.Clear(); break; } SkipSpcBack(q); int c = Ch(q - 1); if(c == '>' && !wasid) { q--; r.Add() = CompleteIdBack(q) + ">"; wasid = true; continue; } if(iscid(c)) { if(wasid) break; String id; for(;;) { id = IdBack(q) + id; SkipSpcBack(q); if(Ch(q - 1) != ':') break; while(Ch(q - 1) == ':') q--; id = "::" + id; SkipSpcBack(q); } r.Add() = id; wasid = true; continue; } else { if(c == ']') { if(wasid) break; r.Add("[]"); q = ParsBack(q - 1); wasid = false; continue; } else if(c == ')') { if(wasid) break; r.Add("()"); q = ParsBack(q - 1); wasid = false; continue; } wasid = false; c = Ch(q - 1); if(c == '>' && Ch(q - 2) == '-') { r.Add("->"); q -= 2; continue; } if(c == '.') { r.Add("."); q--; continue; } } break; } Reverse(r); return r; } int memcmp_i(const char *s, const char *t, int n) { while(n--) { int q = ToUpper(*s++) - ToUpper(*t++); if(q) return q; } return 0; } void AssistEditor::SyncAssist() { String name; name = ReadIdBack(GetCursor()); assist.Clear(); int typei = type.GetCursor() - 1; for(int p = 0; p < 2; p++) { VectorMap over; for(int i = 0; i < assist_item.GetCount(); i++) { const CppItemInfo& m = assist_item[i]; if((typei < 0 || m.typei == typei) && (p ? memcmp_i(name, m.name, name.GetCount()) == 0 && memcmp(name, m.name, name.GetCount()) : memcmp(name, m.name, name.GetCount()) == 0)) { int q = over.Find(m.name); if(q < 0 || over[q] == m.typei) { assist.Add(RawToValue(m)); if(q < 0) over.Add(m.name, m.typei); } } } } assist.Sort(0, CppItemInfoOrder); } void AssistEditor::Assist() { if(!assist_active) return; CloseAssist(); Parser parser; Context(parser, GetCursor()); int q = GetCursor(); assist_cursor = q; assist_type.Clear(); assist_item.Clear(); Index in_types; while(iscid(Ch(q - 1))) q--; SkipSpcBack(q); thisback = false; if(Ch(q - 1) == '(') { --q; String id = IdBack(q); if(id == "THISBACK") { thisback = true; thisbackn = false; GatherItems(parser.current_scope, false, in_types, false); PopUpAssist(); return; } if(id == "THISBACK1" || id == "THISBACK2" || id == "THISBACK3" || id == "THISBACK4") { thisback = true; thisbackn = true; GatherItems(parser.current_scope, false, in_types, false); PopUpAssist(); return; } } if(Ch(q - 1) == ':') { while(Ch(q - 1) == ':') q--; Vector tparam; String scope = ParseTemplatedType(Qualify(parser.current_scope, CompleteIdBack(q)), tparam); GatherItems(scope, false, in_types, true); } else { String tp; Vector xp = ReadBack(q); if(xp.GetCount()) { Index typeset = ExpressionType(parser, xp); for(int i = 0; i < typeset.GetCount(); i++) if(typeset[i].GetCount()) GatherItems(typeset[i], xp.GetCount(), in_types, xp.GetCount() == 0); } else { GatherItems(parser.current_scope, false, in_types, true); Index in_types2; GatherItems("", false, in_types2, true); } } PopUpAssist(); } void AssistEditor::PopUpAssist(bool auto_insert) { if(assist_item.GetCount() == 0) return; int lcy = max(16, BrowserFont().Info().GetHeight()); type.Clear(); type.Add(AttrText("").Ink(SColorHighlight())); for(int i = 0; i < assist_type.GetCount(); i++) { String s = assist_type[i]; if(s[0] == ':' && s[1] == ':') s = s.Mid(2); s = Nvl(s, ""); if(s[0] == '<') type.Add(AttrText(s).Ink(SColorMark())); else type.Add(Nvl(s, "")); } type.SetCursor(0); if(!assist.GetCount()) return; int cy = min(300, lcy * max(type.GetCount(), assist.GetCount())); cy += 4; cy += HeaderCtrl::GetStdHeight(); assist.SetLineCy(lcy); Point p = GetCaretPoint() + GetScreenView().TopLeft(); Rect wa = GetWorkArea(); int cx = min(wa.Width() - 100, HorzLayoutZoom(600)); Rect r = RectC(p.x, p.y, cx, cy); if(p.x + cx > wa.right) p.x = wa.right - cx; if(p.y + cy + GetFontSize().cy < wa.bottom) popup.SetRect(p.x, p.y + GetFontSize().cy, cx, cy); else popup.SetRect(p.x, p.y - cy, cx, cy); popup.BackPaint(); if(auto_insert && assist.GetCount() == 1) { assist.GoBegin(); AssistInsert(); } else popup.Ctrl::PopUp(this, false, false, true); } void AssistEditor::Complete() { CloseAssist(); Index id; int l = GetCursorLine() - 1; while(l >= 0) { String x = GetUtf8Line(l); CParser p(x); while(!p.IsEof()) if(p.IsId()) id.FindAdd(p.ReadId()); else p.SkipTerm(); l--; } for(int i = 0; i < id.GetCount(); i++) { CppItemInfo& f = assist_item.Add(id[i]); f.name = id[i]; f.natural = id[i]; f.access = 0; f.kind = 100; } assist_type.Clear(); PopUpAssist(true); } void AssistEditor::Abbr() { CloseAssist(); int c = GetCursor(); int ch; String s; while(IsAlpha(ch = Ch(c - 1))) { s.Insert(0, ch); --c; } int len = s.GetCount(); s = theide->abbr.Get(s, String()); if(IsNull(s)) return; NextUndo(); SetCursor(c); Remove(c, len); int linepos = c; int line = GetLinePos(linepos); WString h = GetWLine(line).Mid(0, linepos); for(int i = 0; i < s.GetCount(); i++) { ch = s[i]; switch(ch) { case '@': c = GetCursor(); break; case '\n': InsertChar('\n'); for(int j = 0; j < h.GetCount(); j++) InsertChar(h[j]); break; default: if((byte)s[i] >= ' ' || s[i] == '\t') InsertChar(s[i]); break; } } SetCursor(c); } void AssistEditor::AssistInsert() { if(assist.IsCursor()) { const CppItemInfo& f = ValueTo(assist.Get(0)); String txt = f.name; int l = txt.GetCount(); int pl = txt.GetCount(); if(!thisback && f.kind >= FUNCTION && f.kind <= INLINEFRIEND) txt << "()"; int cl = GetCursor(); int ch = cl; while(iscid(Ch(cl - 1))) cl--; while(iscid(Ch(ch))) ch++; Remove(cl, ch - cl); SetCursor(cl); if(thisback) for(;;) { int c = Ch(cl++); if(!c || Ch(cl) == ',' || Ch(cl) == ')') break; if(c != ' ') { if(thisbackn) txt << ", "; txt << ')'; break; } } int n = Paste(ToUnicode(txt, CHARSET_WIN1250)); if(!thisback && f.kind >= FUNCTION && f.kind <= INLINEFRIEND) { SetCursor(GetCursor() - 1); StartParamInfo(f); int x = f.natural.ReverseFind('('); if(x >= 0 && f.natural[x + 1] == ')') SetCursor(GetCursor() + 1); } else if(thisback) { if(thisbackn) SetCursor(GetCursor() - 1); } else if(!inbody) SetCursor(cl + n - thisbackn); else if(pl > l) SetSelection(cl + l, cl + pl); else SetCursor(cl + l); } CloseAssist(); IgnoreMouseUp(); } bool AssistEditor::InCode() { int pos = GetCursor(); int line = GetLinePos(pos); SyntaxState st = ScanSyntax(line); WString l = GetWLine(line); st.ScanSyntax(l, ~l + pos); return !st.comment && !st.string && !st.linecomment; } bool AssistEditor::Key(dword key, int count) { if(browser.Key(key, count)) return true; if((key == K_ESCAPE || key == K_ENTER) && (browser.search.HasFocus() || browser.search_item.HasFocus() || browser.search_scope.HasFocus())) { SetFocus(); SyncCursor(); return true; } if(popup.IsOpen()) { int k = key & ~K_CTRL; ArrayCtrl& kt = key & K_CTRL ? type : assist; if(k == K_UP || k == K_PAGEUP || k == K_CTRL_PAGEUP || k == K_CTRL_END) if(kt.IsCursor()) return kt.Key(k, count); else { kt.SetCursor(kt.GetCount() - 1); return true; } if(k == K_DOWN || k == K_PAGEDOWN || k == K_CTRL_PAGEDOWN || k == K_CTRL_HOME) if(kt.IsCursor()) return kt.Key(k, count); else { kt.SetCursor(0); return true; } if(key == K_ENTER && assist.IsCursor()) { AssistInsert(); return true; } if(key == K_TAB && !assist.IsCursor() && assist.GetCount()) { assist.GoBegin(); AssistInsert(); return true; } } int c = GetCursor(); int cc = GetChar(c); int bcc = c > 0 ? GetChar(c - 1) : 0; bool b = CodeEditor::Key(key, count); if(assist.IsOpen()) { if(!iscid(key) && !(iscid(cc) && (key == K_DELETE || key == K_RIGHT)) && !(iscid(bcc) && (key == K_LEFT || key == K_BACKSPACE))) { if(b) { CloseAssist(); if(key == '.') Assist(); } } else SyncAssist(); } else if(auto_assist && InCode()) { if(key == '.' || key == '>' && Ch(GetCursor() - 2) == '-' || key == ':' && Ch(GetCursor() - 2) == ':') Assist(); else if(key == '(') { int q = GetCursor() - 1; String id = IdBack(q); if(id == "THISBACK" || id == "THISBACK1" || id == "THISBACK2" || id == "THISBACK3" || id == "THISBACK4") Assist(); } } return b; } void AssistEditor::MouseWheel(Point p, int zdelta, dword keyflags) { if(assist.IsOpen()) assist.MouseWheel(p, zdelta, keyflags); else CodeEditor::MouseWheel(p, zdelta, keyflags); } void AssistEditor::LeftDown(Point p, dword keyflags) { CloseAssist(); CodeEditor::LeftDown(p, keyflags); } void AssistEditor::LostFocus() { CloseAssist(); } String AssistEditor::RemoveDefPar(const char *s) { String r; int lvl = 0; bool dp = true; while(*s) { byte c = *s++; if(c == '(') lvl++; if(lvl == 0) { if(c == '=') { dp = false; if(commentdp) r.Cat("/* "); else while(r.GetCount() && *r.Last() == ' ') r.Trim(r.GetCount() - 1); } if(c == ')') { if(!dp && commentdp) r.Cat("*/"); r.Cat(')'); if(CParser(s).Char('=')) break; r.Cat(s); break; } if(c == ',') { if(!dp && commentdp) r.Cat("*/"); dp = true; } } else if(c == ')') lvl--; if(dp || commentdp) r.Cat(c); } return r; } String AssistEditor::MakeDefinition(const String& cls, const String& _n) { String n = TrimLeft(_n); CParser p(n); bool dest = false; const char *beg = n; while(!p.IsEof()) { const char *b = p.GetPtr(); if(p.Id("operator")) return cls.GetCount() ? NormalizeSpaces(String(beg, b) + ' ' + cls + "::" + b) : NormalizeSpaces(String(beg, b) + ' ' + b); if(p.Char('~')) { beg = p.GetPtr(); dest = true; } else if(p.IsId()) { String id = p.ReadId(); if(p.Char('(')) { String rp = RemoveDefPar(p.GetPtr()); if(cls.GetCount() == 0) return NormalizeSpaces(String(beg, b) + ' ' + id + '(' + rp); if(dest) return NormalizeSpaces(String(beg, b) + cls + "::~" + id + '(' + rp); else return NormalizeSpaces(String(beg, b) + ' ' + cls + "::" + id + '(' + rp); } } else p.SkipTerm(); } return n; } void AssistEditor::DCopy() { String r; int l, h; bool decla = false; if(!GetSelection(l, h)) { int i = GetLine(GetCursor()); l = GetPos(i); h = l; while(h < GetLength() && h - l < 1000) { int c = GetChar(h); if(c == ';') { decla = true; break; } if(c == '{') break; h++; if(c == '\"') { while(h < GetLength()) { int c = GetChar(h); if(c == '\"' || c == '\n') break; h++; if(c == '\\' && h < GetLength()) h++; } } } } else decla = true; Parser ctx; Context(ctx, l); String txt = Get(l, h - l); StringStream ss(txt); String cls = ctx.current_scope; CppBase cpp; Parser parser; parser.Do(ss, IgnoreList(), cpp, Null, CNULL, Split(cls, ':')); for(int i = 0; i < cpp.GetCount(); i++) { const Array& n = cpp[i]; bool decl = decla; for(int j = 0; j < n.GetCount(); j++) if(n[j].impl) decl = false; for(int j = 0; j < n.GetCount(); j++) { const CppItem& m = n[j]; if(m.IsCode()) if(decl) r << MakeDefinition(cls, m.natural) << "\n{\n}\n\n"; else { if(cpp.IsType(i)) r << String('\t', Split(cpp.GetKey(i), ':').GetCount()); r << m.natural << ";\n"; } if(m.IsData()) { if(cls.GetCount()) { const char *s = m.natural; while(*s) { if(iscib(*s)) { const char *b = s; while(iscid(*s)) s++; String id(b, s); if(m.name == id) { if(cls.GetCount()) r << cls << "::" << m.name << s; else r << m.name << s; break; } r << id; } else r << *s++; } } else r << "extern " << m.natural; r << ";\n"; } } } WriteClipboardText(r); } 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 = FindNext(m, 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::ContextGoto() { if(designer) return; if(!editor.assist_active) return; int li = editor.GetCursorLine(); 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 q = l.Find("#include"); if(q >= 0) { String path = FindIncludeFile(~l + q + 8, GetFileFolder(editfile)); if(!IsNull(path)) { AddHistory(); EditFile(path); } return; } q = editor.GetCursor(); while(iscid(editor.Ch(q - 1))) q--; String tp; Vector xp = editor.ReadBack(q); Index type; Parser parser; int ci = editor.GetCursor(); for(;;) { int c = editor.Ch(ci); if(c == '{' && editor.Ch(ci + 1)) { ci++; break; } if(c == '}' || c == 0) break; ci++; } editor.Context(parser, ci); if(xp.GetCount() == 0 && IsNull(tp)) type.Add(parser.current_scope); else { type = editor.ExpressionType(parser, xp); if(type.GetCount() == 0) return; } String id = editor.GetWord(); if(id.GetCount() == 0) return; Vector scope; bool istype = false; if(xp.GetCount() == 0) { // 'String' String t = Qualify(CodeBase(), parser.current_scope, id); if(CodeBase().Find(t) >= 0) { scope.Add(t); istype = true; } } if(xp.GetCount() == 1 && iscib(*xp[0])) { // 'Vector::Iterator' String t = Qualify(CodeBase(), parser.current_scope, xp[0] + "::" + id); if(CodeBase().Find(t) >= 0) { scope.Add(t); istype = true; } } if(scope.GetCount() == 0) for(int i = 0; i < type.GetCount(); i++) { // 'x.attr' Index done; String r; if(GetIdScope(r, type[i], id, done)) scope.Add(r); } if(scope.GetCount() == 0) { Index done; String r; if(GetIdScope(r, "", id, done)) // global scope.Add(r); } if(scope.GetCount() == 0) return; for(int j = 0; j < scope.GetCount(); j++) { q = CodeBase().Find(scope[j]); if(q >= 0) { const Array& n = CodeBase()[q]; for(int i = 0; i < n.GetCount(); i++) { if(n[i].IsType() == istype && n[i].name == id) { JumpToDefinition(n, i); return; } } } } } void Ide::JumpToDefinition(const Array& n, int q) { 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, GetCppFile(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++; } GotoCpp(n[qimpl >= 0 ? qimpl : qcpp >= 0 ? qcpp : q]); } void Ide::IdeGotoCodeRef(String coderef) { LLOG("IdeGotoLink " << coderef); if(IsNull(coderef)) return; String scope, item; SplitCodeRef(coderef, scope, item); int q = CodeBase().Find(scope); if(q < 0) return; const Array& n = CodeBase()[q]; q = FindItem(n, item); if(q >= 0) JumpToDefinition(n, q); } bool AssistEditor::Esc() { bool r = false; if(assist.IsOpen()) { CloseAssist(); r = true; } if(param_info.IsOpen()) { for(int i = 0; i < PARAMN; i++) param[i].line = -1; param_info.Close(); r = true; } return r; }