developing usage tree

This commit is contained in:
Mirek Fidler 2025-03-30 10:39:04 +02:00
parent d2d07bd834
commit ea3443a4c6
9 changed files with 140 additions and 81 deletions

View file

@ -13,7 +13,7 @@ uses(!NOSO) plugin/png;
uses(!NOSO) Draw; uses(!NOSO) Draw;
file file
Builders.h options(BUILDER_OPTION) PCH, Builders.h,
CppBuilder.cpp, CppBuilder.cpp,
MakeFile.cpp, MakeFile.cpp,
CCJ.cpp, CCJ.cpp,

View file

@ -83,7 +83,7 @@ void GatherBaseVirtuals(const String& cls, const String& signature, Index<String
// two bases // two bases
for(String bcls : Split(m.bases, [](int c) { return iscid(c) || c == ':' ? 0 : 1; })) for(String bcls : Split(m.bases, [](int c) { return iscid(c) || c == ':' ? 0 : 1; }))
GatherBaseVirtuals(bcls, signature, ids, visited); GatherBaseVirtuals(bcls, signature, ids, visited);
for(const auto& f : ~CodeIndex()) // now check virtual methods of this cls for(const auto& f : ~CodeIndex()) // now check virtual methods of this cls
for(const AnnotationItem& m : f.value.items) { for(const AnnotationItem& m : f.value.items) {
@ -115,89 +115,93 @@ void GatherVirtuals(const VectorMap<String, String>& bases, const String& cls,
} }
} }
void Ide::GetGlobalUsageIds(const String& id, bool& isvirtual, bool& isstatic, bool& istype, Index<String>& ids)
{
isvirtual = false;
isstatic = false; // to limit file static variables to single file
istype = false;
String cls;
for(const auto& f : ~CodeIndex())
for(const AnnotationItem& m : f.value.items) {
if(m.id == id) {
if(m.isvirtual) {
isvirtual = true;
cls = m.nest;
break;
}
if(IsStruct(m.kind)) {
istype = true;
cls = m.nest;
break;
}
if(m.id == id && m.isstatic && f.key == editfile &&
m.nest.GetCount() == m.nspace.GetCount()) // ignore class variables
isstatic = true;
}
}
ids.FindAdd(id);
if(isvirtual) {
Index<String> visited;
String signature = ScopeWorkaround(id.Mid(cls.GetCount()));
Index<String> base_id;
GatherBaseVirtuals(cls, signature, base_id, visited);
VectorMap<String, String> bases;
for(const auto& f : ~CodeIndex()) // check derived classes
for(const AnnotationItem& m : f.value.items)
if(IsStruct(m.kind))
for(String bcls : Split(m.bases, [](int c) { return iscid(c) || c == ':' ? 0 : 1; }))
bases.Add(bcls, m.id);
visited.Clear();
for(const String& cls : base_id)
GatherVirtuals(bases, cls, signature, ids, visited);
}
}
bool Ide::IsLocalAtCursor(const String& id, Point ref_pos)
{
int li = editor.GetCursorLine();
bool local = false;
AnnotationItem cm = editor.FindCurrentAnnotation(); // what function body are we in?
if(IsFunction(cm.kind)) // do local variables
for(const AnnotationItem& lm : editor.locals)
if(lm.id == id && lm.pos.y >= cm.pos.y && lm.pos.y <= li && ref_pos == lm.pos)
return true;
return false;
}
void Ide::Usage(const String& id, const String& name, Point ref_pos) void Ide::Usage(const String& id, const String& name, Point ref_pos)
{ {
if(IsNull(id)) if(IsNull(id))
return; return;
ResetFileLine(); ResetFileLine();
int li = editor.GetCursorLine();
bool local = false;
AnnotationItem cm = editor.FindCurrentAnnotation(); // what function body are we in?
if(IsFunction(cm.kind)) { // do local variables
for(const AnnotationItem& lm : editor.locals)
if(lm.id == id && lm.pos.y >= cm.pos.y && lm.pos.y <= li) {
if(ref_pos == lm.pos) {
local = true;
break;
}
}
}
NewFFound(); NewFFound();
Progress pi("Indexing files");
while(Indexer::IsRunning()) {
if(pi.StepCanceled())
break;
GuiSleep(10);
}
Index<String> unique; Index<String> unique;
if(local) { if(IsLocalAtCursor(id, ref_pos)) {
AddReferenceLine(editfile, ref_pos, name, unique); AddReferenceLine(editfile, ref_pos, name, unique);
for(const ReferenceItem& lm : editor.references) for(const ReferenceItem& lm : editor.references)
if(lm.id == id && lm.ref_pos == ref_pos) if(lm.id == id && lm.ref_pos == ref_pos)
AddReferenceLine(editfile, lm.pos, name, unique); AddReferenceLine(editfile, lm.pos, name, unique);
} }
else { else {
bool isvirtual = false; bool isvirtual;
bool isstatic = false; // to limit file static variables to single file bool isstatic; // to limit file static variables to single file
bool istype = false; bool istype;
String cls;
Progress pi("Indexing files");
while(Indexer::IsRunning()) {
if(pi.StepCanceled())
break;
GuiSleep(10);
}
for(const auto& f : ~CodeIndex())
for(const AnnotationItem& m : f.value.items) {
if(m.id == id) {
if(m.isvirtual) {
isvirtual = true;
cls = m.nest;
break;
}
if(IsStruct(m.kind)) {
istype = true;
cls = m.nest;
break;
}
if(m.id == id && m.isstatic && f.key == editfile &&
m.nest.GetCount() == m.nspace.GetCount()) // ignore class variables
isstatic = true;
}
}
Index<String> ids; Index<String> ids;
ids.FindAdd(id);
GetGlobalUsageIds(id, isvirtual, isstatic, istype, ids);
if(isvirtual) {
Index<String> visited;
String signature = ScopeWorkaround(id.Mid(cls.GetCount()));
Index<String> base_id;
GatherBaseVirtuals(cls, signature, base_id, visited);
VectorMap<String, String> bases;
for(const auto& f : ~CodeIndex()) // check derived classes
for(const AnnotationItem& m : f.value.items)
if(IsStruct(m.kind))
for(String bcls : Split(m.bases, [](int c) { return iscid(c) || c == ':' ? 0 : 1; }))
bases.Add(bcls, m.id);
visited.Clear();
for(const String& cls : base_id)
GatherVirtuals(bases, cls, signature, ids, visited);
}
UsageId(name, id, ids, istype, isstatic, unique); UsageId(name, id, ids, istype, isstatic, unique);
} }
@ -211,7 +215,6 @@ void Ide::UsageId(const String& name, const String& id, const Index<String>& ids
int q = id.ReverseFind("::"); int q = id.ReverseFind("::");
String constructor = id + "::" + (q >= 0 ? id.Mid(q + 2) : id) + "("; String constructor = id + "::" + (q >= 0 ? id.Mid(q + 2) : id) + "(";
String destructor = id + "::~("; String destructor = id + "::~(";
SortByKey(CodeIndex());
int kind = Null; int kind = Null;
for(int src = 0; src < 2; src++) for(int src = 0; src < 2; src++)
for(const auto& f : ~CodeIndex()) { for(const auto& f : ~CodeIndex()) {
@ -231,7 +234,7 @@ void Ide::UsageId(const String& name, const String& id, const Index<String>& ids
Add(m.pos); Add(m.pos);
} }
} }
if(!IsNull(kind)) if(!IsNull(kind))
FFoundSetIcon(CxxIcon(kind)); FFoundSetIcon(CxxIcon(kind));
} }
@ -261,6 +264,7 @@ void Ide::Usage()
void Ide::IdUsage() void Ide::IdUsage()
{ {
SortByKey(CodeIndex());
if(designer) if(designer)
return; return;
if(editfile.EndsWith(".lay")) { if(editfile.EndsWith(".lay")) {
@ -307,6 +311,43 @@ void Ide::IdUsage()
Usage(ref_id, name, ref_pos); Usage(ref_id, name, ref_pos);
} }
void Ide::DoUsageTree(String parent_id, String id, String fn, Point mpos, String name, Index<String>& unique, int& lvl, String& current, Index<String>& done)
{
/* AddReferenceLine(fn, mpos, name, unique);
if(parent_id != current) {
current = parent_id;
if(lvl < 40 && !IsNull(parent_id) && done.Find(parent_id) < 0 && done.GetCount() < 100) {
done.Add(parent_id);
lvl++;
for(int pass = 0; pass < 2; pass++)
for(const auto& f : ~CodeIndex()) {
for(const AnnotationItem& m : f.value.items) {
if(m.id == parent_id && m.definition != (bool)pass) {
String current;
DLOG(lvl << " " << parent_id);
Usage(parent_id, m.name, m.pos, [&](String parent_id, String id, String fn, Point mpos, String name) {
DoUsageTree(parent_id, id, fn, mpos, name, unique, lvl, current, done);
});
lvl--;
return;
}
}
}
lvl--;
}
}
*/}
void Ide::UsageTree()
{
/* Index<String> unique, done;
int lvl = 0;
String current;
IdUsage([&](String parent_id, String id, String fn, Point mpos, String name) {
DoUsageTree(parent_id, id, fn, mpos, name, unique, lvl, current, done);
});
*/}
void Ide::FindDesignerItemReferences(const String& id, const String& name) void Ide::FindDesignerItemReferences(const String& id, const String& name)
{ {
SaveFile(); SaveFile();
@ -316,7 +357,7 @@ void Ide::FindDesignerItemReferences(const String& id, const String& name)
int q = CodeIndex().Find(path); int q = CodeIndex().Find(path);
if(q < 0) if(q < 0)
return; return;
auto DoUsage = [&](const AnnotationItem& m) { auto DoUsage = [&](const AnnotationItem& m) {
Index<String> ids, unique; Index<String> ids, unique;
ids.Add(m.id); ids.Add(m.id);
@ -332,7 +373,7 @@ void Ide::FindDesignerItemReferences(const String& id, const String& name)
return; return;
} }
} }
// Probably untyped layout item, need to find all classes using the layout // Probably untyped layout item, need to find all classes using the layout
q = id.ReverseFind("::"); q = id.ReverseFind("::");
if(q < 0) return; if(q < 0) return;
@ -358,3 +399,4 @@ void Ide::FindDesignerItemReferences(const String& id, const String& name)
} }
} }
} }

