#include "ide.h" bool IsCodeItem(const RichTxt& txt, int i) { static Uuid codeitem = CodeItemUuid(); static Uuid stritem = StructItemUuid(); if(i < txt.GetPartCount() && txt.IsPara(i)) { Uuid style = txt.GetParaStyle(i); return style == codeitem || style == stritem; } return false; } bool IsBeginEnd(RichText& txt, int i) { static Uuid begin = BeginUuid(); static Uuid end = EndUuid(); if(i < txt.GetPartCount() && txt.IsPara(i)) { Uuid style = txt.GetParaStyle(i); return style == begin || style == end; } return false; } bool AssistEditor::GetAnnotationRefs(Vector& tl, String& coderef, int q) { if(annotation_popup.IsOpen()) annotation_popup.Close(); if(q < 0) q = GetActiveAnnotationLine(); if(q < 0) return false; coderef = GetAnnotation(q); if(IsNull(coderef)) return false; tl = GetRefLinks(coderef); return true; } int GetMatchLen(const char *s, const char *t) { int i = 0; while(s[i] == t[i] && s[i] && t[i]) i++; return i; } String AssistEditor::BestTopic(const Vector& tl) { // it there are multiple topics, use one that is "close" by path if(tl.GetCount() == 0) return Null; String path = theide ? theide->editfile : Null; int mi = 0; int m = 0; for(int i = 0; i < tl.GetCount(); i++) { int mm = GetMatchLen(tl[i], path); if(mm > m) { mi = i; m = mm; } } return tl[mi]; } bool AssistEditor::GetAnnotationRef(String& t, String& coderef, int q) { Vector tl; if(!GetAnnotationRefs(tl, coderef, q)) return false; t = BestTopic(tl); return true; } RichText AssistEditor::GetCodeTopic(const String& tl, const String& coderef, bool skip_header) { static String last_path; static RichText topic_text; String path = GetTopicPath(tl); if(path != last_path) topic_text = ParseQTF(ReadTopic(LoadFile(path)).text); RichText result; if(AssistDiagnostics) result = ParseQTF("[A1 [@b* " + DeQtf(coderef) + "]&"); for(String cr : AnnotationCandidates(coderef)) { for(int i = 0; i < topic_text.GetPartCount(); i++) if(topic_text.IsTable(i)) { const RichTable& t = topic_text.GetTable(i); Size sz = t.GetSize(); for(int y = 0; y < sz.cy; y++) for(int x = 0; x < sz.cx; x++) { const RichTxt& txt = t.Get(y, x); for(int i = 0; i < txt.GetPartCount(); i++) { if(txt.IsPara(i) && CleanupTppId(txt.Get(i, topic_text.GetStyles()).format.label) == cr) { RichTable r(t, 1); result.CatPick(pick(r)); goto done; } } } } else if(IsCodeItem(topic_text, i) && CleanupTppId(topic_text.Get(i).format.label) == cr) { while(i > 0 && IsCodeItem(topic_text, i)) i--; if(!IsCodeItem(topic_text, i)) i++; while(IsCodeItem(topic_text, i)) { if(CleanupTppId(topic_text.Get(i).format.label) == cr) if(!skip_header) result.Cat(topic_text.Get(i)); i++; } while(i < topic_text.GetPartCount() && !IsCodeItem(topic_text, i) && !IsBeginEnd(topic_text, i)) { if(topic_text.IsPara(i)) result.Cat(topic_text.Get(i++)); else { RichTable table(topic_text.GetTable(i++), 1); result.CatPick(pick(table)); } } goto done; } else if(CleanupTppId(topic_text.Get(i).format.label) == cr) { while(i < topic_text.GetPartCount() && CleanupTppId(topic_text.Get(i).format.label) == cr) { if(!skip_header) result.Cat(topic_text.Get(i)); i++; } while(i < topic_text.GetPartCount() && topic_text.Get(i).format.label.GetCount() == 0 && !IsBeginEnd(topic_text, i)) { if(topic_text.IsPara(i)) result.Cat(topic_text.Get(i++)); else { RichTable table(topic_text.GetTable(i++), 1); result.CatPick(pick(table)); } } goto done; } } done: result.SetStyles(topic_text.GetStyles()); return result; } void AssistEditor::SyncAnnotationPopup() { String coderef; String tl; if(!GetAnnotationRef(tl, coderef)) return; if(tl.GetCount()) { annotation_popup.Pick(pick(GetCodeTopic(tl, coderef, false)), GetRichTextStdScreenZoom()); } else if(SyncRefsFinished) annotation_popup.SetQTF("[A1 [@b* " + DeQtf(coderef) + "]&Not documented yet - click to document"); else annotation_popup.SetQTF("[A1 [@b* " + DeQtf(coderef) + "]&Documentation not loaded yet"); Rect r = GetLineScreenRect(GetActiveAnnotationLine()); int h = annotation_popup.GetHeight(Zx(580)); h = min(h, Zx(550)); int y = r.top - h - DPI(16); if(y < GetWorkArea().top) y = r.bottom; annotation_popup.SetRect(r.left, y, Zx(600), h + DPI(16)); annotation_popup.Ctrl::PopUp(this, false, false, true); } void AssistEditor::OpenTopic(String topic, String create, bool before) { if(theide) theide->OpenTopic(topic, create, before); } AnnotationItem AssistEditor::GetCodeAnnotation(const String& id) { for(const AnnotationItem& h : annotations) if(h.id == id) return h; return AnnotationItem(); } void SplitCodeRef(const String& s, String& scope, String& item) { int q = s.FindFirstOf("( "); q = q >= 0 ? s.ReverseFind(':', q) : s.ReverseFind(':'); if(q < 0) { scope.Clear(); item = s; } else { scope = s.Mid(0, max(q - 1, 0)); item = s.Mid(q + 1); } } void AssistEditor::NewTopic(String group, String coderef) { if(!theide) return; String ef = theide->editfile; String n = GetFileTitle(ef); theide->EditFile(AppendFileName(PackageDirectory(theide->GetActivePackage()), group + ".tpp")); if(!theide->designer) return; TopicEditor *te = dynamic_cast(&theide->designer->DesignerCtrl()); if(!te) return; String scope, item; SplitCodeRef(coderef, scope, item); if(!te->NewTopicEx(IsNull(scope) ? n : Join(Split(scope, ':'), "_"), GetCodeAnnotation(coderef))) theide->EditFile(ef); } void AssistEditor::EditAnnotation(bool leftclick) { if(!SyncRefsFinished) return; String coderef; Vector tl; if(!GetAnnotationRefs(tl, coderef)) return; SetCursor(GetPos64(GetActiveAnnotationLine())); if(leftclick) { // show documentation in help widget auto GoToTopic = [&] (int i) { if(theide) { theide->doc.WhenMatchLabel = [](const WString& lbl, const WString& ref) { return ref.ToString() == CleanupTppId(lbl.ToString()); }; theide->ShowTopics(); for(String cr : AnnotationCandidates(coderef)) if(theide->doc.GoTo(tl[i] + '#' + cr)) break; } }; if(tl.GetCount() > 1) { MenuBar bar; for(int i = 0; i < tl.GetCount(); i++) bar.Add(tl[i], [=] { GoToTopic(i); }); bar.Execute(); return; } if(tl.GetCount()) { GoToTopic(0); return; } } MenuBar bar; if(tl.GetCount()) { for(int i = 0; i < tl.GetCount(); i++) bar.Add("Edit " + tl[i], THISBACK3(OpenTopic, tl[i] + '#' + coderef, String(), false)); bar.Separator(); } String scope, item; SplitCodeRef(coderef, scope, item); VectorMap tpp; int backi = 0; for(int pass = 0; pass < 2; pass++) { for(int i = GetCursorLine(); pass ? i < GetLineCount() : i >= 0; pass ? i++ : i--) { String coderef2; if(GetAnnotationRefs(tl, coderef2, i) && tl.GetCount()) { String scope2, item2; SplitCodeRef(coderef2, scope2, item2); for(const String& t : tl) if(tpp.Find(t) < 0) tpp.Add(t, coderef2); } } if(pass == 0) backi = tpp.GetCount(); } if(tpp.GetCount()) { for(int i = 0; i < tpp.GetCount(); i++) { String l = tpp.GetKey(i); bar.Add("Insert into " + l, THISBACK3(OpenTopic, l + '#' + tpp[i], coderef, i >= backi)); } bar.Separator(); } bar.Add("New reference topic..", THISBACK2(NewTopic, "src", coderef)); bar.Add("New implementation topic..", THISBACK2(NewTopic, "srcimp", coderef)); bar.Separator(); bar.Add("Copy code reference id", [=] { WriteClipboardText(coderef); }); bar.Execute(); }