#include "Browser.h" #define CODEITEM "37138531426314131252341829483370" #define STRUCTITEM "37138531426314131252341829483380" #define BEGINSTYLE "05600065144404261032431302351956" #define ENDSTYLE "96390100711032703541132217272105" static const char styles[] = "[ $$0,0#00000000000000000000000000000000:Default]" "[i448;a25;kKO9;2 $$1,0#" CODEITEM ":codeitem]" "[i448;a25;kKO9;3 $$2,0#" STRUCTITEM ":structitem]" "[l288;2 $$3,0#27521748481378242620020725143825:desc]" "[H6;0 $$4,0#" BEGINSTYLE ":begin]" "[l288;a25;kK~~~.1408;@3;2 $$5,0#61217621437177404841962893300719:param]" "[0 $$7,0#" ENDSTYLE ":end]" ; void IdeGotoCodeRef(const String& ref_id); void TopicEditor::JumpToDefinition() { PostCallback([=] { IdeGotoCodeRef(editor.GetFormatInfo().label); }); } void TopicEditor::Label(String& label) { Save(); WithEditRefLayout dlg; CtrlLayoutOKCancel(dlg, "Code reference"); dlg.label <<= label; if(dlg.Execute() == IDOK) label = ~dlg.label; } Uuid CodeItemUuid() { return ScanUuid(CODEITEM); } Uuid StructItemUuid() { return ScanUuid(STRUCTITEM); } Uuid BeginUuid() { return ScanUuid(BEGINSTYLE); } Uuid EndUuid() { static Uuid u = ScanUuid(ENDSTYLE); return u; } void TopicEditor::FindBrokenRef() { Uuid codeitem = CodeItemUuid(); Uuid stritem = StructItemUuid();; for(;;) { if(IsNull(topicpath)) return; const RichText& txt = editor.Get(); int c = editor.GetCursor(); int i = txt.FindPart(c); while(++i < txt.GetPartCount()) { if(txt.IsPara(i)) { Uuid style = txt.GetParaStyle(i); if(style == codeitem || style == stritem) { RichPara para = txt.Get(i); if(para.format.label == "noref") continue; bool found = false; String lbl_id = CleanupTppId(para.format.label); if(!IsNull(para.format.label)) for(const auto& f : ~CodeIndex()) for(const AnnotationItem& m : f.value.items) if(FindIndex(AnnotationCandidates(m.id), lbl_id) >= 0) { found = true; break; } if(!found) { editor.Move(txt.GetPartPos(i)); return; } } } } if(!topics_list.IsCursor()) break; c = topics_list.GetCursor() + 1; if(c >= topics_list.GetCount()) { PromptOK("No more invalid references."); break; } topics_list.SetCursor(c); if(!IsNull(topicpath)) editor.Move(0); } } void TopicEditor::Tools(Bar& bar) { String l = editor.GetFormatInfo().label; bool b = l.GetCount() > 2 && l != "noref"; bar.Add(b, "See referenced code", IdeCommonImg::Cpp(), THISBACK(JumpToDefinition)) .Key(K_ALT_U).Key(K_ALT_I); bar.Add("Find broken references..", IdeCommonImg::FindBrokenRef(), THISBACK(FindBrokenRef)) .Key(K_CTRL_F3); bar.Add("Generate Table of Contents", TopicImg::TOC(), [=] { InsertTableOfContents(); }); } void TopicEditor::MainTool(Bar& bar) { editor.StyleTool(bar); bar.Gap(); editor.FontTools(bar); bar.Gap(); editor.InkTool(bar); editor.PaperTool(bar); editor.LastFormatTool(bar); bar.Gap(); editor.LanguageTool(bar); editor.SpellCheckTool(bar); bar.Gap(); editor.IndexEntryTool(bar); bar.Break(); editor.HyperlinkTool(bar, Zx(300), K_CTRL_H); bar.Gap(); editor.ParaTools(bar); bar.Gap(); editor.EditTools(bar); bar.Gap(); bar.Add("Print", CtrlImg::print(), THISBACK(Print)) .Key(K_CTRL_P); bar.GapRight(); bar.Break(); editor.LabelTool(bar, Zx(300), K_CTRL_M, "Code reference"); bar.Gap(); Tools(bar); bar.Gap(); editor.TableTools(bar); } String NaturalDeQtf(const char *s) { StringBuffer r; r.Reserve(256); bool cm = false; while(*s) { if(*s == ' ') r.Cat(cm ? ' ' : '_'); else { if((byte)*s > ' ' && !IsDigit(*s) && !IsAlpha(*s) && (byte)*s < 128) r.Cat('`'); r.Cat(*s); if(*s == ',') cm = true; else cm = false; } s++; } return String(r); } String TopicEditor::GetLang() const { int q = topicpath.ReverseFind('@'); if(q >= 0) { int lang = LNGFromText(~topicpath + q + 1); if(lang) return LNGAsText(lang); } return "%"; } String DecoratedItem(const String& name, const String& pretty) { String qtf = "[%00-00K "; Vector n = ParsePretty(name, pretty); for(int i = 0; i < n.GetCount(); i++) { ItemTextPart& p = n[i]; qtf << "["; switch(p.type) { case ITEM_PNAME: qtf << "*"; case ITEM_NUMBER: qtf << "@r"; break; case ITEM_TNAME: qtf << "*@g"; break; case ITEM_NAME: qtf << "*"; break; case ITEM_UPP: qtf << "@c"; break; case ITEM_CPP_TYPE: case ITEM_CPP: qtf << "@B"; break; } qtf << " \1"; qtf << String(~pretty + p.pos, p.len); qtf << "\1]"; } return qtf + "]"; } String CreateQtf(const AnnotationItem& m, const String& lang, bool onlyhdr = false) { String qtf; bool str = IsStruct(m.kind); if(!str) qtf << "[s4 &]"; String st = str ? "[s2;" : "[s1;"; String k = st + ":" + DeQtf(m.id) + ": "; if(IsTemplate(m.kind) && str) { int q = 0; int w = 0; while(q < m.pretty.GetLength()) { if(m.pretty[q] == '<') w++; if(m.pretty[q] == '>') { w--; if(w == 0) { q++; break; } } q++; } qtf << "[s2:noref: " << DecoratedItem(m.name, m.pretty.Mid(0, q)) << "&][s2 " << k; if(q < m.pretty.GetLength()) { while((byte)m.pretty[q] <= 32) q++; qtf << DecoratedItem(m.name, m.pretty.Mid(q)); } } else qtf << k << DecoratedItem(m.name, m.pretty); qtf << "&]"; if(onlyhdr) return qtf; qtf << "[s3%" << lang << " "; if(!str) { Vector n = ParsePretty(m.name, m.pretty); if(!str) { bool was; for(const auto& h : n) if(h.type == ITEM_PNAME) { qtf << " [%-*@r \1" << m.pretty.Mid(h.pos, h.len) << "\1]"; was = true; } if(was) qtf << " ."; } } qtf << "&]"; qtf << "[s7 &]"; return qtf; } void TopicEditor::InsertNew(const AnnotationItem& m) { editor.BeginOp(); editor.PasteText(ParseQTF(styles + CreateQtf(m, GetLang()))); editor.PrevPara(); editor.PrevPara(); } void TopicEditor::GoTo(const String& _topic, const String& link, const AnnotationItem& create, bool before) { if(topics_list.FindSetCursor(_topic) && !IsNull(link)) { editor.Select(editor.GetLength(), 0); for(String cr : AnnotationCandidates(link)) if(editor.GotoLabel([&](const WString& id) { return cr == CleanupTppId(id.ToString()); })) break; if(create.id.GetCount()) { if(!before) for(int pass = 0; pass < 2; pass++) for(;;) { int c = editor.GetCursor(); RichText::FormatInfo f = editor.GetFormatInfo(); if(f.styleid == BeginUuid() || (IsNull(f.label) || f.label == "noref") && pass) break; editor.NextPara(); if(editor.GetCursor() == c) break; } InsertNew(create); } } } void TopicEditor::GoToPart(int ii) { if(ii >= 0 && ii < editor.Get().GetPartCount()) editor.Move(editor.Get().GetPartPos(ii)); } String TopicEditor::GetFileName() const { return grouppath; } void TopicEditor::Save() { SaveTopic(); SaveInc(); } void TopicEditor::InsertTableOfContents() { struct MyIter : RichText::Iterator { RichPara::Number n; RichEdit *editor; int label = 0; String toc_qtf = "[A5* Table of Contents&]"; bool operator()(int pos, const RichPara& para) override { int lvl = para.format.GetNumberLevel(); if(lvl > 0) { n.Next(para.format); editor->SetSelection(pos, pos); RichText::FormatInfo fmt = editor->GetFormatInfo(); fmt.label = AsString(++label); editor->ApplyFormatInfo(fmt); String s = para.GetText().ToString(); s.TrimStart("\t"); toc_qtf << "[^#" << fmt.label << "^ \1" << String(' ', (lvl - 1) * 4) << n.AsText(para.format) << " " << s << "\1&]"; } return false; }; } it; it.editor = &editor; editor.RemoveSelection(); editor.BeginOp(); int cursor = editor.GetCursor(); editor.Get().Iterate(it); editor.SetSelection(cursor, cursor); editor.PasteText(ParseQTF(it.toc_qtf)); } void TopicEditor::SetFocus() { if(editor.IsEnabled()) editor.SetFocus(); else Ctrl::SetFocus(); }