View file

@ -105,6 +105,7 @@ void AnnotationItem::Serialize(Stream& s)
void ReferenceItem::Serialize(Stream& s) void ReferenceItem::Serialize(Stream& s)
{ {
s % id s % id
% parent_id
% pos % pos
% ref_pos % ref_pos
; ;
@ -131,6 +132,7 @@ String CachedAnnotationPath(const String& source_file, const String& defines, co
#ifdef _DEBUG #ifdef _DEBUG
<< "debug" // to have different codebase for development << "debug" // to have different codebase for development
#endif #endif
<< "1" // version
; ;
return CacheFile(GetFileTitle(source_file) + "$" + s.FinishString() + ".code_index"); return CacheFile(GetFileTitle(source_file) + "$" + s.FinishString() + ".code_index");
} }
@ -155,7 +157,7 @@ void DumpIndex(const char *file, const String& what_file)
out << '\t' << n.pos.y << ": " << n.id << " -> " << n.pretty << ", bases: " << n.bases << "\n"; out << '\t' << n.pos.y << ": " << n.id << " -> " << n.pretty << ", bases: " << n.bases << "\n";
out << "\t=== References:\n"; out << "\t=== References:\n";
for(const auto& n : m.value.refs) for(const auto& n : m.value.refs)
out << '\t' << n.pos << " " << n.id << " -> " << n.ref_pos << "\n"; out << '\t' << n.pos << " " << n.id << " -> " << n.ref_pos << ", parent: " << n.parent_id << "\n";
} }
} }

