mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
350 lines
8.4 KiB
C++
350 lines
8.4 KiB
C++
#include "ide.h"
|
|
|
|
struct VirtualMethod : AnnotationItem {
|
|
String defined;
|
|
String last_override;
|
|
};
|
|
|
|
void GatherVirtuals(ArrayMap<String, VirtualMethod>& virtuals, const String& cls,
|
|
Index<String>& visited, bool first)
|
|
{
|
|
if(IsNull(cls) || visited.Find(cls) >= 0)
|
|
return;
|
|
visited.Add(cls);
|
|
for(const auto& f : ~CodeIndex()) // do base classes first
|
|
for(const AnnotationItem& m : f.value.items) {
|
|
if(m.id == cls && m.bases.GetCount()) {
|
|
// we cheat with With..<TopWindow> by splitting it to With... and TopWindow
|
|
for(String s : Split(m.bases, [](int c) { return iscid(c) || c == ':' ? 0 : 1; }))
|
|
GatherVirtuals(virtuals, s, visited, false);
|
|
}
|
|
}
|
|
if(!first)
|
|
for(const auto& f : ~CodeIndex()) // now gather virtual methods of this class
|
|
for(const AnnotationItem& m : f.value.items) {
|
|
if(m.nest == cls && IsFunction(m.kind)) {
|
|
String signature = m.id.Mid(m.nest.GetCount());
|
|
int q = virtuals.Find(signature);
|
|
if(q >= 0)
|
|
virtuals[q].last_override = cls;
|
|
else
|
|
if(m.isvirtual) {
|
|
VirtualMethod& vm = virtuals.Add(signature);
|
|
(AnnotationItem&)vm = m;
|
|
vm.defined = vm.last_override = cls;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct VirtualsDisplay : Display {
|
|
void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const {
|
|
w.DrawRect(r, paper);
|
|
VirtualMethod m = q.To<VirtualMethod>();
|
|
bool focuscursor = (style & (FOCUS|CURSOR)) == (FOCUS|CURSOR) || (style & SELECT);
|
|
PaintCpp(w, r, m.kind, m.name, m.pretty, ink, focuscursor, true);
|
|
}
|
|
};
|
|
|
|
struct VirtualsDlg : public WithVirtualsLayout<TopWindow> {
|
|
ArrayMap<String, VirtualMethod> virtuals;
|
|
Index<String> unneeded_qualifications;
|
|
|
|
virtual bool Key(dword key, int count)
|
|
{
|
|
if(key == K_CTRL_K) {
|
|
find.SetFocus();
|
|
return true;
|
|
}
|
|
return HotKey(key) || find.Key(key, count);
|
|
}
|
|
|
|
void Sync() {
|
|
String name = ~find;
|
|
String k;
|
|
if(list.IsCursor())
|
|
k = list.Get("id");
|
|
list.Clear();
|
|
for(const auto& m : ~virtuals)
|
|
if(ToUpper(m.value.name).Find(name) >= 0)
|
|
list.Add(RawToValue(m.value), m.value.defined, m.value.last_override, m.value.id);
|
|
if(!list.FindSetCursor(k, "id"))
|
|
list.GoBegin();
|
|
}
|
|
|
|
void DblClk() {
|
|
if(list.IsCursor()) {
|
|
list.Set(5, 1);
|
|
Break(IDOK);
|
|
}
|
|
}
|
|
|
|
void Serialize(Stream& s)
|
|
{
|
|
SerializePlacement(s);
|
|
list.SerializeHeader(s);
|
|
s % add_virtual % add_override;
|
|
}
|
|
|
|
typedef VirtualsDlg CLASSNAME;
|
|
|
|
VirtualsDlg(const String& nest, const String& local_bases, const Index<String>& namespaces) {
|
|
Index<String> classes;
|
|
if(local_bases.GetCount())
|
|
for(String b : Split(local_bases, ';'))
|
|
GatherVirtuals(virtuals, b, classes, false);
|
|
else
|
|
GatherVirtuals(virtuals, nest, classes, true);
|
|
|
|
for(String cls : classes) {
|
|
Vector<String> split = Split(cls, ':');
|
|
if(split.GetCount()) {
|
|
split.Drop(); // remove type name (e.g. [Upp, TopWindow] -> [Upp])
|
|
String qual;
|
|
for(String s : split) {
|
|
MergeWith(qual, "::", s);
|
|
if(namespaces.Find(qual) < 0) // e.g. if base is Upp::TopWindow, we will not ignore Upp::
|
|
unneeded_qualifications.FindAdd(qual);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(VirtualMethod& m : virtuals) { // remove unneeded qualifications
|
|
String r;
|
|
const char *s = m.pretty;
|
|
const char *b = s;
|
|
while(*s) {
|
|
if(iscib(*s)) {
|
|
r.Cat(b, s);
|
|
b = s;
|
|
try {
|
|
CParser p(s);
|
|
String qual;
|
|
while(!p.IsEof()) {
|
|
qual = p.ReadId();
|
|
if(!p.Char2(':', ':') || !p.IsId())
|
|
break;
|
|
if(unneeded_qualifications.Find(qual) >= 0)
|
|
b = p.GetPtr(); // skip uneeded qualification
|
|
qual << "::";
|
|
}
|
|
s = p.GetPtr();
|
|
}
|
|
catch(CParser::Error) {
|
|
if(*s) s++; // should not happen, but if it does, move on
|
|
}
|
|
}
|
|
else
|
|
s++;
|
|
}
|
|
r.Cat(b, s);
|
|
m.pretty = r;
|
|
}
|
|
|
|
CtrlLayoutOKCancel(*this, "Virtual methods");
|
|
list.AddColumn("Virtual function").SetDisplay(Single<VirtualsDisplay>());
|
|
list.AddColumn("Defined in");
|
|
list.AddColumn("Last override");
|
|
list.AddIndex("id");
|
|
list.SetLineCy(Arial(Zy(11)).Info().GetHeight() + DPI(3));
|
|
list.ColumnWidths("500 100 100");
|
|
list.WhenLeftDouble = THISBACK(DblClk);
|
|
list.MultiSelect();
|
|
list.EvenRowColor();
|
|
Sizeable().Zoomable();
|
|
find.NullText("Search (Ctrl+K)");
|
|
find.SetFilter([](int c) { return iscid(c) ? ToUpper(c) : 0; });
|
|
find <<= THISBACK(Sync);
|
|
add_override <<= true;
|
|
add_virtual <<= false;
|
|
Sync();
|
|
}
|
|
};
|
|
|
|
INITBLOCK
|
|
{
|
|
RegisterGlobalConfig("VirtualsDlg");
|
|
}
|
|
|
|
AnnotationItem AssistEditor::FindCurrentNest(String *local_bases)
|
|
{
|
|
AnnotationItem cm;
|
|
if(!WaitCurrentFile())
|
|
return cm;
|
|
cm = FindCurrentAnnotation();
|
|
int li = GetCursorLine();
|
|
if(IsFunction(cm.kind) && local_bases) { // do local classes
|
|
for(const AnnotationItem& lm : locals) {
|
|
if(lm.pos.y >= cm.pos.y && lm.pos.y <= li && lm.bases.GetCount())
|
|
cm = lm;
|
|
if(lm.pos.y > li)
|
|
break;
|
|
}
|
|
*local_bases = cm.bases;
|
|
return cm;
|
|
}
|
|
|
|
if(IsNull(cm.nest))
|
|
Exclamation("No class can be associated with current position.");
|
|
return cm;
|
|
}
|
|
|
|
void AssistEditor::Virtuals()
|
|
{
|
|
SortByKey(CodeIndex());
|
|
String local_bases;
|
|
AnnotationItem cm = FindCurrentNest(&local_bases);
|
|
if(IsNull(cm.nest))
|
|
return;
|
|
|
|
Point pos = GetCurrentPos();
|
|
AnnotationItem q; // class declaration to gather explicit namespaces in bases
|
|
for(const AnnotationItem& m : annotations) {
|
|
if(m.pos.y > pos.y)
|
|
break;
|
|
if(IsStruct(m.kind))
|
|
q = m;
|
|
}
|
|
|
|
|
|
Index<String> namespaces; // These namespaces cannot be ignored
|
|
|
|
try {
|
|
int line = q.pos.y;
|
|
while(line < GetLineCount()) {
|
|
String s = GetUtf8Line(line++);
|
|
CParser p(s);
|
|
String prospective_namespace;
|
|
bool done = false;
|
|
while(!p.IsEof()) {
|
|
if(p.IsId())
|
|
prospective_namespace << p.ReadId();
|
|
else
|
|
if(p.Char2(':', ':')) {
|
|
if(prospective_namespace.GetCount() > 2)
|
|
namespaces.Add(prospective_namespace);
|
|
prospective_namespace << "::";
|
|
}
|
|
else {
|
|
prospective_namespace.Clear();
|
|
if(p.Char('{') || p.Char(';')) {
|
|
done = true;
|
|
break;
|
|
}
|
|
p.Skip();
|
|
}
|
|
}
|
|
if(done)
|
|
break;
|
|
}
|
|
}
|
|
catch(CParser::Error()) {}
|
|
VirtualsDlg dlg(cm.nest, local_bases, namespaces);
|
|
LoadFromGlobal(dlg, "VirtualsDlg");
|
|
int c = dlg.Run();
|
|
StoreToGlobal(dlg, "VirtualsDlg");
|
|
if(c != IDOK)
|
|
return;
|
|
String text;
|
|
String ctext;
|
|
if(!dlg.list.IsSelection() && dlg.list.IsCursor())
|
|
dlg.list.Select(dlg.list.GetCursor());
|
|
for(int i = 0; i < dlg.list.GetCount(); i++)
|
|
if(dlg.list.IsSel(i)) {
|
|
VirtualMethod m = dlg.list.Get(i, 0).To<VirtualMethod>();
|
|
m.pretty.TrimEnd(" = 0");
|
|
text << "\t";
|
|
if(dlg.add_virtual)
|
|
text << "virtual ";
|
|
text << m.pretty;
|
|
if(dlg.add_override)
|
|
text << " override";
|
|
text << ";\r\n";
|
|
ctext << MakeDefinition(m, cm.nest + "::");
|
|
}
|
|
Paste(text.ToWString());
|
|
WriteClipboardText(ctext);
|
|
}
|
|
|
|
void AssistEditor::ConvertToOverrides()
|
|
{
|
|
// Returns insertion point after trailing qualifiers and sets has_final
|
|
auto SkipTrailingQualifiers = [](const char *start, bool& hasfinal) -> const char* {
|
|
const char *ins = start;
|
|
hasfinal = false;
|
|
for(;;) {
|
|
const char *q = ins;
|
|
while(*q == ' ' || *q == '\t' || *q == '\n' || *q == '\r') // Multiline support
|
|
q++;
|
|
if(memcmp(q, "const", 5) == 0 && !iscid(q[5]))
|
|
ins = q + 5;
|
|
else
|
|
if(memcmp(q, "noexcept", 8) == 0 && !iscid(q[8]))
|
|
ins = q + 8;
|
|
else
|
|
if(memcmp(q, "final", 5) == 0 && !iscid(q[5])) {
|
|
ins = q + 5;
|
|
hasfinal = true;
|
|
}
|
|
else
|
|
if(*q == '&' || *q == '*')
|
|
ins = q + 1;
|
|
else
|
|
if(*q == '[') { // attributes [[...]]
|
|
int depth = 1;
|
|
q++;
|
|
while(*q && depth) {
|
|
if(*q == '[') depth++;
|
|
else if(*q == ']') depth--;
|
|
q++;
|
|
}
|
|
ins = q;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return ins;
|
|
};
|
|
|
|
Make([&](String& out) {
|
|
String r;
|
|
const char *s = out;
|
|
bool virt = false;
|
|
int lvl = 0;
|
|
while(*s) {
|
|
const char *b = s;
|
|
while(iscid(*s))
|
|
s++;
|
|
int len = int(s - b);
|
|
if(len == 7 && memcmp(b, "virtual", 7) == 0) {
|
|
virt = true;
|
|
lvl = 0;
|
|
while(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') // Multiline support
|
|
s++;
|
|
}
|
|
else {
|
|
r.Cat(b, s);
|
|
if(*s == '(')
|
|
lvl++;
|
|
r.Cat(*s);
|
|
if(*s == ')') {
|
|
lvl = max(lvl - 1, 0);
|
|
if(lvl == 0 && virt) {
|
|
bool hasfinal = false;
|
|
const char *q = SkipTrailingQualifiers(s + 1, hasfinal);
|
|
// Append everything up to insertion point
|
|
r.Cat(s + 1, int(q - (s + 1)));
|
|
// Insert override if not final
|
|
if(!hasfinal)
|
|
r.Cat(" override");
|
|
s = q;
|
|
virt = false;
|
|
continue;
|
|
}
|
|
}
|
|
s++;
|
|
}
|
|
}
|
|
out = r;
|
|
});
|
|
}
|