ultimatepp/uppsrc/ide/clang/CurrentFile.cpp
2022-07-07 00:49:37 +02:00

381 lines
12 KiB
C++

#include "clang.h"
#include <cxxabi.h>
#define LLOG(x)
Semaphore current_file_event;
CurrentFileContext current_file;
bool do_autocomplete;
bool autocomplete_macros;
Point autocomplete_pos;
int64 autocomplete_serial;
Event<const Vector<AutoCompleteItem>&> autocomplete_done;
bool do_annotations;
Event<const Vector<AnnotationItem>&> annotations_done;
CXChildVisitResult current_file_visitor( CXCursor cursor, CXCursor p, CXClientData clientData)
{
CXSourceLocation cxlocation = clang_getCursorLocation(cursor);
bool valid = clang_Location_isFromMainFile(cxlocation);
// TODO: change this for MT
static CXPrintingPolicy pp_id = clang_getCursorPrintingPolicy(cursor);
ONCELOCK {
for(int i = 0; i <= CXPrintingPolicy_LastProperty; i++)
clang_PrintingPolicy_setProperty(pp_id, (CXPrintingPolicyProperty)i, 0);
for(CXPrintingPolicyProperty p : {
CXPrintingPolicy_SuppressSpecifiers,
CXPrintingPolicy_SuppressTagKeyword,
CXPrintingPolicy_SuppressUnwrittenScope,
CXPrintingPolicy_SuppressInitializers,
CXPrintingPolicy_SuppressStrongLifetime,
CXPrintingPolicy_SuppressLifetimeQualifiers,
CXPrintingPolicy_SuppressTemplateArgsInCXXConstructors,
CXPrintingPolicy_TerseOutput,
CXPrintingPolicy_SuppressImplicitBase,
CXPrintingPolicy_FullyQualifiedName })
clang_PrintingPolicy_setProperty(pp_id, p, 1);
};
if(valid) {
String m;
CXCursor parent = clang_getCursorSemanticParent(cursor);
CXCursorKind cursorKind = clang_getCursorKind(cursor);
CXCursorKind parentKind = clang_getCursorKind(parent);
String name = GetCursorSpelling(cursor);
String type = GetTypeSpelling(cursor);
String id = FetchString(clang_getCursorPrettyPrinted(cursor, pp_id));
String scope = GetTypeSpelling(parent);
if(scope.GetCount() == 0) { // type is fully qualified, otherwise scan up for namespaces
CXCursor p = parent;
for(;;) {
CXCursorKind k = clang_getCursorKind(p);
if(findarg(k, CXCursor_Namespace, CXCursor_ClassTemplate) < 0)
break;
String q = GetCursorSpelling(p);
if(scope.GetCount())
q << "::" << scope;
scope = q;
p = clang_getCursorSemanticParent(p);
}
}
int q = scope.Find('('); // 'Struct::(unnamed enum at C:\u\upp.src\upptst\Annotations\main.cpp:47:2)'
if(q >= 0)
scope.Trim(q);
if(scope.GetCount() && *scope.Last() != ':')
scope << "::";
auto Dump = [&] {
#if 0
SourceLocation location(cxlocation);
LOG("=====================");
// DDUMP(location);
// DDUMP((int)cursorKind);
DDUMP(GetCursorKindName(cursorKind));
DDUMP(name);
DDUMP(type);
DDUMP(id);
DDUMP(CleanupId(id));
DDUMP(scope);
DDUMP(clang_isCursorDefinition(cursor));
static CXPrintingPolicy pp = clang_getCursorPrintingPolicy(cursor);
ONCELOCK {
for(int i = 0; i <= CXPrintingPolicy_LastProperty; i++)
clang_PrintingPolicy_setProperty(pp, (CXPrintingPolicyProperty)i, 0);
for(CXPrintingPolicyProperty p : {
CXPrintingPolicy_SuppressSpecifiers,
CXPrintingPolicy_SuppressTagKeyword,
// CXPrintingPolicy_IncludeTagDefinition,
// CXPrintingPolicy_SuppressScope,
CXPrintingPolicy_SuppressUnwrittenScope,
CXPrintingPolicy_SuppressInitializers,
// CXPrintingPolicy_ConstantArraySizeAsWritten,
// CXPrintingPolicy_AnonymousTagLocations,
CXPrintingPolicy_SuppressStrongLifetime,
CXPrintingPolicy_SuppressLifetimeQualifiers,
CXPrintingPolicy_SuppressTemplateArgsInCXXConstructors,
// CXPrintingPolicy_Bool,
// CXPrintingPolicy_Restrict,
// CXPrintingPolicy_Alignof,
// CXPrintingPolicy_UnderscoreAlignof,
// CXPrintingPolicy_UseVoidForZeroParams,
CXPrintingPolicy_TerseOutput,
// CXPrintingPolicy_PolishForDeclaration,
// CXPrintingPolicy_Half,
// CXPrintingPolicy_MSWChar,
// CXPrintingPolicy_IncludeNewlines,
// CXPrintingPolicy_MSVCFormatting,
// CXPrintingPolicy_ConstantsAsWritten,
CXPrintingPolicy_SuppressImplicitBase,
CXPrintingPolicy_FullyQualifiedName })
clang_PrintingPolicy_setProperty(pp, p, 1);
};
{
// DTIMING("PrettyPrinting");
// DDUMP(FetchString(clang_getCursorPrettyPrinted(cursor, pp)));
}
// DDUMP(FetchString(clang_Cursor_getMangling(cursor)));
// DDUMP(CxxDemangle(FetchString(clang_Cursor_getMangling(cursor))));
// DDUMP(GetCursorKindName(parentKind));
// DDUMP(GetCursorSpelling(parent));
// DDUMP(GetTypeSpelling(parent));
#endif
};
// Dump();
switch(cursorKind) {
case CXCursor_StructDecl:
case CXCursor_UnionDecl:
case CXCursor_ClassDecl:
case CXCursor_FunctionTemplate:
case CXCursor_FunctionDecl:
case CXCursor_Constructor:
case CXCursor_Destructor:
case CXCursor_CXXMethod:
m = id;
break;
case CXCursor_VarDecl:
if(findarg(parentKind, CXCursor_FunctionTemplate, CXCursor_FunctionDecl, CXCursor_CXXMethod) >= 0) {
valid = false; // local variable
break;
}
case CXCursor_ClassTemplate:
case CXCursor_FieldDecl:
m << scope << name;
break;
case CXCursor_ConversionFunction:
m << scope << "operator " << type;
break;
case CXCursor_MacroDefinition:
m = name;
break;
case CXCursor_EnumConstantDecl:
m << scope << name;
break;
case CXCursor_EnumDecl:
// case CXCursor_ParmDecl:
case CXCursor_TypedefDecl:
case CXCursor_Namespace:
case CXCursor_UnexposedDecl:
// case CXCursor_NamespaceAlias:
break;
default:
valid = false;
break;
}
if(valid) {
Dump();
if(m.GetCount()) {
CXFile file;
unsigned line_;
unsigned column_;
unsigned offset_;
clang_getExpansionLocation(cxlocation, &file, &line_, &column_, &offset_);
AnnotationItem& r = static_cast<Vector<AnnotationItem> *>(clientData)->Add();
r.line = line_;
r.id = CleanupId(m);
static CXPrintingPolicy pp;
ONCELOCK {
pp = clang_getCursorPrintingPolicy(cursor);
for(int i = 0; i <= CXPrintingPolicy_LastProperty; i++)
clang_PrintingPolicy_setProperty(pp, (CXPrintingPolicyProperty)i, 0);
clang_PrintingPolicy_setProperty(pp, CXPrintingPolicy_TerseOutput, 1);
clang_PrintingPolicy_setProperty(pp, CXPrintingPolicy_SuppressScope, 1);
}
r.pretty = CleanupSignature(FetchString(clang_getCursorPrettyPrinted(cursor, pp)));
}
}
}
clang_visitChildren(cursor, current_file_visitor, clientData);
return CXChildVisit_Continue;
}
void ReadAutocomplete(const CXCompletionString& string, String& name, String& signature)
{
const int chunkCount = clang_getNumCompletionChunks(string);
for(int j = 0; j < chunkCount; j++) {
const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j);
String text = FetchString(clang_getCompletionChunkText(string, j));
if(chunkKind == CXCompletionChunk_Optional)
for(int i = 0; i < clang_getNumCompletionChunks(string); i++)
ReadAutocomplete(clang_getCompletionChunkCompletionString(string, i), name, signature);
else
if(chunkKind == CXCompletionChunk_TypedText) {
name = text;
signature << text;
}
else {
signature << text;
if (chunkKind == CXCompletionChunk_ResultType) {
signature << ' ';
}
}
}
}
void CurrentFileThread()
{
MemoryIgnoreLeaksBlock __;
CurrentFileContext parsed_file;
CXTranslationUnit tu = nullptr;
int64 serial;
auto DisposeTU = [&] {
if(tu) clang_disposeTranslationUnit(tu);
tu = nullptr;
};
auto DoAnnotations = [&] {
if(!tu || !annotations_done) return;
Vector<AnnotationItem> item;
clang_visitChildren(clang_getTranslationUnitCursor(tu), current_file_visitor, &item);
Ctrl::Call([&] {
if(parsed_file.filename == current_file.filename &&
parsed_file.real_filename == current_file.real_filename &&
parsed_file.includes == current_file.includes)
annotations_done(item);
do_annotations = false;
});
};
while(!Thread::IsShutdownThreads()) {
CurrentFileContext f;
bool autocomplete_do, annotations_do, macros;
{
GuiLock __;
f = current_file;
serial = autocomplete_serial;
autocomplete_do = do_autocomplete;
annotations_do = do_annotations;
macros = autocomplete_macros;
}
String fn = f.filename;
if(!IsSourceFile(fn))
fn.Cat(".cpp");
if(f.filename != parsed_file.filename || f.includes != parsed_file.includes ||
f.real_filename != parsed_file.real_filename || !tu) {
parsed_file = f;
DisposeTU();
String cmdline;
cmdline << fn << " -DflagDEBUG -DflagDEBUG_FULL -DflagBLITZ -DflagWIN32 -DflagMAIN -DflagGUI -xc++ -std=c++17 ";
for(String s : Split(f.includes, ';'))
cmdline << " -I" << s;
#ifdef PLATFORM_WIN32
tu = Clang(cmdline, { { ~fn, f.content } },
CXTranslationUnit_DetailedPreprocessingRecord|
CXTranslationUnit_PrecompiledPreamble|
CXTranslationUnit_CreatePreambleOnFirstParse|
CXTranslationUnit_KeepGoing|
CXTranslationUnit_RetainExcludedConditionalBlocks);
#else
tu = Clang(cmdline, { { ~fn, f.content } },
CXTranslationUnit_KeepGoing|
CXTranslationUnit_RetainExcludedConditionalBlocks);
#endif
DoAnnotations();
annotations_do = false;
// DumpDiagnostics(tu);
}
if(Thread::IsShutdownThreads()) break;
CXUnsavedFile ufile = { ~fn, ~f.content, (unsigned)f.content.GetCount() };
if(autocomplete_do && tu) {
CXCodeCompleteResults *results;
{
TIMESTOP("clang_codeCompleteAt");
results = clang_codeCompleteAt(tu, fn, autocomplete_pos.y, autocomplete_pos.x, &ufile, 1,
macros ? CXCodeComplete_IncludeMacros : 0);
}
// DumpDiagnostics(tu);
if(results) {
Vector<AutoCompleteItem> item;
for(int i = 0; i < results->NumResults; i++) {
const CXCompletionString& string = results->Results[i].CompletionString;
int kind = results->Results[i].CursorKind;
if(kind == CXCursor_MacroDefinition && !macros) // we probably want this only on Ctrl+Space
continue;
if(kind == CXCursor_NotImplemented)
continue;
String name;
String signature;
ReadAutocomplete(string, name, signature);
AutoCompleteItem& m = item.Add();
m.name = name;
m.parent = FetchString(clang_getCompletionParent(string, NULL));
m.signature = CleanupSignature(signature);
m.kind = kind;
m.priority = clang_getCompletionPriority(string);
}
clang_disposeCodeCompleteResults(results);
Ctrl::Call([&] {
if(serial == autocomplete_serial)
autocomplete_done(item);
});
}
GuiLock __;
do_autocomplete = false;
}
if(Thread::IsShutdownThreads()) break;
if(annotations_do && tu) {
TIMESTOP("ReParse");
if(clang_reparseTranslationUnit(tu, 1, &ufile, 0))
DisposeTU();
else
DoAnnotations();
}
current_file_event.Wait(500);
}
DisposeTU();
}
void StartCurrentFileParserThread()
{
MemoryIgnoreNonMainLeaks();
MemoryIgnoreNonUppThreadsLeaks(); // clangs leaks static memory in threads
Thread::StartNice([] { CurrentFileThread(); });
}
void SetCurrentFile(const CurrentFileContext& ctx, Event<const Vector<AnnotationItem>&> done)
{
GuiLock __;
current_file = ctx;
do_annotations = true;
annotations_done = done;
current_file_event.Release();
}
void StartAutoComplete(const CurrentFileContext& ctx, int line, int column, bool macros,
Event<const Vector<AutoCompleteItem>&> done)
{
GuiLock __;
static int64 serial;
autocomplete_pos.y = line;
autocomplete_pos.x = column;
do_autocomplete = true;
autocomplete_macros = macros;
autocomplete_serial = serial++;
autocomplete_done = done;
current_file = ctx;
current_file_event.Release();
}
void CancelAutoComplete()
{
GuiLock __;
autocomplete_done.Clear();
do_autocomplete = false;
}
void CancelCurrentFile()
{
GuiLock __;
CancelAutoComplete();
annotations_done.Clear();
}