View file

@ -508,4 +508,4 @@ String MakeDefinition(const AnnotationItem& m, const String& klass)
String MakeDefinition(const AnnotationItem& m) String MakeDefinition(const AnnotationItem& m)
{ {
return MakeDefinition(m, GetClass(m)); return MakeDefinition(m, GetClass(m));
} }

View file

@ -302,6 +302,7 @@ bool ClangVisitor::ProcessNode(CXCursor cursor)
String id = ci.Id(); String id = ci.Id();
int kind = ci.Kind(); int kind = ci.Kind();
if(id.GetCount()) { if(id.GetCount()) {
parent_id = id;
LoadSourceLocation(); LoadSourceLocation();
CppFileInfo& f = info.GetAdd(sl.path); CppFileInfo& f = info.GetAdd(sl.path);
AnnotationItem& r = locals ? f.locals.Add() : f.items.Add(); AnnotationItem& r = locals ? f.locals.Add() : f.items.Add();
@ -356,6 +357,7 @@ bool ClangVisitor::ProcessNode(CXCursor cursor)
ReferenceItem rm; ReferenceItem rm;
rm.pos = sl.pos; rm.pos = sl.pos;
rm.id = ref_ci.Id(); rm.id = ref_ci.Id();
rm.parent_id = parent_id;
rm.ref_pos = ref_loc.pos; rm.ref_pos = ref_loc.pos;
#if 0 #if 0
DLOG("======="); DLOG("=======");
@ -397,9 +399,11 @@ CXChildVisitResult clang_visitor(CXCursor cursor, CXCursor p, CXClientData clien
#endif #endif
ClangVisitor *v = (ClangVisitor *)clientData; ClangVisitor *v = (ClangVisitor *)clientData;
bool bak_locals = v->locals; bool bak_locals = v->locals;
String bak_parent_id = v->parent_id;
if(v->ProcessNode(cursor)) if(v->ProcessNode(cursor))
clang_visitChildren(cursor, clang_visitor, clientData); clang_visitChildren(cursor, clang_visitor, clientData);
v->locals = bak_locals; v->locals = bak_locals;
v->parent_id = bak_parent_id;
#ifdef DUMPTREE #ifdef DUMPTREE
LOGEND(); LOGEND();
#endif #endif

View file

@ -144,6 +144,8 @@ struct AnnotationItem : Moveable<AnnotationItem> {
String nest; // Upp::Class String nest; // Upp::Class
String unest; // UPP::CLASS String unest; // UPP::CLASS
String bases; // base classes of struct/class String bases; // base classes of struct/class
String parent_id;
Point pos = Null; Point pos = Null;
int kind = Null; int kind = Null;
bool definition = false; bool definition = false;
@ -160,6 +162,7 @@ String MakeDefinition(const AnnotationItem& m);
struct ReferenceItem : Moveable<ReferenceItem> { struct ReferenceItem : Moveable<ReferenceItem> {
String id; String id;
String parent_id; // function or struct that contains this item
Point pos; Point pos;
Point ref_pos; Point ref_pos;
@ -243,7 +246,8 @@ class ClangVisitor {
CXLocation GetLocation(CXSourceLocation cxlocation); CXLocation GetLocation(CXSourceLocation cxlocation);
SourceLocation GetSourceLocation(const CXLocation& p); SourceLocation GetSourceLocation(const CXLocation& p);
bool locals = false; bool locals = false;
String parent_id;
public: public:
VectorMap<String, CppFileInfo> info; VectorMap<String, CppFileInfo> info;

View file

@ -486,7 +486,7 @@ public:
struct FoundList : ArrayCtrl { struct FoundList : ArrayCtrl {
Button freplace; Button freplace;
Image icon; Image icon;
FoundList(); FoundList();
}; };
@ -667,7 +667,7 @@ public:
true true
#endif #endif
; ;
// Formats editor's code with Ide format parameters // Formats editor's code with Ide format parameters
void FormatJSON_XML(bool xml); void FormatJSON_XML(bool xml);
@ -716,7 +716,7 @@ public:
int animate_autocomplete = 0, animate_autocomplete_dir = 0; int animate_autocomplete = 0, animate_autocomplete_dir = 0;
int animate_indexer = 0, animate_indexer_dir = 0; int animate_indexer = 0, animate_indexer_dir = 0;
int animate_phase = 0; int animate_phase = 0;
Vector<Ptr<TopWindow>> window; Vector<Ptr<TopWindow>> window;
void NewWindow(TopWindow *win); void NewWindow(TopWindow *win);
@ -1001,7 +1001,12 @@ public:
void UsageId(const String& name, const String& id, const Index<String>& ids, bool istype, bool isstatic, Index<String>& unique); void UsageId(const String& name, const String& id, const Index<String>& ids, bool istype, bool isstatic, Index<String>& unique);
void Usage(); void Usage();
void IdUsage(); void IdUsage();
bool IsLocalAtCursor(const String& id, Point ref_pos);
void GetGlobalUsageIds(const String& id, bool& isvirtual, bool& isstatic, bool& istype, Index<String>& ids);
void Usage(const String& id, const String& name, Point ref_pos); void Usage(const String& id, const String& name, Point ref_pos);
void DoUsageTree(String parent_id, String id, String fn, Point mpos, String name, Index<String>& unique,
int& lvl, String& current, Index<String>& done);
void UsageTree();
bool OpenLink(const String& s, int pos); bool OpenLink(const String& s, int pos);
String GetRefId(int pos, String& name, Point& ref_pos); String GetRefId(int pos, String& name, Point& ref_pos);
void ContextGoto0(int pos); void ContextGoto0(int pos);

View file

@ -106,6 +106,7 @@ KEY(THISBACKS, "Events..", K_ALT_T)
KEY(SWAPS, "Go to definition/declaration", K_ALT_I) KEY(SWAPS, "Go to definition/declaration", K_ALT_I)
KEY(IDUSAGE, "Usage", K_ALT_U) KEY(IDUSAGE, "Usage", K_ALT_U)
KEY(USAGE, "Usage of current function", K_SHIFT|K_ALT_U) KEY(USAGE, "Usage of current function", K_SHIFT|K_ALT_U)
KEY(USAGETREE, "Usage tree", K_ALT_O)
KEY(JUMPS, "Context go to", K_ALT_J) KEY(JUMPS, "Context go to", K_ALT_J)
KEY(TOGGLEINDEX, "File index", K_CTRL_F12) KEY(TOGGLEINDEX, "File index", K_CTRL_F12)
KEY(SEARCHINDEX, "File index search", K_F12) KEY(SEARCHINDEX, "File index search", K_F12)

View file

@ -860,8 +860,9 @@ void Ide::AssistMenu(Bar& menu)
menu.Add(!designer, AK_JUMPS, [=] { ContextGoto(); }); menu.Add(!designer, AK_JUMPS, [=] { ContextGoto(); });
menu.Add(!designer, AK_SWAPS, THISBACK(SwapS)); menu.Add(!designer, AK_SWAPS, THISBACK(SwapS));
menu.Add(!designer, AK_DCOPY, callback(&editor, &AssistEditor::DCopy)); menu.Add(!designer, AK_DCOPY, callback(&editor, &AssistEditor::DCopy));
menu.Add(!designer, AK_IDUSAGE, THISBACK(IdUsage)); menu.Add(!designer, AK_IDUSAGE, [=] { IdUsage(); });
menu.Add(!designer, AK_USAGE, [=] { Usage(); }); menu.Add(!designer, AK_USAGE, [=] { Usage(); });
menu.Add(!designer, AK_USAGETREE, [=] { UsageTree(); });
menu.Add(!designer, AK_GOTOGLOBAL, THISBACK(NavigatorDlg)); menu.Add(!designer, AK_GOTOGLOBAL, THISBACK(NavigatorDlg));
menu.Add(!designer, AK_VIRTUALS, callback(&editor, &AssistEditor::Virtuals)); menu.Add(!designer, AK_VIRTUALS, callback(&editor, &AssistEditor::Virtuals));
menu.Add(!designer, AK_THISBACKS, callback(&editor, &AssistEditor::Events)); menu.Add(!designer, AK_THISBACKS, callback(&editor, &AssistEditor::Events));