Darken/fade code disabled by preprocessor.

This commit is contained in:
Jacob Dufault 2017-05-20 14:45:46 -07:00
parent f3d00dea23
commit b79b98f464
13 changed files with 201 additions and 86 deletions

View File

@ -27,6 +27,7 @@ be productive with cquery. Here's a list of implemented features:
* hover * hover
* diagnostics * diagnostics
* code actions (clang FixIts) * code actions (clang FixIts)
* darken/fade code disabled by preprocessor
# Setup - build cquery, install extension, setup project # Setup - build cquery, install extension, setup project

View File

@ -65,8 +65,6 @@ optional<lsDiagnostic> BuildDiagnostic(CXDiagnostic diagnostic) {
// Report fixits // Report fixits
unsigned num_fixits = clang_getDiagnosticNumFixIts(diagnostic); unsigned num_fixits = clang_getDiagnosticNumFixIts(diagnostic);
if (num_fixits > 0)
std::cerr << "!!!! Got " << num_fixits << " fixits" << std::endl;
for (unsigned i = 0; i < num_fixits; ++i) { for (unsigned i = 0; i < num_fixits; ++i) {
CXSourceRange replacement_range; CXSourceRange replacement_range;
CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range); CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range);

View File

@ -167,6 +167,7 @@ IpcManager* IpcManager::instance_ = nullptr;
bool ShouldDisplayIpcTiming(IpcId id) { bool ShouldDisplayIpcTiming(IpcId id) {
switch (id) { switch (id) {
case IpcId::TextDocumentPublishDiagnostics: case IpcId::TextDocumentPublishDiagnostics:
case IpcId::CqueryPublishInactiveRegions:
return false; return false;
default: default:
return true; return true;
@ -757,6 +758,16 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFil
void PublishInactiveLines(WorkingFile* working_file, const std::vector<Range>& inactive) {
Out_CquerySetInactiveRegion out;
out.params.uri = lsDocumentUri::FromPath(working_file->filename);
for (Range skipped : inactive) {
optional<lsRange> ls_skipped = GetLsRange(working_file, skipped);
if (ls_skipped)
out.params.inactiveRegions.push_back(*ls_skipped);
}
IpcManager::instance()->SendOutMessageToClient(IpcId::CqueryPublishInactiveRegions, out);
}
@ -1104,6 +1115,7 @@ void ParseFile(IndexerConfig* config,
// |is_interactive| will be true, or they can change the flags cquery runs // |is_interactive| will be true, or they can change the flags cquery runs
// with, in which case vscode will get restarted. // with, in which case vscode will get restarted.
if (is_interactive || !new_index->diagnostics.empty()) { if (is_interactive || !new_index->diagnostics.empty()) {
// Emit diagnostics.
Out_TextDocumentPublishDiagnostics diag; Out_TextDocumentPublishDiagnostics diag;
diag.params.uri = lsDocumentUri::FromPath(new_index->path); diag.params.uri = lsDocumentUri::FromPath(new_index->path);
diag.params.diagnostics = new_index->diagnostics; diag.params.diagnostics = new_index->diagnostics;
@ -1111,8 +1123,20 @@ void ParseFile(IndexerConfig* config,
// Cache diagnostics so we can show fixits. // Cache diagnostics so we can show fixits.
WorkingFile* working_file = working_files->GetFileByFilename(new_index->path); WorkingFile* working_file = working_files->GetFileByFilename(new_index->path);
if (working_file) if (working_file) {
working_file->diagnostics = new_index->diagnostics; working_file->diagnostics = new_index->diagnostics;
// Publish source ranges disabled by preprocessor.
if (is_interactive) {
// TODO: We shouldn't be updating actual indexed content here, but we
// need to use the latest indexed content for the remapping.
// TODO: We should also remap diagnostics.
if (indexed_content)
working_file->SetIndexContent(*indexed_content);
PublishInactiveLines(working_file, new_index->skipped_by_preprocessor);
}
}
} }
@ -1666,7 +1690,12 @@ bool QueryDbMainLoop(
working_file->SetIndexContent(*cached_file_contents); working_file->SetIndexContent(*cached_file_contents);
else else
working_file->SetIndexContent(working_file->buffer_content); working_file->SetIndexContent(working_file->buffer_content);
time.ResetAndPrint("[querydb] Loading cached index file for DidOpen");
std::unique_ptr<IndexFile> cache = LoadCachedIndex(config, msg->params.textDocument.uri.GetPath());
if (cache && !cache->skipped_by_preprocessor.empty())
PublishInactiveLines(working_file, cache->skipped_by_preprocessor);
time.ResetAndPrint("[querydb] Loading cached index file for DidOpen (blocks CodeLens)");
break; break;
} }
@ -2093,7 +2122,6 @@ bool QueryDbMainLoop(
} }
} }
response.Write(std::cerr);
ipc->SendOutMessageToClient(IpcId::TextDocumentCodeAction, response); ipc->SendOutMessageToClient(IpcId::TextDocumentCodeAction, response);
break; break;
} }

View File

@ -47,9 +47,125 @@ Range ResolveExtent(const CXCursor& cx_cursor) {
return Resolve(cx_range); return Resolve(cx_range);
} }
struct NamespaceHelper {
std::unordered_map<std::string, std::string> container_usr_to_qualified_name;
void RegisterQualifiedName(std::string usr,
const CXIdxContainerInfo* container,
std::string qualified_name) {
if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr);
if (it != container_usr_to_qualified_name.end()) {
container_usr_to_qualified_name[usr] =
it->second + qualified_name + "::";
return;
}
}
container_usr_to_qualified_name[usr] = qualified_name + "::";
}
std::string QualifiedName(const CXIdxContainerInfo* container,
std::string unqualified_name) {
if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr);
if (it != container_usr_to_qualified_name.end())
return it->second + unqualified_name;
// Anonymous namespaces are not processed by indexDeclaration. If we
// encounter one insert it into map.
if (container->cursor.kind == CXCursor_Namespace) {
// assert(clang::Cursor(container->cursor).get_spelling() == "");
container_usr_to_qualified_name[container_usr] = "::";
return "::" + unqualified_name;
}
}
return unqualified_name;
}
};
struct IndexParam {
// Only use this when strictly needed (ie, primary translation unit is
// needed). Most logic should get the IndexFile instance via
// |file_consumer|.
//
// This can be null if we're not generating an index for the primary
// translation unit.
IndexFile* primary_file = nullptr;
clang::TranslationUnit* tu = nullptr;
FileConsumer* file_consumer = nullptr;
NamespaceHelper ns;
IndexParam(clang::TranslationUnit* tu, FileConsumer* file_consumer) : tu(tu), file_consumer(file_consumer) {}
};
IndexFile* ConsumeFile(IndexParam* param, CXFile file) {
bool is_first_ownership = false;
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_ownership);
// Mark dependency in primary file. If primary_file is null that means we're
// doing a re-index in which case the dependency has already been established
// in a previous index run.
if (is_first_ownership && param->primary_file)
param->primary_file->dependencies.push_back(db->path);
if (is_first_ownership) {
// Report skipped source range list.
CXSourceRangeList* skipped = clang_getSkippedRanges(param->tu->cx_tu, file);
for (unsigned i = 0; i < skipped->count; ++i) {
Range range = Resolve(skipped->ranges[i]);
// clang_getSkippedRanges reports start one token after the '#', move it
// back so it starts at the '#'
range.start.column -= 1;
db->skipped_by_preprocessor.push_back(range);
}
clang_disposeSourceRangeList(skipped);
}
return db;
}
} // namespace } // namespace
IndexFile::IndexFile(const std::string& path) : id_cache(path), path(path) { IndexFile::IndexFile(const std::string& path) : id_cache(path), path(path) {
// TODO: Reconsider if we should still be reusing the same id_cache. // TODO: Reconsider if we should still be reusing the same id_cache.
// Preallocate any existing resolved ids. // Preallocate any existing resolved ids.
@ -156,60 +272,6 @@ bool Contains(const std::vector<T>& vec, const T& element) {
return false; return false;
} }
struct NamespaceHelper {
std::unordered_map<std::string, std::string> container_usr_to_qualified_name;
void RegisterQualifiedName(std::string usr,
const CXIdxContainerInfo* container,
std::string qualified_name) {
if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr);
if (it != container_usr_to_qualified_name.end()) {
container_usr_to_qualified_name[usr] =
it->second + qualified_name + "::";
return;
}
}
container_usr_to_qualified_name[usr] = qualified_name + "::";
}
std::string QualifiedName(const CXIdxContainerInfo* container,
std::string unqualified_name) {
if (container) {
std::string container_usr = clang::Cursor(container->cursor).get_usr();
auto it = container_usr_to_qualified_name.find(container_usr);
if (it != container_usr_to_qualified_name.end())
return it->second + unqualified_name;
// Anonymous namespaces are not processed by indexDeclaration. If we
// encounter one insert it into map.
if (container->cursor.kind == CXCursor_Namespace) {
// assert(clang::Cursor(container->cursor).get_spelling() == "");
container_usr_to_qualified_name[container_usr] = "::";
return "::" + unqualified_name;
}
}
return unqualified_name;
}
};
struct IndexParam {
// Only use this when strictly needed (ie, primary translation unit is
// needed). Most logic should get the IndexFile instance via
// |file_consumer|.
//
// This can be null if we're not generating an index for the primary
// translation unit.
IndexFile* primary_file;
FileConsumer* file_consumer;
NamespaceHelper ns;
IndexParam(FileConsumer* file_consumer) : file_consumer(file_consumer) {}
};
int abortQuery(CXClientData client_data, void* reserved) { int abortQuery(CXClientData client_data, void* reserved) {
// 0 -> continue // 0 -> continue
return 0; return 0;
@ -232,15 +294,11 @@ void diagnostic(CXClientData client_data,
CXFile file; CXFile file;
unsigned int line, column; unsigned int line, column;
clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr); clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr);
bool is_first_time_visiting_file = false; IndexFile* db = ConsumeFile(param, file);
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
if (!db) if (!db)
continue; continue;
if (is_first_time_visiting_file && param->primary_file)
param->primary_file->dependencies.push_back(db->path);
// Build diagnostic. // Build diagnostic.
optional<lsDiagnostic> ls_diagnostic = BuildDiagnostic(diagnostic); optional<lsDiagnostic> ls_diagnostic = BuildDiagnostic(diagnostic);
if (ls_diagnostic) if (ls_diagnostic)
db->diagnostics.push_back(*ls_diagnostic); db->diagnostics.push_back(*ls_diagnostic);
@ -707,14 +765,10 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
CXFile file; CXFile file;
clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(decl->loc), &file, nullptr, nullptr, nullptr); clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(decl->loc), &file, nullptr, nullptr, nullptr);
IndexParam* param = static_cast<IndexParam*>(client_data); IndexParam* param = static_cast<IndexParam*>(client_data);
bool is_first_time_visiting_file = false; IndexFile* db = ConsumeFile(param, file);
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
if (!db) if (!db)
return; return;
if (is_first_time_visiting_file && param->primary_file)
param->primary_file->dependencies.push_back(db->path);
NamespaceHelper* ns = &param->ns; NamespaceHelper* ns = &param->ns;
@ -1165,14 +1219,10 @@ void indexEntityReference(CXClientData client_data,
CXFile file; CXFile file;
clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(ref->loc), &file, nullptr, nullptr, nullptr); clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(ref->loc), &file, nullptr, nullptr, nullptr);
IndexParam* param = static_cast<IndexParam*>(client_data); IndexParam* param = static_cast<IndexParam*>(client_data);
bool is_first_time_visiting_file = false; IndexFile* db = ConsumeFile(param, file);
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
if (!db) if (!db)
return; return;
if (is_first_time_visiting_file && param->primary_file)
param->primary_file->dependencies.push_back(db->path);
// ref->cursor mainFile=0 // ref->cursor mainFile=0
// ref->loc mainFile=1 // ref->loc mainFile=1
// ref->referencedEntity mainFile=1 // ref->referencedEntity mainFile=1
@ -1369,10 +1419,10 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
CXUnsavedFile unsaved; CXUnsavedFile unsaved;
unsaved.Filename = file_contents_path.c_str(); unsaved.Filename = file_contents_path.c_str();
unsaved.Contents = file_contents->c_str(); unsaved.Contents = file_contents->c_str();
unsaved.Length = file_contents->size(); unsaved.Length = (unsigned long)file_contents->size();
unsaved_files.push_back(unsaved); unsaved_files.push_back(unsaved);
} }
clang::TranslationUnit tu(index, file, args, unsaved_files, CXTranslationUnit_KeepGoing); clang::TranslationUnit tu(index, file, args, unsaved_files, CXTranslationUnit_KeepGoing | CXTranslationUnit_DetailedPreprocessingRecord);
perf->index_parse = timer.ElapsedMicrosecondsAndReset(); perf->index_parse = timer.ElapsedMicrosecondsAndReset();
@ -1387,11 +1437,10 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
}; };
FileConsumer file_consumer(file_consumer_shared); FileConsumer file_consumer(file_consumer_shared);
IndexParam param(&file_consumer); IndexParam param(&tu, &file_consumer);
CXFile cx_file = clang_getFile(tu.cx_tu, file.c_str()); CXFile cx_file = clang_getFile(tu.cx_tu, file.c_str());
bool is_first_ownership; param.primary_file = ConsumeFile(&param, cx_file);
param.primary_file = file_consumer.TryConsumeFile(cx_file, &is_first_ownership);
//std::cerr << "!! [START] Indexing " << file << std::endl; //std::cerr << "!! [START] Indexing " << file << std::endl;
CXIndexAction index_action = clang_IndexAction_create(index.cx_index); CXIndexAction index_action = clang_IndexAction_create(index.cx_index);
@ -1405,13 +1454,6 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
auto result = param.file_consumer->TakeLocalState(); auto result = param.file_consumer->TakeLocalState();
for (auto& entry : result) { for (auto& entry : result) {
// TODO: only store the path on one of these.
// TODO: These NormalizePath call should be not needed.
assert(entry->path == NormalizePath(entry->path));
assert(entry->id_cache.primary_file == entry->path);
entry->path = NormalizePath(entry->path);
entry->id_cache.primary_file = entry->path;
entry->last_modification_time = GetLastModificationTime(entry->path); entry->last_modification_time = GetLastModificationTime(entry->path);
entry->import_file = file; entry->import_file = file;
entry->args = args; entry->args = args;

View File

@ -509,6 +509,9 @@ struct IndexFile {
// Diagnostics found when indexing the file. This is not saved. // Diagnostics found when indexing the file. This is not saved.
NonElidedVector<lsDiagnostic> diagnostics; NonElidedVector<lsDiagnostic> diagnostics;
// Source ranges that were not processed.
std::vector<Range> skipped_by_preprocessor;
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
std::vector<IndexType> types; std::vector<IndexType> types;
std::vector<IndexFunc> funcs; std::vector<IndexFunc> funcs;

View File

@ -47,6 +47,9 @@ const char* IpcIdToString(IpcId id) {
case IpcId::WorkspaceSymbol: case IpcId::WorkspaceSymbol:
return "workspace/symbol"; return "workspace/symbol";
case IpcId::CqueryPublishInactiveRegions:
return "$cquery/publishInactiveRegions";
case IpcId::CqueryFreshenIndex: case IpcId::CqueryFreshenIndex:
return "$cquery/freshenIndex"; return "$cquery/freshenIndex";

View File

@ -29,6 +29,9 @@ enum class IpcId : int {
CodeLensResolve, CodeLensResolve,
WorkspaceSymbol, WorkspaceSymbol,
// Custom notifications
CqueryPublishInactiveRegions,
// Custom messages // Custom messages
CqueryFreshenIndex, CqueryFreshenIndex,

View File

@ -1575,6 +1575,18 @@ void Reflect(TVisitor& visitor, Out_ShowLogMessage& value) {
} }
struct Out_CquerySetInactiveRegion : public lsOutMessage<Out_CquerySetInactiveRegion> {
struct Params {
lsDocumentUri uri;
NonElidedVector<lsRange> inactiveRegions;
};
std::string method = "$cquery/setInactiveRegions";
Params params;
};
MAKE_REFLECT_STRUCT(Out_CquerySetInactiveRegion::Params, uri, inactiveRegions);
MAKE_REFLECT_STRUCT(Out_CquerySetInactiveRegion, jsonrpc, method, params);
struct Ipc_CqueryFreshenIndex : public IpcMessage<Ipc_CqueryFreshenIndex> { struct Ipc_CqueryFreshenIndex : public IpcMessage<Ipc_CqueryFreshenIndex> {
const static IpcId kIpcId = IpcId::CqueryFreshenIndex; const static IpcId kIpcId = IpcId::CqueryFreshenIndex;
lsRequestId id; lsRequestId id;

View File

@ -199,6 +199,7 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
REFLECT_MEMBER(args); REFLECT_MEMBER(args);
} }
REFLECT_MEMBER(dependencies); REFLECT_MEMBER(dependencies);
REFLECT_MEMBER(skipped_by_preprocessor);
REFLECT_MEMBER(types); REFLECT_MEMBER(types);
REFLECT_MEMBER(funcs); REFLECT_MEMBER(funcs);
REFLECT_MEMBER(vars); REFLECT_MEMBER(vars);

View File

@ -0,0 +1,21 @@
#ifdef FOOBAR
void hello();
#endif
#if false
#endif
#if defined(OS_FOO)
#endif
/*
OUTPUT:
{
"skipped_by_preprocessor": ["2:1-4:7", "6:1-10:7", "12:1-14:7"]
}
*/

View File

@ -30,6 +30,7 @@ VarDecl b
/* /*
OUTPUT: OUTPUT:
{ {
"skipped_by_preprocessor": ["12:1-28:7"],
"types": [{ "types": [{
"id": 0, "id": 0,
"usr": "c:@E@A", "usr": "c:@E@A",

View File

@ -30,6 +30,7 @@ UnexposedDecl var
/* /*
OUTPUT: OUTPUT:
{ {
"skipped_by_preprocessor": ["12:1-28:7"],
"types": [{ "types": [{
"id": 0, "id": 0,
"usr": "c:@E@A", "usr": "c:@E@A",

View File

@ -81,6 +81,7 @@ unique_ptr<S1, S2>* Foo::foo() { return nullptr; }
/* /*
OUTPUT: OUTPUT:
{ {
"skipped_by_preprocessor": ["7:1-14:7", "17:1-32:7", "35:1-39:7", "42:1-52:7", "57:1-63:7", "68:1-78:7"],
"types": [{ "types": [{
"id": 0, "id": 0,
"usr": "c:@ST>2#T#T@unique_ptr", "usr": "c:@ST>2#T#T@unique_ptr",