mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 09:05:10 +00:00
Darken/fade code disabled by preprocessor.
This commit is contained in:
parent
f3d00dea23
commit
b79b98f464
@ -27,6 +27,7 @@ be productive with cquery. Here's a list of implemented features:
|
||||
* hover
|
||||
* diagnostics
|
||||
* code actions (clang FixIts)
|
||||
* darken/fade code disabled by preprocessor
|
||||
|
||||
# Setup - build cquery, install extension, setup project
|
||||
|
||||
|
@ -65,8 +65,6 @@ optional<lsDiagnostic> BuildDiagnostic(CXDiagnostic diagnostic) {
|
||||
|
||||
// Report fixits
|
||||
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) {
|
||||
CXSourceRange replacement_range;
|
||||
CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range);
|
||||
|
@ -167,6 +167,7 @@ IpcManager* IpcManager::instance_ = nullptr;
|
||||
bool ShouldDisplayIpcTiming(IpcId id) {
|
||||
switch (id) {
|
||||
case IpcId::TextDocumentPublishDiagnostics:
|
||||
case IpcId::CqueryPublishInactiveRegions:
|
||||
return false;
|
||||
default:
|
||||
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
|
||||
// with, in which case vscode will get restarted.
|
||||
if (is_interactive || !new_index->diagnostics.empty()) {
|
||||
// Emit diagnostics.
|
||||
Out_TextDocumentPublishDiagnostics diag;
|
||||
diag.params.uri = lsDocumentUri::FromPath(new_index->path);
|
||||
diag.params.diagnostics = new_index->diagnostics;
|
||||
@ -1111,8 +1123,20 @@ void ParseFile(IndexerConfig* config,
|
||||
|
||||
// Cache diagnostics so we can show fixits.
|
||||
WorkingFile* working_file = working_files->GetFileByFilename(new_index->path);
|
||||
if (working_file)
|
||||
if (working_file) {
|
||||
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);
|
||||
else
|
||||
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;
|
||||
}
|
||||
@ -2093,7 +2122,6 @@ bool QueryDbMainLoop(
|
||||
}
|
||||
}
|
||||
|
||||
response.Write(std::cerr);
|
||||
ipc->SendOutMessageToClient(IpcId::TextDocumentCodeAction, response);
|
||||
break;
|
||||
}
|
||||
|
204
src/indexer.cc
204
src/indexer.cc
@ -47,9 +47,125 @@ Range ResolveExtent(const CXCursor& cx_cursor) {
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
IndexFile::IndexFile(const std::string& path) : id_cache(path), path(path) {
|
||||
// TODO: Reconsider if we should still be reusing the same id_cache.
|
||||
// Preallocate any existing resolved ids.
|
||||
@ -156,60 +272,6 @@ bool Contains(const std::vector<T>& vec, const T& element) {
|
||||
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) {
|
||||
// 0 -> continue
|
||||
return 0;
|
||||
@ -232,15 +294,11 @@ void diagnostic(CXClientData client_data,
|
||||
CXFile file;
|
||||
unsigned int line, column;
|
||||
clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr);
|
||||
bool is_first_time_visiting_file = false;
|
||||
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
|
||||
IndexFile* db = ConsumeFile(param, file);
|
||||
if (!db)
|
||||
continue;
|
||||
if (is_first_time_visiting_file && param->primary_file)
|
||||
param->primary_file->dependencies.push_back(db->path);
|
||||
|
||||
// Build diagnostic.
|
||||
|
||||
optional<lsDiagnostic> ls_diagnostic = BuildDiagnostic(diagnostic);
|
||||
if (ls_diagnostic)
|
||||
db->diagnostics.push_back(*ls_diagnostic);
|
||||
@ -707,14 +765,10 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
|
||||
CXFile file;
|
||||
clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(decl->loc), &file, nullptr, nullptr, nullptr);
|
||||
IndexParam* param = static_cast<IndexParam*>(client_data);
|
||||
bool is_first_time_visiting_file = false;
|
||||
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
|
||||
IndexFile* db = ConsumeFile(param, file);
|
||||
if (!db)
|
||||
return;
|
||||
|
||||
if (is_first_time_visiting_file && param->primary_file)
|
||||
param->primary_file->dependencies.push_back(db->path);
|
||||
|
||||
NamespaceHelper* ns = ¶m->ns;
|
||||
|
||||
|
||||
@ -1165,14 +1219,10 @@ void indexEntityReference(CXClientData client_data,
|
||||
CXFile file;
|
||||
clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(ref->loc), &file, nullptr, nullptr, nullptr);
|
||||
IndexParam* param = static_cast<IndexParam*>(client_data);
|
||||
bool is_first_time_visiting_file = false;
|
||||
IndexFile* db = param->file_consumer->TryConsumeFile(file, &is_first_time_visiting_file);
|
||||
IndexFile* db = ConsumeFile(param, file);
|
||||
if (!db)
|
||||
return;
|
||||
|
||||
if (is_first_time_visiting_file && param->primary_file)
|
||||
param->primary_file->dependencies.push_back(db->path);
|
||||
|
||||
// ref->cursor mainFile=0
|
||||
// ref->loc mainFile=1
|
||||
// ref->referencedEntity mainFile=1
|
||||
@ -1369,10 +1419,10 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
|
||||
CXUnsavedFile unsaved;
|
||||
unsaved.Filename = file_contents_path.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);
|
||||
}
|
||||
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();
|
||||
|
||||
@ -1387,11 +1437,10 @@ std::vector<std::unique_ptr<IndexFile>> Parse(
|
||||
};
|
||||
|
||||
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());
|
||||
bool is_first_ownership;
|
||||
param.primary_file = file_consumer.TryConsumeFile(cx_file, &is_first_ownership);
|
||||
param.primary_file = ConsumeFile(¶m, cx_file);
|
||||
|
||||
//std::cerr << "!! [START] Indexing " << file << std::endl;
|
||||
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();
|
||||
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->import_file = file;
|
||||
entry->args = args;
|
||||
|
@ -509,6 +509,9 @@ struct IndexFile {
|
||||
// Diagnostics found when indexing the file. This is not saved.
|
||||
NonElidedVector<lsDiagnostic> diagnostics;
|
||||
|
||||
// Source ranges that were not processed.
|
||||
std::vector<Range> skipped_by_preprocessor;
|
||||
|
||||
std::vector<std::string> dependencies;
|
||||
std::vector<IndexType> types;
|
||||
std::vector<IndexFunc> funcs;
|
||||
|
@ -47,6 +47,9 @@ const char* IpcIdToString(IpcId id) {
|
||||
case IpcId::WorkspaceSymbol:
|
||||
return "workspace/symbol";
|
||||
|
||||
case IpcId::CqueryPublishInactiveRegions:
|
||||
return "$cquery/publishInactiveRegions";
|
||||
|
||||
case IpcId::CqueryFreshenIndex:
|
||||
return "$cquery/freshenIndex";
|
||||
|
||||
|
@ -29,6 +29,9 @@ enum class IpcId : int {
|
||||
CodeLensResolve,
|
||||
WorkspaceSymbol,
|
||||
|
||||
// Custom notifications
|
||||
CqueryPublishInactiveRegions,
|
||||
|
||||
// Custom messages
|
||||
CqueryFreshenIndex,
|
||||
|
||||
|
@ -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> {
|
||||
const static IpcId kIpcId = IpcId::CqueryFreshenIndex;
|
||||
lsRequestId id;
|
||||
|
@ -199,6 +199,7 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
|
||||
REFLECT_MEMBER(args);
|
||||
}
|
||||
REFLECT_MEMBER(dependencies);
|
||||
REFLECT_MEMBER(skipped_by_preprocessor);
|
||||
REFLECT_MEMBER(types);
|
||||
REFLECT_MEMBER(funcs);
|
||||
REFLECT_MEMBER(vars);
|
||||
|
21
tests/preprocessor/skipped.cc
Normal file
21
tests/preprocessor/skipped.cc
Normal 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"]
|
||||
}
|
||||
*/
|
@ -30,6 +30,7 @@ VarDecl b
|
||||
/*
|
||||
OUTPUT:
|
||||
{
|
||||
"skipped_by_preprocessor": ["12:1-28:7"],
|
||||
"types": [{
|
||||
"id": 0,
|
||||
"usr": "c:@E@A",
|
||||
|
@ -30,6 +30,7 @@ UnexposedDecl var
|
||||
/*
|
||||
OUTPUT:
|
||||
{
|
||||
"skipped_by_preprocessor": ["12:1-28:7"],
|
||||
"types": [{
|
||||
"id": 0,
|
||||
"usr": "c:@E@A",
|
||||
|
@ -81,6 +81,7 @@ unique_ptr<S1, S2>* Foo::foo() { return nullptr; }
|
||||
/*
|
||||
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": [{
|
||||
"id": 0,
|
||||
"usr": "c:@ST>2#T#T@unique_ptr",
|
||||
|
Loading…
Reference in New Issue
Block a user